语法分析似乎命题有点大,不过找不到更好的词了,大牛勿喷。我所说的语法分析是指对汇编级代码进行结构上的分析,目标是将汇编代码转化为更高级的代码,比如C代码。暂时语法分析是不包括数据流分析的,因为精力有限,构思语法树的结构体,以及可行性分析,花了我一个月,核心的分析算法花了我一个星期,在这其中,90%的时间都是用于思考,正好印证了一位朋友说的:技术的东西只要BAIDU GOOGLE就行了,算法的东西却需要思考。

我需要要分析出的语法结构包括你能想到的所有语法结构,但是将这个需求化简之后你会发现,你需要研究的语法结构只有两种,一种是 if 结构,一种是 while 结构,因为 if 语句进一步转化,会转化成 if else语句,或是switch语句,while语句进一步转化可以转化成 for 循环,各种各样的循环。

汇编的跳转可没那么丰富多彩,我所说的结构体都将最终转化为汇编,那么他一定遵循着某种规律。

if 语句和 while 语句很好分析,条件跳转JMP到上面就是while,条件跳转跳到下面就是if,但是不要忘记了 if while 语句里面的条件体可以很简单,也可以很复杂,比如 if (a>1 && (b<1 ||c>2)) 这样的语句如何分析就是语法分析的难点了。

首先,将问题再次简化,我们要分析出的语法树应该是最小单元,也就是说每一个单元都是不可再分解的。比如 if (a && b),while(a || b),都可以分解成两个语句,这样的语句不是最小单元。所以我们需要分析的语句是 if (a || b) 和 while (a && b),那么 if (a || b) 和 while (a && b) 的结构图什么样?如下

【自己想象吧】

他们共同点就是 a 会 JMP到 b的下一条指令,而 b的JMP将影响到两点,第一,他是 if 语句还是 while 语句,第二,影响到他是 && 还是 || ,为什么这么说呢?b的JMP向上,他是 while 语句,那么他的JMP就是跳的循环的开始,我称之为FunStart,那么a将JMP到循环的结束,我称之为FunEnd,判断a之后直接跳出循环,为什么?因为他们的连接符是 &&;同理,b的JMP向下,他是 if 语句,那么他的JMP就是跳到 if 语句的FunStart,可以判断他们的连接符是 ||。其实这些刚开始的时候我并没注意,到最后,思考核心算法的时候才发现他的重要性。这里面有一点很重要,那就是分析出FunStart和FunEnd。

同理,当我分析更复杂的条件语句时,我会发现他们都有着共同的特点:JMP一定是JMP到 下一个JMP语句之后,如果不是,那就一定是FunStart或是FunEnd,而JMP一定不能JMP到上一个JMP语句,那样就会变成死循环了。如果有多个JMP出,那么不能构成语法语句。

好了,分析完成,开始实战,构建语法树,语法树其实只是个壳,他包含了语法单元的类型(Type),包含了语法单元的实体指针,需要的时候再提取指针。

初始化语法书,导入实体结构,由于各种原因,我的WindTrudging获得的实体树排序不正确,得来个语法树排序,一个小问题又来了,函数有时候并不是循序排列的,JMP到函数入口之上的事情时有发生,这样函数的入口点就不是第一条语句,这个问题以后我再处理。

首先分析出IFGOTO语句,这是一切的原点啊!一个CMP TEST JMP 语句可以配对型成一个条件跳转,形成一个IFGOTO语句,在这个时候提取判断参数,判断哪两个数据由CMP 和 TEST 决定,判断方式由JMP语句决定(我说的JMP好像更应该说为JMC),比如>、<、==。CMP 和TEST之间有时候会夹着一些PUSH POP MOV指令之类的,这些指令都是不改变标志位的,将这些做为初始化指令,反正也不影响标志位。

