CppGuide社区 CppGuide社区
首页
  • 最新谷歌C++风格指南(含C++17/20)
  • C++17详解
  • C++20完全指南
  • C++23快速入门
  • C++语言面试问题集锦
  • 🔥C/C++后端开发常见面试题解析 (opens new window)
  • 网络编程面试题 (opens new window)
  • 网络编程面试题 答案详解 (opens new window)
  • 聊聊WebServer作面试项目那些事儿 (opens new window)
  • 字节跳动面试官现身说 (opens new window)
  • 技术简历指南 (opens new window)
  • 🔥交易系统开发岗位求职与面试指南 (opens new window)
  • 第1章 高频C++11重难点知识解析
  • 第2章 Linux GDB高级调试指南
  • 第3章 C++多线程编程从入门到进阶
  • 第4章 C++网络编程重难点解析
  • 第5章 网络通信故障排查常用命令
  • 第6章 高性能网络通信协议设计精要
  • 第7章 高性能服务结构设计
  • 第8章 Redis网络通信模块源码分析
  • 第9章 后端服务重要模块设计探索
  • 🚀 全部章节.pdf 下载 (opens new window)
  • 源码分析系列

    • leveldb源码分析
    • libevent源码分析
    • Memcached源码分析
    • TeamTalk源码分析
    • 优质源码分享 (opens new window)
    • 🔥远程控制软件gh0st源码分析
  • 从零手写C++项目系列

    • C++游戏编程入门(零基础学C++)
    • 🔥使用C++17从零开发一个调试器 (opens new window)
    • 🔥使用C++20从零构建一个完整的低延迟交易系统 (opens new window)
    • 🔥使用C++从零写一个C语言编译器 (opens new window)
    • 从零用C语言写一个Redis
  • Windows 10系统编程
  • 🔥Windows Native API编程
  • 🔥Windows x64 ShellCode入门教程
  • 🔥Windows Shellcode实战
  • Go语言特性

    • Go开发实用指南
    • Go系统接口编程
    • 高效Go并发编程
    • Go性能调优
    • Go项目架构设计
  • Go项目实战

    • 使用Go从零开发一个数据库
    • 🔥使用Go从零开发一个编译器 (opens new window)
    • 🔥使用Go从零开发一个解释器 (opens new window)
    • 🔥用Go从零写一个编排器(类Kubernetes) (opens new window)
  • Rust编程

    • Rust编程指南
  • 数据库

    • SQL零基础指南
    • MySQL开发与调试指南
  • Linux内核

    • 心中的内核 —— 在阅读内核代码之前先理解内核
    • 🔥Linux 5.x内核开发与调试 完全指南 (opens new window)
    • TCP源码实现超详细注释版.pdf (opens new window)
GitHub (opens new window)
首页
  • 最新谷歌C++风格指南(含C++17/20)
  • C++17详解
  • C++20完全指南
  • C++23快速入门
  • C++语言面试问题集锦
  • 🔥C/C++后端开发常见面试题解析 (opens new window)
  • 网络编程面试题 (opens new window)
  • 网络编程面试题 答案详解 (opens new window)
  • 聊聊WebServer作面试项目那些事儿 (opens new window)
  • 字节跳动面试官现身说 (opens new window)
  • 技术简历指南 (opens new window)
  • 🔥交易系统开发岗位求职与面试指南 (opens new window)
  • 第1章 高频C++11重难点知识解析
  • 第2章 Linux GDB高级调试指南
  • 第3章 C++多线程编程从入门到进阶
  • 第4章 C++网络编程重难点解析
  • 第5章 网络通信故障排查常用命令
  • 第6章 高性能网络通信协议设计精要
  • 第7章 高性能服务结构设计
  • 第8章 Redis网络通信模块源码分析
  • 第9章 后端服务重要模块设计探索
  • 🚀 全部章节.pdf 下载 (opens new window)
  • 源码分析系列

    • leveldb源码分析
    • libevent源码分析
    • Memcached源码分析
    • TeamTalk源码分析
    • 优质源码分享 (opens new window)
    • 🔥远程控制软件gh0st源码分析
  • 从零手写C++项目系列

    • C++游戏编程入门(零基础学C++)
    • 🔥使用C++17从零开发一个调试器 (opens new window)
    • 🔥使用C++20从零构建一个完整的低延迟交易系统 (opens new window)
    • 🔥使用C++从零写一个C语言编译器 (opens new window)
    • 从零用C语言写一个Redis
  • Windows 10系统编程
  • 🔥Windows Native API编程
  • 🔥Windows x64 ShellCode入门教程
  • 🔥Windows Shellcode实战
  • Go语言特性

    • Go开发实用指南
    • Go系统接口编程
    • 高效Go并发编程
    • Go性能调优
    • Go项目架构设计
  • Go项目实战

    • 使用Go从零开发一个数据库
    • 🔥使用Go从零开发一个编译器 (opens new window)
    • 🔥使用Go从零开发一个解释器 (opens new window)
    • 🔥用Go从零写一个编排器(类Kubernetes) (opens new window)
  • Rust编程

    • Rust编程指南
  • 数据库

    • SQL零基础指南
    • MySQL开发与调试指南
  • Linux内核

    • 心中的内核 —— 在阅读内核代码之前先理解内核
    • 🔥Linux 5.x内核开发与调试 完全指南 (opens new window)
    • TCP源码实现超详细注释版.pdf (opens new window)
