阅读 IA32 汇编代码 - 确定隐藏字符串?

Reading IA32 Assembly Code - Determining Hidden String?

我是一名学习 IA-32 汇编的 CS 学生。对于一个项目,我们已经获得了一个程序的可执行文件。我们可以使用objdump等工具来检查二进制文件,但不允许查看原始源代码。该程序接受一个输入字符串并将其与另一个神秘字符串进行比较。如果两个字符串 not 相同,程序就会发出警报,我的作业不及格。这将是一个有趣的任务...如果 TA 愿意回答我的问题...Grr...

所以如果你不介意给我一些指点,我想问论坛我是否在正确的轨道上。当我在 CODE 可执行文件上 运行 objdump -d CODE 时,我可以向下钻取并在 main() 函数中看到:

08048a44 <main>:
...
 8048af6:   e8 d0 08 00 00          call   80493cb <get_string>
 8048afb:   89 04 24                mov    %eax,(%esp)
 8048afe:   e8 ad 00 00 00          call   8048bb0 <test_string>

我相当确定 get_string() 从用户那里得到了一个字符串——它可能是 fscanf() 或其他东西的包装函数——然后指向该字符串的指针被保存到寄存器 %eax。下一行将指针移动到 %esp,然后调用 test_string()。这是代码:

08048bb0 <test_string>:
 8048bb0:   83 ec 1c                sub    [=11=]x1c,%esp
 8048bb3:   c7 44 24 04 6c a4 04    movl   [=11=]x804a46c,0x4(%esp)
 8048bba:   08 
 8048bbb:   8b 44 24 20             mov    0x20(%esp),%eax
 8048bbf:   89 04 24                mov    %eax,(%esp)
 8048bc2:   e8 bd 04 00 00          call   8049084 <cmp_strings>
 8048bc7:   85 c0                   test   %eax,%eax
 8048bc9:   74 05                   je     8048bd0 <test_string+0x20>
 8048bcb:   e8 bc 07 00 00          call   804938c <alarm>
 8048bd0:   83 c4 1c                add    [=11=]x1c,%esp
 8048bd3:   c3                      ret    

这是我认为正在发生的事情...

08048bb0 <test_string>:
 8048bb0:   sub    [=12=]x1c,%esp            // Adjusts %esp for new function
 8048bb3:   movl   [=12=]x804a46c,0x4(%esp)  // test_string is stored at [=12=]x804a46c; move that pointer into %esp
 8048bba:                                // ???
 8048bbb:   mov    0x20(%esp),%eax       // Moves test_string ptr to %eax
 8048bbf:   mov    %eax,(%esp)           // Moves test_string ptr to %esp - not sure why...?
 8048bc2:   call   8049084 <cmp_strings> // Calls cmp_strings(), probably with %eax and %esp as argument strings
 8048bc7:   test   %eax,%eax             // %eax is the returned value
 8048bc9:   je     8048bd0 <test_string+0x20>  // Should we jump to alarm()?
 8048bcb:   call   804938c <alarm>       // If we reach here, I flunk
 8048bd0:   add    [=12=]x1c,%esp            // restores %esp to original value
 8048bd3:   ret                          // exits

所以...如果我是对的,第 2 行是这里的重要一行。我怀疑神秘字符串存储在内存地址 [=21=]x804a46c 中。但我不确定。我还注意到,当我使用字符串工具时,我看到了这个:

[linux]$ strings -t x CODE | grep 46c
   246c My dog has fleas.
[linux]$

这很有希望……但不令人信服。内存地址 [=21=]x804a46c 不是 246c.

那么...对于冗长的 post 表示歉意,但是大家可以告诉我我是否在正确的轨道上吗?非常感谢任何见解或智慧!

非常感谢! -饶

除非有一些反调试技巧,cmp_strings() 只接受两个在 test_string() 中给出的参数。自然,它们都是字符串,第一个字符串取自常量位置0x804a46c,而第二个(指向它的指针,当然不是字符串本身)是test_string()的参数.在调用之前,堆栈看起来像这样:

     |_______________|
ESP: | <your string> | <-- cmp_strings() 1st arg
+04: |   0x804a46c   | <-- cmp_strings() 2nd arg
+08: |      ...      |
+0C: |      ...      |
+10: |      ...      |
+14: |      ...      |
+18: |      ...      |
+1C: | return adress | <-- ESP at the start of test_string()
+20: | <your string> | <-- test_string() 1st arg
+24: |      ...      |

您可以使用 GDB 在运行时直接检查 «secret» 字符串的内容(这通常是必需的,因为此处未显示的代码可能会重写它)。只是 break *0x8048bc2run 然后是 x/sb 0x804a46c.

The next line moves the pointer to %esp, then calls test_string().

mov %eax,(%esp)eax 中的值存储到 esp 寻址的内存中,即。在堆栈的顶部。要将该指针复制到 esp 中,您必须执行 mov %eax, %esp 这不是一个好主意,因为 ss:esp 被 CPU.

用作堆栈指针

movl [=15=]x804a46c,0x4(%esp) // test_string is stored at [=15=]x804a46c; move that pointer into %esp

同样,"into esp" 在完全错误的层面上是不准确的。这会将值 0x804a46c 写入内存地址 esp+4,因此如果您要从堆栈中获取 pop 值,它将是弹出的第二个值(右 "under" 堆栈顶部).

mov 0x20(%esp),%eax // Moves test_string ptr to %eax

将 "input string pointer" 加载到 eax。那是 call <test_string> 之前 eax 的那个。您可能是这个意思,写错了评论?

mov %eax,(%esp) // Moves test_string ptr to %esp - not sure why...?

将它存储在 "top of the stack",因此如果您要从此处的堆栈中弹出值,您将首先弹出输入字符串指针,然后是 0x804a46c 值。有关堆栈内容的 ASCII 艺术,请参阅 hidefromkgb 的答案。

然后很可能 call 8049084 <cmp_strings> 从堆栈中选择这两个指针作为参数,做一些事情,并且 return 将正确的字符串设为零(因为任何非零 return value 将使 next je 失败,并触发 call <alarm>.

您可能也应该快速浏览一下 cmp_strings,看看它是否是普通的 C 语言 strcmp 或者它如何 return 为零。

正如 Jester 指出的那样,objdump 神秘的 0x804a46c 内容也应该是可能的。如果是一些早期的任务,它可能属于具有易于阅读的字符串数据的数据部分。

如果这会是一个更困难的任务,它也可以指向代码段中形成一些字符串的假指令......或者最终甚至不是假指令(尽管产生有意义的 asm 代码也形成一些短字符串是在 x86 上不是微不足道的……例如,我曾经在我的 256B intros .com 文件的开头添加 "PED",它只是把堆栈弄乱了一点,不影响我介绍的其余部分……并且在一个大小编码竞赛我使用 xlat 指向代码以获得想要的位模式以在 51 bytes 中绘制希腊国旗)。