4 Fexplorer高级操作
4.1 结构数据操作
结构数据操作包括定义结构数据、结构数据翻译、结构数据自动翻译。
4.1.1 定义结构数据类型
结构数据类型是由存储空间相连的若干基本数据类型构成。前面已经提到,结构数据是构成的数据文件数据部的基本单元,对于分析由结构数据组成的数据块,用户一旦完成组成结构的一组基本数据类型的定义,就可以开始定义结构数据。定义结构数据包括定义结构开始数据项、定义结构结束数据项、添加结构数据。对于包含字符串类型、空间点序列的结构,还要设置字符串的长度、点序列的长度。
4.1.1.1添加新数据结构
(1)准备
在编辑菜单中选择“定义记录/结构”项,打开定义记录/结构开关,允许用户定义结构数据;在显示菜单中选定“记录窗口”,将记录/结构窗口设置为显示记录数据。定义结构的所有操作都在记录窗口中进行。
(2)确定结构体
有两种方式确定结构体。第一种是在记录窗口中,用光标指定一数据项,作为结构的开始数据项,单击鼠标弹出对话框,选择“定义结构开始”,定义结构开始数据项;然后在记录窗口中,用光标指定一数据项,作为结构的结束数据项,,单击鼠标右键,在弹出菜单中选择“定义结构结束”,定义结构结束数据项。第二种方式直接用鼠标在记录窗口选择若干行,作为结构体,此时的结构开始是选择的第一行,结构的结束是选择的最后一行。
(3)添加结构
在记录窗口中,单击鼠标右键,在弹出菜单中选择“添加结构”,将定义的结构数据添加到结构数据文件中。
为了引用方便,每一个结构都需要一个名称,在用户没有指定结构名称之前,新定义的结构采用默认的名称“UnNamedStructure”。将记录窗口切换为结构窗口时,在列表的最后一部分可以看到名为“UnNamedStructure”的新结构数据,用户可以修改默认的名称。在结构窗口中,找到名为“UnNamedStructure”的新添加结构,将位于结构的第一个数据项“注释”栏中的“UnNamedStructure”改为用户指定的名称,如“LocatePoint”。
4.1.1.2定义结构属性
对于字符串、空间点序列等具有长度数据项时,需要在添加结构之前,声明字符串的长度数据项或空间点序列等具有长度的数据项。
(1)设置字符串标志(或结构名称)
在结构窗口中,用光标指定一字符型数据项,单击鼠标右键,在弹出菜单中选择“设置字符串”,被设置的数据项将作为记录的名称显示。
(2)设置空间点
在结构窗口中,用光标指定一数值数据项,单击鼠标右键,在弹出菜单中选择“设置空间点”,该位置的数据项将作为空间点进行解释。
(3)定义标志记录
在结构窗口中,用光标指定一数值数据项,单击鼠标右键,在弹出菜单中选择“设置标志记录”,定义标志记录。标志记录有五种选择:零值、非值、正值、负值、固定值。
4.1.1.3定义后续结构
一个结构之后可能紧跟着一组其它数据结构,比如表征线段或任意几何对象的空间数据点。或者其它特定的附属数据结构。定义后续结构需要定义后续数据结构类型和指定代表后续数据结构长度的数据项。
(1)设置对象长度
在结构窗口中,用光标指定一数据项,作为长度的数据项。单击鼠标右键,在弹出菜单中选择“修改对象长度”。支持的对象类型包括:字符串、空间对象、用户自定义对象三大类。
字符串类型是一类长度字节与字符串分开存放的数据类型,该类型的字符串长度在结构的固定位置,字符串紧跟在结构之后。空间对象包括线、圆、矩形、三角等一系列空间对象,通常对象的属性,如颜色、线宽等数据在结构中定义,而表示对象几何形状的空间数据放在结构之后。用户自定义类型,原则上由用户在本记录文件中定义的所有结构,都可以作为后续结构。
后续结构属于附属数据类型,在使用结构进行数据解释中,附属数据将与主结构一同解释,从而提高解释效率。
4.1.2 利用结构数据进行批量翻译
用户定义一个结构后,就可以利用结构进行批量翻译。操作如下:
①
|
② |
③
|
④ |
(1)在字节流窗口中,用光标选定一个起点①。
(2)数据类型窗口中,选择Stru项②,打开用户定义的结构列表,从中选定刚才定义的结构。
(3)记录/结构添加按钮 ③,从起点开始,一次性将整个结构添加到记录文件中④。
(4)连续点击记录/结构添加按钮,可以连续进行批量翻译,直到整个数据块翻译完成为止。
4.1.3 结构数据自动翻译
当数据块较大时,即使采用批量翻译,工作量也是很大,如果确定了数据块的起止地址,就可以利用定义的结构,对数据块进行自动翻译。
在查询菜单中,选择“结构自动翻译”项,打开“查找-自动翻译对话框”。在结构类型栏选择定义的结构名称,在起始地址、结束地址栏分别填写翻译数据块的开始地址、结束数据地址。
在指定数据块时,也可以通过起始地址——长度方式确定,此时在“结束地址”栏中填写数据块的大小(字节数,十进制)。
对于翻译数据块中的结构类型为非连续,不同的结构类型可通过结构开始的标志字节识别,此时需在“标志字节”栏中填写标志字节(十六进制格式)
确认输入正确无误后,按“开始”按钮,开始自动翻译,分析结果将添加到记录文件中。在记录窗口可以查看分析结果。
4.1.4 结构数据替换
对于已经翻译的结构数据,如果想将一种结构替换为一种新结构,可以利用结构数据替换功能进行新旧结构数据的替换更新。
在查询菜单中,选择“结构替换”项,在源结构、新结构中分别选择旧结构和新结构。如果新结构的起始地址与旧结构不一致,应在“偏移”栏填写新结构起点相对与旧结构起点的偏移量(字节数)。
确认输入正确无误后,按“开始”按钮,开始替换,替换结果将添加到记录文件中。
4.1.5 利用结构格式刷翻译
利用格式刷采用人机交互的方式,可处理复杂的数据翻译,其工作效率和适应能力介于字节翻译与自动翻译之间。
在显示菜单中,选择“格式刷窗口”,弹出一个结构格式列表窗口,该窗口是前期使用过的格式刷。在格式刷窗口中可以继续添加新的格式或删除没用的格式。
使用格式添加按钮 ,可以将当前结构添加到格式窗口中。在格式列表中,双击其中的一项将该格式从窗口中删除。
在使用格式进行结构数据翻译时,先在格式窗口中选择一个格式。然后将光标移动到记录窗口中,当光标移动到当前记录的地址列时,格式刷被激活。此时双击鼠标左键,当前结构数据将被替换成格式刷中的结构。
4.2 结构数据分析
通过基本数据类型分析,可以从字节流中分离出基本数据类型,最终组织成结构数据。如果到此为止的话,整个分析工作仅仅完成一半,因为,如果仅仅知道数据的格式却不知道其含义,是无法满足应用的需求的。
分析结构数据中每一个数据项的含义又是一个难而又难的问题,从某种意义上,分析数据项的含义是对整个数据结构进行解密。
分析数据项的含义的方法一方面需要用户对数据文件及相关背景的了解和经验,如同分析数据结构一样,如果用户具有相关的软件开发经验,可以肯定的是会加快破解的速度、提高猜中的成功率。另一方面,借助必要的工具同样可以加快分析效率,结构数据分析方法正是为此目的设计的。
数据结构分析方法源于对数据出现的频率进行统计分析,这是一种传统的解密方法。频率统计方法可应用与所有整型数据。通过分析结构中的各数据项在数据文件中的各种取值以及出现的次数,可以对数据项的含义进行初步判定,比如LocatePoint中第二个数据项,其取值为90、95、210、211、255、1502、…。从相关的资料得知,这些数据是图层代码数据,因此可以判定该数据项代表图层编号。
另外,结构数据分析对所有数值型数据的范围,即最小值和最大值进行了统计。通过统计数据的范围,同样可以对数据项代表的含义进行判定。例如,LocatePoint中第三、四个数据项为浮点数,最小值和最大值分别为-3.6,1406.6和-6.6,2393.6。而从其它相关的资料得知,这个范围与数据文件表示的区域的范围吻合。因此可以判定,第三、四个数据项表示LocatePoint的坐标。
4.3 空间数据分析
判断数据文件分析是否成功可以通过能否还原数据文件来判断。对于复杂和大型数据文件,百分之百还原数据文件是不现实的,能够将数据文件所表达的对象及主要属性还原出来就可以了,毕竟分析的目的不是重新复制一套原系统,而是要进行二次开发或仅仅是为了学习。
空间数据分析不属于数据文件分析的核心方法,仅仅作为一类空间数据文件的辅助分析方法。某种程度上,是本书提出的数据文件结构分析方法的能力展示。
实际上,空间数据分析也的确提供了很方便的空间数据手段,在与空间数据查询功能联合使用时,可以起到其它分析方法无法替代的作用。有兴趣的读者可以多花些时间,结合软件做更多的尝试。
4.4 数据文件读写程序自动生成
在完成全部数据文件分析后,利用定义的数据结构和数据分析结构,自动生产数据文件的读写程序,该功能可用于辅助程序的编写。
如上图是一个完成解释的数据结构记录文件。在工具菜单中,选取“生成读写程序”,即可生产该数据文件的读写程序。一般情况,由于相关记录数据的标识不完备,自动生成的读写程序需要一定的修改才可运行,但通常这种修改的量很小。
unit GdbIo;
interface
Uses SysUtils,FileUnit,DexpDefs;
Procedure ReadDataFile(Fn:String);
Procedure WriteDataFile(Fn:String);
Implementation
Type
TRPoint=Record
X,Y:Single;
End;
TRPArray=Array of TRPoint;
ByteArr=Array of Byte;
TLineAttr=Record
No:Integer;
Color:Integer;
Wid:Integer;
Layer:Integer;
Name:String;
End;
ByteArr18= Array [0..18-1] of Byte;
TWpt=Record
D0:Longint; //Wpt
Caption:PChar; //名称
D2:Longint;
D3:Byte;
D4:Byte;
D5:Longint;
D6:Byte;
BAyy18:ByteArr18; //Unknown
Lat:Longint; //NS
Lon:Longint; //WE
Flag:Byte; //有效性
Altitude:Double; //高程
Date:PChar; //时间
D13:Byte;
D14:Longint;
D15:Longint;
D16:Longint;
D17:Byte;
D18:Byte;
D19:Byte;
D20:Longint;
D21:Longint;
D22:Longint;
D23:Longint;
End;
TTpt=Record
D0:Longint; //Tpt
Lon:Longint; //WE
Flag:Byte; //有效标志
altitude:Double; //高程
D4:Byte;
APacked:Longint; //unKnown
D6:Word;
End;
TTruck=Record
D0:Byte; //Truck
D1:Longint;
D2:PChar;
D3:Longint;
D4:Byte;
Count:Longint; //Count
PCount:Integer;
P: Array of TTpt; // 重复数2
End;
TTruck0=Record
D0:Longint; //Truck0
D1:PChar;
D2:Longint;
D3:Byte;
Count:Longint; //Count
PCount:Integer;
P: Array of TTpt; // 重复数2
End;
TFEnd=Record
D0:Byte; //FEnd
D1:Longint;
D2:Word;
D3:Byte;
End;
TFHead=Record
D0:PChar; //FHead
D1:Longint;
D2:PChar; //Mark2
D3:Longint;
D4:PChar; //Mark3
D5:PChar; //ADate
D6:PChar; //ATime
D7:PChar; //Mark4
End;
Var
FHead:TFHead;
WptCount:Integer;
Wpt: Array of TWpt; // 重复数181
Truck0:TTruck0;
TruckCount:Integer;
Truck: Array of TTruck; // 重复数186
FEnd:TFEnd;
Procedure ReadAPStr(Var F:File;Var Vars:PChar;
Len:Integer);
Var
B:TChars;
P:Integer;
Begin
SetLength(B,Len);
BlockRead(F,B[0],Len);
P:=ZeroPos(B,Len);
Vars:=StrAlloc(P);
Move(B[0],Vars^,Len);
End;
Procedure WriteAPStr(Var F:File;Var Vars:PChar;
Len:Integer);
Var
B:TChars;
P:Integer;
Begin
SetLength(B,Len);
P:=StrLen(Vars);
Move(Vars^,B[0],P);
BlockWrite(F,B[0],Len);
End;
Procedure ReadAPchar(Var F:File;Var Vars:PChar);
Var
i:Integer;
B:Array of Char;
Begin
i:=0;
repeat
SetLength(B,i+1);
BlockRead(F,B[i],1);
Inc(i);
Until B[i-1]=#0;
Vars:=StrAlloc(i);
Move(B[0],Vars^,i);
End;
Procedure WriteAPchar(Var F:File;Var Vars:PChar);
Var
Len:Integer;
B:Array of Char;
Begin
Len:=StrLen(Vars);
SetLength(B,Len+1);
Move(Vars^,B[0],Len+1);
BlockWrite(F,B[0],Len+1);
End;
Procedure ReadWpt(Var F:File;Var Wpt:TWpt);
Begin
With Wpt Do
Begin
BlockRead(F,D0,Sizeof(Longint));
ReadAPChar(F,Caption);
BlockRead(F,D2,Sizeof(Longint));
BlockRead(F,D3,Sizeof(Byte));
BlockRead(F,D4,Sizeof(Byte));
BlockRead(F,D5,Sizeof(Longint));
BlockRead(F,D6,Sizeof(Byte));
BlockRead(F,BAyy18,Sizeof(ByteArr18));
BlockRead(F,Lat,Sizeof(Longint));
BlockRead(F,Lon,Sizeof(Longint));
BlockRead(F,Flag,Sizeof(Byte));
BlockRead(F,Altitude,Sizeof(Double));
ReadAPChar(F,Date);
BlockRead(F,D13,Sizeof(Byte));
BlockRead(F,D14,Sizeof(Longint));
BlockRead(F,D15,Sizeof(Longint));
BlockRead(F,D16,Sizeof(Longint));
BlockRead(F,D17,Sizeof(Byte));
BlockRead(F,D18,Sizeof(Byte));
BlockRead(F,D19,Sizeof(Byte));
BlockRead(F,D20,Sizeof(Longint));
BlockRead(F,D21,Sizeof(Longint));
BlockRead(F,D22,Sizeof(Longint));
BlockRead(F,D23,Sizeof(Longint));
End;
End;
Procedure WriteWpt(Var F:File;Var Wpt:TWpt);
Begin
With Wpt Do
Begin
BlockWrite(F,D0,Sizeof(Longint));
WriteAPChar(F,Caption);
BlockWrite(F,D2,Sizeof(Longint));
BlockWrite(F,D3,Sizeof(Byte));
BlockWrite(F,D4,Sizeof(Byte));
BlockWrite(F,D5,Sizeof(Longint));
BlockWrite(F,D6,Sizeof(Byte));
BlockWrite(F,BAyy18,Sizeof(ByteArr18));
BlockWrite(F,Lat,Sizeof(Longint));
BlockWrite(F,Lon,Sizeof(Longint));
BlockWrite(F,Flag,Sizeof(Byte));
BlockWrite(F,Altitude,Sizeof(Double));
WriteAPChar(F,Date);
BlockWrite(F,D13,Sizeof(Byte));
BlockWrite(F,D14,Sizeof(Longint));
BlockWrite(F,D15,Sizeof(Longint));
BlockWrite(F,D16,Sizeof(Longint));
BlockWrite(F,D17,Sizeof(Byte));
BlockWrite(F,D18,Sizeof(Byte));
BlockWrite(F,D19,Sizeof(Byte));
BlockWrite(F,D20,Sizeof(Longint));
BlockWrite(F,D21,Sizeof(Longint));
BlockWrite(F,D22,Sizeof(Longint));
BlockWrite(F,D23,Sizeof(Longint));
End;
End;
Procedure ReadTpt(Var F:File;Var Tpt:TTpt);
Begin
With Tpt Do
Begin
BlockRead(F,D0,Sizeof(Longint));
BlockRead(F,Lon,Sizeof(Longint));
BlockRead(F,Flag,Sizeof(Byte));
BlockRead(F,altitude,Sizeof(Double));
BlockRead(F,D4,Sizeof(Byte));
BlockRead(F,APacked,Sizeof(Longint));
BlockRead(F,D6,Sizeof(Word));
End;
End;
Procedure WriteTpt(Var F:File;Var Tpt:TTpt);
Begin
With Tpt Do
Begin
BlockWrite(F,D0,Sizeof(Longint));
BlockWrite(F,Lon,Sizeof(Longint));
BlockWrite(F,Flag,Sizeof(Byte));
BlockWrite(F,altitude,Sizeof(Double));
BlockWrite(F,D4,Sizeof(Byte));
BlockWrite(F,APacked,Sizeof(Longint));
BlockWrite(F,D6,Sizeof(Word));
End;
End;
Procedure ReadTruck(Var F:File;Var Truck:TTruck);
Var
i:Integer;
Begin
With Truck Do
Begin
BlockRead(F,D0,Sizeof(Byte));
BlockRead(F,D1,Sizeof(Longint));
ReadAPChar(F,D2);
BlockRead(F,D3,Sizeof(Longint));
BlockRead(F,D4,Sizeof(Byte));
BlockRead(F,Count,Sizeof(Longint));
Setlength(P,Count);
For i:=0 to Count-1 do
ReadTpt(F,P[i]);
End;
End;
Procedure WriteTruck(Var F:File;Var Truck:TTruck);
Var
i:Integer;
Begin
With Truck Do
Begin
BlockWrite(F,D0,Sizeof(Byte));
BlockWrite(F,D1,Sizeof(Longint));
WriteAPChar(F,D2);
BlockWrite(F,D3,Sizeof(Longint));
BlockWrite(F,D4,Sizeof(Byte));
Count:=Length(P);
BlockWrite(F,Count,Sizeof(Longint));
For i:=0 to Count-1 do
WriteTpt(F,P[i]);
End;
End;
Procedure ReadTruck0(Var F:File;
Var Truck0:TTruck0);
Var
i:Integer;
Begin
With Truck0 Do
Begin
BlockRead(F,D0,Sizeof(Longint));
ReadAPChar(F,D1);
BlockRead(F,D2,Sizeof(Longint));
BlockRead(F,D3,Sizeof(Byte));
BlockRead(F,Count,Sizeof(Longint));
Setlength(P,Count);
For i:=0 to Count-1 do
ReadTpt(F,P[i]);
End;
End;
Procedure WriteTruck0(Var F:File;
Var Truck0:TTruck0);
Var
i:Integer;
Begin
With Truck0 Do
Begin
BlockWrite(F,D0,Sizeof(Longint));
WriteAPChar(F,D1);
BlockWrite(F,D2,Sizeof(Longint));
BlockWrite(F,D3,Sizeof(Byte));
Count:=Length(P);
BlockWrite(F,Count,Sizeof(Longint));
For i:=0 to Count-1 do
WriteTpt(F,P[i]);
End;
End;
Procedure ReadFEnd(Var F:File;Var FEnd:TFEnd);
Begin
With FEnd Do
Begin
BlockRead(F,D0,Sizeof(Byte));
BlockRead(F,D1,Sizeof(Longint));
BlockRead(F,D2,Sizeof(Word));
BlockRead(F,D3,Sizeof(Byte));
End;
End;
Procedure WriteFEnd(Var F:File;Var FEnd:TFEnd);
Begin
With FEnd Do
Begin
BlockWrite(F,D0,Sizeof(Byte));
BlockWrite(F,D1,Sizeof(Longint));
BlockWrite(F,D2,Sizeof(Word));
BlockWrite(F,D3,Sizeof(Byte));
End;
End;
Procedure ReadFHead(Var F:File;Var FHead:TFHead);
Begin
With FHead Do
Begin
ReadAPChar(F,D0);
BlockRead(F,D1,Sizeof(Longint));
ReadAPChar(F,D2);
BlockRead(F,D3,Sizeof(Longint));
ReadAPChar(F,D4);
ReadAPChar(F,D5);
ReadAPChar(F,D6);
ReadAPChar(F,D7);
End;
End;
Procedure WriteFHead(Var F:File;Var FHead:TFHead);
Begin
With FHead Do
Begin
WriteAPChar(F,D0);
BlockWrite(F,D1,Sizeof(Longint));
WriteAPChar(F,D2);
BlockWrite(F,D3,Sizeof(Longint));
WriteAPChar(F,D4);
WriteAPChar(F,D5);
WriteAPChar(F,D6);
WriteAPChar(F,D7);
End;
End;
Procedure ReadDataFile(Fn:String);
Var
i:Integer;
F:File;
Begin
OpenBFile(fn,F);
ReadFHead(F,FHead);
WptCount:=181;
Setlength(Wpt,WptCount);
for i:=0 to WptCount-1 do
ReadWpt(F,Wpt[i]);
ReadTruck0(F,Truck0);
TruckCount:=186;
Setlength(Truck,TruckCount);
for i:=0 to TruckCount-1 do
ReadTruck(F,Truck[i]);
ReadFEnd(F,FEnd);
CloseFile(F);
End;
Procedure WriteDataFile(Fn:String);
Var
i:Integer;
F:File;
Begin
CreateFile(fn,F);
WriteFHead(F,FHead);
for i:=0 to WptCount-1 do
WriteWpt(F,Wpt[i]);
WriteTruck0(F,Truck0);
for i:=0 to TruckCount-1 do
WriteTruck(F,Truck[i]);
WriteFEnd(F,FEnd);
CloseFile(F);
End;
End.
4.5 修改数据
对数据文件中的部分数据项进行修改,通过对照修改前后原系统的变化判断数据项的含义,这种手段在数据文件分析中是常用的方法。
Fexplorer提供修改数据功能,在“编辑”菜单中,选中“直接修改数据文件”。然后查找被修改的数据的位置,确定数据类型,再在修改数据编辑栏中填写新数据,回车。新数据将替换原数据,
并被写入数据文件。