

Windows Shellcode学习笔记——通过VisualStudio生成shellcode
source link: https://3gstudent.github.io/3gstudent.github.io/Windows-Shellcode%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0-%E9%80%9A%E8%BF%87VisualStudio%E7%94%9F%E6%88%90shellcode/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

0x00 前言
shellcode是一段机器码,常用作漏洞利用中的载荷(也就是payload)
在渗透测试中,最简单高效的方式是通过metasploit生成shellcode,然而在某些环境下,需要定制开发自己的shellcode,所以需要对shellcode的开发作进一步研究
0x01 简介
编写Shellcode的基本方式有3种:
- 直接编写十六进制操作码
- 采用C或者Delphi这种高级语言编写程序,编译后,对其反汇编进而获得十六进制操作码
- 编写汇编程序,将该程序汇编,然后从二进制中提取十六进制操作码
本文将介绍如何通过Visual Studio编写c代码来生成shellcode,具体包含以下三部分内容:
- 利用vc6.0的DEBUG模式获取shellcode
- 测试Shellcode自动生成工具——ShellcodeCompiler
- 使用C++编写(不使用内联汇编),实现动态获取API地址并调用,对其反汇编可提取出shellcode
0x02 利用vc6.0的DEBUG模式获取shellcode
注:
本节参考爱无言的《挖0day》附录部分
测试系统:
Windows XP
1、编写弹框测试程序并提取汇编代码
代码如下:
#include "stdafx.h"
#include <windows.h>
int main(int argc, char* argv[])
{
MessageBoxA(NULL,NULL,NULL,0);
return 0;
}
在MessageBoxA(NULL,NULL,NULL,0);
处,按F9下断点
debug模式按F5开始调试,跳到断点
按Alt+8
将当前C代码转为汇编代码,如图

00401028 mov esi,esp
0040102A push 0
0040102C push 0
0040102E push 0
00401030 push 0
00401032 call dword ptr [__imp__MessageBoxA@16 (0042528c)]
call是一条间接内存调用指令,实际使用需要真正的内存地址
按Alt+6
打开查看内存数据的Memory窗口,跳到位置0x0042528c
,如图

0042528C EA 07 D5 77 00 00 00 ..誻...
取前4字节,倒序排列(内存中数据倒着保存):
77D507EA
call命令的实际地址为0x77D507EA
MessageBoxA函数位于user32.dll中,调用时需要提前加载user32.dll
2、编写内联汇编程序并提取机器码
新建工程,使用内联汇编加载上述代码:
#include "stdafx.h"
#include <windows.h>
int main(int argc, char* argv[])
{
LoadLibrary("user32.dll");
_asm
{
push 0
push 0
push 0
push 0
mov eax,0x77D507EA
call eax
}
return 0;
}
编译执行,成功弹框
在push 0处按F9下断点,F5进入调试模式跳至断点处
按Alt+8
将当前VC代码转为汇编代码,如图

