标 题: Shub-Nigurrath的软件狗教程
翻 译: 月中人
时 间: 2006-09-04
17:41
链 接: http://bbs.pediy.com/showthread.php?threadid=31497
详细信息:
从应用程序移除Sentinel SuperPro软件狗 --暨软件狗破解方法详解
|
译者: |
月中人 ([PTG]) |
日期: |
2007-01-28 |
原文标题: |
Removing Sentinel SuperPro dongle from Applications and details on dongle way of cracking |
原文作者: |
Shub-Nigurrath [ARTeam] |
原文版本: |
Version 1.0 – September 2006 |
关键词: |
dongle, simulation, emulation, sentinel superpro, 软件狗, 模拟, 仿真 |
备注: |
首先感谢ARTeam的Shub-Nigurrath写这个软件狗教程。以下是翻译有关的几个问题: 1. 有几个原图不易取得,译者根据原图重画。 2. ‘仿真’意指用外部程序挂钩拦截应用程序与软件狗之间的交互;‘模拟’意指在应用程序内补丁实现假狗、不需要外部驱动程序。 3.【字典】圣天诺Sentinel,特征文件sig/signature,模拟simulation,仿真emulation,单元cell,附签tag,选项卡tab,编译器特性特征文件the
compiler specific signatures,软件狗内部资源Internal
dongle resources,交叉引用xref,重建与替换Rearch
& Replace,中性算法Alogrithm
Half,SP_SUCCESS(0)表示SP_SUCCESS、其值为0 |
目录
1.
摘要 |
2.
可能的方法:仿真与模拟 |
2.1. 软件狗如何工作 |
2.2. 软件狗的仿真 |
2.3. 仿真器如何工作 |
2.4. 模拟器如何工作 |
3.1. 用IDA反汇编 |
3.2. 用OllyDbg反汇编 |
4.1. 什么是Sentinel SuperPro |
4.2. 键存储器的结构 |
|
|
|
4.3. API函数引用 |
5.1. sproFormatPacket |
5.2. sproFindFirstUnit |
5.3. sproOverwrite |
5.4. sproFindNextUnit |
5.5. sproRead |
|
5.6. sproQuery |
6.
补充 |
7.
参考 |
8.
结束语 |
9.
历史 |
10.
致谢 |
欢迎回到我的另一长篇教程。这次我将专门讲解最近花了点时间研究的Sentinel软件狗。我首先了解了一些从应用程序去除Sentinel保护(以及一般意义的软件狗保护)的不同方法。有关这个课题的教程见到的比较少,见到一些也是过时的、不完整的,大多数难以读懂或应用到现代的应用程序上。
感谢许多人的工作,像GoatAss、CyberHeg、Crackz(这里只列出很少几个姓名)。特别要感谢TORO(我与他就本主题长时间聊过,因此我想在此公开向他表示感谢)。我在本教程所讲的东西不完全是新的。但不管怎么说,我更新了他们的技术,在最新的应用程序上测试过,并且把那些不再工作的例行程序(你能在别处找到)做了一点点修改,使它们继续可用。
在Woodmann网站[1]上关于本主题的网页是软件狗有关资料一个极好的来源。
将本教程视为Sentinel软件狗白皮书(不打算面面俱到),我努力把原理解释清楚,同时做点新的贡献。我关注Sentinel,因为它是最受欢迎、被使用最多的软件狗之一。
祝你愉快,
Shub-Nigurrath
这里描述的技术是通用的,不是特别针对任何商业应用程序。整个文档必须被当作为一个关于编程高级技术的文档,如何使用其中信息则完全是你自己的责任。 商业应用程序明确地使用的地方,本文仅就它们的保护而论,不透露任何破解细节。 |
一般而言,对于软件狗保护的应用程序有两个可能的方法:仿真或模拟。我将会使用这两个名词区分这两个方法,其他人可能不同意我这样使用这两个词的含义,但是这样总比叫它们方法A和方法B好些 ;-)
我必须首先建立一个共同的理解平台,以便能够开始详细的讨论。
我想你应该已经有这方面的知识,而且这里不解释特定软件狗的细节,总之为了理解与软件狗打交道的一般方法你需要有点常识。一般来讲,用软件狗保护的应用程序需要几个组件;我尽量用图1总结一下。
图 1 - 应用程序-软件狗交互的概略图
从图的底部向上到顶部,重要的组件依次为:
n 被保护的应用程序。应用程序与软件狗应用程序开发工具箱本地交互,调用它的API。从应用程序的观点看,就像调用一个代理对象,向它提出请求并等待回应或数据。对于知道这些软件工程对象的人来说,这是典型的设计模式。
n 应用程序开发库。这个组件是由API构成,我们可以假定它们就是代理方式:它们在应用程序本地运行提供编程接口,而在相反方面它们实现通信协议。它们只是消息调度程序和请求收集程序(总之像任何其他API一样)。开发库以一个dll或标准库(即动态链接或静态链接)和程序相连,并且向应用程序提供我所说的服务(从1到n)。
n 通信协议。这个元件要求在系统中安装软件狗驱动程序实现通信。
n 软件狗处理程序。当软件狗收到一个请求时,它必须启用[原文invoke]一个特定的请求处理程序。实际的软件狗逻辑当然比图1所示更加复杂,但是我们不妨就这么理解。软件狗处理程序使用软件狗内部资源(键、算法、数据,最后还有CPU)回答应用程序提出的问题。
n 软件狗内部资源。软件狗可能支持键、数据库和运行某些算法的CPU(这也是Sentinel的情况)。通常越难去除保护的软件狗,它的内部资源就越复杂。跟踪这些组件运行或者读出数据常常是不可能的,或至少很难。
仔细一想,显然,整个通信是基于一个信任机制。我们的攻击就是要设法推翻这个信任模型(见参考[2]中类似情况的讨论)。
你知道,我们可能有几个办法在应用程序和软件狗之间破坏这个信任模型。准确地讲有两种最重要的办法,我称之为仿真和模拟。
其实质是编写一个外部程序在系统上运行,拦截(挂钩)发送给软件狗驱动程序的请求,完全按照与软件狗相同的方式做出回应。可以用不同的技术来做,但是通常没有必要弄得复杂,只需要普通挂钩那些给软件狗驱动程序发送请求的系统API就足矣。
TORO,但不光是他,发布了一些真正好用的仿真器,此外还有其他一些著名的仿真器(如Glasha)。
这些程序的问题是它们的使用期限与其所仿真的特定软件狗紧密相连。另一个重要限制是,它们至少需要插入原配软件狗一次以收集回应数据,用于以后仿真!
这里我将使用TORO的Sentinel仿真器(在一些论坛上发布,如exetools)。和所有的仿真器一样,第一个要完成的任务要求你插入原配软件狗并启动仿真数据收集程序。
这个收集程序本质上是一个软件狗通信协议记录器:它监视程序的请求和键的回应,并把获得的所有信息储存起来,见图2。
图 2 - Sentinel SuperPro软件狗监视程序:收集来自应用程序的请求和来自软件狗的应答
其工作机制非常简单;我们用来捕获消息的两个著名钩子挂在:应用程序调用的软件狗API和驱动程序调用的软件狗API。为了有效率地工作,仿真程序必须在Windows将请求传给ring0级驱动程序之前成功挂钩,该程序就是如此。
如你所见,应用程序调用的几个函数属于Sentinel编程接口(见参考[3]),稍后我们再讨论它们。
第二个组件我称之为软件狗通信协议播放器,它播放前面一步所收集的信息。使用同样的挂钩技术,当然消息传递方向与第一个是相反的。
你能看到这项技术的好处,它不修改应用程序仅仅把问题私下解决了。另一方面它也有一些限制:你至少需要插入原配软件狗一次,而且被仿真的数据将会包含你的软件狗数据,甚至于序列号。总之不是太好..
你可以使用两个更高级的技术编写仿真器。那就是编写一个Ring0级驱动程序仿真器,仿真所有的驱动程序功能,并且再编写一个Ring0级的驱动程序过滤器,拦截对真正的软件狗驱动程序的调用的。不管怎样,其潜在的逻辑总是一样的,拦截对软件狗(在Ring3或Ring0)的调用并且应答,使用事先储存的、真正软件狗会回答的数据。你可能发现有些应用程序增加复杂化:软件狗公司开始把应用程序和软件狗之间的通信数据包加密/解密。数据包在发送给软件狗之前被应用程序加密,返回给应用程序的回应也是如此。
编写这些软件狗的仿真器,即是仿真这些加解密算法。因此唯一有效的方法是对算法及有关数据包进行逆向工程,或者从应用程序中剥离加密/解密算法。 模拟一个软件狗意味着需要在被保护应用程序中加入一些代码,它们模拟软件狗对应用程序请求做出的回应,如此使得应用程序不需要任何外部模块:软件狗、驱动器、仿真器。补丁的应用程序又将是一个正常的应用程序。为此,我们将需要修正那些在应用程序内实现保护设计的代码片(见图1)。 在图3我概略描述这个方法。 图 3 - 在应用程序之内模拟软件狗 图3清楚地显示我们需要在补丁的应用程序内安排一个模拟的软件狗存储器,并且用一些例行程序模拟原始的服务处理程序:很大不同在于原始回应读自模拟的存储器而非读自软件狗。应用程序中不与软件狗交互的部份保持原状。 我在本教程主要集中讨论后一种方式。 我使用一个商业应用程序(任何来自FreedomScientific[4]的应用程序也都可以)的一部份作为例子,它是用Sentinel
SuperPro软件狗保护。在这里我们不关心整个应用程序,我们需要的是它那个独立的软件狗许可证确认程序,我在本教程发行物中带了。这个程序对我们来说仅仅像一个crackme。 最适合用来分析软件狗程序的工具是IDA和OllyDbg(1)加上一些特定软件狗库的特征文件(你可以从各论坛上免费得到,在本教程发行物中也有这些),它们能告诉程序在哪里调用软件狗服务代理。 (1) 的确要感谢那些Ollydbg插件,我们稍后将会使用。它们能够读.sig文件,因此这里不是必需要IDA。不管怎样,OD优越的代码分析功能很是方便。 花点时间熟悉一下IDA和代码结构,下面我们将集中精力展示以上讲述的基本原理。 原始程序和补丁程序如图4所示。 图 4 - 原始的dongleviewer和补丁的dongleviewer 注意 如果在你的IDA安装文件夹中还没有那些*.sig文件,启动前先从本教程发行物中拷贝这些文件。 不管你用什么办法得到IDA
5.0版(幸运的话用官方版,否则从软件下载区分享),发动你的本地拷贝:拖放原始的dongleviewer.exe到IDA主窗口并按OK钮。IDA就开始分析该程序。请小憩片刻,等待它完成。 一旦IDA准备好,你就打开IDA的Libraries文件夹,见如下快照: 如果没有Libraries附签,也可以用花按钮 打开它。 选择该选项卡并按鼠标加键(或者Ins键),添加你先前所拷贝的新特征文件(2)。我建议把它们全部添加以确保万一,因为你还完全不知道应用程序是使用哪个版本的SDK创建的。你应该看到类似于图5的东西,根据匹配的最高版本号可知,应用程序似乎是使用6.x版(非6.2版)的Sentinel SDK编译的。而且,没有外部的动态链接库,因此所使用的SDK是与应用程序静态链接的。 (2) 你必须确定IDA已经应用了编译器特性特征文件。 图 5 – 已应用的IDA特征文件 现在,应用名称以后你可以在名称选项卡上看到结果:IDA只是把库函数名称相应地赋予代码中的函数。 依照IDA帮助文件,以下是各函数名称的图注: L (深蓝) 库函数 F
(深蓝) 普通函数 C
(浅蓝) 指令 A
(深绿) ASCII码字符串 D
(浅绿) 数据 I
(紫色) 输入函数的名称 预期我们将在后续章节重点介绍Sentinel
API之一:sproRead。现在主要讲一下调用图。 你应该在Strings
选项卡中寻找那个被有效包含的驱动程序: .text:00476800
在它上面双击鼠标左键,按x键查找关于那个符号的引用,然后你将会到达函数I386SPRO500MSOFTCIA(x),特征文件帮我们识别出这个函数(见图6)(3)。 (3) 如果你是见到传统的代码视图(IDA 5.0预置),在代码视图任何空白处点击鼠标右键,选择“Graph View”。
图 6 I386SPRO500MSOFTCIA(x)的图解 现在只要按 钮生成对I386SPRO500MSOFTCIA(x)函数的引用图。生成一个相当大的图,但我只要你注意图表的底部:IDA把来自特征文件的函数用彩色标明(浅蓝色),那么由于我们只应用了Sentinel特征文件,所以这些是Sentinel软件狗的库函数,被程序使用或仅仅链接它(记得静态链接一个*.lib文件可能链接它所有的函数,这取决于链接程序选项)。 你也必须确知一件事:如果IDA报告某个函数不被引用,这不一定表示它在运行时间不会被调用,因为可能有动态消息、改变的代码以及IDA解析引擎出错。总之,我们需要对这个结果进行判断,或者说它是我们开始探究的出发点。 图7显示这个图表的底部:注意,I386SPRO500MSOFTCIA被其他内部函数调用,然后有导入库函数如sproExtendedRead(这个函数不被程序使用,但是它在里面做个多余的代码)和sproQuery或sproRead(这个函数则相反,频繁地被调用,而且没它不行)。 图 7 - I386SPRO500MSOFTCIA(x)的交叉引用图底部 这类分析对我们大有帮助,因为它告诉我们必须调查哪些库函数调用。监视程序也能提供相同的信息,如第2.3小节所示。但是,你必须明白,一个监视程序只监视被程序有效执行的调用,而非所有可能的调用。如果在你的模拟中漏掉什么东西(某个函数没有被调用,或某个菜单没有被激活),它很可能是不完全补丁。联合使用这两种方法比较安全,而且让你更有机会做一个完全补丁。 注意 你能够理解一个模拟器如何可能挂钩系统,拦截对CreateFileA的调用。 既使没有IDA,也可能使用OllyDbg反汇编该程序。你会发现GODUP插件(包含在这个文档之内)极为有用,见图8。 图 8 - GODUP插件细节 这个插件能够读IDA的.sig文件(4)并且把函数名称输入反汇编的程序之内(5),见图9。 (4) 的确,它不支持所有可能的类型,但它支持这里使用的Sentinel特征文件。 (5) 你必须手动插入特征文件路径。 图 9 - GODUP应用一个特征文件 如前述,应用所有的特征文件(6),然后按CTRL-N键查看Olly已经为当前程序定义的名称,搜索“spro”开头的名称(所有的Sentinel高层函数以这些字母开头),你会从其他函数中找出下列各项: (6) 记得去选中“Overwrite
existing labels”栏[重写现有标签] 如你所见,GODUP识别出的这几个函数与IDA识别的没有不同:与OlltDbg识别出50次相比,IDA识别出300次左右而胜出。 图 10 -
IDA输出MAP映射文件 那么,最好的替代选择是从IDA输出MAP文件,然后用这个插件(7)把映像文件输入到OllyDbg,见图10。最后这种工作方式竟然产生最好的结果! (7) 有另外一个插件也能做这种输入,但是它比GODUP慢得很多。 此时我们已经差不多,是时候稍微停顿一下并简要地了解Sentinel的PDK如何工作。我将使用其文档中一些信息[3],你应该在读过本教程之后继续完成你的培训过程。 注意 一般来讲,如果你想要打补丁一个软件狗保护的应用程序,那么真正首先要做的是读开发者手册,了解应用程序可能使用软件狗的什么服务及其使用方法。 除了提供一个功能全面、简单易用的软件保护系统之外,Sentinel
SuperPro还让你能够增加示范产品限制、升级示范产品到完全许可版本并提供附加功能的使用权,完全不需要再运送一个新的硬体键过去或者访问客户位置。 Sentinel SuperPro 6.1还提供了一个附加的能力 - 允许你的客户使用一键多客户端。Sentinel SuperPro 6.1还允许你给你的分发商编排特定的一些键,因此你能够限定他们可以激活和更新多少个产品键。Sentinel SuperPro硬体键有两种形式因素:并行端口或者USB,见图11。 图 11 -
Sentinel SuperPro键 每个Sentinel
SuperPro键包含128字节存储器,按每个单元16位组成64个单元(字)。单元按十六进制从位置0到 当你规划一个单元的时候,你赋予它各种不同的属性。这些属性决定该单元(和它所含的字)该由你的应用程序如何使用。单元属性包括单元类型、访问码和单元数值。 通常,每个单元包含下列字类型之一: n 数据字:一个数据字能储存数据,如从属许可证、客户信息、序列号、口令和校验数位。你编码你的应用程序读某个字,然后鉴定所储存的数值并做相应行为。一个数据字单元可能被规划成只读或者可读/写。 n 计数器字:一个计数器字包含你设定的一个初值然后由你的应用程序减量。计数器字的一种典型使用是限制一个应用程序的示范品可运行次数。 n 算法:一个算法包含一个位模式,规定硬体键应该如何加密你的应用程序所发送的查询数据。该键使用一个算法--并且是个内部储存的专有算法--转换查询数据然后返回一个值给你的应用程序。你设计让应用程序发送查询给该键,然后鉴定返回值并且做出相应行为。 算法是活动的或不活动的。只有活动的算法能给一个查询返回有效的回应。算法是否活动由该单元值中活动的/不活动的位来控制。 每个键的00单元到07单元是内部级单元,它们包含着固定的、预编程的系统信息,见图13。 单元 内容 可读否? 00 键序列号;每个键按顺序分配。a 是 01 开发者ID;唯一对应你的公司/产品。 是 02–07 彩虹技术保留使用。 否 图 13 -
Sentinel SuperPro键的内部级单元内容 (a序列号是16位的,并且不保证它是唯一的) 程序可以使用从08单元到 每个单元有一个访问码,用它控制该单元能够被你的应用程序如何使用--它定义单元的单元类型属性。例如,一些单元类型的访问码允许单元数值被读出和被重写,而另一些单元是只读的或根本不可写入。访问码是数字0到数字3,见图14。 访问码 描述 0 可读/写的数据字 你的应用程序可以读该单元的这个字,而且,如果给出写口令,可以修改它的内容。 1 只读(锁定)数据字 你的应用程序可以读该单元的这个字,但是如果没有重写口令,则不能改变它。 2 计数器字 该单元包含一个字(数值),你的应用程序可以用写口令减量它。如果没有重写口令,则不能(用减量之外的方法)改变该单元的值。 3 锁定并隐藏的字/算法字 你的应用程序不能读该单元的数值。修改它需要重写口令。该单元数值(内容)是隐藏的(不可读)。 图 14 – Sentinel SuperPro键的单元访问码 每个单元被赋予一个代码,定义你想要如何使用此所选单元。这个代码叫做一个单元类型。单元类型划分储存在单元中的数据类型,继而影响该单元能被如何使用。每个单元类型用两个字母的缩写来识别;例如,CW标识一个计数器字。某些单元类型被设计成按组使用。例如,算法能与计数器和口令产生关联。其他单元类型有地址限制,意即只有键上特定单元才能指定为这种类型,见图15。 图16中列出Sentinel SDK的主要API函数。 函数 描述 sproActivate() 激活一个不活动的算法,以便它能被sproQuery()函数使用。 sproDecrement() 减量一个计数器字或者读/写数据字。如果计数器关联到一个活动的算法,那么计数器减到零则使该算法不活动。 sproEnumServer() 依照指定的开发者ID,列举在网络上运行的服务器个数。 sproExtendedRead() 读键的任何非隐藏的存储器单元的数值和访问码。 sproFindFirstUnit() 搜索隶属于一个指定的开发者ID的所有键。 sproFindNextUnit() 搜索隶属于同一个开发者ID的下一个键。 sproFormatPacket() 验证数据包(RNBO_SPRO_APIPACKET)的大小并初始化字段为默认值。在任何其他API函数被调用之前,这个函数必须先被调用一次。 sproGetContactServer() 返回为一个指明的API数据包设定的联系服务器。 sproGetFullStatus() 返回扩充的状态信息。它只用于支持目的。 sproGetHardLimit() 检索硬体键所支持的最大许可证数目(硬限制)。 sproGetKeyInfo() 从一个指明的服务器获得关于键的信息。 sproGetSubLicense() 取得一个指明的单元中的从属许可证。 sproGetVersion() 返回Sentinel
SuperPro驱动程序版本号。 sproInitialize() 执行驱动程序需要的任何初始化。 sproOverwrite() 改变除了保留单元00-07之外的任何单元的数值和/或者访问码。 sproQuery() 发送一个数据字符串给键,用一个指定的算法加密它,并返回加密的字符串给应用程序。 sproRead() 读键的任何非隐藏单元的数值。 sproReleaseLicense() 通过指定单元地址为0而释放一个许可证,或者通过指定从属许可证单元的单元地址和要释放的从属许可证个数而从一个指明的单元释放一个从属许可证。 sproSetContactServer() 为一个指明的API数据包设定联系服务器。 sproWrite() 改变任何访问码为0(可以读/写数据)的单元的数值和/或它的访问码 图 16 -
主要的Sentinel
Superpro API函数 在SDK官方报告中它的API名称带前缀“RNBO”,但是我们通常提及这些API名称时不带这个前缀。从破解角度看最重要的是:sproFormatPacket、sproInitialize、sproFindFirstUnit、sproRead、sproQuery、sproOverwrite。 以下是一个典型的调用顺序,你经常能在程序中见到: 1. sproFormatPacket() –
初始化数据包。 2. sproInitialize() –
执行必需的初始化。 3. sproFindFirstUnit() –
建立与键的通信并且取得一个许可证。 4. sproRead() –
读单元并且返回它的值。 5. sproQuery() –
发送查询字符串并且给回应值指定一个位置。 例如,sproRead如图17所示,我们马上要编写一个新的,并将二者做个比较。 图 17 -
Sentinel sproRead API API的返回值才是重要的,即程序要求从一个有效调用得到什么东西。 对于已经熟练掌握该主题的人来说,现在开始本教程真正的部分。为了完全没有经验的读者达到知识要求,本教程前面部分一直在讲授必需的基础知识。其实补丁从这里才真正开始。 和我们下面将要模拟所有其他API一样,对于这个API我们通常只关心它总是返回SP_SUCCESS(0)。 图18是该原始API在IDA中的反汇编结果,图19是同一个API在OllyDbg中的反汇编结果。 图 18 -
在IDA里的原始sproFormatPacket 图 19 –在OllyDbg里的原始sproFormatPacket 补丁很简单如图20所示,它仅仅把EAX原本来自[ESP+C]返回值清零。如你所见,我在第一个跳转(0x476EF8)开始重写原始API,其目的是用来略过在Sentinel
API入口点上的一些基本的反干预检验。 图 20 -
补丁的sproFormatPacket 注意,关于这个API,PDK有如下说明: 这个函数本质上是一个“取得许可证”调用。如果有找到Sentinel SuperPro键,RB_SPRO_APIPACKET记录将会包含有效的许可证数据,否则数据包将被标记为无效的。如果你试图以一个已有许可证的APIPACKET作为参数调用这个函数,它返回错误SP_INVALID_OPERATION。 仅当给定的参数RB_SPRO_APIPACKET是无效的时候,它返回不同于SP_SUCCESS(0)的一个数值。在开发阶段这类错误是典型的,因此对我们来说没有问题。这意味着通常不必补丁这个API,总之我通常只是为避免意外而补丁它。 同样地对于这个API,重要的是要返回0
(SP_SUCCESS)。把描述开发者细节的developerID储存起来,一定会有用的,如开发者指南所指出的: unsigned short int sproFindFirstUnit( RB_SPRO_APIPACKET packet, unsigned
short int developerID ); developerID是由彩虹技术[Rainbow
Technologies]或零售商指定给你。它标识所要寻找的Sentinel
SuperPro装置。 图 21 -
补丁的sproFindFirstUnit 如果你在sproFindFirstUnit开始处放一个断点,你将会发觉,像其他任何第一个参数是接收一个RB_SPRO_APIPACKET数据包的API函数一样,这个数据包只是个ASCII字符串“Br”。 这个函数用来改变Sentinel
SuperPro键中任何字的数值和访问码。对我们来说,只要修正程序使之返回SP_SUCCESS给程序检查。 图22显示一些显然无用的指令;他们实际上是无用的,仅仅为调试目的。 图 22 -
补丁的sproOverwrite 这个API查找下一个隶属于该开发者ID的Sentinel
SuperPro键,这个开发者ID由调用sproFindFirstUnit确定。该函数经常被应用程序调用来正确地操作软件狗,因此和sproFindFirstUnit一样必须被打上补丁,见图23。 图 23 -
补丁的sproFindNextUnit 这个API函数和下一个sproQuery函数是我们需要模拟的两个最重要函数。之前所有的API补丁是为了让程序可以无阻碍而且无错误地结束这些API。而我们的主要破解工作是在这两个API里面做。 原始的原型是: unsigned short int sproRead( RB_SPRO_APIPACKET packet, unsigned
short int address, unsigned
short int *data ); 其中 n packet:指向RB_SPRO_APIPACKET记录的一个指针。 n address:要读入那个字的Sentinel SuperPro键存储器单元地址。 n data:指向某个位置的一个指针,该位置将会包含从Sentinel SuperPro键读出的数据。 图17列表原始的sproRead。我们将要做的是本地模拟软件狗存储器,使用与原始的sproRead所用的相同空间。图24中给出概要图解:新的sproRead将会从一个用来模拟狗存储器的内存区域读取数值,该值就是原始API函数会返回给调用程序的数值。数据输入参数作为一个索引使用,从本地内存取到正确的数值。 图 24 -
补丁的sproRead图解 该函数在下面列出。它与Crackz、GoatAss等人已经提出的那些方法很相似,但是由于未对准模拟存储器部分,他们的解决方案有的不工作、有的出错、有的不完全(至少从我的经验看):他们没有正确地考虑每个单一单元的大小。如果你需要更多的模拟存储器,在地址0x00477308处减少被增加到EAX的偏移量,如你所见,有许多空间仍然可用。 图 25 -
补丁的sproRead函数的载入部份 现在我们已经做好一个sproRead模拟器了,然后你还需要用正确数值填充本地内存单元。这最后一步完全取决于目标,而且必须通过检查程序对返回值做什么处理才能到达成功的彼岸。你必须跟踪程序、看它对返回的数据做什么处理(了解正确数值是什么),这是最令人厌烦的事情之一。 1. 用FF值填充所有内存单元,容易做 2. 在补丁的sproRead开始时放置一个断点BP,并跟踪程序直到它到达相应的内存单元 3. 在该单元写入数据值(该数值在[ESP+10]):例如,如果被存取的单元是 4. 使用HW硬件断点,跟踪被写的内存进入调用者,直到你弄清该程序对那个数值做什么处理。通常你会见到CMP之类。 5. 把你发现的真正数值写回该内存单元,取代那个虚设值。记得要使用LSB逻辑去写WORD值。 上面代码已经包含我从DongleViewer发现的东西。如你所见,有的单元仍然保留为FF值,因此并非所有的单元被程序使用。 例如,程序DongleViewer使用这些数据值调用sproRead:10h、11h、12h、17h、13h、14h
(8) (8) 你可以在0x0047730B处放一个断点并检查在数据栈上的[ESP+10]。 只有13h单元立即被这个程序检查。请看: 因此该程序使用之前所读入ECX的数值与AX做或运算,得到一个数值,如FFFF13FF。然后该数值立即在这里被检查。 显然,单元13应该有的正确数值是0001(考虑WORD对准),然后该数值以反向顺序(考虑LSB逻辑)储存于0x00477373处,即为0100。 注意 尝试做个实验,在地址0x 我也用过这个附加方法补丁sproRead,在某些情况下此法更稳定,而且也正确地清理存储器,因此对跟踪来说,它是更好的解决方案。逻辑几乎是相同的,所以你能自己读懂下面的代码(注意,下面列表的存储器部分没有经过初始化)。 sproQuery函数稍微更难写些,在需要逆向的函数中它总是最复杂的。API只是传递数据给一个算法或者给一个储存于键的查找表,而且返回一个数值,再由应用程序检查返回值。 如果你有程序的一个有效键,跟踪该函数的回应,你能得到正确的数值。或者如果你没有任何一个有效的软件狗,跟踪应用程序并猜测它怎么处理返回数据(按照和sproRead一样的方法),你也能得到正确的数值。 unsigned short int RNBOsproQuery( RB_SPRO_APIPACKET packet, unsigned
short int address, VOID *queryData, VOID *response, unsigned
long *response32, unsigned
short int length ); 其中: n Packet:指向RB_SPRO_APIPACKET记录的一个指针。 n Address:要查询的字的地址。 n QueryData:指向查询字节串第一个字节的指针。 n Response:指向回应字节串第一个字节的指针。 n Response32: 指向某位置的指针,那里将会包含查询回应最后四个字节的一个拷贝。 n Length: 这是发送到活动算法的查询字节串的字节数,也是回应缓冲区的长度。 如果成功,该函数返回SP_SUCCESS(0)。 与图24的逻辑是相同的,但实现不同。 当程序在0x 但是,当你停在0x 此时寄存器EDI是指向querydata(0x 图 26 -
原始的sproQuery sproQuery真正的问题是,除了跟踪程序之外,没有其他方法可以找到查询值queryvalue和回应数值。你可以按照和sproRead一样的步骤,走一步看一步,如果程序实现得不好你会很容易找到正确的数值。 对于我们当前这个例子,做法如下: 1. 当你停在0x 2. 应用程序在0x00453BF1处停下,见图27。 图 27 -
DongleViewer进入sproQuery在断点之后停下 该缓冲区,已被sproQuery前面的调用填充,现在它的地址储存在EDI,而且与储存在ESI的地址0x00514132处的内容逐字节比较。如果有什么不对劲,程序就跳出。修正它的另一个可选办法是将JNZ换成JZ,但是记得我们说过,我们只专注于静态链接到应用程序的API函数库那些部份,而不改变只为某个应用程序所特有的代码。 3. 如果你再按一次F9,你将会发现缓冲区已经被删除。 这次我们是运气好
(我有意选择这个例子
;-)。 好了,快到本教程结束的时候了。我没有全部讲解软件狗参数,而是只讲到一些特殊的问题。还是讲了有关封包以及其他与键复杂交互的一些东西。无论如何,这里所描述的通用方法对所有Sentinel保护的程序仍然有效,而且部分地可应用于所有软件狗保护的程序。
2.4
模拟器如何工作
3
反汇编Sentinel保护的程序
3.1
用IDA反汇编
3.2
用OllyDbg反汇编
4
关于Sentinel应用程序编程接口的一些细节
4.1
什么是Sentinel SuperPro
4.2
键存储器的结构
4.2.1
内部级单元与可编程单元
4.2.2
访问码
4.2.3
单元类型
4.3
API函数引用
5
改写Sentinel API函数
5.1
sproFormatPacket
5.2
sproFindFirstUnit
5.3
sproOverwrite
5.4
sproFindNextUnit
5.5
sproRead
ped
ped
5.5.1
sproRead Approach #2
ped
5.6
sproQuery
6
补充
只改变程序中继承Sentinel SDK的部分,其另一个好处是,由于SDK库独立于程序生产者,所以这些部分在该程序后续版本中是不变的。因此带有‘重建与替换’引擎的补丁程序很有机会在同一程序的后续版本继续使用。
注意 |
我在本教程发行物中配发了一个Sentinel保护的crackme(在参考[1]中也有这个Sprocrackme.exe),你可以应用这里学到的技术拿它做个练习,看看自己是否确实理解了本教程所传授的原理。 |
[1] Dongles Web Resources, http://www.woodmann.com/crackz/Dongles.htm
[2]
“The Weakness
of the Windows API, Part
[3] “Sentinel SuperPro Developer's Guide”, Rainbow Technologies, included into the distribution of this tutorial.
[4] Freedom Scientific, http://www.freedomscientific.com/
好吧,这个课题结束了,希望以上所述将会帮助你更好地理解操作系统是如何操纵进程以及我们能够用哪些手段控制进程并让使用软件狗的程序失去保护。我一贯主张读者使用本教程更加深入学习软件狗和Sentinel系统是如何工作的,并使用这些例子完善你的RCE技术,而不要去破解程序。
本教程免费提供的所有代码可以公开使用,只要你觉得它有用请对ARTeam和作者们表示一下感谢。不要应用这些原理做违法的事,这里给出的所有资讯应该只用于学习和帮助人们更好地理解应用程序代码安全技术。 |
n 1.0 版本 - 第一次公开发行!
我想感谢所有的ARTeam成员,TORO和本教程β版的读者,... 当然,还要谢谢你读完这个相当长而且复杂的文档!
http://cracking.accessroot.com