HarmonyOS和HMS专场CTF Risc-V Pwn题解

上周末抽空打了一下HarmonyOS和HMS专场CTF,做了两个Risc-V的Pwn题目。

Risc-V分析方法

静态和动态分析

计算机指令集可以分为两种:复杂指令集和精简指令集。
复杂指令集以x86指令集最为常见,多用于传统桌面软件,善于处理复杂的计算逻辑。精简指令集有ARM、MIPS和Risc-V等。ARM广泛应用于移动手持终端以及IoT设备,但是ARM指令集虽然开放但是授权架价格太高,而Risc-V是一套开源的精简指令集架构,企业可以完全免费的使用。
目前来讲,现有的工具链已经足以支持Risc-V的逆向分析。
在静态分析层面,Ghidra 9.2对于Risc-V的反编译效果不错(IDA 7.5尚不支持Risc-V),所以静态分析Risc-V用ghidra已经足够。
在动态调试层面,qemu已经集成了risc-v架构,可以支持该架构的模拟运行。在有lib的情况下,通过QEMU用户模式,添加-L参数选择lib路径,通过-g指定调试端口。在gdb高版本中,已经可以支持risv架构,同时配合gef插件,设置target remote连接QEMU的调试端口:

可以对risc-v进行正常的调试,包括下断点,查看内存等常见功能。

解决了工具链的问题,在逆向层面来讲,已经大大的降低了risc-v分析的门槛。为了更好的进行漏洞挖掘与利用,需要稍微学习一下risc-v的函数调用规则和指令集。

Risc-V函数调用约定

Risc-V函数调用也是基于寄存器和栈的,每次函数调用时优先利用寄存器传参,如果寄存器无法满足需求再利用栈传参。Risc-V寄存器种类和数量如下表所示:

在参数保存之后,通过jal指令跳转到函数开始执行。jal指令的规范为:

jal ra, offset

将会把下一条指令(pc+4)地址存放到ra寄存器中,然后跳转到当前地址+offset位置开始执行。
在子函数中,将会把ra寄存器存放到栈上,在函数返回时从栈上恢复ra寄存器,这里也就存在栈溢出的机会
我们以下面这段代码作为demo:

#include <stdio.h>

int add1(int m ,int n)
{
    return m + n ;
}

int add2(int m ,int n)
{
    char ss[10] = "hello";
    printf("%s", ss);
    return m + n ;
}

int main(){
    int m = 2;
    int n = 3;
    int sum1 = add1(m , n);
    printf("%d\n", sum1);

    int sum2 = add2(m , n);
    printf("%d\n", sum2);
    return 0;
}

编译后看一下反汇编:

将参数mv到a0和a1寄存器上后,跳转执行add1函数:

此时add1函数并没有调用子函数,即为叶子函数,此时并不需要从栈中恢复ra寄存器。
而在add2中,在函数开始位置将ra寄存器存放到栈上:

在函数结束后从栈上恢复ra寄存器:

harmoshell

这是一个简单的shell,可以支持touchlsrm等命令:

echo里面有一个栈溢出

点击收藏 | 1 关注 | 1
  • 动动手指,沙发就是你的了!
登录 后跟帖