【 拨壳机 】 EXE 档进入点搜寻程式 (程式原理说明书) - 软蛀 - ========================================================================== 信箱 Email:werongho@ms11.hinet.net 90网:Werong Ho 90:90/2 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┌━━┐ ┃前言┃ └━━┘ 拨壳机是笔者发展出的小程式 ,它能够自动追踪执行档 ,待程式执行完解压 缩、保护、病毒....等等外壳後 ,自动加以存档 ,并还原成执行档 ,使之成为无 压缩、保护、病毒的原始程式。在介绍这个软体之前 ,笔者必需介绍其所用的技 术 ,这样您的功力就会更上一层楼。 有很多朋友都知道 PKLITE、LZEXE、SPACE MACKER .... 等等软体都能够将 执行档压缩 ,使得程式占用磁片或硬碟的空间大幅缩小 ,也可以增加载入电脑的 读取速度 ,这是因为压缩档执行後会在电脑内自我解压缩 ,并执行这个解压缩後 的程式 ;由於这些动作都是在记忆体内完成 ,使用者根本感觉不到 ,不过压缩後 的档案虽然可以节省磁片空间、加快载入速度 ,但是原来的程式码被压小了 ,程 式码变成另一个样子 ,使用者因此无法对此档做修改、扫毒 ,同时程式载入记忆 体时 ,解压程式会自动配置较大的记忆体供程式摆放压缩、解压缩的程式码 ,於 是在解压过程会占用近两倍的记忆体大小 ,直到解压完毕 ,於是稍微大一点的程 式经过压缩 ,可能就会因为记忆体不足而无法执行了 ,尤其是国外的公益软体 , 经过压缩、电话线的资料传送来到台湾 ,国人想要将它中文化 ,第一道难题就是 将执行档解压缩。 EXESHAPE、EXEWRITE、EXEMAKE 这些软体都是将记忆体内的程式码还原 成执行档的工具 ,要使用这个工具的话 ,必需要先学会玩 DEBUG ,并追踪执行档 至进入点 ,才能回存程式码并还原成执行档 ,不过到底哪里是程式进入点呢 ,相 信大家都有所迷惑 ,而且万一要追踪的程式有坚强的防破能力 ,那么根本就没有 机会追踪到程式进入点。 我想很多朋友都会使用 EXEWRITE 或是 EXESHAPE 这套程式 ,但是先决 条件是要会玩 DEBUG ,且有能力追踪程式至进入点 ;现在的保护程式防破能力越 来越强 ,强到使用失去相容性的保护手段 ,其目地就是要当掉十分强悍的 S-ICE 除错程式 ,很不幸的为了当掉 S-ICE ,应用程式不断的 RESET 8259 、停止键盘 控制权、干掉 BIOS 堆叠区 ,结果一些好用的长驻程式都无用武之地 (如电动克 星、抓图程式) ,甚至会甘扰周边硬体的动作 (如PREXCM会甘扰滑鼠) ,所以将这 个失去相容性的外壳去除 ,已经变成非常需要。 笔者发展了这套『拨壳机』能搜寻程式进入点 ,并自动计算执行档资料 的正确长度 ,加以回存 ,让使用者可以将它还原 ,免除外壳的困扰。这套程式使 用了大量的80386特有命令 , 并以共生的方式追踪程式 ,因此不怕像RESET 8259 、干掉BIOS堆叠、干掉所有中断向量表、程式摆在 40:0 的地方执行 ,只有那种 会侦测是否载入长驻程式的外壳会造成当机 ,否则顶多是找不到进入点 ,而不会 当机。 -------------------------------------------------------------------------- ┌━━━━━━━━━┐ ┃技术一、 档案回写┃ └━━━━━━━━━┘ ※首先先要了解 EXE 档的档头的记录∶ (表格⒈) 位元组差距值 ┌━━━┬━━━━━━━━━━━━━━━━━━━━━━━━━┐ ┃0000H ┃EXE 档案记号的第一个部份 ( 4DH ) ┃ ├━━━┼━━━━━━━━━━━━━━━━━━━━━━━━━┤ ┃0001H ┃EXE 档案记号的第二个部份 ( 5AH ) ┃ ├━━━┼━━━━━━━━━━━━━━━━━━━━━━━━━┤ ┃0002H ┃档案大小除以512所得的馀数 ┃ ├━━━┼━━━━━━━━━━━━━━━━━━━━━━━━━┤ ┃0004H ┃档案的大小,以页为单位,一页为512个BYTE ┃ ├━━━┼━━━━━━━━━━━━━━━━━━━━━━━━━┤ ┃0006H ┃重新订位表格的项目数目 ┃ ├━━━┼━━━━━━━━━━━━━━━━━━━━━━━━━┤ ┃0008H ┃以 PARA ( 16 BYTE) 为单位的表头大小 ┃ ├━━━┼━━━━━━━━━━━━━━━━━━━━━━━━━┤ ┃000AH ┃程式载入之後所需的最小记忆体数 ┃ ├━━━┼━━━━━━━━━━━━━━━━━━━━━━━━━┤ ┃000CH ┃程式载入之後所需的最大记忆体数 ┃ ├━━━┼━━━━━━━━━━━━━━━━━━━━━━━━━┤ ┃000EH ┃堆叠的分段位移 ( SS ) ※⒈ ┃ ├━━━┼━━━━━━━━━━━━━━━━━━━━━━━━━┤ ┃0010H ┃在程式开始时 SP 的初值 ┃ ├━━━┼━━━━━━━━━━━━━━━━━━━━━━━━━┤ ┃0012H ┃查核字元 ┃ ├━━━┼━━━━━━━━━━━━━━━━━━━━━━━━━┤ ┃0014H ┃IP 的初值 ┃ ├━━━┼━━━━━━━━━━━━━━━━━━━━━━━━━┤ ┃0016H ┃程式码模组的分段位移量 ( CS ) ※⒉ ┃ ├━━━┼━━━━━━━━━━━━━━━━━━━━━━━━━┤ ┃0018H ┃档案中第一个重定位项目的差距值 ┃ ├━━━┼━━━━━━━━━━━━━━━━━━━━━━━━━┤ ┃001AH ┃覆叠号码 ( 0 代表程式的常驻部份 ) ┃ ├━━━┼━━━━━━━━━━━━━━━━━━━━━━━━━┤ ┃001BH ┃变数保留空间 ┃ └━━━┼━━━━━━━━━━━━━━━━━━━━━━━━━┤ ┃重新订位表格 (每 2 Word一组) ┃ ├━━━━━━━━━━━━━━━━━━━━━━━━━┤ ┃变数保留空间 ┃ ├━━━━━━━━━━━━━━━━━━━━━━━━━┤ ┃程式与资料段 ┃ ├━━━━━━━━━━━━━━━━━━━━━━━━━┤ ┃堆叠段 ┃ └━━━━━━━━━━━━━━━━━━━━━━━━━┘ 在这些资讯中 ,最难的就是重定位项目的建立 ;例如在组合语言中我们以 MOV AX,@DATA 或 CALL xxxx:xxxx ,这些会随著载入记忆体的位置而有所变动 的数值 ,就是我们要找的重定位表。底下是介绍程式载入後 ,移位表的变化。 00000 ┌━━━━━━━━━━┬━━━━━━━┐ ┃中断向量表     ┃同左     ┃ 00400 ├━━━━━━━━━━┼━━━━━━━┤ ┃MS-DOS ┃同左 ┃ ├━━━━━━━━━━┼━━━━━━━┤ 若前相 ┃ . ┃ . ┃ 程,同 ┃ . ┃ . ┃ 式环。 ┃ . ┃ . ┃ 载境 | ┃ . ┃ . ┃ 入不 +->├━━━━━━━━━━┼━━━━━━━┤<-0600 ┃mechine assembly ┃某一常驻程式 ┃ ┃ code code ├━━━━━━━┤<-0700 ┃ ┃ . . ┃ 程式本体┃ . . ┃ . . ┃ ┃ . . ┃ . . ┃ ┃ . . ┃ . . ┃ ┃B83412 MOV AX,1234┃ B83413 MOV AX,1334 ┃ . . ┃ . . ┃ ┃ . . ┃ . . ┃ ┃ . . └━━━┐ ┃ ┃9A34000001 CALL 1000:0034 ┃ CALL 1100:0034 ┃ . . ┌━━━┘ . ┃ ┃ . . ┃ . . ┃ 10034 ├━━━━━━━━━━┤ . . ┃ 其CS之 ┃ . . ┃ . . ┃ 它节程 ┃ . . ├━━━━━━━┤10134 不区式->┃ . . ┃ . . ┃ 在内 ┃ . . ┃ . . ┃ ┃ . . ┃ . . ┃ 12340 ├━━━━━━━━━━┤ . . ┃ ┃DATA SEGMENT ┃ . . ┃ ┃ . ├━━━━━━━┤13340 ┃ . ┃DATA SEGMENT ┃ ┃ . ┃ . ┃ ^ ┃ . ┃ . ┃ | ┃DATA ENDS ┃ . ┃ 皆 ├━━━━━━━━━━┤ . ┃ 固 ┃ . ┃DATA ENDS ┃ 定 ┃ . ├━━━━━━━┤ 差 ┃ . ┃ . ┃ 一 ┃ . ┃ . ┃ 个 ┃ . ┃ . ┃ 定 ┃ ┃ . ┃ 值 EXE 档在载入记忆体时 ,DOS 会先把程式主体载入段落记忆体的偏移100H的 地方 ,然後根据档头的重定位表将指定的程式码加上段落值(DS) ,最後将控制权 交给应用程式 ,完成整个载入的动作 ;也就是说你可以在 DS:0100h 的地方找到 程式码的开头。 从上面在的图我们可以发现有些程式码会随著载入记忆体段落不同 ,而有著 不同的变化 ,如果能在记忆体不同的时候将程式码存档 ,就可以根据这个变化制 作出档案重定位表了。所有的 EXE 档的档头资料都齐全了 ,你应该会还原执行 档了吧。 -------------------------------------------------------------------------- ┌━━━━━━━━━━━┐ ┃技术二、 'T' 旗标介绍┃ └━━━━━━━━━━━┘ 旗标暂存器可以用来显示 CPU 最近一次执行的结果 ,每一个位元都代表不同 的意思 ,其中 TF 是一个陷阱旗标 ,当此位元设定为 '1' 的时候 , CPU 每执行 一个指令就会产生 INT_01 中断 ,供应用程式拦截除错使用。 ┌━┬━┬━┬━┬━┬━┬━┬━┬━┬━┬━┬━┬━┬━┬━┬━┐ ┃xx┃xx┃xx┃xx┃OF┃DF┃IF┃TF┃SF┃ZF┃xx┃AF┃xx┃PF┃xx┃CF┃ └━┴━┴━┴━┴━┴━┴━┴━┴━┴━┴━┴━┴━┴━┴━┴━┘ ┌━━━━━━━━━━━━━━━┐ CF:进位旗标 ┃ 最简单的打开 'T' 旗标的程式: ┃ PF:同位旗标 ┃ ┃ AF:辅助进位旗标 ┃ PUSHF ┃ ZF:零旗标 ┃ POP AX ┃ SF:正负旗标 ┃ OR AX,0100h ┃ OF:溢位旗标 ┃ PUSH AX ┃ TF:陷阱旗标 ┃ POPF ┃ IF:中断旗标 └━━━━━━━━━━━━━━━┘ DF:方向旗标 -------------------------------------------------------------------------- ┌━━━━━━━━━━━━━━━━━━━┐ ┃技术三、 硬体中断 - 除错暂存器的运用.┃ └━━━━━━━━━━━━━━━━━━━┘ 除错暂存器对於程式除错过程十分有用 ,它可以不必依靠 'T' 旗标 ,或是 变动程式码即可产生硬体中断 ,只要将记忆体的绝对位址告诉除错暂存器 ,并设 定发生硬体中断的条件 ,便可以产生 INT_01 的硬体中断。 你可以设定电脑执行、读取、写入、读写某一位址 ,便产生 INT_01 的硬体 中断 ,如果善加利用的话 ,它确实是一个软体除错的最好方法 ,由於它是 80386 CPU 才提供的内建命令 ,所以设定此暂存器後 ,并不会因此降低电脑执行速度 , 真是太帅了。 80386、80486 都有 8 个除错暂存器 ,我们分别以 DR0~DR7 来表示它 ,每 个暂存器都是 32Bit ,以下是它的简易设定方式∶ DR0~DR3 提供应用程式设定发生中断的32位元绝对位址 DR4~DR5 保留 DR6 表示发生中断(INT_1)的原因 bit14 若为 '1' 表示这个中断的发生是因为 'T' 旗标 bit0 若为 '1' 表示这个中断的发生原因为 DR0 发生的 bit1 若为 '1' 表示这个中断的发生原因为 DR1 发生的 bit2 若为 '1' 表示这个中断的发生原因为 DR2 发生的 bit3 若为 '1' 表示这个中断的发生原因为 DR3 发生的 DR7 表示发生中断(INT_1)的条件 ,每一个 bit 代表意义如下∶ ┌31━━━━━━━━━━━━━━━━━━━━━━━━━━━━00┐ ┃L┃R┃L┃R┃L┃R┃L┃R┃ ┃ ┃ ┃G┃L┃G┃L┃G┃L┃G┃L┃ ┃E┃/┃E┃/┃E┃/┃E┃/┃001000┃1┃1┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃N┃W┃N┃W┃N┃W┃N┃W┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃3┃3┃2┃2┃1┃1┃0┃0┃ ┃ ┃ ┃3┃3┃2┃2┃1┃1┃0┃0┃ └━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┘ LEN 表示发生中断的资料长度 R/W 表示发生中断的条件 00 表示 BYTE 00 执行 01 表示 WORD 01 写入 10 保留 10 未定义 11 表示 DWORD 11 读取与写入 G 表示只要符合条件便会发生中断 L 表示使用硬体中断 ,且可供其它程式使用 例如: DR0=000A0000h (32位元绝对位址) DR7=00012303h 表示当读取 A000:0000 的记忆体时 ,会发生 INT_01 的中断 ,且 DR6 的 Bit0='1' ,记得使用者读完後 ,要自己把此 Bit 清为 '0' 唷。 -------------------------------------------------------------------------- ┌━━━━━━━━━━━┐ ┃技术四、 中断表大搬移┃ └━━━━━━━━━━━┘ 前面提到产生硬体中断时 ,其实就是产生 INT_01h ,我们可以靠拦截此中断 向量而得知是否产生硬体中断 ,不过万一你要除错的程式也要用到 INT_01h 的 时候 ,势必两者就会打架 ,最後只剩一个程式拥有INT_01 ,为了省掉不必要的控 制权争夺战 ,笔者便想到了此「乾坤大挪移」 ,将所有的中断表通通搬到别的地 方 ,将原先的中断表格 0000:0000 ~ 0000:03FF 的记忆体保留给应用程式 ,我 再另辟一块中断向量表 ,讲到这里 ,我相信有读者一定看不懂它是什么意思 ,原 来平常产生中断时 ,CPU 会去查最前端的1K记忆体 ,并将 CS、IP 指向它所代表 的位址继续执行 ,不过自 80386 以後的电脑就允许你把此表随意搬移到记忆体 的任何一个角落 ,例如 1000:0000 的地方 ,甚至是 1MB 以外的地方。 原来 80386 多了一个 IDT 可以设定 ,用来告诉电脑中断表的位址 ,它的控 制很简单 ,例如 LIDT FWORD PTR CS:[IDTABLE] 而 IDTABLE 内的值为 03FFh , 12345678h ^^^^^长度 ^^^^^^^^^中断表所在位址 使用「乾坤大挪移」的方法很像一般 TSR 的处理方式 未经过搬移前的中断处理方式: INT_X ━━━━━→ 根据 0000:0000 去查出所在中断服务程式位址 经过搬移後的中断处理方式: INT_X ━━━━━→ 以 IDT 表指向的位址查出中断表服物程式位址 如果您将所有的中断指向自己 ,那么你就可以决定是不是要带动原先的中 断服务程式 ,如同下图: INT_X ━━→ 自己的中断 ━━→ 呼叫原先的旧中断 1. 笔者使用 LIDT 将中断表的 TABLE 搬到自己的程式内 ━━━━━━━━━━━━━━━━━━━━━━━━━ xor eax,eax xor edx,edx mov ax,cs mov cl,04h shl eax,cl mov dx,offset idttab add eax,edx mov bx,offset idtadds mov cs:[bx+02h],eax ;计算自己的中断表所在记忆体绝对位址 mov bx,offset idtadds lidt fword ptr cs:idtadds ;中断表大搬移「乾坤大挪移」 . . idtadds dw 03ffh,0000h,0000h idttab dw offset new_00,code,offset new_01,code,offset new_02,code dw offset new_03,code,offset new_04,code,offset new_05,code . . 省略 (共256个中断) . 现在你已经把中断表给搬移了 ,於是中断发生时 ,电脑不会再去抓最前面1K 的中断表了 ,当发生中断时 ,电脑改成根据上面重设的位址去抓向量表。如果您 想要拦截什么中断就可以从这地方下手。 2. 将 256 个中断表指向自己 ,且服务程式都要去带动原始 INT new_00 : push 0000h jmp int_emu new_01 : push 0001h jmp int_emu new_02 : push 0002h jmp int_emu ;限於篇幅本处未全刊出 int_emu : ;共要处理 255 个中断 pushf ; push ax push bx push bp push ds cli mov bp,sp xor bx,bx mov ds,bx mov bx,ss:[bp+0ah] add bx,bx add bx,bx mov ax,ds:[bx] ;根据所产生的中断码 ,去 0:0 的相对 mov cs:int_stack,ax ;应位址找出指向位址 mov ax,ds:[bx+02h] mov cs:int_stack+02h,ax pop ds pop bp pop bx pop ax popf add sp,02h jmp dword ptr cs:oopstack ;带动原始中断 於是一场控制权争夺战终於避过了 ,应用程式拦截属於它的中断(最前面1K) 而您拦截属於自己的中断表 ,再去带动一下应用程式的中断表 ,於是大家都可以 和平共存了。 -------------------------------------------------------------------------- ┌━━━━━━━━┐ ┃技术整合实地应用┃ └━━━━━━━━┘ 1. 先将自己的中断表处理程式安装好 2. 使用 AX=4B01h INT_21h 载入要拨壳的程式 3. 打开 'T' 旗标并执行原始程式 ,应用程式每执行一个指令就会发生 INT_01 然後就可以监看何时程式执行到进入点 ,并在适当时机打开除错暂存器 ,取 代 'T' 旗标的执行 ,用以加快程式执行速度 4. 若发现程式经过 RETF、JMP FAR....等等长程跳跃指令 ,便检查 DS=ES 且 DS:[0000h] 的程式码为 CD20 ,这些条件都符合的话 ,那它应该就是 程式进入点 ,把程式码加以存档 ,再用档案还原技术变回 .EXE 档 举个最简单的例子来说 , .COM 格式的档案在载入记忆体後 ,一定是 DS=ES=CS ,且IP值一定等於 100h ,所以我们可以设定除错暂存器 ,当程式执行到 DS:100h 的 时候产生硬体中断 ,会发生此中断的原因只有两种可能 ,一种是程式刚载入时执行产 生的 ,另一种是经过解压、保护的手续後 ,又回到 DS:100 去执行解压後的主程式 , 所以我们知道当中断下在 DS:100 的地方 ,产生硬体中断的条件为"执行" ,那么一共 会发生两次中断 ,当发生第二次中断的时後 ,档案加以回写 ,於是我们就完成了拨壳 的动作了。 大多数写保护程式的人都习惯的先验证保护是否过关 ,然後才去将主程式解码 , 执行 ,而拨壳机也是利用这个习惯去找到真正的程式进入点 ,我们只需要监视 DS:100 的地方被写入几次 ,就可以知道它有几层壳 ,就算不代表壳的层数 ,起码可以知道复 杂程度。就在最後一次写入 DS:100 的时候我们打开 'T' 旗标去追程式码 ,直到遇 到 RETF.IRET 这种长程跳越指令以後 ,就是它真正的程式进入点 ,屡试不爽。 在本程式的 TEST.EXE 就是监视 DS:100 被写入的次数 ,并秀给使用者看。 RIPPER32.EXE 才是根据壳数去拨它 ,笔者顺便把完整的原始程式秀给各位去改 良它。至於笔者提到保护模式版的拨壳机 ,那是因为很多保护程式会去读写除错暂 存器 ,使得拨壳机失效 ,所以笔者利用进入保护模式 ,并在最後一次读写除错暂存 器或产生 INT_1 的时候 ,偷偷下断点 ,这样就可以与别的应用程式并存 ,这点很 奸诈 ,至於应用程式读写除错暂存器的次数可以由笔者另一套软体 KPEMU310.ZIP 内可以找到 ,但是因为保护模式版拨壳机相容性太差劲 ,我不愿意流出来害人。 或许各位会觉得本篇好像是在探讨程式进入点 ,其实当你找到程式进入点 ,便可 以透过档案回写技术 ,将资料档还原成执行档了 ,这样您是否懂了 ? 有关 EXEWRITE、RIPPER32(拨壳机) 程式都可以在笔者BBS站台找到 (02)5955461 --------------------------------------------------------------------------