GitHub (opens new window)
  • Windows Shellcode实战 专栏说明
  • shellcodisation在病毒学中的应用
  • 编写shellcode
  • WiShMaster:shellcodisation过程
  • 使用WiShMaster开发应用程序
  • 未来工作
    • 5.1 pycparser工具(pycparser)
    • 5.2 GCC编译器的fPIC选项(Option fPIC of gcc)
    • 5.3 趣味使用编译指令(Playing with pragma for fun...)
  • 总结
目录

未来工作

# 5 未来工作

WiShMaster 版本2已能够生成shellcode,但仍处于开发阶段。其主要目标是改进C代码分析,以消除正则表达式解析对代码施加的最后几项限制。

以下段落总结了我正在研究的一些思路。

# 5.1 pycparser工具(pycparser)

pycparser是一个用Python编写的C语言解析器(C parser)和抽象语法树生成器(AST generator),可在谷歌代码网站[13]上获取。与使用浏览文件(browse file)解析代码相比,它可能是一个更好的解决方案:首先,它似乎能提供更多信息;其次,它解除了WiShMaster与微软编译器(Microsoft compiler)之间的关联。

# 5.2 GCC编译器的fPIC选项(Option fPIC of gcc)

GCC编译器集成了一个有趣的选项“fPIC”,允许生成不包含硬编码地址(hardcoded address)的代码。PIC是“位置无关代码(Position Independent Code)”的缩写。

启用该选项后,GCC会在每次访问全局数据(global data)之前插入一个函数调用,将当前的指令指针寄存器(eip)的值移至基址指针寄存器(ebx),然后相对于基址指针寄存器(ebx)访问数据。

我们以Linux系统为例:

文件 test.c

#include <stdio.h>

char g_szMessage[] = "Hello !";

void DisplayMessage(void)
{
    printf(">>> %s <<<\n", g_szMessage);
}

