用了几个晚上的时间,将我破解vfp程序的心得用抓图的方式总结了一下,希望对
爱好vfp程序的朋友能有所帮助,其中的错误也请大家指正。
Visual Foxpro做为一种面向对象的数据库语言,有其简单易用的特点,虽然经常受到各种各样的批评,微软发布6.0版后,也曾声明版本不再更新,但现在看来,VFP程序还是有一定的生命力的,微软自6.0版以后开发了7.0版,目前8.0beta版在微软的官方网站已有下载。尤其在我国,早期DOS时代DBaseIII曾被广泛运用,VFP出现后,源程序稍加改动就能变成可在Window平台运行的VFP版本,可以节省大量的资源,减少人员的二次培训。因此在我国VFP程序的用量还是较大的,专门开发此类程序的公司也不在少数。
由于VFP程序的编译是一种伪编译过程,经过一定的处理,可以得到完整的源代码。自从ReFox、UnFoxAll等反编译软件出现后,为保护作者自己的利益,软件的加密保护就显得十分重要,相应的,VFP程序的加密也越来越严。但由于VFP固有的特点—即解释执行,VFP的解释器必须要得到可识别的代码,所以VFP程序的破解相对其他语音编译的程序来说,还是比较简单的。
一、VFP EXE程序的特点
VFP标准的编译EXE文件是如下一种格式:
可执行部分+APP文件+结构说明
其中可执行部分是标准的PE文件,APP部分就是VFP编译的应用程序代码,最后的结构为14个字节,用来描述APP文件,其中重要的是最后4个字节,反映的是APP文件的大小。
头部的可执行部分只是简单地加载VFP的解释器(解释器名称为vfp6r.dll、vfp7r.dll、vfp8r.dll分别对应6.0、7.0、8.0.版本)执行app文件。现在的加密软件大多利用这个特点,自己构造一个PE头,利用各种壳的技术保护APP代码。
因此我们可以这样认为,对于VFP的可执行文件,除APP文件以外的可执行部分,都可认为是壳。
二APP文件的结构
1.app文件结构
app文件是vfp生成的应用程序或Active Document
其结构为:
字节偏移 说明
0-1 文件标识 &Hf2fe为vfp的app
2 加密标识 bit 0 为1,未加密
bit 0 为0,加密,此时bit 4(0或1)区分为两类
3-4 编译版本 &H0220 -vfp 6.0/7.0
&H021f -vfp 5.0
5-6 文件数目
7-8 主文件位置(从0开始)
9-12 文件名列表地址
13-16 文件说明结构列表地址
17-20 文件名列表长度
21-36 加密类型密码表
37-38 解密key
39-40 文件校验和
文件名列表地址
[目录名],00,文件名,00,文件名,00,...
文件说明结构列表地址
每个文件占用25个byte,总长度为文件数*25
0 文件类型 0为代码 其他数据
1-4 文件开始地址
5-8 文件结束地址
9-12 文件名相对文件名列表的首地址
其他不详
三、VFP的加密特点及脱壳
VFP的加密基本上可以分为两种—文件型加密和内存型加密,这两种加密的区分主要在APP文件的存放位置,存放在某个文件中就为文件型,存放在内存中就叫内存型。但对解密者来说,关心的是算法。以上的分类加不加以区别问题不大。
1、文件头简单替换型
这种加密方式是比较早期的一种加密方式,能够阻止反编译软件,但APP代码在文件中以明码出现,如xx王5.05B(http://www.cxsw168.com)
脱壳方法:
打开HEX,查找FEF2找到后与APP文件的结构对比一下,发现没有问题,把从FEF2开始到文件尾的数据存成一个文件,取名XXX.app,脱壳完成。
2、文件隐藏型
这类加密在exe文件中已经没有app的明码了,app文件是由主程序运行过程中动态生成的,以文件形式存在硬盘的某处。如xx通用计划管理系统6.02(http://www.sx168.com)
用TRW2000加载程序
下断 bpx createfilea
createfilea的定义为:
HANDLE CreateFile(
LPCTSTR lpFileName, // pointer to name of the file
DWORD dwDesiredAccess, // access (read-write) mode
DWORD dwShareMode, // share mode
LPSECURITY_ATTRIBUTES lpSecurityAttributes, // pointer to security attributes
DWORD dwCreationDisposition, // how to create
DWORD dwFlagsAndAttributes, // file attributes
HANDLE hTemplateFile // handle to file with attributes to copy
);
每次中断后观察打开的文件名,经过两次foxpro.int下面一个就是脱壳后的文件名了。
文件放在c:\windows\history\…\…exe
因为c:\windows\history\是windows的系统目录,下面的文件不可显示,程序运行结束后又会删除这个文件,所以必须在程序没有结束前得到这个文件
方法一:用suspend将程序挂起,用procdump kill现有进程,回dos利用xcopy /s 将所有文件考出,处理
方法二、利用copyfilea函数
BOOL CopyFile(
LPCTSTR lpExistingFileName,
// pointer to name of an existing file
LPCTSTR lpNewFileName, // pointer to filename to copy to
BOOL bFailIfExists // flag for operation if file exists
);
在上图的位置r eax 0 r ebx 63e630 r edx 63e660
将63e660地址的内容改成目的文件如 d:\ab\s.exe
然后编一段代码
a eip
push eax
push edx
push ebx
call copyfilea
最后一句不能直接输入,要用exescope找到import中copyfilea的位置,计算相对地址用ff 15 xx xx xx xx填入
单步运行完copyfilea后,隐藏的文件就被考出来了。
考出的文件是exe文件,用1的方法就能得到app文件,但这个app文件可运行,用unfoxall反编译会出错,后面会讲到app文件的标准化处理。
3 改变解释器动态解码
这种加密方式采用了狗的方式,用trw2000加载跟踪时,在文件开始运行的代码部分会看到大量的花指令,再将主文件加密的同时,它把解释动态连接库也进行了加壳,运行时用壳中的代码将app文件解码后执行。因vfp6r.dll被改动,无法放到系统目录下,因此文件安装目录下能看到vfp6r.dll,用exescope文件观察此文件,能看到gtide段,这就是这种文件的特征。
例如:xx建筑工程预结算系统6.6版(http://fjjlsoft.yeah.net)
处理方法:采用2的方法下断createfilea,得到调用文件后,下断readfile,
readfile定义如下:
BOOL ReadFile(
HANDLE hFile, // handle of file to read
LPVOID lpBuffer, // pointer to buffer that receives data
DWORD nNumberOfBytesToRead, // number of bytes to read
LPDWORD lpNumberOfBytesRead, // pointer to number of bytes read
LPOVERLAPPED lpOverlapped // pointer to structure for data
);
第3次读200byte时中断,在读出的代码处下断,bpm xxxxxxxx,观察程序如何解码
解码部分的指令
0167:0C2FA505 INC DWORD [EBP-04] 已处理字节数
0167:0C2FA508 MOV EAX,[EBP+0C] 要处理字节数
0167:0C2FA50B CMP [EBP-04],EAX
0167:0C2FA50E JNC 0C2FA53A
0167:0C2FA510 MOV EAX,[EBP-04]
0167:0C2FA513 MOV ECX,[EBP+08] 要处理数据的首地址
0167:0C2FA516 XOR EDX,EDX
0167:0C2FA518 MOV DL,[ECX+EAX] 要处理的数据送DL
0167:0C2FA51B MOV AL,[EDX+0C3352E2] 0C3352E2—0C3354E2 512个字节为密钥
0167:0C2FA521 MOV [EBP-08],AL
0167:0C2FA524 XOR EAX,EAX
0167:0C2FA526 MOV AL,[EBP-08]
0167:0C2FA529 MOV AL,[EAX+0C3353E2]
0167:0C2FA52F MOV ECX,[EBP-04]
0167:0C2FA532 MOV EDX,[EBP+08]
0167:0C2FA535 MOV [EDX+ECX],AL
JMP 0C2FA505
W 0C3352E2 0C3354E2 key.bin
记下读出的内容,用hex workshop 在主文件中查找,将到文件尾的内容写入另一个文件code.bin
用VB的语言,上述的解密算法为
Dim keyfiledata(0 to 511) As Byte
Dim codefiledata() As Byte
Dim middata() As Byte
Dim keyfilelen As Long
Open " Key.bin" For Binary As #1
Get #1, 1, keyfiledata()
Close #1
keyfilelen = FileLen("code.bin")
Redim keyfiledata(0 to keyfilelen-1) as byte
Redim middata(0 to keyfilelen-1) as byte
Open "code.bin" For Binary As #2
Get #2, i, codefiledata
Close #2
For I=0 to keyfilelen-1
Middata(i) = keyfiledata(keyfiledata(codefiledata (i)) + 256)
Next i
Open "code.app" For Binary As #2
Put #2, 1, middata()
Close #2
MsgBox "finish"
得到app文件
4 改变解释器的加密方式
这种文件的加密方式的app文件是以加密app的方式出现,加密者将vfp6r.dll的解密部分代码和文件头标志加以改变,变成自己的动态连接库,这个库文件同样放在文件的安装目录下,但有的程序采用了一定的手段让用户看不到这个文件。
例如:xx工程造价软件6.6版(http://www.ztys.com)
这个软件安装后,改变注册表HKEY_LOCAL_MACHINE\software\Microsoft\windows\currentversion\explorer\advanced\folder\hidden\showall\checkedvalue
将键值由0改为1,这样隐含系统文件你就看不到了,我认为这种方法是不可取得,文件加密的同时不应干扰用户其他的功能,利用这种方法有点缺德之嫌。
就以这个程序为例,首先比较一下正规的vfp6r.dll和程序用的vfp6r.dll有何不同
0167:0C02102E MOV ECX,02
0167:0C021033 MOV EDI,0C2F24C8 [edi]为c4c5 正常为fef2
0167:0C021038 MOV ESI,EBX
0167:0C02103A XOR EDX,EDX
0167:0C02103C REPE CMPSB
0167:0C02103E JNZ NEAR 0C0EF608 不等则跳,与其它值比较
0167:0C021044 MOV AL,[EBX+02]
0167:0C021047 NOT EAX
0167:0C021049 TEST AL,01
0167:0C02104B JNZ 0C021096 是加密的则跳
0167:0C02104D MOV ESI,01
0167:0C021052 MOV BYTE [0C30D2FC],00
0167:0C021059 MOVZX CX,[EBX+04]
0167:0C02105E MOVZX AX,[EBX+03]
0167:0C021063 SHL ECX,08
0167:0C021066 ADD EAX,ECX
0167:0C021068 MOV ECX,[0C2F24C4]
0167:0C02106E AND EAX,FFFF
0167:0C021073 CMP EAX,ECX 比较文件的版本
0167:0C021075 JNZ NEAR 0C0EF5E8
0167:0C02107B MOV ECX,EBX
0167:0C02107D CALL 0C0210C6
0167:0C021082 CMP AX,[EBX+27]
0167:0C021086 JNZ NEAR 0C0EF5FB
0167:0C02108C MOV EAX,ESI
0167:0C02108E POP EDI
0167:0C02108F POP ESI
0167:0C021090 POP EBP
0167:0C021091 POP EBX
0167:0C021092 POP ECX
0167:0C021093 RET 0C
0167:0C021096 MOV ECX,EBX
0167:0C021098 CALL 0C0A863D 解密部分0
0167:0C02109D MOV ECX,EBX
0167:0C02109F CALL 0C0A8727 解密部分1
0167:0C0210A4 MOV ESI,[ESP+1C]
0167:0C0210A8 MOV ECX,[ESI]
0167:0C0210AA CALL 0C005392 解密部分2
0167:0C0210AF MOV ECX,EBX
0167:0C0210B1 CALL 0C0A879D 解密部分3
0167:0C0210B6 MOV [ESI],EAX
0167:0C0210B8 MOV BYTE [0C30D2FC],01
0167:0C0210BF MOV ESI,02
0167:0C0210C4 JMP SHORT 0C021059
0167:0C0A863D SUB ESP,BYTE +10
0167:0C0A8640 MOV AL,[ECX+02]
0167:0C0A8643 PUSH EBX
0167:0C0A8644 NOT EAX
0167:0C0A8646 SHR EAX,04
0167:0C0A8649 MOV BX,[ECX+25] 25-26位解密的密钥
0167:0C0A864D AND EAX,BYTE +01
0167:0C0A8650 MOV [ESP+10],EAX
0167:0C0A8654 LEA EAX,[ECX+25]
0167:0C0A8657 PUSH EBP
0167:0C0A8658 PUSH ESI
0167:0C0A8659 XOR ESI,ESI
0167:0C0A865B AND EBX,FFFF
0167:0C0A8661 PUSH EDI
0167:0C0A8662 MOV [ESP+14],ECX
0167:0C0A8666 MOV [ESP+18],EAX
0167:0C0A866A MOV [0C3171EE],SI
0167:0C0A8671 MOV [ESP+10],EBX
0167:0C0A8675 MOV EBP,0F
0167:0C0A867A MOV EAX,ESI
0167:0C0A867C CDQ
0167:0C0A867D XOR EAX,EDX
0167:0C0A867F SUB EAX,EDX
0167:0C0A8681 AND EAX,BYTE +07
0167:0C0A8684 XOR EAX,EDX
0167:0C0A8686 MOV EDI,EAX
0167:0C0A8688 MOV AL,[ESI+ECX+15]
0167:0C0A868C SUB EDI,EDX
0167:0C0A868E MOV EDX,01
0167:0C0A8693 MOV ECX,EDI
0167:0C0A8695 SHL EDX,CL
0167:0C0A8697 MOV ECX,EBP
0167:0C0A8699 AND DL,AL
0167:0C0A869B NEG DL
0167:0C0A869D SBB EDX,EDX
0167:0C0A869F NEG EDX
0167:0C0A86A1 SHL EDX,CL
0167:0C0A86A3 MOV ECX,ESI
0167:0C0A86A5 OR [0C3171EE],DX 计算的key1,以后还要用到
0167:0C0A86AC MOV EDX,01
0167:0C0A86B1 SHL EDX,CL
0167:0C0A86B3 MOV ECX,EDI
0167:0C0A86B5 AND EDX,EBX
0167:0C0A86B7 MOV BL,01
0167:0C0A86B9 NEG EDX
0167:0C0A86BB SBB EDX,EDX
0167:0C0A86BD SHL BL,CL
0167:0C0A86BF NEG EDX
0167:0C0A86C1 SHL DL,CL
0167:0C0A86C3 MOV ECX,[ESP+14]
0167:0C0A86C7 NOT BL
0167:0C0A86C9 AND BL,AL
0167:0C0A86CB OR DL,BL
0167:0C0A86CD MOV [ESI+ECX+15],DL
0167:0C0A86D1 INC ESI
0167:0C0A86D2 DEC EBP
0167:0C0A86D3 CMP EBP,BYTE -01
0167:0C0A86D6 JNG 0C0A86DE
0167:0C0A86D8 MOV EBX,[ESP+10]
0167:0C0A86DC JMP SHORT 0C0A867A
0167:0C0A86DE MOV EDI,[ESP+18]
0167:0C0A86E2 LEA EAX,[ECX+03]
0167:0C0A86E5 CMP EAX,EDI
0167:0C0A86E7 MOV DL,FE
0167:0C0A86E9 JNC 0C0A871F
0167:0C0A86EB MOV ESI,[ESP+1C]
0167:0C0A86EF MOV CX,[0C3171EE]
0167:0C0A86F6 MOV BL,[EAX]
0167:0C0A86F8 XOR DL,CL
0167:0C0A86FA XOR BL,DL
0167:0C0A86FC TEST ESI,ESI
0167:0C0A86FE MOV [EAX],BL
0167:0C0A8700 MOV DL,BL
0167:0C0A8702 JZ NEAR 0C0D34C6
0167:0C0A8708 IMUL CX,CX,273D 正常为184d
0167:0C0A870D ADD ECX,236D 正常为347d
0167:0C0A8713 INC EAX
0167:0C0A8714 CMP EAX,EDI
0167:0C0A8716 JC 0C0A86F6
0167:0C0A8718 MOV [0C3171EE],CX
0167:0C0A871F POP EDI
0167:0C0A8720 POP ESI
0167:0C0A8721 POP EBP
0167:0C0A8722 POP EBX
0167:0C0A8723 ADD ESP,BYTE +10
0167:0C0A8726 RET
其他几处call中也有改动,这里就不细说了,因为这部分代码很短写个解密程序处理最为方便,处理前首先用hex workshop查找c4c5ee,将从这开始到最后的代码存成一个文件,处理完后将app文件中的c4c5全部改为fef2,注意这时文件的checksum值会变动,运行时会报告不是vfp的对象文件,动态跟踪一下,在上面第一段代码ret之后就是chexksum的比较,按正确的改过来就可以了.
5 vfpexenc内存加密
vfpexenc的加密方式比较特殊,它把动态解出的加密app文件放在内存中而不是放在一个文件里,但这对解密者没有什么不同,难度是vfpexenc在代码中增加了anti-debug代码,发现有跟踪立即停止运行,报告”文件无法打开”,结束。可是幸运的是,加密的vfp程序中并没有采用vfpexenc中的seh、sidt判断int1的技术,而只是简单地判断内存进程标题名,这种厚己薄彼的做法给破解者带来了极大的方便。
先看看程序是如何反跟踪的(vfpexenc3.7)
0167:0041994A PUSH DWORD FF
0167:0041994F PUSH EDI
0167:00419950 PUSH EBX
0167:00419951 CALL `USER32!GetClassNameA`
0167:00419956 TEST EAX,EAX
0167:00419958 JNG 004199BC
0167:0041995A LEA EDX,[EBP+FFFFFEF8]
0167:00419960 MOV EAX,EDI
0167:00419962 CALL 00407E90
0167:00419967 MOV EAX,[EBP+FFFFFEF8]
0167:0041996D LEA EDX,[EBP-04]
0167:00419970 CALL 00407684
0167:00419975 MOV EAX,[EBP-04]
0167:00419978 MOV EDX,00419A70 edx 是要判断的文件名
0167:0041997D CALL 00404620
0167:00419982 JZ 004199B1 相等就结束了
0167:00419984 MOV EAX,[EBP-04]
0167:00419987 MOV EDX,00419A80 比较第二个
0167:0041998C CALL 00404620
0167:00419991 JZ 004199B1
0167:00419993 MOV EAX,[EBP-04]
0167:00419996 MOV EDX,00419A98 比较第3个
0167:0041999B CALL 00404620
0167:004199A0 JZ 004199B1
0167:004199A2 MOV EAX,[EBP-04]
0167:004199A5 MOV EDX,00419AA8 比较第4个
0167:004199AA CALL 00404620
0167:004199AF JNZ 004199BC
0167:004199B1 PUSH BYTE +00
0167:004199B3 PUSH EBX
0167:004199B4 CALL `USER32!EnableWindow`
0167:004199B9 OR ESI,BYTE -01
0167:004199BC PUSH DWORD FF
0167:004199C1 PUSH EDI
0167:004199C2 PUSH EBX
0167:004199C3 CALL `USER32!GetWindowTextA` 取WindowText
0167:004199C8 TEST EAX,EAX
0167:004199CA JNG 00419A1D
0167:004199CC LEA EDX,[EBP+FFFFFEF4]
0167:004199D2 MOV EAX,EDI
0167:004199D4 CALL 00407E90
0167:004199D9 MOV EAX,[EBP+FFFFFEF4]
0167:004199DF LEA EDX,[EBP-04]
0167:004199E2 CALL 00407684
0167:004199E7 MOV EDX,[EBP-04]
0167:004199EA MOV EAX,00419AB4
0167:004199EF CALL 004047BC
0167:004199F4 TEST EAX,EAX
0167:004199F6 JG 00419A1A
0167:004199F8 MOV EDX,[EBP-04]
0167:004199FB MOV EAX,00419AC4
0167:00419A00 CALL 004047BC
0167:00419A05 TEST EAX,EAX
0167:00419A07 JG 00419A1A
0167:00419A09 MOV EDX,[EBP-04]
0167:00419A0C MOV EAX,00419AE4
0167:00419A11 CALL 004047BC
0167:00419A16 TEST EAX,EAX
0167:00419A18 JNG 00419A1D
0167:00419A1A OR ESI,BYTE -01
0167:00419A1D PUSH BYTE +02
0167:00419A1F PUSH EBX
0167:00419A20 CALL `USER32!GetWindow`
0167:00419A25 MOV EBX,EAX
0167:00419A27 TEST EBX,EBX
0167:00419A29 JNZ NEAR 0041994A
用d 00419A70就能看到要比较的文件,分别是whxmdi0、filemonclass、ollydbg、trw2000、import reconstructor、prodump
我用的是trw2000,只要将内存中的trw2000改为其他的名字就不会有问题了,为了一劳永逸,我用hex workshop将trw2000.exe及.sys中的ansi、unicode码的trw都改成了trd
再运行时这段反跟踪代码就不起作用了。
反跟踪解决了,下一步就是脱壳了
例子:xx大师3.57b (http://www.cnytsoft.com/)
这个软件是由vfpexenc加密的, vfpexenc加密的软件会有一个vfpnc段,是这种加密方式的一个重要标志。下面看看它的动态解密算法:
0167:0041D79A MOV EAX,[00421AC8]
0167:0041D79F SUB EAX,BYTE +0E
0167:0041D7A2 CMP EDI,EAX
0167:0041D7A4 JNZ 0041D7AA
0167:0041D7A6 XOR EBX,EBX
0167:0041D7A8 JMP SHORT 0041D7B5
0167:0041D7AA MOV EBX,EDI
0167:0041D7AC SUB EBX,[00421AC8]
0167:0041D7B2 ADD EBX,BYTE +0E
0167:0041D7B5 MOV EAX,[00421AC0]
0167:0041D7BA SUB EAX,EBX
0167:0041D7BC CMP EAX,[EBP+10]
0167:0041D7BF JC 0041D7C6
0167:0041D7C1 MOV ESI,[EBP+10]
0167:0041D7C4 JMP SHORT 0041D7C8
0167:0041D7C6 MOV ESI,EAX
0167:0041D7C8 MOV EDX,[EBP+0C] 目的地址
0167:0041D7CB MOV EAX,[00421ACC]
0167:0041D7D0 ADD EAX,EBX * 源地址—从此地址开始内存放的是加密的app文件
0167:0041D7D2 MOV ECX,ESI 个数
0167:0041D7D4 CALL 0040282C 将源地址内容移动到目的地址
0167:0041D7D9 CMP BYTE [00421AD6],00 flag 1成功
0167:0041D7E0 JZ 0041D7EE
0167:0041D7E2 LEA EAX,[EBP+0C] 解密数据地址
0167:0041D7E5 MOV EDX,ESI 数据个数
0167:0041D7E7 CALL 0041D5A0 解密算法
0167:0041D7EC JMP SHORT 0041D7FA
0167:0041D7EE LEA EAX,[EBP+0C]
0167:0041D7F1 MOV ECX,EBX
0167:0041D7F3 MOV EDX,ESI
0167:0041D7F5 CALL 0041D5DC
0167:0041D7FA MOV EAX,[EBP+14]
0167:0041D7FD MOV [EAX],ESI
0167:0041D7FF OR EDI,BYTE -01
0167:0041D802 MOV EAX,EDI
0167:0041D804 POP EDI
0167:0041D5A0 PUSH EBX
0167:0041D5A1 PUSH ESI
0167:0041D5A2 MOV EBX,EDX
0167:0041D5A4 MOV ESI,EAX
0167:0041D5A6 CALL 0041D578 处理解密的密钥,得到正确的密钥
0167:0041D5AB MOV EDX,EBX
0167:0041D5AD DEC EDX
0167:0041D5AE TEST EDX,EDX
0167:0041D5B0 JL 0041D5D2
0167:0041D5B2 INC EDX
0167:0041D5B3 XOR EAX,EAX
0167:0041D5B5 MOV ECX,[ESI] 加密数据地址
0167:0041D5B7 MOV CL,[ECX+EAX] 取加密数据
0167:0041D5BA AND ECX,FF
0167:0041D5C0 MOV EBX,[00421AD0] 密钥的地址
0167:0041D5C6 MOV CL,[EBX+ECX] 得到正确的数据
0167:0041D5C9 MOV EBX,[ESI]
0167:0041D5CB MOV [EBX+EAX],CL
0167:0041D5CE INC EAX
0167:0041D5CF DEC EDX
0167:0041D5D0 JNZ 0041D5B5
0167:0041D5D2 CALL 0041D578 将密钥重新加密
0167:0041D5D7 POP ESI
算法有了,首先要得到密钥,0041D5C0 MOV EBX,[00421AD0] ebx开始的512个字节就是了,按3的方法存成一个文件。因为程序用完密钥后,会再次加密,只能在这部分代码处得到正确的key。
关键是得到app,看这一句0167:0041D7D0 ADD EAX,EBX ,这时eax就是app的开始地址,文件的长度可通过在此设断,eax会逐渐增大,得到最大值+长度就是app的末尾。同样按3的方法存成一个文件,再编个程序处理,就能得到正确的app。
四、 App文件的标准化处理
用上面的方法得到的app文件可以在vfp的解释环境下运行,但加密者利用vfp程序动态运行和静态调试的不同,将app文件的结构加以改变,用来阻止unfoxall等软件得到源码。其主要的方法有如下几种:
1. 改变文件描述表的起始地址数据
①文件头标志
②FF 未加密
③0220 VFP6.0/7.0版本
④文件数295
⑤主文件位置 第2个(0为第一个)
⑥文件名列表位置 505B9F
⑦文件描述表位置 5067AF
看5057AF内容00—可执行程序 00707866—文件开始地址 00000041—文件结束地址
文件的结束地址小于文件的开始地址,文件的长度为负值,UNFOXALL反编译时一定出错,但运行时为何不出错呢?因为这个文件是加入的一个垃圾文件,运行是永远用不到它,当然不会出错了,它的用途就是对付反编译的。将66 78 70 改成29 00 00 就不会有问题了。
2.改变fxp mpx vct sct文件结构
fxp 具有与app文件相同的结构,0-40字节定义同上,不同的是其文件数目为1
从第&h29开始为fxp文件的定义
字节偏移 说明
0-1 自定义过程和函数数目
2-3 自定义类数目
4-7 指向代码开始位置
8-11 自定义过程和函数定义开始位置
12-15 自定义类定义开始位置
16-19 与调试有关,具体不详
20-23 调试信息数量
24-27 调试信息开始地址
自定义过程或函数定义
0-1 过程名长度
2-n+1 过程名
n+2-n+5 过程代码开始位置
n+6-n+9 状态
自定义类定义
0-1 类名长度
2-n+1 类名
n+2-n+3 父类名长度m
n+3-n+m+3 父类名
~4 byte 类代码开始地址
~2 byte 状态
下面举个例子,这个是xx大师的主文件。
①自定义函数或过程数量171个
②自定义类数量 3个
③程序开始地址
④自定义函数或过程描述表地址
⑤自定义类描述表地址
⑥文件代码总长度
⑦第1行代码长
⑧第2行代码长
标准结构③应为25,UNFOXALL反编译时如果③不是25就会反编译出一些乱码,将③变成25,在85前插入一个0,这样还得修正各种指针,包括自定义函数或过程、自定义类描述表中的地址指针
自定义函数或过程
①第1个自定义函数或过程名称长度 9
②第1个自定义函数或过程名称 hd_is_errX
③代码开始地址1458
④第2个自定义函数或过程名称长度 6
①第一个自定义类名称长度 9
②第一个自定义类名称 mytoolbar
③第一个自定义类父类名称长度 7
④第一个自定义类父类名称 Toolbar
⑤第一个自定义类代码位置 014869
⑥第2个自定义类名称长度 12
将相应的全部指针按移动的字节数加以修正,如果子程序特别多,最好写个程序处理比较方便
另外vct sct中保存的代码也可以按如上方式处理。但必须要保证长度不能改变。
3 改变scx vcx结构
加密者将scx vcx文件最后去掉若干个0,这样modi form 是会告知不是表。因为scx vcx是结构固定的表,可参照vfp关于表结构的帮助加以修订,修订比较简单,这里就不多说了。
五、 总结
目前vfp文件的加密强度不是很大,主要是壳与解释库之间的界限还比较分明,vfpexenc在模糊它们之间的界限方面做了些工作,我想这可能是今后vfp文件加密的方向。
Vfp文件的破解与其他语言编写文件的破解不同,vfp文件一旦被破,得到的将是文件的源代码,因此,希望破解者能最大限度地保护作者的著作权。
Sway [CCG]
2003.03.28