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
1

在我的案例中,Kernel32的基地址是:00007FFA63570000

所以,00007FFA63570000现在在r8和rbx中

mov ebx, [rbx+0x3C] (move into lower 32 bits of the rbx register, hence why we use ebx)
1

我们的x64dbg调试器将显示:dword ptr ds:[rbx+3C]=[kernel32.00007FFA6357003C]=F8

这是我们的PE签名偏移量,如下图所示。我们首先获取kernel32.00007FFA6357003C所指向的值,即F8,然后将其添加到kernel32中。

Windows x64 ShellCode入门教程 02(配图)

add rbx, r8 = 00007FFA635700F8 (PE header/DOS header)
1

现在,我们处于[IMAGE_OPTIONAL_HEADER64][IMAGE_DATA_DIRECTORY]中

mov edx, [rbx+0x88]
1

这是为了获取导出表的偏移量

Windows x64 ShellCode入门教程 02(配图)

F8 + 0x88 = 180对我来说,这等于00000000000A3D80,也就是导出表的相对虚拟地址(RVA)。

add rdx, r8
1

00007FFA63613D80 → RVA导出表的地址

mov r10d, [rdx+0x14]          ; r10d (the lower 32 bits of r10) now holds the function count.
1

Windows x64 ShellCode入门教程 02(配图)

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

00000000000A5814 在x64dbg中

Windows x64 ShellCode入门教程 02(配图)

add r11, r8                   ; AddressOfNames VMA
1

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
1
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
1
2
3
4
5
6
7
8

X64dbg输出= dword ptr ds:[rdx+24]=[kernel32.00007FFA63613DA4]=A7280

Windows x64 ShellCode入门教程 02(配图)

add r11, r8                   ; AddressOfNameOrdinals VMA
1

添加到kernel32 = 00007FFA63617280

; Get the function ordinal from AddressOfNameOrdinals
inc rcx
mov r13w, [r11+rcx*2]         ; AddressOfNameOrdinals + Counter. RCX = counter
1
2
3

虚拟内存地址 + rcx(1612)× 2(字节)= WinExec的序号值!!!

Windows x64 ShellCode入门教程 02(配图)

;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
1
2
3
4

Windows x64 ShellCode入门教程 02(配图)

add r11, r8                   ; AddressOfFunctions VMA in R11. Kernel32+RVA for addressoffunctions
mov eax, [r11+r13*4]        ; Get the function RVA.
1
2

R11 = 相对虚拟内存地址

R13 = Winexec序号(0x64C = 十进制1612)

*** 4(字节)= 1612 = Winexec**

X64dbg = dword ptr ds:[r11+r13*4]=[kernel32.00007FFA636156D8]=608B0

Windows x64 ShellCode入门教程 02(配图)

add rax, r8                   ; Found the WinExec WinApi!!!
1

将kernel32添加到我们的WinExec相对虚拟地址中

Windows x64 ShellCode入门教程 02(配图)

RAX现在持有WinExec的实际地址!!!

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

上次更新: 2026/07/14, 19:37:40

社区交流

讨论与留言

前往 GitHub Issues →

本留言区仅对应当前文章,欢迎补充观点、提出问题或帮助修正文中疏漏。 留言由 GitHub/Gitalk 提供,需要使用 GitHub 登录。

最近更新
第 1 课:专栏导论与安全边界
07-01
第 3 课:安装编译与调试环境
07-01
第 4 课:第三方库下载、编译与依赖管理
07-01
更多文章>