跳转到帖子

游客您好,欢迎来到黑客世界论坛!您可以在这里进行注册。

赤队小组-代号1949(原CHT攻防小组)在这个瞬息万变的网络时代,我们保持初心,创造最好的社区来共同交流网络技术。您可以在论坛获取黑客攻防技巧与知识,您也可以加入我们的Telegram交流群 共同实时探讨交流。论坛禁止各种广告,请注册用户查看我们的使用与隐私策略,谢谢您的配合。小组成员可以获取论坛隐藏内容!

TheHackerWorld官方

Windows外壳代码学习笔记3354通过VisualStudio生成外壳代码

精选回复

发布于

0x00 前言

Shellcode是一段机器码,在exploit中常用作payload(即有效载荷)。

在渗透测试中,最简单有效的方法是通过metasploit生成外壳代码。但是在某些情况下,需要定制和开发自己的shellcode,所以有必要进一步研究shellcode的开发。

0x01 简介

有三种编写外壳代码的基本方法:

写直接十六进制操作码

用高级语言c或Delphi编写程序。编译后反汇编得到十六进制操作码。

写一个汇编程序,汇编程序,然后从二进制中提取十六进制操作码。

本文将介绍如何通过Visual Studio编写C代码生成shell代码,包括以下三个部分:

使用vc6.0的调试模式获取外壳代码

测试外壳代码自动生成工具——外壳代码编译器

用c写(不用内联汇编),实现API地址和调用的动态获取,反汇编提取shellcode。

0x02 利用vc6.0的DEBUG模式获取shellcode

注:

在本节中,请参考Ai武文《挖0day》的附录。

测试系统:

Windows XP

1、编写弹框测试程序并提取汇编代码

代码如下:

#include 'stdafx.h '

#包括

int main(int argc,char* argv[])

{

MessageBoxA(NULL,NULL,NULL,0);

返回0;

}

At MessageBoxA(NULL,NULL,NULL,0);按F9键断开该点

在调试模式下,按F5开始调试并跳转到断点。

按Alt 8将当前C代码转换成汇编代码,如图

2-2.png

00401028 mov esi,esp

0040102A推送0

0040102C推送0

0040102E推送0

00401030推送0

00401032呼叫dword ptr[_ _ imp _ _ messagebox a @ 16(0042528 c)]

Call是间接内存调用指令,实际使用需要真实的内存地址。

按Alt 6打开内存窗口查看内存数据,跳转到0x0042528c位置,如图所示。

2-3.png

042528cea 07d577 00 00 00.00 .

取前4个字节,按逆序排列(存储器中的数据是反向存储的):

77D507EA个

call命令的实际地址是0x77D507EA

MessageBoxA函数位于user32.dll,调用时需要预先加载user32.dll。

2、编写内联汇编程序并提取机器码

新建项目,使用内联汇编加载上述代码:

#include 'stdafx.h '

#包括

int main(int argc,char* argv[])

{

LoadLibrary(' user 32 . dll ');

_asm

{

按0

按0

按0

按0

mov eax,0x77D507EA

呼叫eax

}

返回0;

}

编译,成功弹开。

在push 0处按F9断点,F5进入调试模式,跳转到断点。

按Alt 8将当前VC代码转换成汇编代码,如图

2-4.png

12:按0

0040103C推送0

13:按0

0040103E按0

14:按0

00401040推送0

15:按0

00401042推送0

16: mov eax,0x77D507EA

00401044 mov eax,77D507EAh

17:调用eax

00401049呼叫eax

然后提取内存中上述代码的数据,如图。

2-5.png

范围是0040103C-0040104A。

注:

call eax的地址是00401049,表示起始地址。完整代码的长度需要1。

按Alt 6打开内存窗口查看内存数据

跳到0x0040103C,其内容如下:

040103 c 6a 006 a 006 a 006 a 00 b 8 ea 07d 577 ffd 0j . j . j . j . Ge。傅;饲料单位.

0040103C-0040104A的内容如下:

6A 00 6A 00 6A 00 6A 00 B8 EA 07 D5 77 FF D0

这个机器代码是下一步要使用的外壳代码。

3、编写加载shellcode的测试程序

#include 'stdafx.h '

#包括

int main(int argc,char* argv[])

{

LoadLibrary(' user 32 . dll ');

char shellcode[]=' \ x6A \ x00 \ x6A \ x00 \ x6A \ x00 \ xB8 \ xEA \ x07 \ xD5 \ x77 \ xFF \ xD0 ';

((void(*)(void))外壳代码)();

返回0;

}

成功执行外壳代码。

注:

由于Win7系统引入了ASLR机制,我们不能在shellcode中使用固定的内存地址,上述方法在Win7中并不常见。

0x03 Shellcode自动生成工具——ShellcodeCompiler

下载地址:

https://github.com/NytroRST/ShellcodeCompiler

特点:

发展

开源工具

和NASM一起

可以实现封装api,可以转换成bin格式的shell代码和asm汇编代码。

实际测试:

Source.txt的内容如下:

函数messagebox a(“user 32 . dll”);

函数exit process(' kernel 32 . dll ');

MessageBoxA(0,“这是一个MessageBox示例”,“Shellcode编译器”,0);

exit process(0);

在cmd下运行:

ShellcodeCompiler.exe-r source . txt-o shellcode . bin-a assembly . ASM

注:

和ShellcodeCompiler.exe文件夹NASM在同一个目录下。

执行后,shellcode保存在Shellcode.bin文件中。

要测试生成的外壳代码,您可以添加-t参数,以便在生成过程中执行一次外壳代码。

我参考ShellcodeCompiler的代码提取其执行shellcode的函数,实现读取文件和在文件中加载shellcode。完整的代码如下:

#包括

size_t GetSize(char * szFilePath)

{

size_t大小;

FILE* f=fopen(szFilePath,' Rb ');

fseek(f,0,SEEK _ END);

size=ftell(f);

倒带(f);

fclose(f);

返回大小;

}

无符号char * ReadBinaryFile(char * SZ file path,size_t *size)

{

无符号char * p=NULL

FILE * f=NULL

size _ t RES=0;

//获取大小并分配空间

* size=GetSize(SZ file path);

if (*size==0)返回NULL

f=fopen(szFilePath,' Rb ');

if (f==NULL)

{

printf('二进制文件不存在!\ n’);

返回0;

}

p=新的无符号字符[* size];

//读取文件

倒带(f);

res=fread(p,sizeof(无符号字符),*size,f);

fclose(f);

if (res==0)

{

删除[]p;

返回NULL

}

返回p;

}

int main(int argc,char* argv[])

{

char * szFilePath=argv[1];

无符号char * BinData=NULL

size _ t size=0;

BinData=ReadBinaryFile(SZ file path,size);

void *sc=VirtualAlloc(0,size,MEM _保留| MEM _提交,页面_执行_读写);

if (sc==NULL)

{

返回0;

}

memcpy(sc,BinData,size);

(*(int(*)))sc)();

返回0;

}

0x04 C++编写(不使用内联汇编),实现动态获取API地址并调用,对其反汇编可提取出shellcode

对于ShellcodeCompiler来说,最大的缺点就是使用了内联汇编。在64位下,vc默认不支持内联汇编,因此该方法无法生成64位外壳代码。

注:

支持delphi 64位内联汇编

虽然vc不能直接使用64位下的内联汇编,但是所有的程序段都可以编译在一个asm文件下。

在X64上恢复VS keyword __asm的方法可以称为:

http://bbs.pediy.com/showthread.php?p=1260419

那么如果你想开发一个64位的shellcode,最直接的方法就是用纯C编写而不是内联汇编,实现API地址的动态获取并调用,最后反汇编得到shellcode。

好处如下:

它易于调试,并且源代码的可读性大大增强。

但是网上没有找到现成的代码,就按照原理自己试着实现了一下。

注:

1.编译外壳代码需要以下步骤:

获取kernel32.dll的基本地址

找到GetProcAddress函数地址。

使用GetProcAddress确定LoadLibrary函数的地址

使用LoadLibrary加载DLL文件

使用GetProcAddress查找函数的地址(例如,MessageBox)

指定函数参数

调用函数

2.另一个参考:

http://bbs.pediy.com/showthread.php?t=203140

Reference通过c实现加载一个第三方dll。

以此为参考进行修改,以实现我们想要的功能:

实现API地址和调用的动态获取

完整的代码已经上传到github:

https://github.com/3gstudent/Shellcode-Generater

特点:

X86和x64支持

纯C实现,动态获取GetProcAddress和LoadLibrary函数的地址。

编译前,按如下方式配置VisualStudio:

1.使用释放模式。编译器最近的调试模式可能会以相反的顺序产生函数,并插入许多位置相关的调用。

2.禁用优化。默认情况下,编译器会优化那些未使用的函数,而这可能正是我们所需要的。

3.禁用堆栈缓冲区安全检查(/Gs)。函数开头和结尾调用的堆栈检查函数存在于二进制文件中的特定位置,导致输出函数无法重定位,这对shellcode没有意义。

然后打开IDA下生成的exe获取机器码。

0x05 补充

下一步研究内容:

如何在X64上恢复VS关键字__asm后获得64位外壳代码

留下回复

创建帐户或登录后发表意见

最近浏览 0

  • 没有会员查看此页面。