编写并提取简易 ShellCode

ShellCode 通常是指一个原始的可执行代码的有效载荷,ShellCode 这个名字来源于攻击者通常会使用这段代码来获得被攻陷系统上的交互 Shell 的访问权限,而现在通常用于描述一段自包含的独立的可执行代码片段。

ShellCode 通常会与漏洞利用并肩使用,或是被恶意代码用于执行进程代码的注入,漏洞利用等,通常情况下 ShellCode 代码无法独立运行,必须依赖于父进程或是 Windows 文件加载器才能够被运行,本章将通过一个简单的弹窗(MessageBox)来实现 ShellCode 有效载荷的提取。

在编写ShellCode之前,我们需要查找一个函数地址,这里要调用 MessageBox() 这个API函数,所以说首先需要获取该函数的地址,这个函数默认放在了 User32.dll 库中,你可以通过编写一个小程序来获取:

如下代码如果在VC6.0环境下是可以正常编译通过的,但如果在VS2010之后则需要修改字符集
VS2013版本:需要修改 解决方案 -> 属性 -> 配置属性 -> 常规 -> 字符集(使用多字节字符集)

#include <windows.h>
#include <iostream>
typedef void(*MYPROC)(LPTSTR);

int main()
{
	HINSTANCE LibAddr;
	MYPROC ProcAddr;
	LibAddr = LoadLibrary("user32");                             // 获取User.dll基地址
	printf("动态库基地址 = 0x%x\n", LibAddr);
	ProcAddr = (MYPROC)GetProcAddress(LibAddr, "MessageBoxA"); // 获取MessageBox基地址
	printf("函数相对地址 = 0x%x", ProcAddr);
	getchar();
	return 0;
}

上方的代码经过编译运行以后会得到两个返回结果,由结果可知,User32.dll 的基地址是 0x76320000 而 MessageBox 在当前系统中的地址为 0x763a1f70 ,当然这两个地址在不同版本的Windows系统中,应该是不同的,所以我这里的地址和你的应该也是不同的。

在获取到 MessageBox 的内存地址以后,我们接着需要获取一个 ExitProecess() 函数的地址,这个API函数的作用是让程序正常退出,这是因为我们注入代码以后,原始的堆栈地址会被破坏,堆栈失衡后会导致程序崩溃,所以为了稳妥起见我们还是添加一行正常退出为好。

ExitProcess 函数位于 Kernel32.dll 这个动态链接库里面,我们只需要对上方的获取代码稍加修改,然后编译运行就可得到相应的结果,这里我获取到的地址是 0x75874b80


既然获取到了相应的内存地址,那么接下来就需要通过汇编来编写可执行代码片段了,在编写这段代码之前,先来了解一下汇编语言的调用约定,在汇编语言中,要想调用某个函数,需要使用CALL语句,而在CALL语句的后面,要跟上该函数在系统中的地址,前面我们已经获取到了相应的内存地址了,所以在这里就可以通过CALL相应的地址来调用相应的函数。

在实际的编程中,一般还是先将地址赋给eax寄存器,然后再CALL相应的寄存器实现调用,比如现在有一个函数 lyshark(a,b,c,d),我们想调用它,那么它的汇编代码就应该编写为:

push d
    push c
    push b
    push a
    mov eax,AddressOflyshark    // 获取偏移地址
    call eax

根据上方的调用方式,我们可以在VS中利用内联汇编来调用ExitProcess() 这个函数了。

xor ebx, ebx
    push ebx
    mov eax, 0x75874b80
    call eax

接着编写 MessageBox() 这个函数调用。与上个函数不同的是,这个API函数包含有四个参数,当然第一和第四个参数,我们可以赋给0值,但是中间两个参数都包含有较长的字符串,这个该如何解决呢?我们不妨先把所需要用到的字符串转换为ASCII码值,转换的网站这里推荐:ASCII 在线转换器 ,ASCII码,ASCII 转码—在线工具本工具是根据ASCII表做对应转码操作,转换完成后即可看到对应的内容,ASCII码一般用于配置文件防止乱码使用。icon-default.png?t=N7T8https://www.sojson.com/ascii.html