12: push 0
0040103C push 0
13: push 0
0040103E push 0
14: push 0
00401040 push 0
15: push 0
00401042 push 0
16: mov eax,0x77D507EA
00401044 mov eax,77D507EAh
17: call eax
00401049 call eax
接着提取上述代码在内存中的数据,如图
范围是0040103C - 0040104A
注:
call eax的地址为00401049,表示起始地址,完整代码的长度需要+1
按Alt+6
打开查看内存数据的Memory窗口
跳到0x0040103C,内容如下:
0040103C 6A 00 6A 00 6A 00 6A 00 B8 EA 07 D5 77 FF D0 j.j.j.j.戈.誻..
截取0040103C - 0040104A的内容如下:
6A 00 6A 00 6A 00 6A 00 B8 EA 07 D5 77 FF D0
这段机器码就是接来下要使用的shellcode
3、编写加载shellcode的测试程序
#include "stdafx.h"
#include <windows.h>
int main(int argc, char* argv[])
{
LoadLibrary("user32.dll");
char shellcode[]="\x6A\x00\x6A\x00\x6A\x00\x6A\x00\xB8\xEA\x07\xD5\x77\xFF\xD0";
((void(*)(void))&shellcode)();
return 0;
}
成功执行shellcode
注:
由于Win7系统引入了ASLR机制,因此我们不能在shellcode中使用固定的内存地址,上述方法在Win7下不通用
0x03 Shellcode自动生成工具——ShellcodeCompiler
下载地址:
https://github.com/NytroRST/ShellcodeCompiler
特点:
- c++开发
- 借助NASM
- 可实现封装api,转换为bin格式的shellcode和asm汇编代码
实际测试:
Source.txt内容如下:
function MessageBoxA("user32.dll");
function ExitProcess("kernel32.dll");
MessageBoxA(0,"This is a MessageBox example","Shellcode Compiler",0);
ExitProcess(0);
cmd下运行:
ShellcodeCompiler.exe -r Source.txt -o Shellcode.bin -a Assembly.asm
注:
ShellcodeCompiler.exe和文件夹NASM放于同级目录
执行后shellcode保存在Shellcode.bin文件中
为便于测试生成的shellcode,可在生成过程中加入-t
参数执行一次shellcode
我参考ShellcodeCompiler的代码将其执行shellcode的功能提取出来,实现了读取文件并加载文件中的shellcode,完整代码如下:
#include <windows.h>
size_t GetSize(char * szFilePath)
{
size_t size;
FILE* f = fopen(szFilePath, "rb");
fseek(f, 0, SEEK_END);
size = ftell(f);
rewind(f);
fclose(f);
return size;
}
unsigned char* ReadBinaryFile(char *szFilePath, size_t *size)
{
unsigned char *p = NULL;
FILE* f = NULL;
size_t res = 0;
// Get size and allocate space
*size = GetSize(szFilePath);
if (*size == 0) return NULL;
f = fopen(szFilePath, "rb");
if (f == NULL)
{
printf("Binary file does not exists!\n");
return 0;
}
p = new unsigned char[*size];
// Read file
rewind(f);
res = fread(p, sizeof(unsigned char), *size, f);
fclose(f);
if (res == 0)
{
delete[] p;
return NULL;
}
return p;
}
int main(int argc, char* argv[])
{
char *szFilePath=argv[1];
unsigned char *BinData = NULL;
size_t size = 0;
BinData = ReadBinaryFile(szFilePath, &size);
void *sc = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (sc == NULL)
{
return 0;
}
memcpy(sc, BinData, size);
(*(int(*)()) sc)();
return 0;
}
0x04 C++编写(不使用内联汇编),实现动态获取API地址并调用,对其反汇编可提取出shellcode
对于ShellcodeCompiler,最大的不足是使用了内联汇编,vc在64位下默认不支持内联汇编,所以该方法无法生成64位shellcode
注:
delphi支持64位的内联汇编
vc在64位下虽然不能直接使用内联汇编,但是可以将程序段全部放到一个asm文件下进行编译
X64上恢复VS关键字__asm的方法可参照:
http://bbs.pediy.com/showthread.php?p=1260419
那么,想要开发一个64位的shellcode,最直接的方式就是不使用内联汇编,纯c++编写,实现动态获取API地址并调用,最后对其反汇编进而得到shellcode
好处如下:
便于调试,源代码的可读性大大增强
但是,我在网上并没有找到现成的代码,于是根据原理尝试自己实现
注:
1、编写shellcode需要实现以下步骤:
- 获取kernel32.dll基地址
- 定位GetProcAddress函数地址
- 使用GetProcAddress确定LoadLibrary函数地址
- 使用LoadLibrary加载DLL文件
- 使用GetProcAddress查找某个函数的地址(例如MessageBox)
- 指定函数参数
2、另一个参考资料:
http://bbs.pediy.com/showthread.php?t=203140
参考资料通过c++实现了加载一个第三方dll
以此为参考进行修改,实现我们想要的功能:
实现动态获取API地址并调用
完整代码已上传至github:
https://github.com/3gstudent/Shellcode-Generater
特点:
- 支持x86和x64
- 纯c++实现,动态获取GetProcAddress和LoadLibrary函数的地址
编译前对VisualStudio做如下配置:
1、使用Release模式。近来编译器的Debug模式可能产生逆序的函数,并且会插入许多与位置相关的调用。
2、禁用优化。编译器会默认优化那些没有使用的函数,而那可能正是我们所需要的。
3、禁用栈缓冲区安全检查(/Gs)。在函数头尾所调用的栈检查函数,存在于二进制文件的某个特定位置,导致输出的函数不能重定位,这对shellcode是无意义的
接着在IDA下打开生成的exe获得机器码即可
0x05 补充
接下来研究的内容:
- 在X64上恢复VS关键字__asm后,如何获取64位shellcode
Recommend
-
59
今天给大家介绍的是一款名叫sRDI的注入工具,它可以基于Shellcode实现反射型DLL注入,并能够将DLL转换成独立的Shellcode。 工具介绍
-
17
alphanumeric shellcode(纯字符shellcode)是比较实用的一项技术,因为有些时候程序会对用户输入的字符进行限制,比如只允许输入可见字符,这时就需要用到纯字符的shellcode了。 原理很简单,就是使用纯字符对应的汇编指令完成shell...
-
13
0x00 前言 Donut是一个shellcode生成工具,可以将.NET程序集转换为shellcode。这是对execute-assembly的进一步利用,隐蔽性更高,可扩展性更强。 结合byt3bl33d3r的SILENTTRINI...
-
7
Windows Shellcode学习笔记——利用VirtualAlloc绕过DEP 0x00 前言 接着介绍DEP绕过的另一种方法——利用VirtualAlloc绕过DEP。通过VirtualAlloc函数可以申请一段具有可执行属性的内存,相比于VirtualProtect,传入VirtualAll...
-
10
0x00 前言 在掌握了栈溢出的基本原理和利用方法后,接下来就要研究如何绕过Windows系统对栈溢出利用的重重防护,所以测试环境也从xp转到了Win7(相比xp,Win7的防护更全面)。本文将要介绍经典的DEP绕过方法——通过VirtualProtect绕过DEP ...
-
10
0x00 前言 在
-
13
Windows Shellcode学习笔记——shellcode在栈溢出中的利用与优化 0x00 前言 在
-
14
0x00 前言 之前在《Windows Shellcode学习笔记——通过VisualS...
-
6
VisualStudio神级插件——JetBrains Resharper2021.1.5学习版+教程_懒得勤快的博客_互联网分享精神如果你是一名.NET开发人员,但是你却不使用ReSharper,那么你就不是一个高效的码农了,因为这是一个强大的神器,你值得拥有!当然,用它的代价是,启动VS会变得非...
-
2
dotnet 通过 DockerfileContext 解决项目放在里层文件夹导致 VisualStudio 构建失败 本文告诉大家,如何解决 csproj 项目文件放入到里层的文件夹,不放在 sln 所在文件夹的第一层子文件夹,导致 VisualStudio 2022 在构建 docker 映像提示找不到文...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK