逆向工程递归函数
Reverse Engineering a recursive function
我有一个作业要反转一个函数
汇编器输出是这样的:
0x00000000004010c4 <+0>: sub [=10=]x18,%rsp
0x00000000004010c8 <+4>: lea 0xc(%rsp),%rcx
0x00000000004010cd <+9>: lea 0x8(%rsp),%rdx
0x00000000004010d2 <+14>: mov [=10=]x402995,%esi
0x00000000004010d7 <+19>: mov [=10=]x0,%eax
0x00000000004010dc <+24>: callq 0x400cb0 <__isoc99_sscanf@plt>
0x00000000004010e1 <+29>: cmp [=10=]x2,%eax
0x00000000004010e4 <+32>: jne 0x4010ed <phase_4+41>
0x00000000004010e6 <+34>: cmpl [=10=]xe,0x8(%rsp)
0x00000000004010eb <+39>: jbe 0x4010f2 <phase_4+46>
0x00000000004010ed <+41>: callq 0x401671 <explode_bomb>
0x00000000004010f2 <+46>: mov [=10=]xe,%edx
0x00000000004010f7 <+51>: mov [=10=]x0,%esi
0x00000000004010fc <+56>: mov 0x8(%rsp),%edi
0x0000000000401100 <+60>: callq 0x401086 <func4>
0x0000000000401105 <+65>: cmp [=10=]x3,%eax
0x0000000000401108 <+68>: jne 0x401111 <phase_4+77>
0x000000000040110a <+70>: cmpl [=10=]x3,0xc(%rsp)
0x000000000040110f <+75>: je 0x401116 <phase_4+82>
0x0000000000401111 <+77>: callq 0x401671 <explode_bomb>
0x0000000000401116 <+82>: add [=10=]x18,%rsp
0x000000000040111a <+86>: retq
我对此功能的解决方案如下所示:
我认为 func4 应该 return 3
int phase4(const char* read ) {
int var1, var2;
if ((sscanf(read, "%d %d", &var1, &var2) != 2) || (var1 < 0xe))
explode_bomb();
if (func4(var1, 0, 0xe /*14*/) != 3)
explode_bomb();
if (var2 != 3)
explode_bomb();
return 3;
}
func4 看起来像这样:
0x0000000000401086 <+0>: sub [=12=]x8,%rsp
0x000000000040108a <+4>: mov %edx,%eax
0x000000000040108c <+6>: sub %esi,%eax
0x000000000040108e <+8>: mov %eax,%ecx
0x0000000000401090 <+10>: shr [=12=]x1f,%ecx
0x0000000000401093 <+13>: add %ecx,%eax
0x0000000000401095 <+15>: sar %eax
0x0000000000401097 <+17>: lea (%rax,%rsi,1),%ecx
0x000000000040109a <+20>: cmp %edi,%ecx
0x000000000040109c <+22>: jle 0x4010aa <func4+36>
0x000000000040109e <+24>: lea -0x1(%rcx),%edx
0x00000000004010a1 <+27>: callq 0x401086 <func4>
0x00000000004010a6 <+32>: add %eax,%eax
0x00000000004010a8 <+34>: jmp 0x4010bf <func4+57>
0x00000000004010aa <+36>: mov [=12=]x0,%eax
0x00000000004010af <+41>: cmp %edi,%ecx
0x00000000004010b1 <+43>: jge 0x4010bf <func4+57>
0x00000000004010b3 <+45>: lea 0x1(%rcx),%esi
0x00000000004010b6 <+48>: callq 0x401086 <func4>
0x00000000004010bb <+53>: lea 0x1(%rax,%rax,1),%eax
0x00000000004010bf <+57>: add [=12=]x8,%rsp
0x00000000004010c3 <+61>: retq
我的 C 代码如下所示:
int func4(unsigned rsi, unsigned rdi, unsigned rdx) {
unsigned rax = rdx;
rax -= rsi;
unsigned rcx = rax;
rcx >>= (unsigned)0x1f;
rax += rcx;
rax >>= (signed)1;
rcx = rax + rsi;
if (rcx <= rdi) {
rax = 0;
if (rcx >= rdi)
return rax;
else {
rax = func4(rdi, rsi + 1, rdx);
rax = rax + rax + 1;
}
} else {
rdx = rcx - 1;
rax = func4(rdi, rsi, rdx);
rax = rax + rax;
}
return rax;
}
但是当我尝试从 -512 到 512 的值时,结果我从来没有得到 3;我做错了什么?
编辑:
我找到了如下所示的解决方案:
int func4(int32_t di, int32_t si, int32_t dx) {
int32_t ax = dx;
ax = ax - si;
int32_t cx = ax;
cx = (uint32_t)cx >> (uint32_t)0x1f;
ax = ax + cx;
ax = (int32_t)ax >> (int32_t)1;
cx = ax + si;
if (cx <= di)
goto first;
dx = cx - 1;
ax = func4(di, si, dx);
ax = ax + ax;
goto fin;
first:
ax = 0;
if (cx >= di)
goto fin;
si = cx + 1;
ax = func4(di, si, dx);
ax = ax + ax + 1;
fin:
return ax;
}
快速浏览一下,问题可能出在这里:
rax >>= (signed)1; // sar %eax
这相当于:
rax = rax >> (signed)1;
它执行无符号移位(因为移位运算符的符号由第一个操作数而不是第二个操作数决定)。所以你应该写:
rax = (unsigned)((signed)rax >> 1);
编辑: 同样,您翻译了 jle
和 jge
不正确。这些指令进行有符号比较,而相应的 C 代码进行无符号比较。也解决这个问题:
if ((signed)rcx <= (signed)rdi) {
rax = 0;
if ((signed)rcx >= (signed)rdi)
...
如何从汇编生成 C:
写出程序集。然后声明与寄存器同名的 C 变量,并通过汇编将每个算术或逻辑汇编指令替换为 C 指令,并用 if goto 构造每个分支。如果你有电话,你当然必须知道调用约定。
一旦 C 可用,逐渐让它更像人,在每个点测试它针对程序集(如果有)或类似程序集的 C(如果你不能)的行为 assemble 程序集)。
我有一个作业要反转一个函数
汇编器输出是这样的:
0x00000000004010c4 <+0>: sub [=10=]x18,%rsp
0x00000000004010c8 <+4>: lea 0xc(%rsp),%rcx
0x00000000004010cd <+9>: lea 0x8(%rsp),%rdx
0x00000000004010d2 <+14>: mov [=10=]x402995,%esi
0x00000000004010d7 <+19>: mov [=10=]x0,%eax
0x00000000004010dc <+24>: callq 0x400cb0 <__isoc99_sscanf@plt>
0x00000000004010e1 <+29>: cmp [=10=]x2,%eax
0x00000000004010e4 <+32>: jne 0x4010ed <phase_4+41>
0x00000000004010e6 <+34>: cmpl [=10=]xe,0x8(%rsp)
0x00000000004010eb <+39>: jbe 0x4010f2 <phase_4+46>
0x00000000004010ed <+41>: callq 0x401671 <explode_bomb>
0x00000000004010f2 <+46>: mov [=10=]xe,%edx
0x00000000004010f7 <+51>: mov [=10=]x0,%esi
0x00000000004010fc <+56>: mov 0x8(%rsp),%edi
0x0000000000401100 <+60>: callq 0x401086 <func4>
0x0000000000401105 <+65>: cmp [=10=]x3,%eax
0x0000000000401108 <+68>: jne 0x401111 <phase_4+77>
0x000000000040110a <+70>: cmpl [=10=]x3,0xc(%rsp)
0x000000000040110f <+75>: je 0x401116 <phase_4+82>
0x0000000000401111 <+77>: callq 0x401671 <explode_bomb>
0x0000000000401116 <+82>: add [=10=]x18,%rsp
0x000000000040111a <+86>: retq
我对此功能的解决方案如下所示: 我认为 func4 应该 return 3
int phase4(const char* read ) {
int var1, var2;
if ((sscanf(read, "%d %d", &var1, &var2) != 2) || (var1 < 0xe))
explode_bomb();
if (func4(var1, 0, 0xe /*14*/) != 3)
explode_bomb();
if (var2 != 3)
explode_bomb();
return 3;
}
func4 看起来像这样:
0x0000000000401086 <+0>: sub [=12=]x8,%rsp
0x000000000040108a <+4>: mov %edx,%eax
0x000000000040108c <+6>: sub %esi,%eax
0x000000000040108e <+8>: mov %eax,%ecx
0x0000000000401090 <+10>: shr [=12=]x1f,%ecx
0x0000000000401093 <+13>: add %ecx,%eax
0x0000000000401095 <+15>: sar %eax
0x0000000000401097 <+17>: lea (%rax,%rsi,1),%ecx
0x000000000040109a <+20>: cmp %edi,%ecx
0x000000000040109c <+22>: jle 0x4010aa <func4+36>
0x000000000040109e <+24>: lea -0x1(%rcx),%edx
0x00000000004010a1 <+27>: callq 0x401086 <func4>
0x00000000004010a6 <+32>: add %eax,%eax
0x00000000004010a8 <+34>: jmp 0x4010bf <func4+57>
0x00000000004010aa <+36>: mov [=12=]x0,%eax
0x00000000004010af <+41>: cmp %edi,%ecx
0x00000000004010b1 <+43>: jge 0x4010bf <func4+57>
0x00000000004010b3 <+45>: lea 0x1(%rcx),%esi
0x00000000004010b6 <+48>: callq 0x401086 <func4>
0x00000000004010bb <+53>: lea 0x1(%rax,%rax,1),%eax
0x00000000004010bf <+57>: add [=12=]x8,%rsp
0x00000000004010c3 <+61>: retq
我的 C 代码如下所示:
int func4(unsigned rsi, unsigned rdi, unsigned rdx) {
unsigned rax = rdx;
rax -= rsi;
unsigned rcx = rax;
rcx >>= (unsigned)0x1f;
rax += rcx;
rax >>= (signed)1;
rcx = rax + rsi;
if (rcx <= rdi) {
rax = 0;
if (rcx >= rdi)
return rax;
else {
rax = func4(rdi, rsi + 1, rdx);
rax = rax + rax + 1;
}
} else {
rdx = rcx - 1;
rax = func4(rdi, rsi, rdx);
rax = rax + rax;
}
return rax;
}
但是当我尝试从 -512 到 512 的值时,结果我从来没有得到 3;我做错了什么?
编辑:
我找到了如下所示的解决方案:
int func4(int32_t di, int32_t si, int32_t dx) {
int32_t ax = dx;
ax = ax - si;
int32_t cx = ax;
cx = (uint32_t)cx >> (uint32_t)0x1f;
ax = ax + cx;
ax = (int32_t)ax >> (int32_t)1;
cx = ax + si;
if (cx <= di)
goto first;
dx = cx - 1;
ax = func4(di, si, dx);
ax = ax + ax;
goto fin;
first:
ax = 0;
if (cx >= di)
goto fin;
si = cx + 1;
ax = func4(di, si, dx);
ax = ax + ax + 1;
fin:
return ax;
}
快速浏览一下,问题可能出在这里:
rax >>= (signed)1; // sar %eax
这相当于:
rax = rax >> (signed)1;
它执行无符号移位(因为移位运算符的符号由第一个操作数而不是第二个操作数决定)。所以你应该写:
rax = (unsigned)((signed)rax >> 1);
编辑: 同样,您翻译了 jle
和 jge
不正确。这些指令进行有符号比较,而相应的 C 代码进行无符号比较。也解决这个问题:
if ((signed)rcx <= (signed)rdi) {
rax = 0;
if ((signed)rcx >= (signed)rdi)
...
如何从汇编生成 C:
写出程序集。然后声明与寄存器同名的 C 变量,并通过汇编将每个算术或逻辑汇编指令替换为 C 指令,并用 if goto 构造每个分支。如果你有电话,你当然必须知道调用约定。
一旦 C 可用,逐渐让它更像人,在每个点测试它针对程序集(如果有)或类似程序集的 C(如果你不能)的行为 assemble 程序集)。