修改程序功能的方法

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

修改程序功能的方法

作者:史永林倪安胜王宁燕

来源:《电脑知识与技术》2016年第31期

摘要:文章从静态和动态两个方面介绍了修改程序功能的几种方法,并总结了各种方法的优缺点。

关键词:修改;程序功能;截获系统调用

中图分类号:TP311 文献标识码:A 文章编号:1009-3044(2016)31-0059-02

在工作中我们经常会遇到一些需要修改现有程序的情况,如修改程序界面,对程序输入输出数据进行加解密,修改程序的某些功能等。对于游戏爱好者来说,能自己做一个游戏外挂将是很有效增加积分的方法。对于研究病毒的人来说,修改程序是一种病毒感染的常用方法。

1 概述

如果我们能获得程序的源代码,一切都迎刃而解了,直接修改程序代码,可以做任何我们想做的修改。但大多数情况下我们并不能获得程序的源代码,这时我们就需要对二进制的可执行文件进行修改或进行运行控制,改变其运行行为,以达到我们的目的。这可以通过两种方式进行,一种是静态修改二进制文件,另外一种是动态捕获软件对系统功能的调用。

2 静态方法

这种方式主要是对二进制的程序进行直接修改,从而改变其行为。从原理上讲,这种方法可以实现和修改源代码一样的功能,但在现今的技术条件下,我们只能实现有限的修改功能。

如果是用解释型语言编写的程序,如foxpro、java等,可以使用逆向工程工具进行反编译,可以较大程度上恢复源代码,这样对程序的修改就比较方便了,如foxpro程序,可以基本上恢复为源代码。但有些解释性语言如c#,java等,只能恢复一部分代码。对这种形式程序的修改可参阅一些逆向工程的书籍,如文献[1],本文不作讨论。

如果只是修改软件的资源,如进行汉化等,可以使用一些汉化工具直接对资源进行修改。

静态修改基本上遵循如下步骤,首先对二进制程序进行反汇编,使用高级反汇编工具,如IDA Pro可以较大程度上获得各个模块的调用关系和程序的基本框架,以及对windows API 的调用情况等。然后对反汇编的汇编代码进行分析,找出要修改部分的代码所在的位置及调用关系等。最后使用二进制编辑器,如Hex Workshop等或者编写程序对二进制文件进行修改。

其实采用静态方法修改程序功能受到很多方面的限制,一般只能做简单的修改,如crack 密码,跳过某些执行代码等,要进行功能很复杂的修改则需要克服很多的困难,有时甚至是不可能完成的。比如上面所说步骤中的第二步,找到待修改的位置,这其实是一个非常困难的工作,也是修改的关键性步骤。二进制文件的反汇编代码非常大,即使使用IDA Pro这样的工具也只能获得一个很复杂的调用关系图,而且和源代码的调用关系图有很大的差距。我们知道汇编代码的可读性是很差的,这种反汇编的代码的可读性更差。一般通过动态调试和静态分析相结合的方法来分析程序的结构。第三步文件修改也涉及很多工作,如代码放置位置、文件头修改、输入输出函数表修改、函数重定位等,但好在这部分有较成熟的技术,也有一些工具可以做这部分工作,有兴趣的可参阅文献[2]。

3 动态方法

动态方法的核心思想就是截获系统调用,也就是系统的API函数,因为软件不管其功能如何复杂,其都是在调用系统函数的基础上完成的,例如NotePad程序就是调用TextOut或TextOutW函数将文本显示在窗口中的,如果我们截获此调用,则可以截获显示在窗口中的数据,可以进行及时翻译、数据解密等工作。对于对输入输出数据(如硬盘数据,网络数据等)的加解密工作,可以通过各自专有的机制进行,如对硬盘数据可以采用IFS(Installable File System)的过滤驱动来实现,详见[3],对网络数据可以采用NDIS(Network Driver Interface Specification)来实现,详见[4],但这种方式是针对整个系统进行的,对系统的性能有较大影响,而且实现很复杂,需要较强的技术基础,我们修改的是某个程序的功能,不需要在整个系统级别上进行,我们只要捕获此程序的相应的输入输出函数即可,如对网络的WSASend和WSARecv等函数,硬盘的ReadFile和WriteFile等。游戏外挂制作可能根据需要使用多种技术,如模拟鼠标和键盘消息来自动操作游戏,自动发送和捕获网络数据包进行游戏操作模拟等,这里对数据包的捕获即可以通过截获系统调用来实现。