MsgBox标题:alert -->  \x61\x6c\x65\x72\x74\x21
MsgBox内容:hello lyshark --> \x68\x65\x6c\x6c\x6f\x20\x6c\x79\x73\x68\x61\x72\x6b

由于我们使用的是32位汇编,所以上方的字符串需要做一定的处理,我们分别将每四个字符为一组,进行分组,将不满四个字符的,以空格(x20)进行填充:

alert
-------------------------------------------------------------
\x61\x6c\x65\x72
\x74\x21\x20\x20

hello lyshark
-------------------------------------------------------------
\x68\x65\x6c\x6c
\x6f\x20\x6c\x79
\x73\x68\x61\x72
\x6b\x20\x20\x20

上方的空位置之所以需要以 x20 进行填充,而不是 x00 进行填充,是因为 strcpy 这个字符串拷贝函数,默认只要一遇到 x00 就会认为我们的字符串结束了,就不会再拷贝x00后的内容了,所以这里就不能使用 x00 进行填充了,这里要特别留意一下。

接着我们需要将这两段字符串分别压入堆栈存储,这里需要注意,由于我们的计算机是小端序排列的,因此字符的入栈顺序是从后往前不断进栈的,上面的字符串压栈参数应该写为:

alert
-------------------------------------------------------------
    push 0x20202174
    push 0x72656c61

hello lyshark
-------------------------------------------------------------
    push 0x2020206b
    push 0x72616873
    push 0x796c206f
    push 0x6c6c6568

那么下面问题来了,我们如何获取这两个字符串的地址,从而让其成为MessageBox()的参数呢?其实这个问题也不难,我们可以利用esp指针,因为它始终指向的是栈顶的位置,我们将字符压栈后,栈顶位置就是我们所压入的字符的位置,于是在每次字符压栈后,可以加入如下指令:

xor ebx,ebx                    // 清空寄存器
    push 0x20202174         // 字符串 alert 
    push 0x72656c61
    mov eax,esp                 // 获取第一个字符串的地址

    push ebx                      // 压入00,为了将两个字符串分开。

    push 0x2020206b         // 字符串 hello lyshark
    push 0x72616873
    push 0x796c206f
    push 0x6c6c6568
    mov ecx,esp                 // 获取第二个字符串的地址

上方代码完成压栈以后,接下来我们就可以调用MessageBox函数了,其调用代码如下。

push ebx                               // push 0
push eax                              // push "alert"
push ecx                              // push "hello lyshark !"
push ebx                             // push 0
mov eax,0x763a1f70          // 将MessageBox地址赋值给EAX
call eax                               // 调用 MessageBox

最终代码如下

int main()
{
	_asm{
		sub esp, 0x50          // 抬高栈顶,防止冲突
		xor ebx, ebx           // 清空ebx
		push ebx
		push 0x20202174
		push 0x72656c61        // 字符串 "alert"
		mov eax, esp           // 获取栈顶
		push ebx               // 填充00 截断字符串

		push 0x2020206b
		push 0x72616873
		push 0x796c206f
		push 0x6c6c6568         // 字符串 hello lyshark
		mov ecx, esp            // 获取第二个字符串的地址

		push ebx
		push eax
		push ecx
		push ebx
		mov eax, 0x763a1f70    // 获取MessageBox地址
		call eax               // call MessageBox

		push ebx
		mov eax, 0x75874b80   // 获取ExitProcess地址
		call eax              // call ExitProcess
	}
	return 0;
}

我们把上方的代码编译下,然后在程序的“_asm”位置先下一个【F9】断点,然后按【F5】启动调试,接着按下【alt+8】就能够查看所转换出来的机器码。

