首页
社区
课程
招聘
关于VFP中的动态解密[求助]
发表于: 2006-7-10 20:01 7094

关于VFP中的动态解密[求助]

2006-7-10 20:01
7094
下面是我写的APP分离代码,但是分离出的却不是正确的APP文件,请高手帮忙看看,解释一下,谢谢
Private Sub Command1_Click()
On Error Resume Next
'--------------------------------------
Dim a() As Byte

Open Text3.Text For Binary As #1
c = Val(Text2.Text) '这个是获取的APP的大小
ReDim a(c) '代码
Get #1, Val(Text1.Text) + 1, a
Close #1

Text7.Text = Text7.Text & "开始分离文件……" & vbCrLf

'--------------------------------------

'--------------------------------------
Dim e() As Byte

Open Text4.Text For Binary As #1
c = Val(Text6.Text) '这里填的是512
ReDim e(c) '512位密钥
Get #1, Val(Text5.Text) + 1, e '获取512位的密钥
Close #1

'--------------------------------------
'以上是把密文和明文分离出来
'--------------------------------------

Kill "Code.app"

keyfilelen = Val(Text2.Text) 'app的大小
Dim Middata() As Byte
ReDim Middata(keyfilelen)

For i = 0 To keyfilelen - 1
Middata(i) = e(e(a(i)) + 256) '解密
'是否这里的代码有问题?
Next i

Open "Code.app" For Binary As #2
     Put #2, 1, Middata()
  Close #2
Text7.Text = Text7.Text & "分离文件结束……" & vbCrLf

End Sub

[课程]Linux pwn 探索篇!

收藏
免费 0
支持
分享
最新回复 (10)
雪    币: 259
活跃值: (26)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
2
晕,没人回
2006-7-10 22:57
0
雪    币: 280
活跃值: (281)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
3
我对vfp程序也是很头痛,动态跟踪一点规律也没发现。反编译有些又反不出来。跟踪程序时连比较的函数也没找到,很郁闷。不知谁能告知VFP程序的比较函数是什么,还有跳转函数?
2006-7-11 08:17
0
雪    币: 259
活跃值: (10)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
4
[原创]VFP 程序的结构及相关知识(已经更新1)VFP 程序的结构及相关知识

一. VFP 编译的 EXE 文件的结构
    VFP 编译生成的 EXE 文件主要由两部份组成,第一部份(前半部分)是一个标准的 PE 格式的可执行文件,这个标准的 PE 格式文件又叫载入器. 第二部分(后半部分)是一个标准的 .app 文件, 这个 app 与在 VFP 集成开发环境中编译时选择编译为 app 文件时生成的 app 文件基本上是一样的,只是在这个 app 文件的尾部,多了 14 字节的标识部份(这 14 字节的前 10 字节是 00 83 41 00 00 00 00 00 00 00,最后四字节是 app 文件加上14 字节的标识后的长度).
    因此,可以认为 VFP 编译的 EXE 文件是由一个标准的 EXE 文件后面附上一个 .app 文件,再加上 14 字节的标识部份组成. 而且, 前半部份的 EXE 与后半部分的 app 之间没有任何联系.

1.1 载入器的作用
    前面提到的载入器的作用非常简单,主要是检查运行环境,载入相应的 VFPxR.DLL 并调用 VFPxR.DLL 中的 DllWinMain 函数. 在调用 DllWinMain 函数时, 有两个参数, 第一个是一个串参数,必须是 VFP 编译的 EXE 文件的文件名, 第二个是一个 int 型的值,一般用十六进制的 FF 就可以. 因此,你可以用除 VFP 以外的任何编译语言来编译写自己的载入器以达到加密程序的作用.

1.2 VFP 编译的 EXE 文件的运行机制简介
    事实上,一但载入器调用了 VFPxR.DLL 中的  DllWinMain 函数后, VFP 就接管了程序的控制权,你就再也无法用常规的方法控制程序的运行过程了. 下面说一下 VFP 的 DLL 的启动过程:

1.2.1
首先,VFPxR.DLL 会读取文件的最后 14 字节. 并根据最后四字节来定位到 app 在 EXE 文件中的位置并读入长度为 41 字节的 app 头,以检查这是不是一个 VFP 的 EXE 文件.一个标准的 app 头的结构如下:

