D:\resources是什么文件\art\eff面里的文件可以删吗

lwglucky 的BLOG
用户名:lwglucky
文章数:250
评论数:84
访问量:644437
注册日期:
阅读量:5863
阅读量:12276
阅读量:365930
阅读量:1061017
51CTO推荐博文
如何制作一个sig文件呢?
Sig文件的制作流程如下:
&&&&&&&&&&&&&&&&&&&&& PCF&&&&&&&&&&&&&&&&&&&&&&&& Sigmake
Library(.obj) ---------------------& PatternFile(.pat) -------------------& SignatureFile(.sig)
用pcf.exe a.lib a.pat
然后在sigmake a.pat a.sig
若出现冲突,看帮助文档解决冲突,
See the documentation to learn how toresolve collisitions.
:modules/leaves:1, COLLISIONS: 1554
这时会生成一个同名的 a.exc.txt 打开它,首先直接把前4行删掉,不想麻烦的话就直接sigmake -xa.exc a.pat a.sig 这时就可以了,或者在你确定的那个函数行前+,就像这样
_wWinMainCRTStartup&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 00 0000 E8........E9....................................................
_WinMainCRTStartup&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 00 0000 E8........E9....................................................
_wmainCRTStartup&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 00 0000 E8........E9....................................................
+_mainCRTStartup&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 00 0000 E8........E9....................................................
保存,然后在 sigmake a.pat a.sig ,exc文件会自动被读取
复制到IDA的sig目录,点那个玫瑰花,然后添加,就OK啦,此时,一些库函数就会被识别出来。但是我们如何获取lib文件呢?确切的说是如何获得一个obj文件?自己有源码的话就可以直接编译得到obj文件!还可以从lib文件中导出obj,没错,这种方法很常见。但要是dll文件怎么办呢?别急我们有idb_to_pat,丢到ida里,使用这个插件就可以得到pat文件,再用sigmake就可以啦!最后膜拜tnttools大侠!
================================
(c) Copyright 1997 by Ilfak Guilfanov & DataRescue
One major stumbling block in the disassembly of programs written in modern high level languages is the time required to isolate library functions. This time may be considered lost because it does not bring us new knowledge : it is only a mandatory step that allows further analysis of the program and its meaningful algorithms. Unfortunately, this process has to be repeated for each and every new disassembly.
Sometimes, the knowledge of the class of a library function can considerably ease the analysis of a program. This knowledge might be extremely helpful in discarding useless information. For example, a C++ function that works with streams usually has nothing to do with the main algorithm of a program.
It is interesting to note that every high level language program uses a great number of standard library functions, sometimes even up to 95% of all the functions called are standard functions. For one well known compiler, the &Hello, world!& program contains:
library functions
function main()
1 Of course, this is an artificial example but our analysis has shown that real life programs contain, on average, 50% of library functions. This is why the user of a disassembler is forced to waste more than half of his time isolating those library functions. The analysis of an unknown program resembles the resolution of a gigantic crossword puzzle : the more letters we know, the easier it is to guess the next word. During a disassembly, more comments and meaningful names in a function means a faster understanding of its purpose. Widespread use of standard libraries such as OWL, MFC and others increase even more the contribution of the standard functions in the target program.
A middle sized program for Win32, written in C++, using modern technologies (e.g., AppExpert or a similar wizard) calls anything from 1000 to 2500 library functions.
To assist IDA users we attempted to create an algorithm to recognize the standard library functions. We wanted to achieve a practical, usable result and therefore accepted some limitations
we only consider programs written in C/C++
we do not attempt to achieve perfect function recognition : this is theoretically impossible. Moreover the recognition of some functions may lead to undesirable consequences. For example, the recognition of the following function
ret would lead to many misidentifications. It is worth noting that in modern C++ libraries one can find a lot of functions that are absolutely identical byte-to-byte but have different names.
we only recognize and identify functions located in the code segment, we ignore the data segment.
when a function has been sucessfully identified, we assign it a name and an eventual comment. We do not aim to provide information about the function arguments or about the behaviour of the function.
and we imposed the following constraints upon ourselves
we try to avoid false positives completely. We consider that a false positive (a function wrongly identified) is worse than a false negative (a function not identified). Ideally, there should be no false positive at all.
the recognition of the functions must require a minimum of processor and memory resources.
because of the polyvalent architecture of IDA - it supports tens of very different processors - the identification algorithm must be platform-independent, i.e. it must work with the programs compiled for any processor.
the main() function should be identified and properly labelled as the library' startup-code is of no interest.
Memory usage The main obstacle to recognition and identification is the sheer quantity of functions and the size of the memory they occupy. If we evaluate the size of the memory occupied by all _versions_ of all libraries produced by all compiler _vendors_ for memory _models_, we easily fall into the tens of gigabytes range.
Matters get even worse if we try to take OWL, MFC, MFC and similar libraries into account. The storage needed is huge. At this time, personal computers' users can't afford to set aside hundreds of Megabytes of disk space for a simple utility disassembler. Therefore, we had to find an algorithm that diminishes the size of the information needed to recognize standard library functions. Of course, the number of functions that should be recognized dictates the need for an efficient recognition algorithm : a simple brute force search is not an option.
Variability An additional difficulty arises from the presence of _variant_ bytes in the program. Some bytes are corrected (fixed up) at load time, others become constants at link time, but most of the variant bytes originate from references to external names. In that case the compiler does not know the addresses of the called functions and leaves these bytes equal to zeroes. This so called &fixup information& is usually written to a table in the output file (sometimes called &relocation table& or &relocation information&). The example below
ax, seg _OVRGROUP_
_farmalloc
26: 3B 1E 0000e
bx, word ptr es:__ovrbuffer contains variant bytes. The linker will try to resolve external references, replacing zeroes with the addresses of called functions, but some bytes will stay untouched : references to dynamic libraries or bytes containing absolute address in the program. These references can be resolved only at load time by the system loader. It will try to resolve all external references and replace zeroes with absolute addresses. When the system loader cannot resolve an external referenceI, as it is the case when the program refers to an unknown DLL, the program will simply not run.
Optimizations introduced by some linkers will also complicate the matter because constant bytes will sometim es be changed. For example:
0000: 9A........
far ptr xxx
is replaced by
0002: E8....
near ptr xxx The program will execute as usual, but the replacement introduced by the linker effectively prohibits byte-to-byte comparison with a function template. The presence of variant bytes in a program makes the use of simple checksums for recognition impossible. If functions did not contain variant bytes, the CRC of the first N Bytes would be enough to identify and select a group of functions in a hash table. The use of such tables would greatly decrease the size of the information required for identification : the name of a function, its length and checksum would suffice.
We have already mentionned the fact that the recognition of all standard library functions was not possible or even desirable. One additional proof is the fact that some identical functions do exactly the same thing but are called in a different manner. For example, the functions strcmp() and fstrcmp() are identical in large memory models.
We face a dilemna here : we do not want to discard these functions from the recgnition process since they are not trivial and their labelling would help the user but, we are unable to distinguish them.
And there is more : consider this
xxx At first sight, these pieces of code are not interesting. The problem is that they are present, sometimes in significant number, in standard libraries. The libraries for the Borland C++ v1.5 OS/2 compiler contains 20 calls of this type, in important functions such as read(), write(), etc.
Plain comparison of these functions yields nothing. The only way to distinguish those functions is to discover what other function they call. Generally, all short functions (consisting merely of 2-3 instructions) are difficult to recognize and the probability of wrong recognition is very high. However not recognizing them is undesirable, as it can lead to cascade failures : if we do not recognize the function tolower(), we may fail to recognize strlwr() which refers to tolower().
Copyright Finally, there is an obvious copyright problem : standard libraries may simply not be distributed with a disassembler.
To address those issues, we created a database of all the functions from all libraries we wanted to recognize. IDA Pro now checks, at each byte of the program being disassembled, whether this byte can mark the start of a standard library function.
The information required by the recognition algorithm is kept in a signature file. Each function is represented by a pattern. Patterns are first 32 bytes of a function where all variant bytes are marked. For example:
558BEC0EFF7604..........5BEC0EFF7604..........59595DC3 _registerbgidriver
558BEC1E078AB5E108B4E0AD1E9D1E980E1CA6E0A8A76 _biosdisk
558BEC1EB41AC5DC3.................................... _setdta
558BEC1EB42FCDA8BE8B4E088B93B41A _findfirst where variant bytes are displayed as &..& Several functions start with the same byte sequence. Therefore a tree structure seems particularly well suited to the storage of those functions :
0EFF7604..........5BEC0EFF7604..........59595DC3 _registerbgidriver
078AB5E108B4E0AD1E9D1E980E1CA6E0A8A76 _biosdisk
2FCDA8BE8B4E088B93B41A
_findfirst Sequences of bytes are kept in the nodes of the tree. In this example, the root of the tree contains the sequence &558BEC&, three subtrees stem from the root, respectively starting with bytes 0E, 1E, B4. The subtree starting with B4 gives birth to two subtrees. Each subtree ends with leaves . The information about the function is kept in that (only the name is visible in the above example).
The tree data structure simultaneously achieves two goals :
Memory requirements are decreased since we store bytes common to several functions in tree nodes. This saving is, of course, proportional to the number of functions starting with the same bytes.
It is well suited to fast fast pattern matching. The number of comparisons required to match a specific location within a program to all functions in a signature file grows logarithmically with the number of functions.
It would not be very wise to take a decision based on the first 32 bytes of a function alone. As already suggested, modern real-world libraries contain several functions starting with the same bytes:
B8....8ED8
33C050FF..........83C406
8BF083FEFF
1. _access
(18 9A62) When two functions have the same first 32 bytes, they are stored in the same leaf of the tree. To resolve that situation, we calculate the CRC16 of the bytes starting from position 33 until till the first variant byte. The CRC is stored in the signature file. The number of bytes used to calculate that CRC also needs to be saved, as it differs from function to function. In the above example, the CRC16 is calculated on 20 bytes for the _chmod (bytes 33..52) function and 18 _access function.
There is, of course, a possibility that the first variant byte will be at the 33d position. The length of the sequence of bytes used to calculate the CRC16 is then equal to zero. In practice, this happens rarely and this algorithm gives very low number of false recognitions.
Sometimes functions have the same initial 32-byte pattern and the same CRC16, as in the example below
05B8FFFFEB278ABD8B8....8EC0
0. _tolower (03 41CB) (000C:00)
1. _toupper (03 41CB) (000C:FF) We are unlucky: only 3 bytes were used to calculate the CRC16 and they were the same for both functions. In this case we will try to find a position at which all functions in a leaf have different bytes. (in our example this position is 32+3+000C)
But even this method does not allow to recognize all functions. Here is another example:
... (partial tree is shown here)
0D8A....83CC:
0. _strupr (04 D19F) (REF 0011: _toupper)
1. _strlwr (04 D19F) (REF 0011: _tolower) These functions are identical at non-variant bytes and differ only by the functions they call. In this example the only way to distinguish functions is to examine the name referenced from the instruction at offset 11.
The last method has a disadvantage: proper recognition of functions _strupr() and _strlwr() depends on the recognition of functions _toupper() and _tolower(). It means that in the case of failure because of the absence of reference to _toupper() or _tolower() we should defer recognition and repeat it later, after finding _tolower() or _toupper(). This has an impact on the general design of the algorithm : we need a second pass to resolve those deferred recognitions. Luckily, subsequent passes are only applied to a few locations in the program.
Finally, one can find functions that are identical in non-variant bytes, refer to the same names but are called differently. Those functions have the same implementation but different names. Surprisingly, this is a frequent situation in standard libraries, especially in C++ libraries.
We call this situation a _collision_ which occurs when functions attached to a leaf can not be distinguished from each other by using the described methods. A classical example is:
558BEC1EB441C550E8....5DCB................
0. _remove (00 0000)
1. _unlink (00 0000)
8BDCFAE9....8BDCE9............................
0. @iostream@$vsn
1. @iostream_withassign@$vsn (00 0000) Artificial Intelligence is the only way to resolve those cases. Since our goal was efficiency and speed, we decided to leave artificial intelligence for the future developments of the algorithm.In IDA Pro version 3.6, the practical implementation of the algorithm matches the above description almost perfectly. We have limited ourselves to the C and C++ language but it will be, without doubt, possible to write pre-processors for other libraries in the future.
A separate signature file is provided for each compiler. This segregation decreases the probability of cross-compiler identification collisions. A special signature file, called startup signature file is applied to the entry point of the disassembled program to determine the generating compiler. Once it has been identified, we know which signature file should be used for the rest of the disassembly. Our algorithm successfully discerns the startup modules of most popular compilers on the market.
Since we store all functions' signatures for one compiler in one signature file, it is not necessary to discriminate the memory models (small,compact, medium, large, huge) of the libraries and/or versions of the compilers.
We use special startup-signatures for every format of disassembled file. The signature exe.sig is used for programs running under MS DOS, lx.sig or ne.sig - for OS/2, etc.
To decrease a probability of false recognition of short functions, we must absolutely remember any reference to an external name if such a reference exists. It may decrease, to some degree, the probability of the recognition of the function in general but we believe that such an approach is justified. It is better not to recognize than to recognize wrongly. Short functions (shorter than 4 bytes) that do no contain references to external names are not used in the creation of a signature file and no attempt is made to recognize such functions.
The functions from &ctype.h& are short and refer to the array of types of the symbols, therefore we decided to consider the references to this array as an exception : we calculate the CRC16 of the array of the types of the symbols and store it in the signature file.
Without artificial intelligence, the collisions are solved by natural intelligence. The human creator of a signature file chooses the functions to include and to discard from the signature file. This choice is very easy and is practically implemented by the edition of a text file.
The patterns of the functions are not stored in a signature file under their original form (i.e., they do not look like the example figures). In place of the patterns, we store the arrays of bits determining the changing bytes and the values of the individual bytes are stored. Therefore the signature file contains no byte from the original libraries, except for the names of the functions. The creation of a signature file involves in 2 stages: the preprocessing of the libraries and the creation of a signature file. In the first stage the program 'parselib' is used. It preprocesses *.obj and *.lib files to produce a pattern-file. The pattern-file contains the patterns of the functions, their names, their CRC16 and all other information necessary to create the signature file. At the second stage the 'sigmake' program builds the signature file from the pattern-file.
This division into 2 stages allows sigmake utility to be independent of the format of the input file. Therefore it will be possible to write other preprocessors for files differing from *.obj and *.lib in future.
We decided to compress (using the InfoZip algorithm) the created signature files to decrease the disk space necessary for their storage.
For the sake of user's convenience we attempted to recognize the main() function as often as it was possible. The algorithm for identifying this function differs from compiler to compiler and from program to program. (DOS/OS2/Windows/GUI/Console...).
This algorithm is written, as a text string, in a signature file. Unfortunately we have not yet been able to automate the creation of this algorithm.
As it turns out the signature they may be compressed by a factor bigger than 2. The reason of this compressibility is that about 95% of a signature file are function names. (Example: the signature file for MFC 2.x was 2.5MB before compression, 700Kb after. It contains 33634 an average of 21 bytes is stored per function. Generally, the ratio of the size of a library size to the size of a signature file varies from 100 to 500.
The percentage of properly recognized functions is very high. Our algorithm recognized all but one function of the &Hello World& program. The unrecognized function consists of only one instruction:
off_1234 We were especially pleased with the fact that there was no false recognition. However it does not mean that they will not occur in the future. It should be noted that the algorithm only works with functions.
Data is sometimes located in the code segment and therefore we need to mark some names as &data names&, not as &function names&. It is not easy to examine all names in a modern large library and mark all data names.
The implementation of these data names is planned, some time in the future.
====================================
翻译得不好,有些句子实在是想不出更好的表达方式了,请见谅。网页的格式有点乱,完整的Word文档看附件。
另外我想问下arhat版主,现在只是翻译了个初稿,如可能的话,我想请论坛上的个别朋友校正一下,这需要把英文原稿发过去,不知这是否可以?
:非常感谢Aleaxander的校正,修改过了原来有些别扭的句子,附件已被更新,请重新下载。
另外根据Aleaxander的建议,还有两个问题:
1 strip 应该翻译为&去除&还是&剥离&好呢?似乎2种都可以,个人感觉第2种更形象一些,所以在这篇文档中仍然用的是&剥离&。不知其它章是怎么译的,要改的话我可以再修改。
2 top-level是否翻译成&顶级&?有点别扭,但是都想不出更好的译法。
12 使用FLIRT签名识别库(ibrary recognition using flirt signatures)
在&The initial autoanalysis has been finished&[1]之后,该是到了探索IDA更高级功能的时候了。这一章中我们将讨论一些识别标准代码序列的技术,如包含在静态链接二进制中的库代码,或者编译器插入的标准初始化和辅助函数。
当您对二进制进行逆向工程时,要做的最后一件事就是浪费时间去逆向库函数,实际上这种工作您可以通过阅读帮助文档,源代码,或者从互联网上寻找资料,就能够更简单容易地做到。静态链接二进制所带来的难题是应用程序代码和库代码的区分很模糊。在静态链接二进制中,整个库与应用程序代码互相结合,形成一个密不可分的可执行文件。不过幸运的是,有些工具可以让IDA识别并且标记出库代码,使我们把注意力集中在应用程序自身的代码上。
1 当IDA为一个新加载的二进制完成自动化分析时,会在消息窗口中输出这条信息。
库文件快速识别与鉴定技术(Fast Library Identification and Recognition Technology)
&&& 库文件快速识别与鉴定技术(Fast Library Identification and Recognition Technology),简称为FLIRT[2],包含了一组IDA用来标识库代码序列的方法。FLIRT的核心技术是模式匹配算法,这使得IDA能够迅速确定一个反汇编后的函数是否匹配IDA中许多已知签名的一个。&IDADIR&/sig目录包含了IDA附带的签名文件,在大多数情况下,这些签名与常见的Windows编译器生成的库相关,但少数非Windows的签名也包括在内。
&&& 签名文件使用自定义格式,其中大部分签名数据被压缩和包含在一个IDA指定的头文件中。多数情况下,签名文件的名称不能清楚地表明这个签名文件是由哪个库产生的,签名文件可以包含一个库名字的注释来描述其内容,这取决于它们的创建过程。如果我们从签名文件中提取ASCII内容,查看它们的前几行,往往就能够发现这个注释。下面的Unix风格命令[3]显示,注释一般输出在第二或第三行:
# strings sigfile | head -n 3
&&& 在IDA中有两种办法查看相关的签名文件。第一,通过View -& Open Subviews -& Signatures,可以查看到已经应用于二进制的签名列表;第二,通过 File -& Load File -& FLIRT Signature File,作为手动签名应用程序流程的一部分,显示出所有的签名文件列表。
应用FLIRT签名(Applying FLIRT Signatures)
&&& 当一个二进制第一次打开时,IDA尝试对入口点应用startup签名指定的相关签名文件。原来,各种不同的编译器所产生的入口点代码是相当不同的,入口点签名匹配是一种有效的技术,可以用来鉴定生成一个给定二进制的编译器类型。
2 更多信息请访问:http://www./IDApro/flirt.htm。
3 第2章中介绍了这个strings命令,head命令用来查看源输入文件的开头几行(在这个例子中是3)。
MAIN VS. _START
&&& 我们知道,一个程序的入口点就是将被执行的第一条指令的地址。长期以来,许多C程序员错误地认为这是main函数的地址,实际上并非如此。程序的文件类型,但不是创建这个程序的语言类型,决定了以何种方式提供命令行参数给程序。为了解决加载器传递命令行参数的方式和程序期望的接收方式之间的差异(例如,传递参数给main),在转移控制权给main之前必须执行一些初始化代码。正是这段初始化代码被IDA指定为程序的入口点,并且定义为标签_start。
&&& 这段始化代码还负责其它任何允许main运行前的初始化任务。在C++程序中,该代码负责在main执行前保证全局定义对象的构造函数被调用。同样,为了在程序真正结束前调用全局对象的析构函数,释放代码被插入在执行main之后。
&&& 如果IDA确定了创建一个二进制的编译器类型,将会载入和该编译器库相对应的签名文件,并且应用到二进制的剩余部分。随IDA发布的签名文件趋向于某些私有的编译器,如Microsoft Visual C ++或Borland Delphi。背后的原因是因为与这些编译器相关的二进制库的数量有限。而开源的编译器如GNU gcc,与它相关的二进制库数量和操作系统一样众多,比如每个版本的FreeBSD都有个唯一的C标准库版本。由于最佳模式匹配的原因,签名文件需要与每个不同版本的库相对应,考虑到要收集随每一个Linux发行版而发行的每一个libc.a[4]的版本非常困难,是不切合实际的。从某种程度上说,这些差异是由于改变了库的源代码,因而编译后代码不一样。但是使用不同的编译选项,如优化设置和不同的编译器版本来创建库,也可能会导致巨大的差异。最终的方案是,IDA附带了非常少的开源编译器库的签名文件。不过你将很快看到的好消息是,Hex-
Rays生成工具允许你从静态库中创建你自己的签名文件。
&&& 那么,在什么情况下需要手动应用签名到您的数据库中呢?某些情况下,IDA正确识别出了生成二进制的编译器,但没有编译器库相关的签名文件可用,这时候,您既可以工作在没有签名的世界中,也可以去获取一份该二进制使用的静态库文件副本,然后生成你自己的签名。其他情况下,IDA可能只是简单地表明识别编译器失败,因此无法确定哪些签名应该应用于数据库。在分析初始化函数被完全破坏以便去除编译器标识的混淆代码时,通常会发生这种情况。那么,在你还抱有希望去匹配任何库签名之前,要做的第一件事就是去充分地反混淆该二进制。我们将在第21章讨论处理混淆代码的技术。
&&& 不管何种原因,如果您想手动应用签名到一个数据库,可以通过 File -& Load File -& FLIRT Signature File,打开的签名选择对话框如图12-1所示。
4 libc.a是类Unix操作系统中静态链接版本的C标准库的二进制文件。
图12-1:选择FLIRT签名
&&& IDA的&IDADIR&/sig目录下的每个.sig文件都被显示在File栏中。注意,没有任何方法可以更改.sig文件的存放位置。如果您曾经生成了您自己的签名,需要连同其它所有的.sig文件一起放到&IDADIR&/sig目录。Library name栏显示了嵌入进每个文件的库名字注释,记住这些注释只能由签名创建者(这可能是你!)来指定。
&&& 当一个库模块被选择后,相应.sig文件中包含的签名就被加载,与数据库中的每个函数作比较。一次只能应用一组签名,如果您想应用多个不同的签名到数据库,需要重复此过程。当一个与签名相匹配的函数被找到后,该函数就被标识为库函数,并且根据已匹配的签名进行自动重命名。
警告:只有命名为IDA伪名称的函数名才可以被自动重命名。换句话说,如果您已经重命名了一个函数,之后这个函数匹配上了一个签名,那么这个函数将不会再被重命名。因此,在您的分析过程中尽可能早点应用签名是有好处的。
还记得,静态链接二进制模糊了应用程序代码和库代码的区别。如果你足够幸运,有一个没有剥离符号信息的静态链接二进制,你将至少拥有有用的函数名称(与创建它的可靠程序员一样有用)来帮助您通过代码进行分类。但是,如果该二进制已被剥离符号,你将可能拥有数以百计个函数,所有这些由IDA生成的函数名称无法表明该函数是什么功能。在这两种情况下,都只有在签名有效时IDA才能够确定库函数(未被剥离符号二进制中的函数名称并不能给IDA提供足够的信息来最终确定一个函数是库函数)。图12-2显示了一个静态链接二进制的导航面板。
图12-2:未被签名的静态链接二进制
这个图中,没有函数被识别为库函数,因此您可能会发现需要自己分析的代码比你真正需要的多得多。在应用一组正确的签名之后,导航面板转化为图12-3。
图12-3:应用签名后的静态链接二进制
&&& 如你所看到的,导航面板提供了对于一组特定签名有效性的最好说明,有了这很大比例的相匹配签名,大量的部分代码将被标记为库代码并相应重命名。例如在图12-3中,集中在导航图的极左部分代码很可能就是实际应用程序的具体代码。
&&& 在应用签名时有两点值得记住。第一,即使一个二进制文件没有被剥离符号,签名也是有用的,这种情况下您应用签名更多的是帮助IDA识别库函数,而不是重命名这些函数;第二,静态链接的二进制可能由多个独立的库组成,需要应用多组签名以便完全识别所有的库函数。每增加一组签名应用,导航面板的将有部分变化以反映发现的库代码。图12-4显示了这样一个例子,在这个图中,你会看到一个静态链接二进制使用了C标准库和OpenSSL[5] 加密库。
图12-4:多组签名中应用第一组后的静态二进制
具体来说,您会看到随着应用程序使用正确的OpenSSL版本签名,IDA标记了一小段(地址范围左边的高亮段)作为库代码。静态链接的二进制往往是先创建应用程序本身的代码,然后添加所需的库,最后形成可执行文件。鉴于这种情况,我们可以得出这样的结论,OpenSSL库代码添加在内存空间的右边,而应用程序代码则最有可能在OpenSSL库的左边。如果我们在图12-4的基础上继续应用签名,最终得到如图12-5显示。
图12-5:多组签名中接着应用的静态二进制
在这个例子中,我们为libc,libcrypto,libkrb5,libresolv等等应用了签名。某些情况中,我们基于库中字符串位置来选择签名;其他情况下,我们基于二进制中已被定位的有着密切联系的其它库来选择签名。从显示结果中继续看到,导航区右半段是个黑色带,极左边的边界处是一段更小的黑色带。二进制的剩余未知部分需要进一步的分析。这时候,我们应该知道,黑色带的右侧更广阔区域是一个未知的库,而黑色带的左边是应用程序代码。
5更多信息请访问:http://openssl.org/.
创建FLIRT签名文件(Creating FLIRT Signature Files)
&& 正如我们之前讨论的,IDA为每一个存在的静态库发布签名文件是不现实的。为了给IDA用户提供工具和必要的信息,以便他们创建自己的签名,Hex-Rays发布了Fast Library Acquisition for Identification and Recognition(FLAIR)工具集。授权用户可以通过IDA发行CD获取或者去Hex-Rays网站[6]下载。象IDA的其他插件一样,该工具集以Zip文件发布。IDA 5.2版本的FLAIR52.zip包含了相关的FLAIR工具。Hex-Rays不一定会为每个版本的IDA发布一个新版,所以你应该使用不高于你的IDA版本的最新版FLAIR。
&& 安装FLAIR工具集是个简单事情,只需解压缩相关的Zip文件即可,但我们强烈建议您建立专门的FLAIR目录来存放,因为这个Zip文件是没有组织的顶级目录。FLAIR工具中你会看到几个由文本文件构成的文档,值得关注的文件包括这些:
readme.txt
这是一个签名创建过程的顶级概述。
&& 这个文件描述了静态库解析器plb.exe的使用,在第219页&创建模式文件&中对库解析器有更详细的讨论。
&& 这个文件详细描述了模式文件的格式,这是签名创建过程的第一步。模式文件的描述也在第219页&创建模式文件&中。
sigmake.txt
这个文件描述了sigmake.exe的使用,它用来从模式文件中生成签名文件。更多细节请参阅第221页&创建签名文件&。
此外顶层内容中感兴趣的包括bin目录,它包含了FLAIR工具集的所有可执行文件;还有startup目录,包含了与各种编译器的通常初始化顺序和输出文件类型(PE,ELF,等等)相关的模式文件。FLAIR工具集还有个很重要的一点是,尽管所有的工具都只能运行在Windows命令行下,但生成的签名文件可用于所有的IDA版本(Windows,Linux和OS X)。
6 当前的最新版本flair52.zip从这里获得:http://www./idapro/ida/flair52.zip,下载时需要用户名和密码。
创建签名概述
创建签名文件的基本流程不是很复杂,可归结为四个简单的步骤。
1. 获取一份您所要创建的签名文件的静态库文件。
2. 利用一个FLAIR解析器为该库创建一个模式文件。
3. 运行sigmake.exe处理由此产生的模式文件,生成一个签名文件。&
4. 复制它到&IDADIR&/sig目录,为IDA中安装新的签名文件。
不幸的是,现实中只有最后一个步骤和说的一样容易。在以下各节,我们来更详细地讨论前三个步骤。
识别和获取静态库
签名创建过程的第一步是为你想要创建的签名找到一份静态库,这可能有点挑战性,原因有很多。第一个问题就是确定哪些库是你真正需要的,如果你正在分析的二进制文件并没有被剥离符号,那么在您反汇编后可能会幸运地看到很多实际函数的名字,这种情况下,Google将会提供一些可能有用的备选信息。被剥离了符号的二进制由于缺乏函数名,不太容易找到其起源,为了确认库有个很好的办法,就是通过strings来搜索足够独特的字符串,如以下:
OpenSSL 0.9.8a 11 Oct 2005
版权告示和错误字符串往往是足够独特的,可以再次使用Google来缩小搜索范围。如果您是从命令行运行strings,请记住使用-a选项来强制strings扫描整个二进制;否则,你可能会错过一些有用的字符串数据。
在开放源代码库,你会发现源代码是现成的。不幸的是,虽然源代码可以帮助你理解二进制的行为,但您却不能用它来生成您的签名。您可以使用源代码来建立自己的静态库版本,然后用该版本来创建签名。然而,在所有可能的情况下,不同的创建过程会使生成的库与你正分析的库相差很大,导致您产生的签名文件是相当不准确的。
解决这个问题的最好办法是确定这个二进制的起源,在这里我们指的是准确的操作系统,操作系统版本,和发行版本(如果有的话)。因此,创建签名时使用的库最好是从相同配置的系统中得到的。当然,这会引出下一个问题:对于一个任意的二进制,它是在什么系统上创建的?对于这个问题,一个好的开始是使用file程序来获得这个二进制的一些初步了解。在第2章我们看到过file程序的一些样例输出,某些情况下,这一输出足够提供可能的备选信息。以下是一个非常典型的file
输出例子:
$ file sample_file_1
sample_file_1: ELF 32-bit LSB executable, Intel 80386, version 1 (FreeBSD),
for FreeBSD 5.4, statically linked, FreeBSD-style, stripped
这种情况下,我们可以一开始就直接从FreeBSD 5.4系统得到libc.a。然而,下面的例子是比较含糊的:
$ file sample_file_2
sample_file_2: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV),
for GNU/Linux 2.6.9, statically linked, stripped
由于可用的Linux发行版本也不是许多,我们可以缩小Linux系统的范围。借助于strings我们找到如下字符串:
GCC: (GNU) 4.1.1
(Red Hat 4.1.1-1)
在这里,范围已缩小到Red Hat(或其衍生版本)附带的gcc 4.1.1版本。GCC标记表明该二进制使用了常见的gcc编译器编译,我们很幸运地看到,这些字符串在剥离过程中幸免于难,并且对strings仍然可见。
请记住,file程序并不能解决所有的文件识别问题。下面的一个简单示例输出表明,file似乎知道正在检查的文件类型,但输出是很不明确的。
$ file sample_file_3
sample_file_3: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV),
dynamically linked (uses shared libs), stripped
这个例子是在Solaris 10 X86系统中输出的。这时,使用strings程序可能会对查明这一事实有所帮助。
创建模式文件
在这时你应该有了所要创建签名的一个或多个库文件,下一步就是为每个库创创建一个模式文件。模式文件要使用正确的FLAIR解析器程序来创建。像可执行文件,库文件被创建为各种规范的文件格式一样,FLAIR为多种流行的库文件格式提供了不同的解析器,详细说明见FLAIR的readme.txt文件,在FLAIR的bin目录中可以找到以下解析器:
解析OMF库(通常是Borland编译器使用)
解析COFF库(通常是Microsoft编译器使用)
&&& 解析ELF库(许多Unix系统使用)
&&& 解析Sony PlayStation PSX库
ptmobj.exe
&&& 解析TriMedia库
pomf166.exe
&&& 解析Kiel OMF 166对象文件
要为指定的库创建一个模式文件,需要指定库格式对应的解析器,要解析的库名称,以及所产生的模式文件名称。例如,对于一份FreeBSD 6.1系统的libc.a,您可以使用如下命令:
$ ./pelf libc.a libc_FreeBSD61.pat
libc.a: skipped 0, total 986
在这里,解析器输出了被解析的文件(libc.a),跳过的函数个数(0)[7],及产生的签名模式数量(986)。每个解析器接受的命令选项略有不同,只根据解析器的使用情况作相应输出。如果不带任何参数的执行一个解析器,将会显示该解析器所能接受的所有命令行选项。plb.txt文件包含了plb.exe解析器支持选项的更详细信息,这文件是个不错的基本来源信息,因为其他解析器能接受的大多数选项以它为准。许多情况下,简单地指定要被解析的库名称和要产生的模式文件就足够了。
模式文件是个文本文件,其中每一行代表一个被解析库的函数。前面创建的模式文件中有几行显示如下:
08B450C8B4D4A75F88B&...& 00 D :0000 _wmemset&
D568B7D088B750CFCC1E902F3A55E8B4.... 00 E :0000 _wmemcpy
BF38B4D088B550C83C204 19 A9BE
FLAIR的pat.txt文件描述了一个单独模式的格式。概括地说,模式的第一部分列出了函数的初始字节序列,最多为32个字节,具体容量可能会由于入口位置而有所不同。每个字节使用两个点显示,当一个函数少于32字节时用点来填充以补满64[8]个字符(如前面代码中的_wmemset)。除了最初的32个字节,还有一些附加信息被记录,以便在签名匹配过程中提供更精确的信息。附加信息被编码到每个模式行,包括计算函数部分内容的CRC16[9] 值,函数的长度以字节为单位,并列出被函数引用的符号名称。一般来说,引用了许多其它符号的长函数会形成更复杂的模式行。在前面生成的libc_FreeBSD61.pat文件中,一些模式行的长度超过20000个字符。
&&& 有些第三方程序员开发了一些实用工具,用来从现有的IDA数据库中生成模式文件。例如J.C.Roberts编写的一个IDA插件:IDB_2_PAT[10],它能够从现有的数据库中为一个或多个函数生成模式;如果你在其它的数据库中遇到相似的代码,或者正分析一个二进制,但是没有获得原来创建它的库文件,这些工具是很有用的。
7 plb和pcf解析器可能会跳过某些函数,这取决于提供给解析器的命令行选项和被解析的库结构。
8 每个字节有两个字符,显示32个字节的内容需要64个字符。
9 这是一个16位循环冗余校验值,该CRC16用于模式生成的执行细节见FLAIR工具的crc16.cpp文件。
10 更多信息请访问:http://www.openrce.org/downloads/details/26/IDB_2_PAT.
创建签名文件
一旦你为某个库创建了一个模式文件,签名创建过程的下一步就是生成一个适用于IDA的.sig文件。IDA签名文件的格式与模式文件完全不同,签名文件使用了一种专有的二进制格式,被设计为尽量减少占用的空间,力求能够表达模式文件中的所有信息,以便签名文件和实际的数据库内容有效地匹配。对签名文件结构的一个高级别描述,可在Hex-Rays[11] 网站上找到。
FLAIR的sigmake.exe程序用于从模式文件中创建签名。模式创建和签名创建分为两个不同的阶段,签名创建过程完全独立于模式创建过程,而且允许使用第三方的模式创建工具。创建签名的最简单方法是,使用sigmake.exe解析一个.pat文件并创建一个.sig文件,如下所示:
$ ./sigmake libssl.pat libssl.sig
如果一切顺利,会生成一个.sig文件,并准备安装到&IDADIR&/sig。然而,这个过程很少能够顺利地进行。
注意:sigmake的说明文档sigmake.txt中建议,签名文件的名字最好遵守MS-DOS 8.3名称长度规范。这不是一个硬性的要求,但是,如果使用了长文件名,将会只有文件名称的前8个字符能够显示在签名选择对话框中。
创建签名往往是个反复的过程,因为在这一阶段有冲突发生时必须处理。任何时候如果两个函数有相同的模式就会发生冲突。如果冲突不以某种方式解决,在应用签名的过程中就无法确定哪个函数是真正匹配的。因此,sigmake必须使产生的每一个签名正好对应一个函数名。如果这个基本条件没有满足,导致一个或多个函数有着相同的签名,sigmake就会拒绝生成签名文件,取而代之生成一个排斥文件(.exc)。第一次使用sigmake传递一个新的.pat文件(或一组.pat文件)时,更典型的输出可能如下。
$ ./sigmake libc_FreeBSD61.pat libc_FreeBSD61.sig
See the documentation to learn how to resolve collisions.
: modules/leaves: , COLLISIONS: 911
文档文件sigmake.txt叙述了sigmake的使用及解决冲突的过程。事实上,每次sigmake执行时,先搜索相应的排斥文件,因为该文件中包含了sigmake在处理已命名模式文件中可能会遇到的冲突,及怎样解决冲突的有用信息。如果没有这个排斥文件,当冲突发生时,sigmake就会生成一个排斥文件,而不是生成一个签名文件。在前面的例子中,我们会找到一个新产生的名为libc_FreeBSD61.exc的文件。当第一次被创建时,排斥文件是个文本文件,包含了sigmake在处理模式文件中遇到的冲突细节,排斥文件必须被修改以提供sigmake关于如何解决模式冲突的指示。编辑一个排斥文件的一般过程如下。
11更多信息请访问:http://www./idapro/flirt.htm.
sigmake生成的所有排斥文件开头几行都如下所示:
;--------- (delete these lines to allow sigmake to read this file)
; add '+' at the start of a line to select a module
; add '-' if you are not sure about the selection
; do nothing if you want to exclude all modules
这几行的目的是提醒你应该怎样做,才能解决冲突以成功地生成签名。首要事情就是删除以分号开头的这四行,否则sigmake在随后的执行中将无法解析排斥文件。接下来的步骤是告诉sigmake您想要解决的冲突。从libc_FreeBSD61.exc提取出来的几行示例如下:
___ntohs 00 C3................................................
___htons 00 C3................................................
_index& 00 C908A84DB75F531C05BC3..............
_strchr&& 00 C908A84DB75F531C05BC3..............
_rindex 00 C31CD84DB75F35BC3..........
_strrchr 00 C31CD84DB75F35BC3..........
这几行详细区分了三个冲突。这种情况下,我们被告知函数ntohs和htons无法区分,index和strchr具有相同的签名,rindex和strrchr有冲突。如果您熟悉以上几个函数的话,对这种结果可能并不感到奇怪,因为这些冲突函数的功能基本上是相同的(例如,index和strchr执行同样的操作)。
为了让你自己控制签名,sigmake期望你对每组适当函数相关的签字指定不超过一个函数。如果您想在数据库匹配上相应签名的任何时候都应用该名字,就在其前面添加一个字符(+);如果你想在数据库匹配上相应签名时简单地添加一条注释,就在其前面添加一个减号字符(-);如果你想在数据库匹配上相应签名时不应用任何名字,就不要添加任何字符。对前面所述的三个冲突,下面列出了一个可能有效的解决方式:
+___ntohs 00 C3................................................
___htons& 00 C3................................................
_index&& 00 C908A84DB75F531C05BC3..............
_strchr&& 00 C908A84DB75F531C05BC3..............
_rindex& 00 C31CD84DB75F35BC3.......
-_strrchr 00 C31CD84DB75F35BC3..........
&& 这表示,第一个签名匹配时我们选择使用ntohs名字,第二个签名匹配时什么也不做,第三个签名匹配时添加strrchr为注释。在解决冲突时有必要记住以下几点:
1. 要最简单地解决冲突,只需删除排斥文件的开头四行。
2. 在每个冲突组中不要为一个以上的函数添加+/-。
3. 如果发生冲突的组中只包含一个函数,不要添加+/-在其前面,只是保持原样。
4. sigmake再次失败后会导致注释行等数据被附加到现有的排斥文件后面。在运行sigmake前应该删除这些额外的,并纠正原始数据(因为如果数据是正确的,sigmake在第二次也不会失败)。
一旦对排斥文件作出了正确的更改,必须保存该文件,并使用和最初使用时相同的命令行参数重新运行sigmake。通过第二次运行,sigmake应该能找到并依照你的排斥文件,从而成功地生成一个.sig文件。注意,sigmake操作成功是指没有输出错误消息并生成了一个.sig文件,如下所示:
$ ./sigmake libc_FreeBSD61.pat libc_FreeBSD61.sig
在签名文件成功生成后,就可以复制它到您的&IDADIR&/sig目录下使它生效。然后,您就可以在File -& Load File -& FLIRT Signature File中使用它了。
请注意,我们并没有介绍模式创建和sigmake的所有选项,plb.txt和sigmake.txt提供了所有有效选项的列表。使用sigmake时我们唯一要注意的是-n选项,这个选项让您可以插入一个描述性名称到签名文件中,这个名称被显示在签名选择过程中(见图12-1),它有助于您对有效签名进行排序。以下命令行插入一个&FreeBSD 6.1 C standard library&名称到所产生的签名文件中:
$ ./sigmake -n&FreeBSD 6.1 C standard library& libc_FreeBSD61.pat libc_FreeBSD61.sig
作为一种替代办法,库名称还可以在排斥文件中被指定。然而,由于排斥文件并不是在所有创建签名的情况中都需要,所以命令行选项通常是更有用的。如需进一步详情,请参阅sigmake.txt。
Startup签名
IDA还有一种专用签名,称为startup签名。startup签名使用在当二进制被第一次加载到数据库时,试图识别用于创建二进制的编译器。如果IDA可以识别出用来创建二进制的编译器,那么在开始分析二进制期间,与识别出的编译器相对应的签名文件就被自动加载。
由于当一个文件被第一次载入时,编译器类型最初是未知的,根据载入的二进制文件类型,startup签名被分组选择载入。例如,当一个Windows PE二进制被载入时,PE二进制相关的startup签名被载入,试图识别出用于创建PE二进制的编译器类型。
为了生成startup签字, sigmake处理描述了各种编译器生成的startup函数[12]的模式文件,并组合由此产生的签名到一个指定类型的单独签名文件中。FLAIR的startup目录包含了IDA所使用的startup模式,startup.bat脚本用来从这些模式文件中创建相应的startup签名。使用sigmake为指定文件格式创建startup签名的示例可参考startup.bat。
对于PE文件,你会在startup目录下看到有若干个pe_*.pat文件,它们描述了一些流行的Windows编译器所使用的startup模式,包括Visual Studio的pe_vc.pat和Cygwin/gcc的&
12 startup函数通常是指程序的入口点。在一个C/C++程序中,startup函数的作用是在传递控制权给main函数前对程序的环境变量进行初始化。
pe_gcc.pat。如果您想为PE文件添加更多的startup模式,需要将它们添加到一个现有的PE模式文件,或者以pe_前缀开头创建一个新的模式文件,以便startup签名创建脚本能够正确地找到您的模式,并将其合并到一个新的PE签名。
关于startup模式最后一个要注意的是它的格式,不幸的是它和库函数生成的模式略有不同。事实上那个不同之处在于,即startup模式行拥有更多的模式签名集,如果与这个模式匹配它也被应用。除了在startup目录下包含startup模式示例外,FLAIR没有其它任何关于startup模式格式的文档。
总结(Summary)
库代码的自动识别是一项重要功能,大大减少了分析静态链接二进制文件所需的时间。通过FLIRT和FLAIR,IDA使代码的自动识别不仅是可能的,而且可扩展为允许用户从现有的静态库中创建他们自己的库签名。熟悉签名生成过程对于需要分析静态链接二进制文件的人来说是一项重要技能。
================================
很多天前,有人提出这个问题。当时没有想到用lib.exe,以为自己要重新发明一个轮子ar2.exe,才可能解析出MS LIB文件中的OBJ文件。今天又看见有人发贴,再次dig一下,不过如此...
下面是在命令行上制作的过程,环境是WinXP+NTFS+VS2003,在我的机子上运行无误。破折号之间的引用都是命令行。
附件是制作好的SIG文件,可以准确地解析出printf()函数,当然还有其它很多很多库函数。
将libc.lib, libcd.lib, libcmt.lib, libcmtd.lib从原文件夹下复制过来。
避免在命令行上输入过长的路径
-----------------
set path = %path%;C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\bin
set path = %path%;C:\Program Files\IDA\addons\Flair.v5.20\bin
-----------------
设置要调用的程序的路径,在你的机子上不一定如此
-----------------------------
for %i in (*.lib) do md %i.fdr
-----------------------------
For: 新建存放对象文件的文件夹
-----------------------------
cd v:\libc.lib.fdr
for /F &skip=3&& %i in ('link.exe -lib /list ..\libc.lib') do link.exe -lib /extract:%i ..\libc.lib
cd v:\libcd.lib.fdr
for /F &skip=3&& %i in ('link.exe -lib /list ..\libcd.lib') do link.exe -lib /extract:%i ..\libcd.lib
cd v:\libcmt.lib.fdr
for /F &skip=3&& %i in ('link.exe -lib /list ..\libcmt.lib') do link.exe -lib /extract:%i ..\libcmt.lib
cd v:\libcmtd.lib.fdr
for /F &skip=3&& %i in ('link.exe -lib /list ..\libcmtd.lib') do link.exe -lib /extract:%i ..\libcmtd.lib
-----------------------------
For: 依次提取libc.lib, libcd.lib, libcmt.lib, libcmtd.lib中的所有对象文件。
--------------------------
for %i in (.\libc.lib.fdr\*.obj) do pcf.exe -g0 %i
for %i in (.\libcd.lib.fdr\*.obj) do pcf.exe -g0 %i
for %i in (.\libcmt.lib.fdr\*.obj) do pcf.exe -g0 %i
for %i in (.\libcmtd.lib.fdr\*.obj) do pcf.exe -g0 %i
-------------------------
pcf.exe -g0 .\libc.lib.fdr\*.obj
pcf.exe -g0 .\libcd.lib.fdr\*.obj
pcf.exe -g0 .\libcmt.lib.fdr\*.obj
pcf.exe -g0 .\libcmtd.lib.fdr\*.obj
-------------------------
For: 由.obj文件生成.pat文件。为了避免pcf.exe在执行的过程中处理非COFF文件时中断、出现提示信息&is not ar/coff file\npress enter to exit&,加上参数&-g0&。
-------------------------
sigmake -n&VC7 Static Lib (ST/MT & Rel/Dbg) By TnTTools& libc.lib.fdr\*.pat+libcd.lib.fdr\*.pat+libcmt.lib.fdr\*.pat+libcmtd.lib.fdr\*.pat vc7libc
See the documentation to learn how to resolve collisitions.
: modules/leaves: 0, COLLISIONS: 2690
-------------------------
sigmake -n&VC7 Static Lib (ST/MT & Rel/Dbg) By TnTTools& libc.lib.fdr\*.pat+libcd.lib.fdr\*.pat+libcmt.lib.fdr\*.pat+libcmtd.lib.fdr\*.pat vc7libc
--------------------------
For: 转化成SIG文件vc7libc.sig,在这里我把四个静态库文件放在一起,当然你可以分开放。通过第一次运行sigmake,知道有冲突存在。手工编辑.EXE文件后再次运行sigmake,生成vc7libc.sig。见附件。
.text:00402A03&&&&&&&&&&&&&&&& push&&& offset aUsage&& ; & Usage: \n&
.text:00402A08&&&&&&&&&&&&&&&& call&&& sub_403772
.text:00402A0D&&&&&&&&&&&&&&&& add&&&& esp, 4
.text:00402A10&&&&&&&&&&&&&&&& push&&& offset aHashH&& ; &&& hash -h\n&
.text:00402A15&&&&&&&&&&&&&&&& call&&& sub_403772
.text:00402A1A&&&&&&&&&&&&&&&& add&&&& esp, 4
.text:00402A03&&&&&&&&&&&&&&&& push&&& offset aUsage&& ; & Usage: \n&
.text:00402A08&&&&&&&&&&&&&&&& call&&& _printf
.text:00402A0D&&&&&&&&&&&&&&&& add&&&& esp, 4
.text:00402A10&&&&&&&&&&&&&&&& push&&& offset aHashH&& ; &&& hash -h\n&
.text:00402A15&&&&&&&&&&&&&&&& call&&& _printf
.text:00402A1A&&&&&&&&&&&&&&&& add&&&& esp, 4
The Art Of Reverse Engineering
注意,我在这里讨论的仅是一种很特别的情况:VC静态库文件libc.lib, libcmt.lib。原因起源于论坛上网友的一个疑问。如果直接调用pcf.exe处理这两个文件会遇到问题。
没有必要编写什么程序来包裹它,一是它根本不是万能的SIG制作流程,在实际中,各种情况都有可能遇到;二是我们需要了解CONSOLE下STDOUT的各种输出信息(大部分人不感兴趣);三是自动处理EXC后的手工编辑必不可少(至少在我看来如此)。
==========================
最近在科锐上课学了如何生成sig文件,研究了一下PAT文件格式,下面是得到的信息,欢迎大家指正。
一、特征码区(前32个字节):
存放的是OBJ文件代码段的前16个机器码,存放机器码的方法如下:
1.& 将值的16进制数转换成对应的ASCII码(如:E8 09 --& 45 38 30 39)。
2.& CALL或者Jxx后面跟随的绝对地址、相对地址都不做为特征码,用 2E 即 &.& 替代。
3.& 如果OBJ文件的机器码少于16字节,用 2E 补齐。
4.& 如果OBJ文件的机器码多于16字节,将多余的特征码放入第六部分附加特征码空间中。
二、被忽略的特征码数量(4个字节):
&&& 前后两个字节为空格,用于分隔,中间两个字节表示后面附加特征码空间机器码与前面特征码区中间省略的特征码的长度。最大值为FF。
三、校验码(4个字节):
&&& 对忽略的特征码进行CRC-16校验算法(校验公式为 0x8408 = 216 + 212 + 25 + 1),并将值的16进制数转换成ASCII码填入这4个字节中,下面有校验算法的源码
四、代码段长度(6个字节):
&&& 前后两个字节为空格,用于分隔,中间4个字节表示代码段的长度,以16进制显示。
五、标识符信息(长度不定):
该信息是一个结构体数组,结构体如下:
unsigned char[]&&&&&& 标识处偏移行数,以空格结束
unsigned char[]&&& 标识名,以空格结束
标识处偏移行数有三种表示方式:
1.& 第一个变量以 : 开头,后面接偏移,最后空格结束(如 : 0000 )
2.& 如果是标识符则以 : 开头,偏移后面接 @ ,最后空格结束(如 : 004C@ )
3.& 如果是函数名、标识符则以 ^ 开头,后面接偏移,最后空格结束(如 ^005B )
六、附加特征码空间(长度不定、可选):
&&& 如果特征码超过32字节,剩余部分减去被忽略的字节数量(即第二部分的值),其他放入这个区域中
七、结束标识(最后7个字节):
0D 0A 2D 2D 2D 0D 0A&& -----&&& ..---..
标志PAT文件结束
上面是综合性的归纳,下面给两个小例子说明一下(一个是有第六部分的、一个是没有第六部分的):
1、没有第六部分的例子:
Offset&&&&& 0& 1& 2& 3& 4& 5& 6& 7&& 8& 9& A& B& C& D& E& F
第一部分: 特征码区,这里只有16字节的机器码
&& 42 38 30 33 30 30 30 30& 30 30 43 33 39 30 39 30&& B090
&& 39 30 39 30 39 30 39 30& 39 30 39 30 39 30 39 30&& 9090
&& 2E 2E 2E 2E 2E 2E 2E 2E& 2E 2E 2E 2E 2E 2E 2E 2E&& ................
&& 2E 2E 2E 2E 2E 2E 2E 2E& 2E 2E 2E 2E 2E 2E 2E 2E&& ................
第二部分: 被忽略特征码区数量区,这里是0
&& 20 30 30 20&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 00&
第三部分: 校验码,这里是0
&&&&&&&&&&&&&& 30 30 30 30&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 0000
第四部分: 代码段长度,这里是10H,即是16个字节
&&&&&&&&&&&&&&&&&&&&&&&&&&& 20 30 30 31 30 20&&&&&&&&&&&&&&&&& 0010&
第五部分: 标识符信息,这里只有一个函数名
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 3A 30&&&&&&&&&&&&&&&& :0
&& 30 30 30 20 3F 74 32 40& 40 59 41 48 58 5A 20&&&&& 000 ?t2@@YAHXZ&
第六部分: 这里特征码只有16字节,不用另外的空间存放
第七部分: 结束标识
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 0D&&&&&&&&&&&&&&&&& .
&& 0A 2D 2D 2D 0D 0A&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& .---..
2、有第六部分的例子:
Offset&&&&& 0& 1& 2& 3& 4& 5& 6& 7&& 8& 9& A& B& C& D& E& F
第一部分: 特征码区,这里只有32字节的机器码,多余部分放在第六部分处
&& 35 31 38 44 30 34 32 34& 35 30 36 38 2E 2E 2E 2E&& 518D....
&& 2E 2E 2E 2E 46 46 31 35& 2E 2E 2E 2E 2E 2E 2E 2E&& ....FF15........
&& 38 42 34 43 32 34 30 38& 38 33 43 34 30 38 38 44&& 8B4C8D
&& 34 31 46 46 38 33 46 38& 30 33 37 37 34 46 46 46&& 41FF83F803774FFF
第二部分: 被忽略特征码数量区,这里是2
&& 20 30 32 20&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 02&
第三部分: 校验码,这里是B198
&&&&&&&&&&&&&& 42 31 39 38&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& B198
第四部分: 代码段长度,这里是90H,即是144个字节
&&&&&&&&&&&&&&&&&&&&&&&&&&& 20 30 30 39 30 20&&&&&&&&&&&&&&&&& 0090&
第五部分: 标识符信息,这里有函数名、标识符、跳转标志等
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 3A 30&&&&&&&&&&&&&&&& :0
&& 30 30 30 20 5F 73 73 77& 69 74 63 68 20 3A 30 30&& 000 _sswitch :00
&& 38 30 40 20 6F 66 66 5F& 34 30 31 30 37 43 20 3A&& 80@ off_40107C :
&& 30 30 32 36 40 20 6C 6F& 63 5F 34 30 31 30 32 36&& 0026@ loc_401026
&& 20 3A 30 30 33 38 40 20& 6C 6F 63 5F 34 30 31 30&&& :0038@ loc_4010
&& 33 37 20 3A 30 30 34 41& 40 20 6C 6F 63 5F 34 30&& 37 :004A@ loc_40
&& 31 30 34 38 20 3A 30 30& 35 43 40 20 6C 6F 63 5F&& C@ loc_
&& 34 30 31 30 35 39 20 5E& 30 30 30 36 20 73 7A 53&& 06 szS
&& 74 72 20 5E 30 30 30 43& 20 73 63 61 6E 66 20 5E&& tr ^000C scanf ^
&& 30 30 32 37 20 63 61 73& 65 31 20 5E 30 30 32 44&& 0027 case1 ^002D
&& 20 70 72 69 6E 74 66 20& 5E 30 30 33 39 20 63 61&&& printf ^0039 ca
&& 73 65 32 20 5E 30 30 34& 42 20 63 61 73 65 33 20&& se2 ^004B case3&
&& 5E 30 30 35 44 20 63 61& 73 65 34 20 5E 30 30 36&& ^005D case4 ^006
&& 46 20 64 65 66 61 75 6C& 74 20&&&&&&&&&&&&&&&&&&&& F default&
第六部分: 剩余代码区
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 2E 2E 2E 2E 2E 2E&&&&&&&&&&&& ......
&& 2E 2E 36 38 2E 2E 2E 2E& 2E 2E 2E 2E 46 46 31 35&& ..68........FF15
&& 2E 2E 2E 2E 2E 2E 2E 2E& 38 33 43 34 30 34 33 33&& ........83C40433
&& 43 30 35 39 43 33 36 38& 2E 2E 2E 2E 2E 2E 2E 2E&& C059C368........
&& 46 46 31 35 2E 2E 2E 2E& 2E 2E 2E 2E 38 33 43 34&& FF15........83C4
&& 30 34 33 33 43 30 35 39& 43 33 36 38 2E 2E 2E 2E&& ....
&& 2E 2E 2E 2E 46 46 31 35& 2E 2E 2E 2E 2E 2E 2E 2E&& ....FF15........
&& 38 33 43 34 30 34 33 33& 43 30 35 39 43 33 36 38&& 83C8
&& 2E 2E 2E 2E 2E 2E 2E 2E& 46 46 31 35 2E 2E 2E 2E&& ........FF15....
&& 2E 2E 2E 2E 38 33 43 34& 30 34 33 33 43 30 35 39&& ....83C
&& 43 33 36 38 2E 2E 2E 2E& 2E 2E 2E 2E 46 46 31 35&& C368........FF15
&& 2E 2E 2E 2E 2E 2E 2E 2E& 38 33 43 34 30 34 33 33&& ........83C40433
&& 43 30 35 39 43 33 2E 2E& 2E 2E 2E 2E 2E 2E 2E 2E&& C059C3..........
&& 2E 2E 2E 2E 2E 2E 2E 2E& 2E 2E 2E 2E 2E 2E 2E 2E&& ................
&& 2E 2E 2E 2E 2E 2E&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& ......
第七部分: 结束标识
&&&&&&&&&&&&&&&&&&&& 0D 0A& 2D 2D 2D 0D 0A&&&&&&&&&&&&&&&&& ..---..
这里两个例子的OBJ文件以及PAT文件我会在附件中给出,另外CRC校验码算法(pcf文件夹中已经提供)也一起给出。
呃&&看来我权限不足哈,等我权限够了再上传......
这里还有一个问题没有跟出算法,不知谁有好的解决方法:
第二部分的被忽略的特征码是按照什么规律忽略的?
===============
了这篇文章
附件下载:      
类别:┆阅读(0)┆评论(0)
请输入验证码:

我要回帖

更多关于 java resources文件夹 的文章

 

随机推荐