網(wǎng)絡(luò)安全編程:PE編程實(shí)例之查殼工具
PE文件結(jié)構(gòu)中大多用的是偏移地址,因此,只要偏移地址和實(shí)際的數(shù)據(jù)相符,那么PE文件格式有可能是嵌套的。也就是說(shuō),PE文件是可以變形的,只要保證其偏移地址和PE文件格式的結(jié)構(gòu)基本就沒(méi)多大問(wèn)題。
對(duì)于PE可執(zhí)行文件來(lái)說(shuō),為了保護(hù)可執(zhí)行文件或者是壓縮可執(zhí)行文件,通常會(huì)對(duì)該文件進(jìn)行加殼。接觸過(guò)軟件破解的人應(yīng)該都清楚殼的概念。下面來(lái)寫(xiě)一個(gè)查殼的工具。
首先,用ASPack給前面寫(xiě)的程序加個(gè)殼。打開(kāi)ASPack加殼工具,如圖1所示。
圖1 ASPack加殼工具界面
對(duì)測(cè)試用的軟件進(jìn)行一次加殼,不過(guò)在加殼前先用PEiD查看一下,如圖2所示。
圖2 PEiD查殼
從圖2可以看出,該程序是Visual C++ 5.0 Debug版的程序。其實(shí)該程序是用Visual C++ 6.0寫(xiě)的,這里是PEiD識(shí)別有誤。不過(guò)只要用Visual C++ 6.0進(jìn)行編譯選擇Release版時(shí),PEiD是可以正確進(jìn)行識(shí)別的。使用ASPack對(duì)該程序進(jìn)行加殼,然后用PEiD查殼,如圖3所示。
圖3 用PEiD查看加殼后的文件
從圖3中可以看出,PEiD識(shí)別出文件被加過(guò)殼,且是用ASPack進(jìn)行加殼的。PEiD如何識(shí)別程序被加殼,以及加了哪種殼呢?在PEiD的目錄下有一個(gè)特征碼文件,名為“userdb.txt”。打開(kāi)這個(gè)文件,看大概內(nèi)容就能知道里邊保存了殼的特征碼。程序員的任務(wù)就是自己實(shí)現(xiàn)一個(gè)這個(gè)殼的識(shí)別工具。
殼的識(shí)別是通過(guò)特征碼進(jìn)行的,特征碼的提取通常是選擇文件的入口處。殼會(huì)修改程序的入口處,因此對(duì)于殼的特征碼來(lái)說(shuō),選擇入口處比較合適。這里的工具主要是用來(lái)學(xué)習(xí)和演示用的,因此寫(xiě)的查殼工具要能識(shí)別兩種類型,第一種類型是可以識(shí)別用Visual C++ 6.0編譯出來(lái)的文件,第二種類型是可以識(shí)別ASPack加殼后的程序。當(dāng)然,ASPack加殼工具的版本眾多,這里只要能識(shí)別上面所演示版本的ASPack就可以了。
如何提取特征碼呢?程序無(wú)論是在磁盤(pán)上還是在內(nèi)存中,都是以二進(jìn)制的形式存在的。特征碼是從程序的入口處進(jìn)行提取的,那么可以使用C32Asm以十六進(jìn)制的形式打開(kāi)這些文件,在入口處提取特征碼,也可以用OD將程序載入內(nèi)存后提取特征碼。這里選擇使用OD提取特征碼。用OD載入未加殼的程序,如圖4所示。
圖4 OD載入為加殼文件的入口處
可以看到,這就是未加殼程序的入口處代碼。在圖4中,“HEX數(shù)據(jù)”列中就是代碼對(duì)應(yīng)的十六進(jìn)制編碼,這里要做的就是提取這些十六進(jìn)制編碼。提取結(jié)果如下:
- "\x55\x8B\xEC\x6A\xFF\x68\x00\x65\x41\x00" \
 - "\x68\xE8\x2D\x40\x00\x64\xA1\x00\x00\x00" \
 - "\x00\x50\x64\x89\x25\x00\x00\x00\x00\x83" \
 - "\xC4\x94"
 
根據(jù)這個(gè)步驟,把ASPack的特征碼也提取出來(lái),提取結(jié)果如下:
- "\x60\xE8\x03\x00\x00\x00\xE9\xEB\x04\x5D" \
 - "\x45\x55\xC3\xE8\x01\x00\x00\x00\xEB\x5D" \
 - "\xBB\xED\xFF\xFF\xFF\x03\xDD\x81\xEB\x00"
 - "\xC0\x01"
 
有了這些特征碼,就可以開(kāi)始編程了。先來(lái)定義一個(gè)數(shù)據(jù)結(jié)構(gòu),用來(lái)保存特征碼,該結(jié)構(gòu)如下:
- #define NAMELEN 20
 - #define SIGNLEN 32
 - typedef struct _SIGN
 - {
 - char szName[NAMELEN];
 - BYTE bSign[SIGNLEN + 1];
 - }SIGN, *PSIGN;
 
利用該數(shù)據(jù)結(jié)構(gòu)定義2個(gè)保存特征碼的全局變量,具體如下:
- SIGN Sign[2] =
 - {
 - {
 - // VC6
 - "VC6",
 - "\x55\x8B\xEC\x6A\xFF\x68\x00\x65\x41\x00" \
 - "\x68\xE8\x2D\x40\x00\x64\xA1\x00\x00\x00" \
 - "\x00\x50\x64\x89\x25\x00\x00\x00\x00\x83" \
 - "\xC4\x94"
 - },
 - {
 - // ASPACK
 - "ASPACK",
 - "\x60\xE8\x03\x00\x00\x00\xE9\xEB\x04\x5D" \
 - "\x45\x55\xC3\xE8\x01\x00\x00\x00\xEB\x5D" \
 - "\xBB\xED\xFF\xFF\xFF\x03\xDD\x81\xEB\x00"
 - "\xC0\x01"
 - }};
 
程序界面是在PE查看器的基礎(chǔ)上完成的,如圖5所示。
圖5 查殼程序結(jié)果
提取特征碼后,查殼工作只剩特征碼匹配了。這非常簡(jiǎn)單,只要用文件的入口處代碼和特征碼進(jìn)行匹配,匹配相同就會(huì)給出相應(yīng)的信息。查殼的代碼如下:
- VOID CPeParseDlg::GetPeInfo()
 - {
 - PBYTE pSign = NULL;
 - // 定位文件入口位置
 - pSign = (PBYTE)((DWORD)m_lpBase
 - + m_pNtHdr->OptionalHeader.AddressOfEntryPoint);
 - // 比較入口特征碼
 - if ( memcmp(Sign[0].bSign, pSign, SIGNLEN) == 0 )
 - {
 - SetDlgItemText(IDC_EDIT_PEINFO, Sign[0].szName);
 - }
 - else if ( memcmp(Sign[1].bSign, pSign, SIGNLEN) == 0 )
 - {
 - SetDlgItemText(IDC_EDIT_PEINFO, Sign[1].szName);
 - }
 - else
 - {
 - SetDlgItemText(IDC_EDIT_PEINFO, "未知");
 - }
 - }
 
這樣,查殼程序的功能就完成了。在程序中提取的特征碼的長(zhǎng)度為32字節(jié),由于這里只是一個(gè)簡(jiǎn)單的例子,大家在提取特征碼的時(shí)候,為了提高準(zhǔn)確率,需要多進(jìn)行一些測(cè)試。




















 
 
 
 
 
 
 