本帖最后由 ShaBility 于 2010-8-7 10:26 编辑
FuckProtect 1.0.0.7 试炼品下载: http://www.52pojie.cn/thread-51624-1-2.html
ximo牛研制的壳,很好很强大,脱壳过程不再说,因为之前已经有人发脱文了。
这次只简单分析一下CodeReplace(可以下载附件进行调试,已脱壳)
OD载入,随便点进一个CALL
004020B3 55 push ebp
004020B4 E8 C7AC0000 call 0040CD80 ; <-CodeReplace
004020B9 56 push esi
004020BA FF75 14 push dword ptr ss:[ebp+14]
004020BD FF75 10 push dword ptr ss:[ebp+10]
004020C0 FF75 0C push dword ptr ss:[ebp+C]
004020C3 FF75 08 push dword ptr ss:[ebp+8]
004020C6 E8 A70B0000 call 00402C72
004020CB 85C0 test eax, eax
004020CD 75 09 jnz short 004020D8
004020CF C745 EC 0000000>mov dword ptr ss:[ebp-14], 0
004020D6 EB 73 jmp short 0040214B ; <-CodeReplace
004020D8 E8 A3AC0000 call 0040CD80
004020DD 56 push esi
点进看看
0040CD80 60 pushad
0040CD81 8B5424 20 mov edx, dword ptr ss:[esp+20]
0040CD85 83EA 05 sub edx, 5
0040CD88 52 push edx ; 获取被代码替换的地址
0040CD89 E8 D2FFFFFF call 0040CD60
进入到call 0040CD60
0040CD60 8B5424 04 mov edx, dword ptr ss:[esp+4] ; 被代码替换的地址
0040CD64 33C0 xor eax, eax
0040CD66 81F2 10200000 xor edx, 2010 ; 异或0x2010
0040CD6C B9 00D54000 mov ecx, 0040D500 ; 关键点1
0040CD71 3B11 cmp edx, dword ptr ds:[ecx]
0040CD73 74 06 je short 0040CD7B
0040CD75 40 inc eax
0040CD76 83C1 04 add ecx, 4 ; 判断下一个DWORD
0040CD79 ^ EB F6 jmp short 0040CD71
0040CD7B C3 retn
关键点1是啥呢 Ctrl+G 0040D500
0040D500 00403249 111_.00403249
0040D504 0040324E 111_.0040324E
0040D508 004034B8 111_.004034B8
0040D50C 00403603 111_.00403603
0040D510 0040365B ASCII "P@"
0040D514 004036FD 111_.004036FD
0040D518 0040394C 111_.0040394C
0040D51C 00403C75 111_.00403C75
0040D520 00403D32 111_.00403D32
0040D524 00403D37 111_.00403D37
0040D528 00403D21 111_.00403D21
0040D52C 00403D9C 111_.00403D9C
0040D530 00403DB5 111_.00403DB5
0040D534 00403EF2 111_.00403EF2
0040D538 004000A4 111_.004000A4 //edx的值
0040D53C 004000C8 111_.004000C8
0040D540 00400182 111_.00400182
0040D544 0040021B 111_.0040021B
0040D548 00400202 111_.00400202
0040D54C 00400209 111_.00400209
0040D550 00400255 111_.00400255
0040D554 0040027B 111_.0040027B
0040D558 004002DA 111_.004002DA
0040D55C 00400343 111_.00400343
0040D560 00400397 111_.00400397
0040D564 004003E8 111_.004003E8
0040D568 00400451 111_.00400451
0040D56C 00400456 111_.00400456
原来,0040D500 就是保存了各个被代码替换的地址 知道了这些,继续跑下去吧
0040CD8E 8DB480 00DF4000 lea esi, dword ptr [eax+eax*4+40DF00] //关键点2
0040CD95 33C9 xor ecx, ecx
0040CD97 E8 00000000 call 0040CD9C
0040CD9C 5D pop ebp
0040CD9D 81ED 4C1F4000 sub ebp, 00401F4C
0040CDA3 8DBD 861F4000 lea edi, dword ptr [ebp+401F86] //edi=0040CDD6 预留空位写原来的代码
其实,关键点2就是偷的代码,其算法为:
(GetCall- 40D500)+((GetCall- 40D500)/4)+40DF00
GetCall就是0040D500 保存的被代码替换的地址
Ctrl+G 0040CDD6
0040CDD6 90 nop //这就是预留的空位
0040CDD7 90 nop
0040CDD8 90 nop
0040CDD9 90 nop
0040CDDA 90 nop
0040CDDB 68 90909090 push 90909090
0040CDE0 C3 retn
0040CDA9 B9 05000000 mov ecx, 5 //偷的是5个字节
0040CDAE F3:A4 rep movs byte ptr es:[edi], byte ptr [esi] //写入
0040CDB0 83EF 05 sub edi, 5
0040CDB3 33C0 xor eax, eax
0040CDB5 8BC8 mov ecx, eax
0040CDB7 8A040F mov al, byte ptr [edi+ecx]
0040CDBA 34 28 xor al, 28 //开始解密,每个字节异或0x28
0040CDBC 3E:880439 mov byte ptr [ecx+edi], al
0040CDC0 41 inc ecx
0040CDC1 83F9 05 cmp ecx, 5
0040CDC4 ^ 72 F1 jb short 0040CDB7 //判断是否解密完
0040CDC6 5A pop edx //edx=被代码替换的地址
0040CDC7 83C2 05 add edx, 5 //返回地址
0040CDCA 8DBD 8C1F4000 lea edi, dword ptr [ebp+401F8C]
0040CDD0 8917 mov dword ptr [edi], edx //修正返回地址
0040CDD2 61 popad
0040CDD3 83C4 04 add esp, 4 //后面的代码处有入栈操作,故ESP+4
0040CDD6 8BEC mov ebp, esp //被偷的代码已经写回来了
0040CDD8 83EC 1C sub esp, 1C
0040CDDB 68 90909090 push 90909090
0040CDE0 C3 retn
因为40d500 保存被代码替换的地址和保存偷代码的 40DF00都是硬编码,所以我们可以写个修复程序,贴上关键源码
//把需要修复的程序改名为fuck.exe,修复前请备份
int main()
{
//这个可以自己修改
char szFilename[MAX_PATH] = "fuck.exe";
int sel;
UINT i=0, count=0;
hFile = CreateFile(
szFilename,
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (hFile == INVALID_HANDLE_VALUE)
{
printf("Fail to open file\n");
getchar();
return 0;
}
if (CreateMap()==FALSE)
{
printf("Error\n");
getchar();
return 0;
}
if (IsPE()==FALSE)
{
printf("Error\n");
getchar();
return 0;
}
while(1)
{
//判断是否最后一个区段
printf("File must be DUMPED!!!\nImpRec Fixed? 1:Yes|2:No\n->");
scanf("%d", &sel);
if (sel == 1 || sel ==2)
{
break;
}
}
PIMAGE_DOS_HEADER imDos = PIMAGE_DOS_HEADER(lpBase);
PIMAGE_NT_HEADERS imNt = PIMAGE_NT_HEADERS((DWORD)lpBase + imDos->e_lfanew);
PIMAGE_SECTION_HEADER imSec = PIMAGE_SECTION_HEADER((DWORD)imNt + sizeof(IMAGE_NT_HEADERS));
i = imNt->FileHeader.NumberOfSections;
i--;
if (sel == 1)
{
i--;
}
PIMAGE_SECTION_HEADER imfkSec = PIMAGE_SECTION_HEADER((DWORD)imSec + sizeof(IMAGE_SECTION_HEADER) * i);
DWORD dwGetCallRVA = 0x1500 + imfkSec->VirtualAddress;
DWORD dwGetCallOffset = RVAToOffset(dwGetCallRVA);
DWORD dwCrOffset = RVAToOffset((dwGetCallRVA + 0xA00));
DWORD dwCallAddr, dwRead;
BYTE bCr[5] = "";
DWORD dwWrite;
while (1)
{
//读被代码替换的地址
SetFilePointer(hFile, dwGetCallOffset,NULL,FILE_BEGIN);
ReadFile(hFile,&dwCallAddr,sizeof(DWORD),&dwRead,NULL);
if (dwCallAddr == 0x0)
{
break;
}
dwCallAddr ^=0x2010;
//printf("%08X : ", dwCallAddr);
SetFilePointer(hFile, dwCrOffset,NULL,FILE_BEGIN);
ReadFile(hFile, &bCr, sizeof(bCr), &dwRead, NULL);
for (i = 0; i < 5; i++)
{
bCr[i] ^= 0x28;
//printf("%02X ", bCr[i]);
}
dwCallAddr = RVAToOffset((dwCallAddr-imNt->OptionalHeader.ImageBase));
SetFilePointer(hFile, dwCallAddr,NULL,FILE_BEGIN);
//修复
WriteFile(hFile, &bCr, sizeof(bCr), &dwWrite, NULL);
//printf("\n");
count++;
dwCrOffset += sizeof(bCr);
dwGetCallOffset += sizeof(DWORD);
}
printf("Replace : %d\n", count);
printf("File Fixed\n");
SetEndOfFile(hFile);
DeleteMap();
getchar();
getchar();
return 0;
}
写得比较挫,都是抄的。 |