int main(int argc, char * argv[])
{
    DisplayMessage();
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

正常编译时,生成的代码包含硬编码地址:

标准编译生成的汇编代码(Assembly generated with a standard compilation)

$ gcc test.c -o test
$ objdump -d test
...
8048354 <DisplayMessage>:
8048354:       55                      push   %ebp
8048355:       89 e5                   mov    %esp,%ebp
8048357:       83 ec 08                sub    $0x8,%esp
804835a:       c7 44 24 04 bc 95 04 08 movl   $0x80495bc,0x4(%esp)
8048362:       c7 04 24 a8 84 04 08    movl   $0x80484a8,(%esp)
8048369:       e8 22 ff ff ff          call   8048290 <printf@plt>
804836e:       c9                      leave
804836f:       c3                      ret
...
1
2
3
4
5
6
7
8
9
10
11
12
13

但如果使用fPIC选项:

fPIC选项编译生成的汇编代码(Assembly generated with the fPIC option)

$ gcc test.c -o test -fPIC
$ objdump -d test
...
08048384 <DisplayMessage>:
8048384:       55                      push   %ebp
8048385:       89 e5                   mov    %esp,%ebp
8048387:       53                      push   %ebx
8048388:       83 ec 14                sub    $0x14,%esp
804838b:       e8 4f 00 00 00          call   80483df <__i686.get_pc_thunk.bx>
8048390:       81 c3 5c 12 00 00       add    $0x125c,%ebx
8048396:       8b 83 f8 ff ff ff       mov    0xfffffff8(%ebx),%eax
804839c:       89 44 24 04             mov    %eax,0x4(%esp)
80483a0:       8d 83 0c ef ff ff       lea    0xffffef0c(%ebx),%eax
80483a6:       89 04 24                mov    %eax,(%esp)
80483a9:       e8 0a ff ff ff          call   80482b8 <printf@plt>
80483ae:       83 c4 14                add    $0x14,%esp
80483b1:       5b                      pop    %ebx
80483b2:       5d                      pop    %ebp
80483b3:       c3                      ret

080483df <__i686.get_pc_thunk.bx>:
80483df:       8b 1c 24                mov    (%esp),%ebx
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

然而,该选项无法解决导入函数(imported function)的解析问题;此外,数据之间的间隔是硬编码的,因此必须在shellcode中保持相同的内存映射(mapping)。但最重要的是,该选项在Windows系统上似乎无法工作。我尝试了Cygwin[14]和MinGW[15]中的GCC编译器,每次都得到相同的提示“不支持该选项”(或其他不太明确的提示)。实际上,该选项是在Linux系统中添加的,用于支持真正的共享库(shared library)。Windows系统的工作方式不同,当动态链接库(dll)未在首选加载地址(preferred load address)加载时,它完全依赖重定位表(relocation table)来修补硬编码地址。以下链接提供了关于此主题的有趣信息[16]。

Philippe Biondi开发的Linux系统shellcode生成器ShellForge[17]特别使用了该选项。

# 5.3 趣味使用编译指令(Playing with pragma for fun...)

我们可以使用一些编译指令(pragma directive)将各种元素分组到一个特殊的节(section)中。

例如,如果我们对“simpletest”的代码稍作修改:

修补后代码片段(Extract of patched code)

#pragma code_seg("mysection")

__declspec(allocate("mysection"))
CHAR g_szMessage[] = "This is a message stored as a global variable";
__declspec(allocate("mysection"))
CHAR sz00000001[] = ">>> %s <<<";
__declspec(allocate("mysection"))
CreateFileTypeDef fp_CreateFile;
#define CreateFile  fp_CreateFile

VOID DisplayMessage(IN CHAR * szMessage)
{
    PrintMsg(LOG_LEVEL_TRACE, sz00000001, szMessage);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

这些指令指示将函数放置在新的节“mysection”中。“__declspec”允许将全局数据添加到代码节(code section)中。

最后,查看生成的映射文件(.map file):

生成的映射文件片段(Extract of the generated map file)

...
0003:00000000  0000127eH  mysection                               CODE
...
0003:00000000                          ?fp_CreateFile@@...KKPAX@ZA
0003:00000004                          ?sz00000001@@3PADA
0003:00000010                          ?g_szMessage@@3PADA
0003:00000050                          ?DisplayMessage@@YAXPAD@Z
0003:00000080                          ?DisplayFile@@YAHPAD@Z
0003:00000200                          ?DisplayData@@YAHXZ
...
0042f000                          f       display.obj
0042f004                          f       display.obj
0042f010                          f       display.obj
0042f050                          f       display.obj
0042f080                          f       display.obj
0042f200                          f       display.obj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

可以看到,函数和全局数据位于同一个节中,因此在地址空间(address space)中非常接近。查看汇编代码:

函数PrintMsg的调用(Call of the function PrintMsg)

PrintMsg(LOG_LEVEL_TRACE, sz00000001, szMessage);
0042F059  mov         eax, dword ptr [szMessage]  ; szMessage = g_szMessage = 0x0042F010
0042F05C  push        eax
0042F05D  push        offset sz00000001 (42F004h)
0042F062  push        2
0042F064  push        0
1
2
3
4
5
6

代码中仍然包含硬编码地址,但如果我们提取整个“mysection”节,并将其映射到另一个进程中的相同地址,它可能会正常工作。

函数CreateFile的调用(Call of the function CreateFile)

if ((hFile = CreateFile(szFilePath, ..., NULL)) != INVALID_HANDLE_VALUE)
0042F0AC  push        0
...
0042F0C2  call        dword ptr [fp_CreateFile (42F000h)]
1
2
3
4

该技术存在一些限制:

  • 仍然需要处理导入函数;
  • 仍然需要对代码进行一些修改:替换字符串、添加编译指令;
  • 无法得到真正的shellcode,因为它包含硬编码地址,且必须映射到特定地址。

不过,这可能是一个有趣的解决方案。

使用WiShMaster开发应用程序
总结

← 使用WiShMaster开发应用程序 总结→

最近更新
01
C++语言面试问题集锦 目录与说明
03-27
02
第四章 Lambda函数
03-27
03
第二章 关键字static及其不同用法
03-27
更多文章>
Copyright © 2024-2026 沪ICP备2023015129号 张小方 版权所有
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式