在 Masm 中的定义为:

IMAGE_VFP_HEADER STRUCT
   Magic             dw ?            ; 标识: 一定是 FE F2
   EncryptFlag       db ?            ; 编译方式: 可为 FF 或 EE,FF 为非加密编译, EE 为加密编译
   Version           dw ?            ; 版本号: 20 02
   NumberOfLists     dw ?            ; 文件总数
   OrdinalOfMainPrg  dw ?            ; 主文件序号
   EndPosOfLists     dd ?            ; 文件描述符起始偏移
   StartPosOfLists   dd ?            ; 文件名表起始偏移
   LengthOfLists     dd ?            ; 文件表大小
   Reserved          db 12h dup(?)   ; 保留字节
   Checksum          dw ?            ; 校验和
IMAGE_VFP_HEADER ENDS

在 C++ 中的定义为:

typedef struct _FOX_HEADER
{
WORD Magic;            // 标识: 一定是 FE F2
BYTE EncryptFlag;      // 编译方式: 可为 FF 或 EE,FF 为非加密编译, EE 为加密编译
WORD Version;          // 版本号
WORD NumberOfLists;    // 文件总数
WORD OrdinalOfMainPrg; // 主文件序号
DWORD EndPosOfLists;    // 文件描述符起始偏移
DWORD StartPosOfLists;  // 文件名表起始偏移
DWORD LengthOfLists;    // 文件表大小
BYTE Reserved[18];     // 保留字节
WORD Checksum;         // 校验和
} FOX_HEADER, *PFOX_HEADER;
      
因此, VFP 将从读入的 app 头中确定文件大小, 主文件位置及编译方式和文件表位置等重要信息.
一但 VFP 检测到文件内容无误后, 将读入主文件并运行它.

1.2.2
VFP 自身在读入 app 内容并运行其中的伪编译代码时,并不是一次把整个 app 读入到内存中,而是根据需要,只读入需要的那一部分内容.

1.2.3
如果是加密编译的 app ,则 app 文件内容的解密方式与 1.2.2 提及的方式相似.

1.2.4
VFP 是如何确定某一个需要的文件在 app 中的位置,文件类型的呢?这就需要了解 VFP 的 app 中的文件描述符的相关知识。
在 VFP 的 app 中, 每一个文件都有一个文件描述符来记录此文件的类型,起始偏移,结束偏移,文件大小等信息. 还记得前面定义的结构 IMAGE_VFP_HEADER 吗? 在此结构中, 有一个成员 EndPosOfLists. 此成员即指向文件描述符的起始地址. app 中有多少个文件就有多少个文件描述符. 一个文件描述符长 25 字节, 在 asm 中其定义如下:

FOX_FILEDESCRIPTOR struct
   FileType          db ?   ; 1 字节, 文件类型
   StartPosOfData    dd ?   ; 4 字节, 文件起始偏移
   EndPosOfData      dd ?   ; 4 字节, 文件结束偏移
   PosOfFileDir      dd ?   ; 4 字节, 文件目录名偏移
   PosOfFileName     dd ?   ; 4 字节, 文件名表偏移
   Reserved1         dd ?   ; 4 字节, 保留
   Reserved2         dd ?   ; 4 字节, 保留
FOX_FILELIST ends

或在 C++ 中

typedef struct
{
BYTE FileType;          // 文件类型(表单、程序、图像、图标等)
DWORD StartPosOfData;   // 文件起始偏移
DWORD EndPosOfData;     // 文件结束偏移
DWORD PosOfFileDir;     // 文件目录名偏移
DWORD PosOfFileName;    // 文件名表偏移
DWORD Reserved1;        // 保留字节
DWORD Reserved2;        // 保留字节
} FOX_FILEDESCRIPTOR,* PFOX_FILEDESCRIPTOR;

以下是一个示例文件描述符:

00 29 00 00 00 78 00 00 00 00 00 00 00 11 00 00 00 00 00 00 00 00 00 00 00

此文件描述符表明:此文件的文件类型是 0, 文件起始偏移是 0x29, 文件结束偏移是 0x78, 文件目录名偏移是 0, 文件名偏移是 0x11.

FileType 成员的值及对应属性如下:

