ShellCode的幾種調用方法


ShellCode是一種漏洞代碼,中文名也叫填充數據,一般是用C語言或者匯編編寫。在研究的過程中,自己也學到了一些東西,發現其中也有許多坑,所以貼出來,如果大家有碰到的,可以參考一下。

 

以啟動電腦上的計算器為例,編寫ShellCode其實就是兩部分,一是獲取ShellCode字節碼,二是調用它。

獲取方式一般是觀察代碼反匯編和內存相結合:

VOID Test()
{
    HMODULE v1 =  LoadLibraryA("kernel32.dll");//0X7778ff70
    //WinExec("calc.exe", SW_SHOW);

    /*
        00E31E5E  mov         esi,esp
        00E31E60  push        0E36B30h
        00E31E65  call        dword ptr ds:[0E3A060h]
        00E31E6B  cmp         esi,esp
        00E31E6D  call        __RTC_CheckEsp (0E3111Dh)
        00E31E72  mov         dword ptr [v1],ea,0x
            WinExec("calc.e,0xe", SW_SHOW);



        00E31E75  mov         esi,esp
        00E31E77  push        5
        00E31E79  push        0E36B40h
        00E31E7E  call        dword ptr ds:[0E3A064h]
        00E31E84  cmp         esi,esp
        00E31E86  call        __RTC_CheckEsp (0E3111Dh)



    return 0;
    */
    __asm
    {

        push    ebp;
        mov        ebp, esp;
        xor eax, eax;
        push eax;
        sub esp, 08h;
        mov byte ptr[ebp - 0Ch], 63h; //c
        mov byte ptr[ebp - 0Bh], 61h; //a
        mov byte ptr[ebp - 0Ah], 6Ch; //l
        mov byte ptr[ebp - 09h], 63h; //c
        mov byte ptr[ebp - 08h], 2Eh; //.
        mov byte ptr[ebp - 07h], 65h; //e
        mov byte ptr[ebp - 06h], 78h; //x
        mov byte ptr[ebp - 05h], 65h; //e

        lea eax, [ebp - 0ch];
        push eax;                    //將calc.exe壓入棧內

        mov        eax, 0x7778ff70;
        call    eax;                    //調用WinExec

        mov esp, ebp;
        pop    ebp;
    }

}

然后就是所謂的苦力活,將反匯編中的字節碼一個一個抄出來,整合成為一個ShellCode

 

CHAR ShellCode[] = {
    0x55,0x8B,0xEC,0x51,0x51,0x83,0x65,0xFC,0x00,0x56,0x57,0xC7,0x45,0xF8,0x63,0x61,
    0x6C,0x63,0x64,0xA1,0x18,0x00,0x00,0x00,0x33,0xC9,0x8B,0x40,0x30,0x8B,0x40,0x0C,
    0x8B,0x78,0x1C,0x8B,0x3F,0x8B,0x47,0x20,0x66,0x83,0x78,0x10,0x2E,0x74,0x06,0x41,
    0x83,0xF9,0x02,0x7C,0xEE,0x8B,0x4F,0x08,0xBA,0xB9,0x6B,0xFF,0xCB,0xE8,0x23,0x00,
    0x00,0x00,0x8B,0x4F,0x08,0xBA,0x13,0xB9,0xE6,0x25,0x8B,0xF0,0xE8,0x14,0x00,0x00,
    0x00,0x6A,0x01,0x8D,0x4D,0xF8,0x51,0xFF,0xD0,0x6A,0x00,0x6A,0x00,0xFF,0xD6,0x5F,
    0x5E,0x8B,0xE5,0x5D,0xC3,0x55,0x8B,0xEC,0x83,0xEC,0x10,0x53,0x56,0x8B,0xF1,0x89,
    0x55,0xF0,0x33,0xD2,0x57,0x8B,0x46,0x3C,0x8B,0x5C,0x30,0x78,0x03,0xDE,0x89,0x5D,
    0xF4,0x8B,0x4B,0x20,0x03,0xCE,0x39,0x53,0x18,0x76,0x39,0x8B,0x39,0x33,0xC0,0x03,
    0xFE,0x8A,0x1F,0x84,0xDB,0x8B,0x5D,0xF4,0x74,0x1C,0x8B,0xD8,0x8A,0x07,0x6B,0xDB,
    0x21,0x0F,0xBE,0xC0,0x03,0xD8,0x47,0x8A,0x07,0x84,0xC0,0x75,0xF1,0x89,0x5D,0xF8,
    0x8B,0x5D,0xF4,0x8B,0x45,0xF8,0x3B,0x45,0xF0,0x74,0x12,0x83,0xC1,0x04,0x42,0x3B,
    0x53,0x18,0x72,0xC7,0x33,0xC0,0x5F,0x5E,0x5B,0x8B,0xE5,0x5D,0xC3,0x8B,0x43,0x24,
    0x8D,0x04,0x50,0x0F,0xB7,0x0C,0x30,0x8B,0x43,0x1C,0x8D,0x04,0x88,0x8B,0x04,0x30,
    0x03,0xC6,0xEB,0xE2
};

