一、目标

一、目标

李老板: 奋飞呀,AI都把代码还原的活给干了,咱们这个代码还原的教程还要写吗?

奋飞: 当然要写了,AI是AI,那是批量化流水线操作,能有我这种纯手工还原的代码帅?我这可是有温度的代码。总不能有了翻译软件,你就不学英语了吧?当然日语可以不用学了。

有个名人说过:你永远赚不到超出你认知范围外的钱。所以奋飞说:你无法指挥AI干超出你认知范围外的活。

二、步骤

2、条件断点 3、数据打印

我们要想知道程序运行到 0x1170 时 x4=0xdd89ca68 的来历,那就需要从前面的代码开始Trace,

往上找了找 在 0x1144 有个判断,说明前面经过了若干次循环。

.text:0000000000001144 7F 01 08 EB                 CMP             X11, X8
.text:0000000000001148 88 CC FF 54                 B.HI            loc_AD8

为了分析起来方便,我只想在最后一次循环的时候Trace代码来分析,这样就需要条件断点了。

在做条件断点之前,我们先把 X11 和 X8 的值打印出来。

debugger.addBreakPoint(module.base + 0x1144, new BreakPointCallback() {
    @Override
    public boolean onHit(Emulator<?> emulator, long address) {
        Arm64RegisterContext ctx = emulator.getContext();
        int iX11 = ctx.getXInt(11);
        int iX8 = ctx.getXInt(8);;

        System.out.printf("X11 = %d , X8 = %d\n", iX11,iX8);

        return true;
    }
});

跑一下,

X11 = 64 , X8 = 64

奇怪只打印了一次,那说明这里不是循环,或者是个假循环。

那就继续往上找吧

text:0000000000000C8C 8C 81 00 91                 ADD             X12, X12, #0x20 ; ' '
.text:0000000000000C90             ; 156:       while ( v24 < 0x10 );
.text:0000000000000C90 03 FA FF 54                 B.CC            loc_BD0
.text:0000000000000C94 FE 17 50 29                 LDP             W30, W5, [SP,#0x110+var_90]
.text:0000000000000C98 11 03 19 4A                 EOR             W17, W24, W25

感觉这个 0xC94 应该是最后一次计算的位置了。 (我们可以先在 0xC94 下一个断点,如果只触发一次说明可用,如果触发多次,说明它在循环体里面。)

5、Trace代码

Unidbg Trace代码有两种方式,一种是直接写代码来实现, 把Trace结果存入traceCode1.log

try {
  // Trace 的范围是 0xC94 到 0x1170
    emulator.traceCode(module.base+0xC94, module.base+0x1170).setRedirect(new PrintStream(new File("traceCode1.log")));
} catch (IOException e) {
    throw new IllegalStateException(e);
}

另一种是在调试命令行输入Trace命令。

先在 0xC94下断点,进入调试窗口,输入 traceCode 命令然后直接回车即可。

traceCode
Set trace LinuxModule{base=0x40000000, size=12288, name='libnative-lib.so'} instructions success.

如果要把Trace结果保存到文件,就加上文件名 traceCode traceCode1.log

然后 c 命令继续执行,Trace结果就出来了。

开始还原算法

在Trace结果里面搜索 0xdd89ca68 , 发现他是通过 0x68ca89dd 翻转来的

0x40001154: "rev w4, w22" w22=0x68ca89dd => w4=0xdd89ca68

这里遇到不熟悉的指令可以很自然的咨询AI了

ai1
1:ai1

我们先把 0x68ca89dd 相关的结果挑出来,原则就是 从结果向入参回溯。

0x40001048: "add w12, w12, w15" w12=0x47448b48 w15=0x93f6f2bb => w12=0xdb3b7e03

0x40001098: "orn w12, w12, w11" w12=0xdb3b7e03 w11=0x49ac16b => w12=0xfb7f7e97
0x4000109c: "eor w12, w12, w16" w12=0xfb7f7e97 w16=0xa96ba62 => w12=0xf1e9c4f5
0x400010a0: "add w12, w12, w15" w12=0xf1e9c4f5 w15=0x93f6f2bb => w12=0x85e0b7b0
0x400010b0: "add w12, w12, w15" w12=0x85e0b7b0 w15=0x4212f2e5 => w12=0xc7f3aa95

0x4000107c: "add w11, w11, w17" w11=0xffa26fc9 w17=0x2cc489bc => w11=0x2c66f985
0x40001088: "add w11, w11, w17" w11=0x2c66f985 w17=0xf3d1564b => w11=0x20384fd0
0x4000108c: "ror w11, w11, #0xb" w11=0x20384fd0 => w11=0xfa040709

0x400010b4: "ror w12, w12, #0x1a" w12=0xc7f3aa95 => w12=0xfceaa571
0x40001090: "add w11, w11, w16" w11=0xfa040709 w16=0xa96ba62 => w11=0x49ac16b

0x400010b8: "add w12, w12, w11" w12=0xfceaa571 w11=0x49ac16b => w12=0x18566dc

0x40001048: "add w12, w12, w15" w12=0x47448b48 w15=0x93f6f2bb => w12=0xdb3b7e03

0x40001098: "orn w12, w12, w11" w12=0xdb3b7e03 w11=0x49ac16b => w12=0xfb7f7e97
0x4000109c: "eor w12, w12, w16" w12=0xfb7f7e97 w16=0xa96ba62 => w12=0xf1e9c4f5
0x400010a0: "add w12, w12, w15" w12=0xf1e9c4f5 w15=0x93f6f2bb => w12=0x85e0b7b0
0x400010b0: "add w12, w12, w15" w12=0x85e0b7b0 w15=0x4212f2e5 => w12=0xc7f3aa95

0x4000107c: "add w11, w11, w17" w11=0xffa26fc9 w17=0x2cc489bc => w11=0x2c66f985
0x40001088: "add w11, w11, w17" w11=0x2c66f985 w17=0xf3d1564b => w11=0x20384fd0
0x4000108c: "ror w11, w11, #0xb" w11=0x20384fd0 => w11=0xfa040709

0x400010b4: "ror w12, w12, #0x1a" w12=0xc7f3aa95 => w12=0xfceaa571
0x40001090: "add w11, w11, w16" w11=0xfa040709 w16=0xa96ba62 => w11=0x49ac16b

0x400010b8: "add w12, w12, w11" w12=0xfceaa571 w11=0x49ac16b => w12=0x18566dc

0x40001108: "add w22, w12, w22" w12=0x18566dc w22=0x67452301 => w22=0x68ca89dd

然后打开 VSCode开始写代码

int w12,w11,w15;

w12 = w12 | ~w11          // 0x40001098: "orn w12, w12, w11" w12=0xdb3b7e03 w11=0x49ac16b => w12=0xfb7f7e97
w12 = w12 ^ w16                 // 0x4000109c: "eor w12, w12, w16" w12=0xfb7f7e97 w16=0xa96ba62 => w12=0xf1e9c4f5

// 上面这两步 其实就是
// w12 = (w12 | ~w11) ^ w16

w12 = w12 + w15                                // 0x400010a0: "add w12, w12, w15" w12=0xf1e9c4f5 w15=0x93f6f2bb => w12=0x85e0b7b0
w12 = w12 + Num_w15()        // 0x400010b0: "add w12, w12, w15" w12=0x85e0b7b0 w15=0x4212f2e5 => w12=0xc7f3aa95
w12 = w12 >> 0x1a;        // 0x400010b4: "ror w12, w12, #0x1a" w12=0xc7f3aa95 => w12=0xfceaa571
w12 = w12 + w11;                // 0x400010b8: "add w12, w12, w11" w12=0xfceaa571 w11=0x49ac16b => w12=0x18566dc

是的,还原算法就是这么的朴实无华而又枯燥无味。

w12 = (w12 | ~w11) ^ w16 这个算式让我们有点兴奋

他和标准MD5 算法的 I 非常相似。

//F,G,H,I四个非线性变换函数
#define F(x,y,z) ((x & y) | (~x & z))
#define G(x,y,z) ((x & z) | (y & ~z))
#define H(x,y,z) (x^y^z)
#define I(x,y,z) (y ^ (x | ~z))

这样的话可能我们就可以轻松一点,在标准MD5算法的框架上去还原,只找出里面魔改的部分。

忍不住用AI了

把这个枯燥无味的过程扔给 Cursor 会怎样

用C语言还原w12的计算过程,添加中文注释

0x40001048: "add w12, w12, w15" w12=0x47448b48 w15=0x93f6f2bb => w12=0xdb3b7e03

0x40001098: "orn w12, w12, w11" w12=0xdb3b7e03 w11=0x49ac16b => w12=0xfb7f7e97
0x4000109c: "eor w12, w12, w16" w12=0xfb7f7e97 w16=0xa96ba62 => w12=0xf1e9c4f5
0x400010a0: "add w12, w12, w15" w12=0xf1e9c4f5 w15=0x93f6f2bb => w12=0x85e0b7b0
0x400010b0: "add w12, w12, w15" w12=0x85e0b7b0 w15=0x4212f2e5 => w12=0xc7f3aa95

0x4000107c: "add w11, w11, w17" w11=0xffa26fc9 w17=0x2cc489bc => w11=0x2c66f985
0x40001088: "add w11, w11, w17" w11=0x2c66f985 w17=0xf3d1564b => w11=0x20384fd0
0x4000108c: "ror w11, w11, #0xb" w11=0x20384fd0 => w11=0xfa040709

0x400010b4: "ror w12, w12, #0x1a" w12=0xc7f3aa95 => w12=0xfceaa571
0x40001090: "add w11, w11, w16" w11=0xfa040709 w16=0xa96ba62 => w11=0x49ac16b

0x400010b8: "add w12, w12, w11" w12=0xfceaa571 w11=0x49ac16b => w12=0x18566dc

#include <stdio.h>

int main() {
    unsigned int w12 = 0x47448b48;
    unsigned int w15 = 0x93f6f2bb;
    unsigned int w11 = 0xffa26fc9;
    unsigned int w17 = 0x2cc489bc;
    unsigned int w16 = 0xa96ba62;

    w12 = w12 + w15;
    printf("w12 = %x\n", w12);

    w12 = (w12 | ~w11);
    printf("w12 = %x\n", w12);

    w12 = w12 ^ w16;
    printf("w12 = %x\n", w12);

    w12 = w12 + w15;
    printf("w12 = %x\n", w12);

    w12 = w12 + 0x4212f2e5;
    printf("w12 = %x\n", w12);

    w11 = w11 + w17;
    printf("w11 = %x\n", w11);

    w11 = w11 + 0xf3d1564b;
    printf("w11 = %x\n", w11);

    w11 = (w11 >> 11) | (w11 << (32 - 11));
    printf("w11 = %x\n", w11);

    w12 = (w12 >> 26) | (w12 << (32 - 26));
    printf("w12 = %x\n", w12);

    w11 = w11 + w16;
    printf("w11 = %x\n", w11);

    w12 = w12 + w11;
    printf("w12 = %x\n", w12);

    return 0;
}

感觉比我写的好, 程序员要进化出一个新品种了,就是给AI写 prompt

三、总结

以前觉得当leader很幸福,啥活都不用干,直接指挥小弟干活就行。

后来真当了leader才发现,我去,你得A语言到Z语言都会,不然做php的小弟说这玩意实现不了,做Flash的小弟说这个功能得做一个月。

AI也是如此,机械重复性的工作它比你做的好,创意和指路的活还得你自己干。

ffshow
1:ffshow

决定战争胜负的是人,而不是一两件新式武器。这一论断永远都不过时

100

关注微信公众号,最新技术干货实时推送

100