完成之后,对语法树进行分割,识别所有交叉JMP集合,原因在上面已经说了,对分割好的语法树每一个进行分析,需要分析出只有一个JMP出点的集合,否则抛弃该集合,得到JMP出点,为了方便获得FunStart FunEnd,获得JMP集合的最高点和最低点,在两点之间不能有其他JMP节点,因为在判断语句中不可能还有其他JMP语句。有些JMP集合之中,还会有些没连接,那是没有交叉但是也在JMP集合中的,需要自己添加。

分析好这些,OK,开始进行 if while 语句的分析了,在上面,我分析IFGOTO语句的过程中已经将Condition也就是条件分析出来了,判断JMP出点是JMP向上,还是JMP向下,向上则调用WHILE分析函数,向下则调用IF分析函数。

接下来就是核心算法了,如何分析条件语句相当关键,经过一个星期的思考,想出分析的方法,我称之为阴阳法。首先我们分析JMP到JMP集合之内的JMP,即是说这个JMP不是JMP出点,也就是说不会JMP到FunStart或是FunEnd,这样的JMP我们可以用括号将其括起来,设在上为UP,在下为DOWN,上JMP地址为JMPUP,下JMP地址为JMPDOWN,我们的JMP在分析可以被括号括起来之后,之间的节点将被分割出来做为新的链表,我称之为下沉,下沉的节点在原来的位置会产生新的节点,就好像什么事也没发生,JMP节点却越来越少了,然后再分析括号里面的JMP(在算法实现上就是递归调用),里面的JMP只有三种可能,一个是JMP到JMPUP的JMP地址,第二个是JMP到JMPDOWN的JMP地址,第三个就是JMP到UP和DOWN之内(这又是一个递归了),如果出现例外,那么SORRY,无法转化为高级语言,对于第三种情况,我们使用递归调用,那么又回到开始的时候,又开始下沉,结果我们得到这样的结论,凡是JMP到UP和DOWN之间,我们都可以将其递归,从而“消失”,问题就再一次化简了,因为只剩下两种情况。我们假设,JMP到JMPUP,那么JMP为阳,JMP到JMPDOWN,JMP为阴,因为UP为第一个节点,他一定是阳,如果第二个节点JMP到JMPUP,那么他就和UP是平行的关系,如果第二个节点JMP到JMPDOWN,那么他就是阴,阴阳相遇,就可以将第二个节点到DOWN节点用括号括起来,下沉,然后递归,形成一个新的节点,以此类推,不断递归的结果,最后一定只剩下一个节点,JMP向FunStart或是FunEnd,JMP向FunStart的就是WHILE,JMP向FunEnd的就是IF,万法归一,大自然和谐而统一,感觉不错。

本来应该再画几个图,更形象点的表达其转化过程,但是没那艺术细胞啊(),神马PS之类的也不懂用,没办法了,能上看雪的朋友想必非常聪明,能看得懂。

和sssccc讨论了下,开个WTG(WindTrudging Group)的群,WindTrudging的源代码将在群中发布,因为如果有志开发的朋友,共同交流是非常必要的,没兴趣开发的朋友,要源代码也没有用。
加入WTG请进入 http://hi.baidu.com/chinahanwu/blog/item/3a2c6831cc6f2b08eac4af58.html

WindTrudging的设计思想就是动态获取程序运行的轨迹加以分析,可能有朋友说IDA就有F5了何必再做?静态的东西,别人可以弄很多陷阱,比如花指令,但是一个程序,只能有一个逻辑,不管程序怎么运行,不可能运行到崩溃吧?不管怎么复杂,都必定有其规律。语法分析窗口调用了SyntaxHighlighter的代码,在此表示感谢,如果有哪位朋友优化一个更好的版本,那更好了。WindTrudging的运行速度还需要优化,相对来说插件还很不成熟的,还有很多需要改进的地方,希望大家多多支持。

从运行原理上说程序可以截获所有数据流,分析出参数和变量,并且拦截主程序到DLL之间的参数调用,判断哪些是假函数,那些是真函数,这些都在程序中留有接口,只是没时间做,留给下一个版本了。

WindTrudging下载地址 http://www.rayfile.com/zh-cn/files/ba4c3238-2bb3-11e0-86c8-0015c55db73d/89996605/