Windows x64 ShellCode入门教程 02
# Windows x64 ShellCode入门教程 02
我本想在第二部分讨论如何移除空字节(NULL bytes),我保证这部分内容一定会有!但我从x64 shellcode和汇编文章的第一部分收到了一些不错的反馈,还有一些关于计算PE偏移量的问题。所以,我想用第二部分来解释我是如何得出代码中使用的特定偏移量的。让我们开始吧!
首先,我们来获取一个像样的PE查看器。我选择使用Pepper作为查看x64二进制文件的PE查看器:Pepper x64 PE查看器 (opens new window)
我将详细讲解PE头的每个部分以及导出表。让我们以本系列文章中的汇编代码作为参考:
mov r8, rbx ; mov kernel32.dll base addr into r8
在我的案例中,Kernel32的基地址是:00007FFA63570000。
所以,00007FFA63570000现在在r8和rbx中
mov ebx, [rbx+0x3C] (move into lower 32 bits of the rbx register, hence why we use ebx)
我们的x64dbg调试器将显示:dword ptr ds:[rbx+3C]=[kernel32.00007FFA6357003C]=F8
这是我们的PE签名偏移量,如下图所示。我们首先获取kernel32.00007FFA6357003C所指向的值,即F8,然后将其添加到kernel32中。

add rbx, r8 = 00007FFA635700F8 (PE header/DOS header)
现在,我们处于[IMAGE_OPTIONAL_HEADER64][IMAGE_DATA_DIRECTORY]中
mov edx, [rbx+0x88]
这是为了获取导出表的偏移量

F8 + 0x88 = 180对我来说,这等于00000000000A3D80,也就是导出表的相对虚拟地址(RVA)。
add rdx, r8
00007FFA63613D80 → RVA导出表的地址
mov r10d, [rdx+0x14] ; r10d (the lower 32 bits of r10) now holds the function count.

xor r11, r11 ; Zero R11 before use
mov r11d, [rdx+0x20] ; r11d (the lower 32 bits of r11) now holds the AddressOfNames RVA
2
00000000000A5814 在x64dbg中

add r11, r8 ; AddressOfNames VMA
00007FFA63615814
mov rcx, r10 ; r10 has our total function count. Set RCX loop counter
; Loop over Export Address of Names Table to find WinApi names
kernel32findfunction:
mov ebx, [r11+rcx*4] ; EBX = RVA for first AddressOfName
2
3
4
5
6
- r11 = 函数名的相对虚拟地址(RVA)
- +rcx = 函数列表中的位置
- 4 = rcx * 4:由于AddressOfNames中的每个RVA都是4字节的条目,将rcx乘以4可得到正确的偏移量,以检索特定函数名的RVA。
循环结束后,我们的函数名就会被找到,并且函数名中的位置将存储在rcx中。
我们将rcx入栈,然后将其弹出到r15中,这就引导我们来到了这里:
OrdinalLookupSetup: ;We found our target WinApi position in the functions lookup
pop r15 ;Winexec position
js OrdinalLookup
OrdinalLookup:
mov rcx, r15 ;Winexec location in function names
xor r11, r11 ;clear r11
mov r11d, [rdx+0x24] ; AddressOfNameOrdinals RVA
2
3
4
5
6
7
8
X64dbg输出= dword ptr ds:[rdx+24]=[kernel32.00007FFA63613DA4]=A7280

add r11, r8 ; AddressOfNameOrdinals VMA
添加到kernel32 = 00007FFA63617280
; Get the function ordinal from AddressOfNameOrdinals
inc rcx
mov r13w, [r11+rcx*2] ; AddressOfNameOrdinals + Counter. RCX = counter
2
3
虚拟内存地址 + rcx(1612)× 2(字节)= WinExec的序号值!!!

;With the function ordinal value, we can finally lookup the WinExec address from AddressOfFunctions.
; Get function address from AddressOfFunctions
xor r11, r11 ; clear r11
mov r11d, [rdx+0x1c] ; AddressOfFunctions RVA
2
3
4

add r11, r8 ; AddressOfFunctions VMA in R11. Kernel32+RVA for addressoffunctions
mov eax, [r11+r13*4] ; Get the function RVA.
2
R11 = 相对虚拟内存地址
R13 = Winexec序号(0x64C = 十进制1612)
*** 4(字节)= 1612 = Winexec**
X64dbg = dword ptr ds:[r11+r13*4]=[kernel32.00007FFA636156D8]=608B0

add rax, r8 ; Found the WinExec WinApi!!!
将kernel32添加到我们的WinExec相对虚拟地址中

RAX现在持有WinExec的实际地址!!!
就是这样!现在你可以根据自己的喜好使用Winexec了,而且你应该也对如何遍历PE文件、解析PE头、查看导出目录信息等有了更清晰的认识。谢谢!