这段代码有什么作用,它在 C 中是什么样子的?

逆向工程 艾达 x86
2021-07-07 10:12:49

我正在使用 IDA 反汇编一个文件,其中一个部分包含这个。这是在做什么?在 C 中它会是什么样子?

我相信它将 edx 压入堆栈,并使用 _atoi 将其转换为整数,但是在那之后 eax 中还剩下什么,为什么将它与 5 进行比较?

mov     ecx, [ebp+argv]
mov     edx, [ecx+4]
push    edx             ; char *
call    _atoi
add     esp, 4
mov     [ebp+var_60], eax
cmp     [ebp+var_60], 5
jle     short loc_401167

编辑:得到了一个很好的答案,这里也是另一个很好的答案。https://stackoverflow.com/questions/33535720/what-does-this-code-do-and-what-does-it-look-like-in-c/33535891#33535891

2个回答

如果您使用 ollydbg 并使用调试信息编译此代码,则似乎是未优化的编译 ollydbg 将在下一列中显示源代码

使用的来源

#include <stdio.h>
#include <stdlib.h>
int main (int argc , char* argv[]) {
  if(argc!=2){return -1;}
  signed int foo =0;
  if((foo = atoi(argv[1])) > 5) {goto blah;}
  printf("notok");return 0;
  blah:
  printf("ok");return 1;
}

没有优化编译

cl /Zi /EHsc /nologo /W4 /analyze *.cpp /link /RELEASE

00401000 a>PUSH    EBP                         ; {
00401001   MOV     EBP, ESP
00401003   PUSH    ECX
00401004   CMP     DWORD PTR SS:[EBP+8], 2     ; if(argc!=2){return -1;}
00401008   JE      SHORT atoitest.0040100F
0040100A   OR      EAX, FFFFFFFF
0040100D   JMP     SHORT atoitest.00401055
0040100F   MOV     DWORD PTR SS:[EBP-4], 0     ; signed int foo =0;
00401016   MOV     EAX, DWORD PTR SS:[EBP+C]   ; if((foo = atoi(argv[1])) > 5) {goto blah;}
00401019   MOV     ECX, DWORD PTR DS:[EAX+4]
0040101C   PUSH    ECX
0040101D   CALL    atoitest.atoi
00401022   ADD     ESP, 4
00401025   MOV     DWORD PTR SS:[EBP-4], EAX
00401028   CMP     DWORD PTR SS:[EBP-4], 5
0040102C   JLE     SHORT atoitest.00401032
0040102E   JMP     SHORT atoitest.00401043
00401030   JMP     SHORT atoitest.00401043
00401032   PUSH    atoitest.0041218C           ; printf("notok");return 0;
00401037   CALL    atoitest.printf
0040103C   ADD     ESP, 4
0040103F   XOR     EAX, EAX
00401041   JMP     SHORT atoitest.00401055
00401043   PUSH    atoitest.00412194           ; printf("ok");return 1;
00401048   CALL    atoitest.printf
0040104D   ADD     ESP, 4
00401050   MOV     EAX, 1
00401055   MOV     ESP, EBP                    ; }
00401057   POP     EBP
00401058   RETN

使用 msvc /O1 编译的相同 src 代码会消除所有保存

cl /Zi /O1 /EHsc /nologo /W4 /analyze *.cpp /link /RELEASE

00401000 a>CMP     DWORD PTR SS:[ESP+4], 2     ; {
00401005   JE      SHORT atoitest.0040100B
00401007   OR      EAX, FFFFFFFF
0040100A   RETN                                ; }
0040100B   MOV     EAX, DWORD PTR SS:[ESP+8]   ; if((foo = atoi(argv[1])) > 5) {goto blah;}
0040100F   PUSH    DWORD PTR DS:[EAX+4]
00401012   CALL    atoitest.atoi
00401017   POP     ECX
00401018   CMP     EAX, 5
0040101B   JLE     SHORT atoitest.0040102C
0040101D   PUSH    atoitest.00412194           ; printf("ok");return 1;
00401022   CALL    atoitest.printf
00401027   XOR     EAX, EAX
00401029   INC     EAX
0040102A   POP     ECX
0040102B   RETN                                ; }
0040102C   PUSH    atoitest.0041218C           ; printf("notok");return 0;
00401031   CALL    atoitest.printf
00401036   XOR     EAX, EAX
00401038   POP     ECX
00401039   RETN                                ; }

具有单一退出且没有 goto 的相同代码

#include <stdio.h>
#include <stdlib.h>
int main (int argc , char* argv[]) {
  if(argc==2)  {
    int foo =0;
    if((foo = atoi(argv[1])) > 5) {
      printf("ok");
    } else {
      printf("notok");
    }
  }
  return 0;
}

未经优化的编译

00401000 a>PUSH    EBP                         ; int main (int argc , char* argv[]) {
00401001   MOV     EBP, ESP
00401003   PUSH    ECX
00401004   CMP     DWORD PTR SS:[EBP+8], 2     ; if(argc==2)  {
00401008   JNZ     SHORT atoitest.00401045
0040100A   MOV     DWORD PTR SS:[EBP-4], 0     ; int foo =0;
00401011   MOV     EAX, DWORD PTR SS:[EBP+C]   ; if((foo = atoi(argv[1])) > 5) {
00401014   MOV     ECX, DWORD PTR DS:[EAX+4]
00401017   PUSH    ECX
00401018   CALL    atoitest.atoi
0040101D   ADD     ESP, 4
00401020   MOV     DWORD PTR SS:[EBP-4], EAX
00401023   CMP     DWORD PTR SS:[EBP-4], 5
00401027   JLE     SHORT atoitest.00401038
00401029   PUSH    atoitest.0041218C           ; printf("ok");
0040102E   CALL    atoitest.printf
00401033   ADD     ESP, 4
00401036   JMP     SHORT atoitest.00401045     ; } else {
00401038   PUSH    atoitest.00412190           ; printf("notok");
0040103D   CALL    atoitest.printf
00401042   ADD     ESP, 4
00401045   XOR     EAX, EAX                    ; return 0;
00401047   MOV     ESP, EBP                    ; }
00401049   POP     EBP
0040104A   RETN

优化编译

00401000 a>CMP     DWORD PTR SS:[ESP+4], 2     ; int main (int argc , char* argv[]) {
00401005   JNZ     SHORT atoitest.0040102B
00401007   MOV     EAX, DWORD PTR SS:[ESP+8]   ; if((foo = atoi(argv[1])) > 5) {
0040100B   PUSH    DWORD PTR DS:[EAX+4]
0040100E   CALL    atoitest.atoi
00401013   POP     ECX
00401014   CMP     EAX, 5
00401017   JLE     SHORT atoitest.00401020
00401019   PUSH    atoitest.00412194           ; printf("ok");
0040101E   JMP     SHORT atoitest.00401025     ; } else {
00401020   PUSH    atoitest.0041218C           ; printf("notok");
00401025   CALL    atoitest.printf
0040102A   POP     ECX
0040102B   XOR     EAX, EAX                    ; return 0;
0040102D   RETN                                ; }

调用之前的部分atoi获取argv[1]堆栈上的内容

  • mov ecx, [ebp+argv]得到argvecx
  • mov edx, [ecx+4]得到argv[1]edx-音符argv是一个指针数组,大小各4个字节,所以[ecx+4]是元素中的“第一”(从0计数)
  • push edx将它作为 的参数放在堆栈上atoi

C 函数在eax* 中返回它们的结果,因此该mov [ebp+var_60], eax指令将 的结果保存atoi(argv[1])到局部变量中。

我们不知道为什么将这个结果与 进行比较5,这是应用程序逻辑的一部分。如果该应用程序是一个crackme,并且挑战的一部分是“找出将哪个数字作为命令行参数提供以使程序做一些特殊的事情”,那么5该特殊数字将是一个很好的候选者。

(*) 在大多数情况下,除非它们不适合那里,例如在双打或结构的情况下。