理解程序集 Hello World

逆向工程 部件
2021-06-15 16:09:30

我正在研究 x86 架构和汇编,以便为研究逆向和开发开发奠定基础。我正在学习关于 opensecuritytraining.info 的课程。

我看到一个 Hello World 示例:

push ebp
mov ebp, esp
push offset aHelloWorld; "Hello world\n"
call ds:__imp__printf
add esp, 4
mov eax, 1234h
pop ebp
retn

此代码由 Windows Visual C++ 2005 生成,关闭缓冲区溢出保护,并使用 IDA Pro 4.9 免费版反汇编。

我试图了解每一行的作用。

第一行是push ebp

我知道ebp代表base pointer它的功能是什么?

我看到在第二行中的值esp被移入ebp并在线搜索我看到前两条指令在汇编程序的开头非常常见。

虽然是ebpesp一开始是空的?我是组装新手。ebp用于堆栈帧,所以当我们有一个函数在我们的代码是其可选的一个简单的程序?

然后 push offset aHelloWorld; "Hello world\n"

后面的部分;是评论所以它不会被执行对吗?第一部分将包含字符串 Hello World 的地址添加到堆栈中,对吗?但是字符串在哪里声明?我不确定我是否理解。

然后 call ds:__imp__printf

它似乎是对函数的调用,无论如何printf是内置函数,对吗?而且也ds代表数据段寄存器使用它是因为我们试图访问不在堆栈上的内存操作数吗?

然后 add esp, 4

我们要向 esp 添加 4 个字节吗?为什么?

那么move eax, 1234h 这里的 1234h 是什么?

然后pop ebx..它在开始时被推了。最后有必要弹出吗?

然后retn(我知道ret在调用函数后返回一个值)。我读到 retn 中的 n 是指调用者推送的参数数量。对我来说不是很清楚。你能帮我理解吗?

1个回答

首先:我建议您尝试编写一些简单的汇编程序或使用像 REIL 这样的中间表示来掌握它(REIL 只有大约 17 条指令)。

push ebp
mov ebp, esp

前两行构建堆栈框架正如您正确提到的, ebp 描述了当前的堆栈帧。所以当这个函数被另一个函数调用时,它会在堆栈上保存之前的基指针,以便在函数返回时能够恢复它。保存该值时,该函数将堆栈上的当前位置分配为该函数的新基指针。

这种分配称为函数序言,在不同的反汇编程序中很常见。主要是ebp用于引用函数参数,而esp可以自由修改(通过将变量压入堆栈等)

push offset aHelloWorld; "Hello world\n" 

该函数将字符串“Hello world\n”地址压入堆栈。最后的评论只是为了方便。请注意,在堆栈上推送某些内容会通过给定值的(字节大小)修改 esp。在这种情况下,x32 为 4,x64 引用为 8 ( esp=esp-4)。

call ds:__imp__printf

这个调用并没有直接跳转到函数,因为函数是动态加载的,代码并不知道函数在地址空间中的位置。所以它调用一个对位置的引用,它是“跳转表”,或者在这种情况下是“导入表”。当一个二进制文件被加载时,系统加载器确保它加载依赖项并留下正确的地址供程序使用。imp代表“进口”)。

add esp, 4

基于调用约定(参数如何传递?谁从堆栈中清除它们?) printf 在返回之前不会清除其堆栈。所以调用函数必须自己做。它将堆栈指针加 4 以恢复push指令的更改(从 esp 隐式减去 4)。

mov eax, 1234h

这里没什么特别的。值 0x1234 被移动到寄存器 eax(显然是没有原因的)。我假设这是程序的返回值(unix 约定是如果没有失败,则在 eax 中返回 0)。

pop ebp

该指令从堆栈中读取 ebp 之前的值,并再次将其存储在 ebp 中。请注意,您必须跟踪将事物压入堆栈的顺序(后进先出)。请注意,此指令还隐式地将 4 添加到 esp。

retn

在当前 esp 的位置恢复执行。当一个函数被调用时,调用函数将要返回的地址压入堆栈。此外,此函数将 esp 加 4 以从堆栈中“删除”地址。