001613CE 83 EC 50             sub         esp,50h  
001613D1 33 DB                xor         ebx,ebx  
001613D3 53                   push        ebx  
001613D4 68 74 21 20 20       push        20202174h  
001613D9 68 61 6C 65 72       push        72656C61h  
001613DE 8B C4                mov         eax,esp  
001613E0 53                   push        ebx  
001613E1 68 6B 20 20 20       push        2020206Bh  
001613E6 68 73 68 61 72       push        72616873h  
001613EB 68 6F 20 6C 79       push        796C206Fh  
001613F0 68 68 65 6C 6C       push        6C6C6568h  
001613F5 8B CC                mov         ecx,esp  
001613F7 53                   push        ebx  
001613F8 50                   push        eax  
001613F9 51                   push        ecx  
001613FA 53                   push        ebx  
001613FB B8 70 1F 3A 76       mov         eax,763A1F70h  
00161400 FF D0                call        eax  
00161402 53                   push        ebx  
00161403 B8 80 4B 87 75       mov         eax,75874B80h  
00161408 FF D0                call        eax

我们直接将上方的这些机器码提取出来,从而编写出完整的ShellCode,最终测试代码如下。

#include <windows.h>
#include <stdio.h>
#include <string.h>
#pragma comment(linker,"/section:.data,RWE")

unsigned char shellcode[] = "\x83\xec\x50"
"\x33\xdb"
"\x53"
"\x68\x74\x21\x20\x20"
"\x68\x61\x6c\x65\x72"
"\x8b\xc4"
"\x53"
"\x68\x6b\x20\x20\x20"
"\x68\x73\x68\x61\x72"
"\x68\x6f\x20\x6c\x79"
"\x68\x68\x65\x6c\x6c"
"\x8b\xcc"
"\x53"
"\x50"
"\x51"
"\x53"
"\xb8\x70\x1f\x3a\x76"
"\xff\xd0"
"\x53"
"\xb8\x80\x4b\x87\x75"
"\xff\xd0";


int main(int argc,char **argv)
{
	LoadLibrary("user32.dll");
	__asm{
		lea eax,shellcode
		call eax
	}
	return 0;
}

