世上的唯美,是那雙清澈的眼眸 ——題記
以前看《自己動(dòng)手寫操作系統(tǒng)》的時(shí)候,書中有段代碼改變了自身并且完成了正確的跳轉(zhuǎn),這個(gè)想法實(shí)在漂亮(記得是從實(shí)模式跳轉(zhuǎn)到保護(hù)模式,晚了不去查了)。
但當(dāng)時(shí)驚艷之后沒(méi)做深入考慮,這是個(gè)失誤。代碼可以改變自身,利用這點(diǎn)可以做一點(diǎn)簡(jiǎn)陋的保護(hù)機(jī)制(代碼加密,當(dāng)然也可以用于病毒程序)。但是估計(jì)保護(hù)效果
不會(huì)很好,下面分析一下卡巴斯基給出例子。
#include <windows.h>
#define CRYPT_LEN ((int)crypt_end - (int)for_crypt)
mark_begin()//用以標(biāo)記,待加密函數(shù)在文件中的起始位置
{
__asm _emit 'K' __asm _emit 'P' __asm _emit 'N' __asm _emit 'C'
}
for_crypt(int a,int b)//待加密函數(shù)
{
return a + b;
}crypt_end(){}
mark_end()//用以標(biāo)記,待加密函數(shù)在文件中的結(jié)束位置
{
__asm _emit 'K' __asm _emit 'P' __asm _emit 'N' __asm _emit 'C'
}
crypt_it(unsigned char *p,int c)//解密過(guò)程
{
int a;
VirtualProtect(p,c,PAGE_READWRITE,(DWORD*)&a);
for (a = 0;a < c; a++) *p++ ^=0x66;
VirtualProtect(p,c,PAGE_READONLY,(DWORD*)&a);
}
main()
{
crypt_it((unsigned char*)for_crypt,CRYPT_LEN);//調(diào)用解密函數(shù),解密被保護(hù)的代碼
printf("%02Xh\n",for_crypt(0x69,0x66));//函數(shù)運(yùn)行結(jié)果
}
這個(gè)文件編譯連接完成后,肯定是沒(méi)法運(yùn)行的。
加密工作是在編譯完之后,用十六進(jìn)制編輯器修改兩個(gè)KPNC之間部分代碼(此處就是^=0x66的逆運(yùn)算,簡(jiǎn)單的XOR算法)。
當(dāng)然完成之后兩個(gè)KPNC標(biāo)志也要換成比較迷惑的數(shù)值,但這些都是徒勞的,沒(méi)法抵擋灰闊那雙清澈的眼眸。如果為了泄被破解之憤,大可以改成WOCA或者F.UCK等標(biāo)志。
for (a = 0;a < c; a++) *p++ ^=0x66;試圖修改.text段,這回異常的。所以連接obj文件時(shí)加上選項(xiàng)/section:.text ERW指定具有讀寫和執(zhí)行屬性。
而Windows API VirtualProtect(p,c,PAGE_READWRITE,(DWORD*)&a);修改了被保護(hù)代碼的內(nèi)存頁(yè)面屬性,指定為可讀寫。
這種保護(hù)對(duì)于靜態(tài)反匯編來(lái)說(shuō)很容易制造混亂。但是在調(diào)試面前卻不堪一擊,那個(gè)API可是很顯眼啊。另外代碼段中多出一堆亂七八糟的東西也是很扎眼的。解密算法也較容易分析出來(lái)(及時(shí)算法復(fù)雜也只是時(shí)間問(wèn)題)。不過(guò)跟反調(diào)試技術(shù)結(jié)合一下,應(yīng)該有點(diǎn)用處。
只是玩味一下這個(gè)東西。
另外:也可以在堆棧中分配空間,那是可寫的,再把.text段中的負(fù)責(zé)“修改自身”的函數(shù)通過(guò)memcpy放到堆中,調(diào)用堆中的修改函數(shù)來(lái)改變自身代碼。雖然沒(méi)有異常,但是可能存在代碼重定位問(wèn)題(畢竟代碼執(zhí)行的地址無(wú)法預(yù)知了)。