0  Fxp,Spx,Qpx 等编译后的程序文件
2  dbf 文件
3  fpt/vct/sct/frt/lbt 等表文件的备注文件
6  jpg,bmp,ico 等图形文件(config.fpw 的属性也是 6)
8  vcx     类库文件
9  scx     表单文件
10 frx     报表文件
11 lbx     标签文件

前面提到的 "文件名偏移" 是什么意思呢? VFP 的 app 中,文件描述符中没有文件名的串信息,它只有一个指针指向文件名表中的一个地址。
这是一个相对于文件名表偏移的相对偏移。VFP 就是用文件名表偏移加上文件名偏移来寻址文件名的。举例如下:

假定在 app 头中,文件名表起始偏移为 0x4BA823,某一文件的文件描述符中此文件的文件名表偏移为 0x11。则此文件的文件名串信息的偏移为 0x4BA823 + 0x11 = 0x4BA834

另外一点,在文件名表中实际上还包含了此文件所在的相对目录。只是目录信息在分离和处理 app 文件时不是必须的,因此一般可以不管它。
文件目录串信息的获取与文件名串信息的获取方法相同。

示完待续......

相关附件

附件二: 一个简单的 VFP 载入器及合并器.

masm 程序 loader.asm
-----------------------------------------------------------------
载入器:也就是 VFP 的 EXE 文件的前半部分那个标准 PE
它是用 masm 写的,非常简单.其处理流程是:
1 用 LoadLibrary 函数来载入 vfpxr.dll
2 用 GetProcAddress 函数来取得 vfpxr.dll 的 DllWinMain 函数地址
3 调用 DllWinMain 函数. 调用此函数有两个参数,第一个参数是 VFP exe 文件本身(也就是载入器本身)
当第一个参数是是载入器本身时, app 文件必须附加在载入器后面,且 app 文件尾部必须有 14 字节的标识符
当第一个参数是磁盘上的一个 app 文件时,则可以是一个标准的 app 文件(直接编译形成的).
第一个参数也可以是另一个标准的 VFP EXE 文件.

具体说明如下:

一.假定载入器的文件名是 loader.exe, 且在其后面没有附加上 app 文件,则以下调用是适当的:
DllWinMain("c:\test\test.app",255)  // 假定 c:\test\test.app 是存在的
DllWinMain("c:\test\test.exe",255)  // 假定 c:\test\test.exe 是存在的,并且它必须是一个标准编译的 VFP EXE 文件

一.假定载入器的文件名是 loader.exe, 且在其后面附加上了 app 文件,并且在 app 尾部加上了 14 字节的标识符, 则以下调用是适当的:
DllWinMain("loader.exe",255) // 以可执行文件本身的文件名为参数

C++ 项目 merge.dsw
------------------------------------------------------
合并器:合并器的作用是把"载入器"与一个标准的 app 文件合并,以生成一个 VFP 的 EXE 文件(同时要在 app 尾部加上 14 字节的标识符).
这是用 C++ 写的,载入器(loader.exe) 已经作为资源加入到了 C++ 项目中.
在运行时,程序会从资源中读取预先加入到资源中的 loader.exe, 然后与你选择的 app 进行合并,并在合并后的文件尾加上 14 字节的标识符
最终形成可以运行的 VFP EXE 文件.

几点注意:
-------------------------------------------------------------
1 程序中只设置了一个版本的 VFP, 即 VFP 6.0
  因为不同版本的 VFP EXE 文件调用的 dll 文件名是不同的,可能这些 dll 文件所在的位置也不尽相同
  为简单的原因,我只考虑了 VFP 6.0 的一种情况.

-----------------------------
  版本的设置在 loader.asm 中
-----------------------------

2 为简单起见,没有考虑 app 文件的压缩和加密
3 没有考虑最终文件的图标替换.

未完待续......

特别感谢:该文由版主learnmore提供
2006-7-11 12:43
0
雪    币: 280
活跃值: (281)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
5
我碰到一个VFP程序,用foxtools反编译得到app文件,找到注册关键如下:
PROCEDURE Command1.Click
x=thisform.text2.value+'-'+thisform.text3.value
y=thisform.text4.value+'-'+thisform.text5.value
Sysuser=checkxlh(x,y)
IF Sysuser
    REPL xxlcj.cjxlh WITH y
    =messagebox('你输入的注册码正确'+CHR(13)+CHR(13)+'     '+x+'-'+y+CHR(13)+CHR(13)+'请牢记……',64,'谢谢')
    Syscomp=ALLT(thisform.text1.value)
        clos table all
        thisform.release