對於ShellCode能不能在各個電腦上都適用,我還不敢保證,因為我之前也嘗試將別人寫的拿過來運行,但是程序崩潰。所以最好自己試着寫一遍。

ShellCode字符也可以寫成這種形式:

char shellcode[] =
"\x55\x8b\xec\x51\x51\x83\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89"
"\xf3\x8d\x4e\x08\x31\xd2\xcd\x80\xe8\xe4\xff\xff\xff\x2f\x62\x69\x6e"
"\x2f\x73\x68\x58";   //...

它利用的是\x的轉義字符特性,其實是一樣的。

 

寫好ShellCode后就該調用它了

方法1:利用動態申請內存,一定是可執行屬性

typedef void (_stdcall *CODE)();


VOID Sub_1()
{
    PVOID p = NULL;
    p = VirtualAlloc(NULL, sizeof(ShellCode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if (p == NULL)
    {
        return;
    }
    memcpy(p, ShellCode, sizeof(ShellCode));
    
    CODE code = (CODE)p;
    code();
}

方法2,:強制類型轉換成函數指針

VOID Sub_2()
{
    ((void(WINAPI*)(void))&ShellCode)();
}

雖然看上去有點復雜,但是拆開分析一下還是很簡單的,首先取了ShellCode的地址,將它強制類型轉換成函數指針,第一個void表示函數返回值,第二個void可以不要,它是說該函數沒有參數,最后在后面加上小括號,注意WINAPI的調用約定一定不能少,我用的VS2015編譯器,寫的控制台程序的默認調用約定是_cdecl。

 

方法3:嵌入式匯編呼叫ShellCode


#pragma comment(linker, "/section:.data,RWE")
VOID Sub_3() { __asm { mov eax, offset ShellCode jmp eax } }

這種方法寫法也比較靈活,其中第一句 mov eax, offset ShellCode 可以用 lea eax, ShellCode 代替,因為它們是等價的

第二句的 jmp 也可以用 call 代替。所以就是四種組合了。

最上面的一句 #pragma comment(linker, "/section:.data,RWE") 是很重要的,我曾經因為沒有它,而導致多次錯誤,卻一直在ShellCode上找原因。

它也是說將這段代碼放入可執行區域。

 

方法4:偽指令

 
         
#pragma comment(linker, "/section:.data,RWE"
VOID Sub_4()
{
    __asm
    {
        
        mov eax, offset ShellCode
       ;_emit 偽指令在當前文本段落的當前位置定義一個字節。 _emit 偽命令類似於 MASM 的 DB 指令。
        _emit 0xFF  
        _emit 0xE0

    }
}

這種方法雖然可以成功執行,但是我也不知道0XFF,0XE0 起到了什么作用。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM