• 标 题:我的还原经历
  • 作 者:askformore
  • 时 间:004-08-24,15:49
  • 链 接:http://bbs.pediy.com

题目:我的还原经历

由于曾经用自己未完全破解(当然在一个偶然才发现)的 EasyCHM 编译了一本定名为abc.chm,并将相关的源文件痛快地删除了,后来发现后,就顺便把 EasyCHM 破解完全!可惜该abc.chm文件内的所有文件都被附加 Unregistered 的东西(“加料料理”,我不太接受),它特征为:每个文件尾部都加上 F1h 长度的固定16进制串。因为释放文件数量比较大,如果人工用16进制工具进行还原,天啊,所以需要批量处理释放文件,于是到 bbs.pediy.com 求助,谁知不久后论坛就给人攻击了,无奈只好亲自就从反编译功能方面着手调试跟踪。反编译当然是释放文件啦,那么就是要写文件,跟踪发现程序(功能已破解完全,不然反编译还会“加料料理”)反编译中会对每一个被释放文件以每次1000h的长度划分到缓冲区(待写),不足1000h就认为是该文件的最后一次写操作。

下面跟踪到写文件必经地方(具体地址已忽略):

代码:
00333314   jmp dword ptr ds:[<&kernel32.WriteFile>]; // 可从这修改到指定代码实行的地方,如:999900 0033331C   push ebp 0033331D   mov ebp,esp 0033331F   push ecx 00333320   push ebx 00333321   push esi 00333322   push edi 00333323   mov esi,ecx 00333325   mov edi,edx 00333327   mov ebx,eax 00333329   mov eax,dword ptr ss:[ebp+10] 0033332C   movzx edx,word ptr ds:[ebx+4] 00333330   and edx,eax 00333332   cmp eax,edx 00333334   jnz short EasyCHM.0033338E 00333336   push 0                              ; // lpOverlapped 倘若在指定FILE_FLAG_OVERLAPPED的前提下打开文件,这个参数就必须引用一个特殊的结构。那个结构定义了一次异步写操作。否则,该参数应置为空 00333338   lea eax,dword ptr ss:[ebp-4] 0033333B   push eax                            ; // lpNumberOfBytesWritten 实际写入文件的字节数量 0033333C   mov eax,dword ptr ds:[ebx+8] 0033333F   imul esi 00333341   push eax                            ; // nNumberOfBytesToWrite 要写入数据的字节数量。 00333342   push edi                            ; // lpBuffer 要写入的一个数据缓冲区 00333343   mov eax,dword ptr ds:[ebx] 00333345   push eax                            ; // hFile 文件的句柄 00333346   call dword ptr ss:[ebp+C]           ; // 实际是 Call 00333314 ; kernel32.WriteFile 00333349   test eax,eax             ; // 写文件成功则返回 1 0033334B   jnz short EasyCHM.0033335E 0033334D   call <jmp.&kernel32.GetLastError>


WriteFile 函数堆栈参考:

代码:
0081E760    00333349  /CALL 到 WriteFile 来自 EASYCHM.00333346 0081E764    000000C8  |hFile = 000000C8 (region) 0081E768    0081E7E8  |Buffer = 0081E7E8 0081E76C    00001000  |nBytesToWrite = 1000 (4096.) 0081E770    0081E784  |pBytesWritten = 0081E784 0081E774    00000000  \pOverlapped = NULL 0081E778    01281D0C  ASCII "<br><hr><br> This file is decompiled from a .CHM file <br>by an UNREGIST>......"


由于发此贴时还原文件走运,所以未发觉代码有问题(可能出现的情况未作考虑和cmps指令理解不佳),现在修正如下:

实行代码Begin:

代码:
00999900   pushfd                          ; // 压入标志  --\ 00999901   pushad                          ; // 压入寄存器--/ 共 9 DWORDS 00999902   mov esi,dword ptr ss:[esp+3C]   ; // 放入取得Unregistered字符串解码地址 00999906   mov edi,dword ptr ss:[esp+2C]   ; // 取得待写 buffer 首地址 0099990A   mov edx,dword ptr ss:[esp+34]   ; // 取写文件大小(buffer)的实际地址 0099990E   mov ecx,dword ptr ds:[edx]      ; // 取写文件大小的实际地址 00999910   cmp ecx,0F1                     ; // 假设不幸,Unregistered字符串被分别分割到两个缓冲区(最后一个和倒数第二个)。 00999916   jb short EASYCHM.00999950       ; // 就要让它暂停,将当前的文件名在eax中给出(需手工查找修复,但为数应该不会多的,可能就那么一二三个。) 00999918   add edi,ecx                     ; // 调整到待写缓冲区的底部 0099991A   sub edi,0F1                     ; // 缓冲区底部指针 上移 到 指定位置 00999920   mov ecx,0F1                     ; // 从指定的位置比较 00999925   xor eax,eax                     ; // 清 eax 00999927   xor ebx,ebx                     ; // 清 ebx 00999929   cmps byte ds:[esi],byte es:[edi]; // 此指令要理解其含义,一开始搞错了,小心! 0099992A   sete al                         ; // 相等让 al 为 True 0099992D   add bl,al                       ; // 累计测试结果 0099992F   inc esi                         ; // 调整到下一字节 00999930   inc edi                         ; // 调整到下一字节 00999931   dec ecx                         ; //  00999932   jnz short EASYCHM.00999929      ; // 循环直到 ecx 为 0 00999934   cmp bl,0F1                      ; // 测试相等的字节数是否为 F1 00999937   jnz short EASYCHM.00999948     ; // 不相等则正常处理 00999939   sub dword ptr ss:[edx],0F1      ; // 重写 buffer Size 00999940   sub dword ptr ss:[esp+30],0F1   ; // ******重新写要输出 Size 关键****** 00999948   popad                           ; // 还原标志 00999949   popfd                           ; // 还原寄存器 0099994A   jmp dword ds:[kernel32.WriteFile; // 恢复执行原指令 00999950   mov eax,dword ptr ss:[esp+8D]   ; // 当前被写的文件名地址填到 eax ,执行后 have a look eax 00999954   jmp short EASYCHM.00999948      ; // 在此指令上下断点,运行中不能批量修复的文件则会在此中断,需手工到释放目录查找eax指向的文件修复。


实行代码End:

代码已经写好了,现在就来执行一下,谁知只成功释放了一个还原文件,就进入了异常陷井,进入了一个无法继续的异常。重新试了好几次都这样,于是从地址00333349下断,一直跟踪发现这条指令有问题:

代码:
00333379   cmp esi,dword ptr ss:[ebp-4]    ; // 陷井:如果你更改了的实输出 Size ,则判断属于异常错误!esi为原输出 Size,[ebp-4]为你实际输出 Size 0033337D   je short EasyCHM.0033339D //不跳。。。哈哈


修改后重来,按确定 ---- 就这样,我的abc.chm成功地还原了!:D