scanf 返回值

逆向工程 拆卸 部件 x86
2021-06-22 09:45:33

查看这段代码,我无法弄清楚scanf()函数的返回值存储在哪里

编辑

从手册页scanf()返回一个int值表示成功匹配和分配的输入项的数量。

我期待0x04EAX寄存器(用户输入= AAAA),但是从返回后scanf()EAX = 0x00000000

       0x080483e4      55             push ebp
       0x080483e5      89e5           mov ebp, esp
       0x080483e7      83ec18         sub esp, 0x18
       0x080483ea      83e4f0         and esp, 0xfffffff0
       0x080483ed      b800000000     mov eax, 0
       0x080483f2      83c00f         add eax, 0xf
       0x080483f5      83c00f         add eax, 0xf
       0x080483f8      c1e804         shr eax, 4
       0x080483fb      c1e004         shl eax, 4
       0x080483fe      29c4           sub esp, eax
       0x08048400      c70424488504.  mov dword [esp], str.IOLI_Crackme_Level_0x02 ; [0x8048548:4]=0x494c4f49 ; "IOLI Crackme Level 0x02\n"
       0x08048407      e810ffffff     call sym.imp.printf         ; int printf(const char *format)
       0x0804840c      c70424618504.  mov dword [esp], str.Password: ; [0x8048561:4]=0x73736150 ; "Password: " ; const char * format
       0x08048413      e804ffffff     call sym.imp.printf         ; int printf(const char *format)
       0x08048418      8d45fc         lea eax, ebp - 4
       0x0804841b      89442404       mov dword [local_4h_2], eax
       0x0804841f      c704246c8504.  mov dword [esp], 0x804856c  ; [0x804856c:4]=0x50006425 ; const char * format
       ;-- eip:
       0x08048426 b    e8e1feffff     call sym.imp.scanf          ; int scanf(const char *format)
       0x0804842b      c745f85a0000.  mov dword [local_8h], 0x5a  ; 'Z' ; 90
       0x08048432      c745f4ec0100.  mov dword [local_ch], 0x1ec ; 492
       0x08048439      8b55f4         mov edx, dword [local_ch]
       0x0804843c      8d45f8         lea eax, ebp - 8
       0x0804843f      0110           add dword [eax], edx
       0x08048441      8b45f8         mov eax, dword [local_8h]
       0x08048444      0faf45f8       imul eax, dword [local_8h]
       0x08048448      8945f4         mov dword [local_ch], eax
       0x0804844b      8b45fc         mov eax, dword [local_4h]
       0x0804844e      3b45f4         cmp eax, dword [local_ch]
   ┌─< 0x08048451      750e           jne 0x8048461
   │   0x08048453      c704246f8504.  mov dword [esp], str.Password_OK_: ; [0x804856f:4]=0x73736150 ; "Password OK :)\n" ; const char * format
   │   0x0804845a      e8bdfeffff     call sym.imp.printf         ; int printf(const char *format)
  ┌──< 0x0804845f      eb0c           jmp 0x804846d
  │└─> 0x08048461      c704247f8504.  mov dword [esp], str.Invalid_Password ; [0x804857f:4]=0x61766e49 ; "Invalid Password!\n" ; const char * format
  │    0x08048468      e8affeffff     call sym.imp.printf         ; int printf(const char *format)
  │       ; JMP XREF from 0x0804845f (main)
  └──> 0x0804846d      b800000000     mov eax, 0
       0x08048472      c9             leave
       0x08048473      c3             ret

此外,我找不到对用户输入的任何引用(来自上一次scanf()调用)。我认为它是local_4h因为它与0x00052b24存储在local_ch.

 var local_4h = 0xffa46fd4  0xf7f66000  ... @eax edi
 var local_8h = 0xffa46fd0  0x00000001  .... esi
 var local_ch = 0xffa46fcc  0x00000000  .... ecx

但是,我无法理解如何。

有关更多信息,这是scanf()调用前的堆栈

- offset -   0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0xffa46fb0  6c85 0408 d46f a4ff 0000 0000 fbb4 ddf7  l....o..........
0xffa46fc0  dc63 f6f7 f481 0408 0c9f 0408 0000 0000  .c..............
0xffa46fd0  0100 0000 0060 f6f7 0000 0000 5644 dcf7  .....`......VD..

并注册:

 eax = 0xffa46fd4
 ebx = 0x00000000
 ecx = 0x00000000
 edx = 0xf7f67870
 esi = 0x00000001
 edi = 0xf7f66000
 esp = 0xffa46fb0
 ebp = 0xffa46fd8
 eip = 0x08048426
 eflags = 0x00000286
 oeax = 0xffffffff
2个回答

根据 man scanf

成功时,这些函数返回成功匹配和分配的输入项数;在早期匹配失败的情况下,这可能比规定的要少,甚至为零。

如果在第一次成功转换或匹配失败之前到达输入末尾,则返回值 EOF。如果发生读取错误,也会返回 EOF,在这种情况下,将设置流的错误指示符(请参阅 ferror(3)),并设置 errno 以指示错误。

同样在 x86 中的函数期间,堆栈看起来像这样

► 0x80484f9 <main+62>    call   __isoc99_scanf@plt            <0x80483a0>
    format: 0x80485e0 ◂— 0x25006425 /* '%d' */
    vararg: 0xffffcfe8 —▸ 0xffffd0ac —▸ 0xffffd264 ◂— 0x5f474458 ('XDG_')
[──────────────────────────STACK──────────────────────────]
00:0000│ esp  0xffffcfc0 —▸ 0x80485e0 ◂— and    eax, 0x64250064 /* '%d' */
01:0004│      0xffffcfc4 —▸ 0xffffcfe8 —▸ 0xffffd0ac —▸ 0xffffd264 ◂— ...
02:0008│      0xffffcfc8 —▸ 0xf7f9f244 —▸ 0xf7e07020 (_IO_check_libio) ◂—  call   0xf7f0eb09

堆栈的顶部是第一个参数,format后面是对变量的引用,这些变量将存储与格式字符串匹配的值。在您的情况下mov dword [esp], 0x804856c,将格式字符串推送到堆栈顶部esp在此代码段上方的某处,您必须mov dword [esp+4], stack_address_ref添加将保存解析值的变量。

感谢sudhackar评论,我能够理解scanf()

mov dword [esp], 0x804856c

将格式字符串压入堆栈:

:> px @ 0x804856c
- offset -   0 1  2 3  4 5  6 7  8 9  A B  C D  0123456789ABCD
0x0804856c  2564 0050 6173 7377 6f72 6420 4f4b  %d.Password OK
0x0804857a  203a 290a 0049 6e76 616c 6964 2050   :)..Invalid P
0x08048588  6173 7377 6f72 6421 0a00 0000 0000  assword!......

它是一个%d所以它引用了一个十进制变量。用户输入后(在本例中密码是一个数字23),局部变量 atebp-0x04包含其值:

:> afvd
var local_4h = 0xffe6cf34  0x00000017  ....
var local_8h = 0xffe6cf30  0x00000001  .... eax
var local_ch = 0xffe6cf2c  0x00000000  .... ecx

实际上:

0x17=23

与以前不同的是,以前的密码是AAAA而不是十进制数字scanf()无法匹配变量并且没有返回任何内容。

我只有最后一个小疑问:比较radare2和objdump之间的反汇编代码我发现了一个小差异,但我认为这很重要:

radare2

   0x08048418      8d45fc         lea eax, ebp - 4
   0x0804841b      89442404       mov dword [local_4h_2], eax
   0x0804841f      c704246c8504.  mov dword [esp], 0x804856c  
   0x08048426 b    e8e1feffff     call sym.imp.scanf

来自objdump

   8048418: 8d 45 fc                lea    eax,[ebp-0x4]
   804841b: 89 44 24 04             mov    DWORD PTR [esp+0x4],eax
   804841f: c7 04 24 6c 85 04 08    mov    DWORD PTR [esp],0x804856c

是否有任何原因导致 的指令差异0x08048418