WIndows x64 ShellCode开发 第三章 反向Shell编写
字数 3654
更新时间 2026-03-12 13:17:03

Windows x64 汇编编写反向Shell Code教学文档

第一章:概述与目标

本文档旨在系统性地教授如何从零开始,使用x64汇编语言手动编写可运行的反向Shell Code。整个过程分为两大阶段:

  1. 理解阶段:通过使用外部API调用的方式,理解反向Shell的完整建立流程与核心API。
  2. 实现阶段:通过纯汇编实现,手动解析PEB、动态获取API地址,最终生成不依赖外部导入表的独立Shell Code。

最终目标是生成一段可被加载器执行的原始字节码(Shell Code),并为后续的免杀处理打下基础。

第二章:理解阶段 - 使用外部API编写反向Shell

本阶段旨在通过简化代码(允许使用EXTERN外部声明),聚焦于理解网络连接与进程创建的逻辑流程。此阶段代码无法直接编译为Shell Code,但易于理解。

2.1 核心API与结构解析

反向Shell的建立主要依赖于以下几个Win32 API和数据结构:

  1. WSAStartup 函数

    • 功能:初始化Winsock网络库。这是进行任何Windows Sockets编程的第一步。
  2. WSASocketA 函数

    • 功能:创建一个套接字(Socket),用于网络通信。
  3. WSAConnect 函数

    • 功能:使用创建的套接字连接到指定的远程地址和端口。
    • 关键参数
      • s:要连接的socket句柄(由WSASocketA返回)。
      • name:指向sockaddr_in结构体的指针,该结构体定义了目标IP地址和端口。
      • namelenname结构体的长度。
  4. CreateProcessA 函数

    • 功能:创建一个新进程(例如cmd.exe)并将其输入/输出重定向到我们创建的套接字。
    • 说明:参数较多,但大部分可置为NULL或0。需重点关注用于重定向标准输入、输出、错误的参数。
  5. STARTUPINFOA 结构体

    • 功能:作为CreateProcessA的关键参数,指定新进程的主窗口特性。对于反向Shell,核心在于设置其hStdInputhStdOutputhStdError成员为我们套接字的句柄,从而实现I/O重定向。

2.2 汇编代码实现逻辑

  1. 调用WSAStartup初始化Winsock。
  2. 调用WSASocketA创建TCP套接字。
  3. 填充sockaddr_in结构体,设置目标IP和端口。
  4. 调用WSAConnect连接到远程主机。
  5. 初始化STARTUPINFOA结构体,将其标准句柄(stdin, stdout, stderr)设置为套接字句柄。
  6. 调用CreateProcessA启动cmd.exe,并将其I/O绑定到套接字。
  7. 清理资源。

注意:此阶段代码编译的程序会被杀毒软件直接拦截,因它明确调用了敏感API。测试时需暂时关闭实时防护。

第三章:实现阶段 - 纯汇编Shell Code开发

这是生成真正Shell Code的核心。所有API地址必须动态解析,不能有外部依赖。

3.1 手动解析PEB定位kernel32.dll基址

由于无法使用导入表,第一步是手动获取核心DLL的基地址。

  • 原理:通过线程环境块(TEB) 找到进程环境块(PEB),进而遍历PEB_LDR_DATA结构中的已加载模块链表(InLoadOrderModuleList)。
  • 目标:遍历该链表,找到kernel32.dll(或kernelbase.dll)的基地址(DllBase)。该基地址是解析其他所有API的起点。

3.2 解析DLL导出表动态获取API地址

