逆向工程核心原理——第三十七章

x64处理器

64位系统中的内存地址为64位(8个字节),所以使用64位大小的指针。所以含有的绝对地址(AV)的指令大小比原来增加了4个字节。

x64系统中,通用寄存器的大小扩展到了64位(8个字节),数量也增加到了18个(新增加了R8~R15寄存器)。x64系统下的所有通用寄存器的名称均以字母“R”开头(x86以字母“E”开头)。

call/jmp指令

在原来x86系统中,FF15 XXXXXXXX指令用于调用API,其中XXXXXXXX表示绝对地址,指向IAT区域的某个位置。

x64系统指令地址从原来的4字节变为现在8个字节,在x86中FF15(CALL)指令后跟着4个字节的绝对地址。若x64中也在FF15后跟随绝对地址,这样在FF15垢面应该跟着8个字节的绝对地址,这样指令的长度就增加。为了防止地址增加,x64系统中指令后面仍然跟着4个字节大小的地址,不过是这4字节大小的地址被解析成“相对地址(RVA)”。

绝对地址和相对地址转化方法为:

函数调用约定

在32位系统中,主要有三种cdecl、stdcall、fastcall,但64位系统中它们都统一为一种变形的fastcall。64位fastcall中可以把4个参数存储到寄存器中,如果超过4个参数,则会将从第5个开始的参数通过栈传递。

栈和栈帧

windows 64 os中使用栈和栈帧的方式也发生了变化。调用子函数时,不再使用PUSH命令,而是通过MOV指令操作寄存器与预定的栈来传递。

练习:

我们通过将下面的代码分别编译成x64、x86,并对其进行观察。

Stack32.cpp\Stack64.cpp

#include <stdio.h>
#include<Windows.h>

void main()
{
	HANDLE hFile = INVALID_HANDLE_VALUE;

	hFile = CreateFileA(
		"c:\\work\\ReverseCore.txt",
		GENERIC_READ,
		FILE_SHARE_READ,
		NULL,
		OPEN_EXISTING,
		FILE_ATTRIBUTE_NORMAL,
		NULL);

	if (hFile != INVALID_HANDLE_VALUE)
	{
		CloseHandle(hFile);
	}
}

Stack32.cpp

首先我们进入main函数可以看到我们传递的参数:

可以看到其中的特征:

1.不使用栈帧。

2.调用子函数时使用栈传递参数。

3.使用PUSH指令压入栈的函数参数不需要main()函数清理。

我们知道stdcall方式调用win32 API时,是由被调用者清理栈。因此我们运行到调用CreateFileA函数,查看栈:

可以看到,这些参数全被压入了栈,然后我们进入CreateFileA():

可以看到,在CreateFileA()中,使用了栈帧,在调用CreateFileW()之前将参数压入了栈,并且在返回之前清理了栈。

我们可以看到CreateFileA()和CreateFileW()参数是相同的,但是占了两份的栈。

Stack64.cpp

这里我们看到,在开始时,分配了48H的栈空间,而在ret之前,关闭了48H的栈空间,这就相当于是使用了一个变形的栈帧。

我们可以看到,64位中不像32位经常使用push/pop指令,第14个参数使用寄存器传递(RCX,RDX,R8,R9),第57个参数使用栈。

我们运行到CreateFileA(),就可以看到参数被传递进了栈:

posted @ 2020-11-02 10:17  Kylimi  阅读(174)  评论(0编辑  收藏  举报