0x00 前言

Windows Shellcode学习笔记——通过VisualStudio生成shellcode,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 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,esp0040102A push 00040102C push 00040102E push 000401030 push 000401032 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 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 00040103C push 013: push 00040103E push 014: push 000401040 push 015: push 000401042 push 016: mov eax,0x77D507EA00401044 mov eax,77D507EAh17: call eax00401049 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 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 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

php shellcode,Windows Shellcode学习笔记相关推荐

  1. Windows异常学习笔记(五)—— 未处理异常

    Windows异常学习笔记(五)-- 未处理异常 要点回顾 最后一道防线 实验一:理解最后一道防线 实验二:新线程的最后一道防线 总结 UnhandledExceptionFilter 实验三:理解U ...

  2. Windows异常学习笔记(四)—— 编译器扩展SEH

    Windows异常学习笔记(四)-- 编译器扩展SEH 要点回顾 编译器支持的SEH 过滤表达式 实验一:理解_try_except 实验二:_try_except 嵌套 拓展SEH结构体 scope ...

  3. Windows异常学习笔记(二)—— 内核异常处理流程用户异常的分发

    Windows异常学习笔记(二)-- 内核异常处理流程&用户异常分发 用户层与内核层异常 内核异常 分析 KiDispatchException 分析 RtlDispatchException ...

  4. Windows异常学习笔记(一)—— CPU异常记录模拟异常记录

    Windows异常学习笔记(一)-- CPU异常记录 基础知识 异常的分类 CPU异常 分析中断处理函数 _KiTrap00 分析 CommonDispatchException 总结 软件模拟异常 ...

  5. Windows APC学习笔记(二)—— 挂入过程执行过程

    Windows APC学习笔记(二)-- 挂入过程&执行过程 基础知识 挂入过程 KeInitializeApc ApcStateIndex KiInsertQueueApc Alertabl ...

  6. Windows APC学习笔记(一)—— APC的本质备用APC队列

    Windows APC学习笔记(一)-- APC的本质&备用APC队列 基础知识 APC的本质 APC队列 APC结构 分析 KiServiceExit 总结 备用APC队列 挂靠环境下Apc ...

  7. Windows系统调用学习笔记(四)—— 系统服务表SSDT

    Windows系统调用学习笔记(四)-- 系统服务表&SSDT 要点回顾 系统服务表 实验:分析 KiSystemService 与 KiFastCallEntry 共同代码 SSDT 实验: ...

  8. Windows系统调用学习笔记(三)—— 保存现场

    Windows系统调用学习笔记(三)-- 保存现场 要点回顾 基本概念 Trap Frame 结构 线程相关的结构体 ETHREAD KTHREAD CPU相关的结构体 KPCR _NT_TIB KP ...

  9. Windows系统调用学习笔记(二)—— 3环进0环

    Windows系统调用学习笔记(二)-- 3环进0环 要点回顾 基本概念 _KUSER_SHARED_DATA 0x7FFE0300 实验:判断CPU是否支持快速调用 第一步:修改EAX=1 第二步: ...

  10. Windows系统调用学习笔记(一)—— API函数调用过程

    Windows系统调用学习笔记(一)-- API函数调用过程 Windows API 实验1:分析ReadProcessMemory 第一步:定位函数 第二步:开始分析 总结 实验2:分析NtRead ...

最新文章

  1. Excel中如何截取字符串中指定字符后的部分字符
  2. 重磅!韩国学者发布最新《人工智能元宇宙》综述论文
  3. 第四周项目一-求四个数的最大公约数
  4. java多线程测试框架(含入参和返回值)
  5. 【ARM】Tiny4412裸板编程之ADC
  6. 前端MVC Vue2学习总结(四)——条件渲染、列表渲染、事件处理器
  7. 【MFC系列-第24天】梯形分页和蝴蝶QQ宠物的实现
  8. python for循环例子_Python for循环生成列表的实例
  9. left join效率为什么低_为什么自考的你学习效率特别低?对号入座
  10. c++中map的基本函数
  11. 屏幕坏了无法使用计算机调试,vivo手机屏幕坏了连接电脑没反应怎么办
  12. maxscale mysql 主从_MariaDB主从配置与MaxScale实现MySQL读写分离
  13. 主分区、扩展分区、逻辑分区和活动分区的区别
  14. 【T+】去掉畅捷通T+左边那些不用的云应用
  15. 无线路由传输速率的秘密
  16. 数据可视化 饼图_饼图之外的生活:合适工作的合适可视化效果
  17. 程序员工作三年晒出9月工资条,直言加班太累了
  18. 计算机投影维修维护,投影机维修常见故障解决方法
  19. Linux 命令缩写及参数
  20. STFT使用overlap-add重建信号

热门文章

  1. 苹果笔记本能玩英雄联盟吗_英雄联盟手游安卓和苹果可以一起玩吗 英雄联盟手游安卓和ios数据互通吗...
  2. glb格式的3D模型怎么下载?gltf格式的3D模型怎么下载?
  3. severlet 学习基础
  4. VC++界面编程之--实现一个画板并提供文字输入功能(MsPaint)
  5. Linux多进程读者和写者问题,读者与写者问题
  6. 第六批入围公示——年度TOP100智能网联供应商评选进行时
  7. 过氧化氢低温等离子灭菌器测试方案
  8. 学考计算机常见打字题,信息技术学考复习题及答案.doc
  9. 自然语言处理基础技术工具篇之Flair
  10. 爬虫项目实战十一:爬取当当网商品信息