上方代码经过编译以后,运行会弹出一个我们自己DIY的MessageBox提示框。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Shellcode Helper v1.62 Coded by TeLeMan (c) 2008-2013 Usage: schelper.exe [options] Options: -i [input file] input file (Default: stdin) -o [output file] output file (Default: stdout) -s input file format (Default: Auto-Detection) -sb input file format is Binary -sp the input file format's parameters -d output file format (Default: C format) -db output file format is Binary -dp the output file format's parameters -search get the start offset by the pattern: e.g. PK\x03\x04 -soff fix the match offset after searching (Default: 0) -off convert the input file from the offset (Default: 0) -len convert the input file with the length (Default: 0 - MAX) -en [encoder] encode shellcode (Default: XorDword) -de [encoder] decode shellcode (Default: Auto-Detection) -ex exclude characters: e.g. 0x00,0x01-0x1F,0xFF (Default: 0x00) -in incude characters only -ep the encoder's parameters -t [pid] execute or inject shellcode into process for testing -td [pid] execute or inject shellcode into process for debugging -stack put shellcode into stack and execute it (ESP is the shellcode start) -noinfo display no normal messages except error messages Available formats: 0 - C 1 - C(HexArray) 2 - Perl 3 - Python 4 - Ruby 5 - JavaScript(Escape) 6 - VBScript(Escape) 7 - Pascal 8 - MASM(Data) 9 - HexDump 10 - BitString 11 - HexString 12 - HexArray(C like) 13 - Base64 14 - Binary 15 - HexString(C like) 16 - HexString(Escape) 17 - HexString(JavaScript,UNICODE) 18 - URI(ISO-8859-1) 19 - XML(PCDATA) 20 - BigNumber 21 - BigNumber(Hex) 22 - BigNumber(BaseX) 23 - FloatPoint 24 - UnixTimestamp 25 - GUID 26 - MASM(ASM) 27 - NASM 28 - YASM(ASM) 29 - FASM(ASM) 30 - JWASM(ASM) 31 - POASM(ASM) 32 - GOASM(ASM) 33 - GNU ASM Available encoders:
### 回答1: Shellcode是一种在操作系统内核中执行的二进制代码。它通常用于漏洞利用和恶意软件中。编写shellcode需要了解汇编语言和操作系统内部工作原理。步骤包括确定目标系统平台、编写汇编代码、使用汇编器将代码转换为机器码并去除不必要的部分。最后,通过十六进制编辑器将机器码转换为可执行的shellcode。 ### 回答2: Shellcode是计算机安全领域中的一个术语,指的是一段精心编写的机器码,用于向远程服务器发送攻击代码。Shellcode由语言所写的基本单元构成,可以看作是一些机器指令的序列,这些指令可以被攻击者从攻击文件中提取并加载到内存中来执行。 在编写Shellcode时,攻击者需要考虑以下几个因素: 1. 选择合适的语言:通常情况下,攻击者使用较低级别的语言编写Shellcode,比如汇编、C语言等,具有较高的攻击性和灵活性。高级语言可被编译成较低级别的机器码,但由于其过于复杂,会使攻击者的目标变得更加难以实现。 2. 确定攻击目标:在编写Shellcode时,攻击者需要明确自己的目标是什么,因为Shellcode的内容和指定的操作系统有关。如果攻击者要攻击Linux操作系统,则需要编写适用于Linux的Shellcode。 3. 了解系统调用:Shellcode本质上是攻击代码,需要与操作系统进行交互才能实现攻击功能。攻击者需要深入了解目标操作系统的系统调用,以便编写能够集成到Shellcode中的函数。 4. 隐藏自己的Shellcode:攻击者的Shellcode需要能够在执行攻击时隐藏自己的存在,防止被服务器的安全防御发现并阻止其执行。因此,编写Shellcode的过程中,添加一些默默执行的优化代码通常会使Shellcode更难被检测出来。 综上所述,Shellcode编写需要仔细考虑许多细节和攻击原理,是一个非常复杂和挑战性的工作。需要具有深入的技术知识、大量的实操经验和强烈的创造力来成功实现。因此,对于那些想要保护自己计算机系统的安全和隐私的用户来说,应该注意提高自己的网络安全意识,以免成为黑客攻击的目标。 ### 回答3: Shellcode是一种机器可执行代码,通常用于利用软件或系统漏洞,实现攻击者的目的。它们是用汇编语言编写的小段程序,目的是在攻击者控制的环境中提供一个命令行接口。Shellcode是计算机安全方面的一个重要组成部分,它们用来攻击网络或操作系统,并实现攻击者的目标,比如窃取敏感信息、获得系统管理员权限等。 Shellcode编写过程需要以下步骤: 1. 选择正确的汇编代码和指令:攻击者需要使用特定的汇编代码和指令,以便在目标系统上实现其目的。这些代码和指令通常是最简单的和最小的,以便在运行时不引起注意,并且可以在目标系统上尽可能快地执行。 2. 手写代码或使用自动化工具:在编写Shellcode时,可以手动编写代码,也可以利用自动化工具(如Metasploit)来生成代码。无论哪种方法都需要足够的经验和技术。 3. 调试和测试:成功编写shellcode需要进行检查,确保其在目标环境中能够准确执行所需的操作。一些测试工具和脚本可以用于对Shellcode进行测试,以保证其正确性和稳定性。 4. 压缩和编码:为了使Shellcode足够小,可以使用压缩和编码技术来减小代码体积。这有助于减小Shellcode在内存中的占用空间,并增加攻击成功的几率。 总的来说,Shellcode编写需要具备丰富的汇编语言编程知识和安全实践经验,同时需要掌握各种测试和修复技术。攻击者使用Shellcode来实施危害,所以安全团队需要相应地采取措施,防范和识别Shellcode攻击。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值