ELSE
   =messagebox('注册码错误',16,'错误')
   thisform.text4.setfocus()
ENDI       

ENDPROC

明显程序调用checkxlh(x,y)函数来计算注册码并比较,正确返回True,但查遍整个app文件,也找不到checkxlh(x,y)函数具体的计算过程,非常郁闷。还好程序是明码比较,用Winhex查找内存的方法可以找到正确注册码。

请教怎样才能找到checkxlh(x,y)函数的原码?或动态跟踪怎么找到伪编译的代码跟踪出来吗?
2006-7-11 16:09
0
雪    币: 200
活跃值: (13)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
用工具修复一下再找!
2006-7-12 11:41
0
雪    币: 280
活跃值: (281)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
7
谢谢,我终于明白了
用VFP打开app文件可看到:
procedure Checkxlh
parameters x,y
a1 = SUBSTR(X,1,6)+"ccl"
a2 = SUBSTR(X,8,6)+"xxl"
a3 = RIGHT("000000"+SYS(2007,A1),6)+"-"+RIGHT("000000"+SYS(2007,A2),6)
a = IIF(A3 = Y,.t.,.f.)
release a1,a2,a3,a4
return A

其中x=thisform.text2.value+'-'+thisform.text3.value
    y=thisform.text4.value+'-'+thisform.text5.value
前两个文本框用“-”连接后参与计算注册码,结果与后两个文本框比较。
但是我发现计算结果不太对。对SYS(2007,A1),6)函数不太清楚,网上搜索SYS(2007) 作用是Checksumvalue。
thisform.text2.value=508645
thisform.text3.value=408582
连接后为508645-408582,即为参数x,计算得:
a1=508645ccl
a2=408582xxl
a3=?
实际注册码为010307-000735,计算过程不知道怎么来的。请指教
2006-7-12 16:41
0
雪    币: 200
活跃值: (13)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
parameters x,y
a1 = SUBSTR(X,1,6)+"ccl"
a2 = SUBSTR(X,8,6)+"xxl"

SUBSTR为截取字符串函数,第一个变量为字符串,第二个变量为开始截取的位置,第三个变量为截取长度。
第一句的意思是从第一位开始截取六个字符再加上"ccl"赋予变量a1。

a3 = RIGHT("000000"+SYS(2007,A1),6)+"-"+RIGHT("000000"+SYS(2007,A2),6)

RIGHT为从右截取字符串函数, 原语法是RIGHT(CEXPR,N)(上句中函数内部有个字符串相加过程),第一个变量是字符串,第二个变量是从右边截取N个字符。

SYS(2007,CEXPR)在VFP中返回一个字符串的检查和值,主要用CRC16来校验数据的有效性或比较两字符串,想不到有人会用来参予注册算法。
2006-7-13 09:29
0
雪    币: 280
活跃值: (281)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
9
自己写了代码放进VFP里运行,真的得到了结果。
vfp语言的SYS函数真的很有意思,参数不同可以有不同的计算方法,似乎天生就是加密的算法。不知其它语言如何实现它的计算?

VFP程序除了反编译外,难道真的没有动态跟踪的好方法吗?我跟了两天,头都大了,还是一点头绪也没有,似乎它的计算过程(如加减)全部封装了,根本不知道从那里算出来的。我用硬件断点在读相应的代码时中断,就算是看到了注册码出来,也不知道它是怎么出来的。非常郁闷。

还有我用VFP变得一段程序在编译成exe文件后运行总提示资源丢失,而在VFP环境下是可以正常运行的。不知怎么回事?
2006-7-13 16:08
0
雪    币: 200
活跃值: (13)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
VFP建议老兄还是别研究了,在VFP论坛里发现很多人跟过后都头大。你编译程序后在本机上能运行,在其他机器上不能运行是因为DLL运行库未找到(VFP*.DLL在VFP的安装目录中能找到)。

sys(2007)在DELPHI印象中没类似函数!
2006-7-13 16:18
0
雪    币: 280
活跃值: (281)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
11
呵呵,多谢指导!
2006-7-13 17:17
0
游客
登录 | 注册 方可回帖
返回
//