获取DLL基址后,需手动解析其PE(Portable Executable)文件结构以定位导出函数。

  1. 定位导出表

    • 从DLL基址找到DOS头(e_lfanew)。
    • 跳到NT头,找到DataDirectory数组,其第一项(索引0)是导出表(Export Directory)的RVA(相对虚拟地址)。
    • 将RVA转换为实际内存地址(VA)。
  2. 遍历导出表查找函数
    导出表关键数组:

    • AddressOfNames:存储函数名称字符串RVA的数组。
    • AddressOfNameOrdinals:存储函数序号(ordinal)的数组,与AddressOfNames一一对应。
    • AddressOfFunctions:存储函数地址RVA的数组,通过序号(ordinal)索引。
    • 查找流程
      a. 遍历AddressOfNames数组,将每个名称字符串RVA转换为指针,与目标API名称字符串(如"GetProcAddress")比较。
      b. 找到匹配项后,记下其在数组中的索引(i)。
      c. 使用索引iAddressOfNameOrdinals中取出函数的序号(ordinal)。
      d. 使用ordinal作为索引,从AddressOfFunctions中取出目标函数的RVA。
      e. 将函数RVA + DLL基址得到该API在内存中的实际地址。
  3. 关键API获取链

    1. 首先需要获取GetProcAddressLoadLibraryA的地址。这通常通过硬编码哈希或直接字符串比较查找kernel32.dll的导出表完成。
    2. 获得GetProcAddress后,即可用它方便地获取其他DLL(如ws2_32.dll)中的API地址,而无需再次手动遍历导出表。
    3. 使用LoadLibraryA动态加载ws2_32.dll并获取其基址。

3.3 所需API清单

实现反向Shell至少需要以下API,需按顺序解析并存储其地址:

  • 来自 kernel32.dllGetProcAddress, LoadLibraryA, CreateProcessA
  • 来自 ws2_32.dllWSAStartup, WSASocketA, WSAConnect

免杀扩展:为隐藏静态特征,可使用哈希算法(如ROR13)对API名称进行哈希。在查找时,比较函数名哈希值而非明文字符串,这样Shell Code中就不会出现敏感的API名称字符串。

3.4 网络编程与进程创建

此部分逻辑与第二章“理解阶段”完全一致,但调用的是动态获取到的API地址:

  1. 调用WSAStartup
  2. 调用WSASocketA创建套接字。调用成功后,返回值(RAX)为套接字句柄。若返回0xFFFFFFFFFFFFFFFFINVALID_SOCKET)则表示失败。
  3. 调用WSAConnect进行连接。
  4. 构造STARTUPINFOA结构体,设置标准句柄重定向。
  5. 调用CreateProcessA启动cmd.exe

3.5 调试与问题排查

开发过程中必须使用调试器(如x64dbg)。

  • 关键断点:在每次调用动态获取的API之前设置断点,检查寄存器中存储的地址是否正确。例如,检查GetProcAddress返回的WSAStartup地址是否有效(应指向ws2_32.WSAStartup),而非乱码。
  • 返回值检查:密切关注关键API的返回值。例如,WSASocketA调用后,RAX中应为有效的套接字句柄(一个非零、非INVALID_SOCKET的值)。

3.6 生成与测试Shell Code

  1. 编译链接:使用汇编器(如NASM)和链接器生成PE文件。
  2. 提取Shell Code
    • 使用二进制编辑工具或编程方法,从生成的PE文件的.text代码节中提取机器码。
    • 注意规避文件中的NULL字节(\x00),因为NULL字节在C字符串中会被截断,导致Shell Code不完整。可通过指令优化来消除。
  3. 加载器测试:编写一个简单的C语言加载器,将提取的Shell Code字节数组放入可执行内存页,然后跳转到该内存地址执行,以验证功能。
    char shellcode[] = {0x48, 0x89, ...};
    void *exec = VirtualAlloc(0, sizeof(shellcode), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    memcpy(exec, shellcode, sizeof(shellcode));
    ((void(*)())exec)();
    

第四章:总结与后续方向

通过以上步骤,即可完成从零手写x64反向Shell Code。关键路径为:手动解析PEB -> 获取kernel32基址 -> 解析导出表获取GetProcAddress/LoadLibrary -> 加载ws2_32.dll并获取网络API -> 执行网络连接与进程创建逻辑 -> 提取机器码

生成基础Shell Code只是第一步。此代码具有明显的静态(如API哈希特征)和行为特征,会被现代杀毒软件轻易检测。后续的免杀(防御规避)工作包括但不限于:

  • Shell Code编码/加密/混淆。
  • 添加垃圾指令(NOP-sleds, 花指令)。
  • 使用更隐蔽的进程注入技术。
  • 系统调用(Syscall)直接调用,绕过用户层API监控。
  • 反调试、反沙箱技术的集成。
相似文章
相似文章
 全屏