最近工作中要经常编写shellcode,纯手工汇编编写shellcode太麻烦,又没有找到一个比较灵活的shellcode生成器框架,因此便有自己动手写了一个简单的基于C++的框架。已经使用了一段时间并没有发现什么问题,现在将代码开源。
上述代码的主要功能是调用两次MessageBox
(一次用的是Ansi
版本一次是Unicode
版本)然后调用ExitProcess
结束进程。可以看出来,使用该C++框架编写shellcode基本上除了前面的导入API代码后续代码就与常规C++没有区别了。考虑到从PE文件中手动提取shellcode也挺麻烦,因此增加了一个提取shellcode的python脚本,添加到了vs中的Post-Build Event
中(代码仓库中的src目录下有配置好的vs2019项目),在编译成功后可自动提取shellcode到编译目录下(*.sh)。
由于MSVC没有关闭SSE指令集的编译选项(使用SSE指令集会导致长字符串被存储到只读区段,然后在通过SSE指令集拷贝到栈中,如果有大佬知道怎么关闭SSE指令集希望一个指点一下),因此只能使用Clang编译器,所幸vs2019可以一键安装Clang编译器,还是比较方便的。有几个比较重要的编译选项一定要改,其余的看个人喜好,暂时未发现对生成的代码有较大影响:
下载代码
上一个版本中提到说到无法获取函数指针,后来想了一个方案:提取shellcode时根据重定位表将函数的VA修改成基于当前EIP的偏移,使用时通过以下代码获取。
后来想了一下觉得这种方案还要借助于提取脚本来做,而且不方便直接调试,因此退而求其次,换了一种方法:在shellcode开始动态获取SCMain函数的SCMainVA(SCMain函数的虚拟地址),然后需要用函数指针时计算一下即可获取到(Foo-SCMain+SCMainVA),但缺点是只能在SCMain函数中获取函数的指针(一般情况也足够用了)。详细代码可见更新,使用函数指针的代码示例:
SC_MAIN_BEGIN()
{
/
/
*
*
*
Place function declaration here!
*
*
*
UINT MyFunc(VOID);
SC_IMPORT_API_BATCH_BEGIN();
SC_IMPORT_API_BATCH(
"User32.dll"
, MessageBoxA);
SC_IMPORT_API_BATCH(
"User32.dll"
, MessageBoxW);
SC_IMPORT_API_BATCH(
"Kernel32.dll"
, ExitProcess);
SC_IMPORT_API_BATCH_END();
/
/
*
*
*
Place code here!
*
*
*
MessageBoxA(NULL, SC_PISTRINGA(
"Hello Ansi!"
), SC_PISTRINGA(
"Hello!"
), MB_OK);
MessageBoxW(NULL, SC_PISTRINGW(L
"Hello Unicode!"
), SC_PISTRINGW(L
"Hello!"
), MB_OK);
ExitProcess(MyFunc());
}
/
/
*
*
*
Place function definition here!
*
*
*
SC_NOINLINE UINT MyFunc(VOID) {
return
0
; }
SC_MAIN_END()
SC_MAIN_BEGIN()
{
/
/
*
*
*
Place function declaration here!
*
*
*
UINT MyFunc(VOID);
SC_IMPORT_API_BATCH_BEGIN();
SC_IMPORT_API_BATCH(
"User32.dll"
, MessageBoxA);
SC_IMPORT_API_BATCH(
"User32.dll"
, MessageBoxW);
SC_IMPORT_API_BATCH(
"Kernel32.dll"
, ExitProcess);
SC_IMPORT_API_BATCH_END();
/
/
*
*
*
Place code here!
*
*
*
MessageBoxA(NULL, SC_PISTRINGA(
"Hello Ansi!"
), SC_PISTRINGA(
"Hello!"
), MB_OK);
MessageBoxW(NULL, SC_PISTRINGW(L
"Hello Unicode!"
), SC_PISTRINGW(L
"Hello!"
), MB_OK);
ExitProcess(MyFunc());
}
/
/
*
*
*
Place function definition here!
*
*
*
SC_NOINLINE UINT MyFunc(VOID) {
return
0
; }
SC_MAIN_END()
CALL $
+
5
POP EAX
LEA EAX, [EAX
+
Foo] ; Foo是需要作回调的函数,编译后会在重定位表中看到这条重定位信息,然后我们在提取shellcode时根据重定位记录修改成偏移即可。
CALL $
+
5
POP EAX
LEA EAX, [EAX
+
Foo] ; Foo是需要作回调的函数,编译后会在重定位表中看到这条重定位信息,然后我们在提取shellcode时根据重定位记录修改成偏移即可。
SC_MAIN_BEGIN()
{
/
/
*
*
*
Place function declaration here!
*
*
*
UINT WINAPI WorkerThread(PCSTR lpAnsiMsg);
SC_IMPORT_API_BATCH_BEGIN();
SC_IMPORT_API_BATCH(
"Kernel32.dll"
, CreateThread);
SC_IMPORT_API_BATCH(
"Kernel32.dll"
, WaitForSingleObject);
SC_IMPORT_API_BATCH(
"Kernel32.dll"
, ExitProcess);
SC_IMPORT_API_BATCH_END();
/
/
*
*
*
Place code here!
*
*
*
HANDLE hWorker
=
CreateThread(
NULL,
0
,
(PTHREAD_START_ROUTINE)SC_PIFUNCTION(WorkerThread),
SC_PISTRINGA(
"Hello Ansi!"
),
0
,
NULL);
WaitForSingleObject(hWorker, INFINITE);
ExitProcess(
0
);
}
/
/
*
*
*
Place function definition here!
*
*
*
SC_NOINLINE UINT WINAPI WorkerThread(PCSTR lpAnsiMsg) {
SC_IMPORT_API_BATCH_BEGIN();
SC_IMPORT_API_BATCH(
"User32.dll"
, MessageBoxA);
SC_IMPORT_API_BATCH(
"User32.dll"
, MessageBoxW);
SC_IMPORT_API_BATCH_END();
MessageBoxA(NULL, lpAnsiMsg, SC_PISTRINGA(
"Hello!"
), MB_OK);
MessageBoxW(NULL, SC_PISTRINGW(L
"Hello Unicode!"
), SC_PISTRINGW(L
"Hello!"
), MB_OK);
return
0
;
}
SC_MAIN_END()
SC_MAIN_BEGIN()
{
/
/
*
*
*
Place function declaration here!
*
*
*
UINT WINAPI WorkerThread(PCSTR lpAnsiMsg);
SC_IMPORT_API_BATCH_BEGIN();
SC_IMPORT_API_BATCH(
"Kernel32.dll"
, CreateThread);
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2021-7-30 14:25
被windpiaoxue编辑
,原因: 更新版本