截获系统调用有多种方法,总体上可以分为内核层捕获和用户层捕获两种。内核层捕获工作在内核层,也就是ring0级,主要是通过对windows并未正式公布的一些内部数据结构的修改和操作来达到捕获函数的目的,这种方法捕获的层次低,可以更接近原始的数据,可以对所有的用户层进程进行捕获,受用户层程序的影响小,缺点是在内核层编程,程序调试、安装和与用户层程序通信都较复杂,另外因为使用的都是windows并未正式公布的一些内部数据结构,程序的可移植性较差,可能只能在特定windows版本上运行。用户层捕获工作在用户层,也就是ring3级,主要是通过对进程的输入函数表进行替换或修改被捕获函数来实现的,这种方式的优点是只影响相应的进程,对整个系统性能影响小,编程方便,容易实现,可移植性较好,缺点是很难实现对所有进程的捕获,因为工作在用户层,所以容易被其他程序影响,可靠性不高。

3.1 内核层捕获

有多种方式可以实现内核层捕获,主要有修改SSDT表、修改IDT表等。其中修改SSDT 表法可移植性好,操作较简单,应用的比较广泛。

3.1.1 修改SSDT表法

SSDT即System Service Dispath Table,就是一个表,这个表中有内核调用的函数地址,我们在用户层调用的所有API函数最终都转化为内核调用,比如用户层的CreateFile函数最终转化为对内核层的NtCreateFile函数的调用,而这个函数的地址就在SSDT表中,如果我们事先把这个地址改成我们特定函数的地址,就可以达到对系统调用的捕获,详见[5]。这种方法可以在windows xp,windows2003 和Vista等操作系统下使用,很多杀毒软件都使用了这种技术。

3.1.2 修改IDT表法

IDT(Interrupt Descriptor Table)即中断描述符表,是用来处理中断的。中断就是停下现在的活动,去完成新的任务。一个中断可以起源于软件或硬件。比如,出现页错误,调用IDT 中的0x0E。或用户进程请求系统服务(SSDT)时,调用IDT中的0x2E。在系统中找到IDT,然后确定0x2E在IDT中的地址,最后用我们的函数地址去取代它,这样一来,用户的进程(可以特定设置)一调用系统服务,我们的hook函数即被激发,详见[5]。这种方法在winxp,win2k3,vsta下失效,为了性能的考虑,xp后的系统都改用sysentry命令来进入

ring0,去调用SSDT中的服务,不再是通过IDT中的 int 2E,但我们仍然可以通过捕获sysentry命令来进行。

3.2 用户层捕获

用户层捕获主要有动态修改进程输入表法[6]和修改被捕获函数的前几个指令法,如DETOURS[7]。

3.2.1 修改进程输入表法

修改进程输入表即可以通过静态方式也可以通过动态方式进行,原理是一样的,只是修改的时机不一样,静态方法直接修改PE文件,直接在PE文件中插入代码并修改输入表指向它,详见[2],动态方法在进程中插入代码并修改输入表,并不修改PE文件,详见[6]。

输入表格式如图1所示,进程通过其来引用动态链接库中的函数,其具体介绍参考[1]。通过修改JMP后的地址指向我们自己插入的代码即可以捕获相应的动态库的函数。

3.2.2 修改被捕获函数法

通过修改被捕获函数的前几个指令,使其通过无条件jump指令跳转到我们自己插入的代码位置,这样即实现了函数的捕获。Detours库提供了完整的捕获方法,包括代码的插入,函数的捕获,目标函数的调用等,详见[7]。图2为Detours的捕获原理示意图,Detours Function 为我们的自定义代码,Target Function为被捕获函数,Trampoline Function为Detours的中间代码,用来保存目标函数被替换的指令,以及跳转到目标函数的指令等。如图可见我们可以选择

相关文档
最新文档