热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

逆向工程第004篇:跨越CM4验证机制的鸿沟(中)

一、前言在上一篇文章的最后,我已经找出了关键的CALL语句,那么这篇文章我就带领大家来一步一步地分析这个CALL。我会将我的思路完整地展现给大家,因此分析过程可能略显冗长,我会分为两篇
一、前言

        在上一篇文章的最后,我已经找出了关键的CALL语句,那么这篇文章我就带领大家来一步一步地分析这个CALL。我会将我的思路完整地展现给大家,因此分析过程可能略显冗长,我会分为两篇文章进行讨论。在整个分析过程中,我也会把我所遇到的瓶颈展示出来,毕竟我在实际分析时,也并不是一帆风顺的,遇到瓶颈属于正常情况,关键是在于应该怎么解决。考虑到绝大部分读者手中应该是没有CM4这款游戏,并没有实际动手进行逆向分析的机会,所以我在文章中添加了足够多的解释与截图,让大家仅仅是看这篇文章,就能够领会逆向分析的基本方法。我希望能够将我的思想阐述清楚,令每一位对逆向工程有兴趣的读者都能有所收获。

 

二、分析程序成功验证的流程

        上文说到,真正影响验证结果的是位于0x0012FCEB处的值,如果该值为0,那么说明验证失败,而影响该值的是位于0x0041DB3处的CALL 004112F8这个语句的返回值,即eax中保存的值。那么我们可以进入这个CALL查看一下,并留意eax中的值的变化。

这个CALL里面只有一条语句,就是一个无条件跳转,它会跳转到0x00413080处的语句,利用IDA Pro可以查看一下该函数的流程图:


图1 函数0x00413080的流程图

        由图1可以发现,该函数中拥有非常多的分支结构,很符合注册码验证的程序特征。对此,我个人的习惯是从后向前进行探索。由图中我所标出的红框中的内容可以发现,程序的所有流程,最终都要汇聚于一处,而再向上观察,可以发现一共有八个小方框,或者可以理解为一共有八种验证结果,那么由此逆推,应当可以发现正确的注册码的验证流程。这里应当先分析红框中的反汇编代码。首先是最后汇聚处的代码:


图2函数0x00413080最后一部分代码

        由于这是函数0x00413080最后的代码,所以我们只需要关心包含有eax的代码即可,因为这里决定了eax中的值究竟是多少,从而最终决定验证是否成功。我们很容易就发现了两条包含有eax的语句,我已经用红框标注出来了,分别是eax的入栈与出栈的语句。可以理解为这里是为了保护eax中的值。可能有读者会问,图2中倒数第5个语句的CALL,其返回值是否会影响eax中的值呢?其实是不会的,如果不放心,当然可以用OD执行一遍,看看执行完这个CALL之后,EAX寄存器的值有没有变为红色(红色表示该寄存器的值有变动)。其实图2中最后的这几句代码的意思是恢复栈帧,让堆栈保持平衡,这里不再详细说明。

        那么我们继续向上分析,来到那八个小方框处,首先看一下第一个方框处的代码:


图3 分析loc_413155处的代码

        我们依旧只关心eax,可以发现图中有两处代码包含有eax,程序首先将0x0这个值赋给[ebp+var_1D1],然后又将[ebp+var_1D1]中保存的值赋给al,那么很明显,此时的eax的值就变成了0,可以认为这第一个小方框(loc_413155)是验证失败时才会执行的,因为它间接地将0值赋给了eax。按照这个思想分析接下来的7个小方框,最终可以发现唯一的一个疑似验证成功的小方框内的反汇编代码:


图4 疑似验证成功处的代码

        之所以认为这里应该就是验证成功处的流程,是因为eax的值被间接地赋值为了0x01,而这就会直接影响之后的验证流程,使得程序会认为验证成功。那么我们就可以按照这条线索不断向上查找,最终能够发现正确的程序流程。因为由上至下,是有多种路径的,而由下至上,路径是唯一的,所以很容易就能够确定路径。结合图1,可以确定流程如下:


图5 成功验证的流程

        在图5中,我已经用红框标出了正确的验证流程,这是非常重要的线索,依据这个,我们就能够确定在每条分支判断中,应该执行哪条语句才能够达到最终的目标。这也是IDA Pro的Graph overview界面带来的好处。需要说明的是,图中绿色箭头代表跳转验证成功时所走的路径,而红色箭头代表验证失败时所走的路径,蓝色代表无条件跳转执行。

 

三、分析程序的验证算法        在图1中可以发现,一共有两处循环操作,那么可以认为这就是程序的验证机制所在,如下图所示:

图6 程序中的两处循环

        按照顺序,我们先分析位于图6中的左侧的第一个循环。其起始处的反汇编代码如下:


图7 第一处循环起始处的反汇编代码

        这里没有什么需要注意的,仅仅是一些初始化,可以知道程序首先要验证4个字符。需注意的是,图7中的loc_4131CE处的代码在验证第一个字符时是不执行的。接下来有:


图8

        依据图8中的代码可以知道,验证程序会将注册码的ASCII码值减去0x30,然后比较该值与9的大小。由此可以认为程序是在验证用户所输入的注册码是否为数字。如果是数字(小于等于9),则将该值保存在[ebp+var_230]的位置,如果不是数字(大于9,即英文字母),则将该值减去7后,保存在[ebp+var_230]的位置。这里无论注册码是数字还是字母,保存完毕后都要跳转到loc_41322C处的语句继续执行:


图9

        由图9的代码我们可以确定,CM4的注册码(前四个字符)要求只能够是由数字或者大写字母组成。因为结合图5可以知道,位于0x0041323F处的代码jg short loc_41324A一旦跳转实现,那么程序就验证失败,而这里主要是验证注册码是不是小写字母。同理位于0x00413248处的代码也会跳转到验证失败的位置。所以可知以上代码是在验证所输入字符的合法性。那么如果编写注册机,代码可以如下编写:

for ( i = 48; i <= 90; i++ )
{
if( i >= 58 && i <= 64 ) continue;
// 如果注册码是数字,则减去48
if( i >= 48 && i <= 57 )
{
tmp[0] = i - 48;
}
// 如果注册码是大写字母,则减去55
else
{
tmp[0] = i - 55;
}
for ( j = 48;j <= 90; j++ ) {}
……
}

        由于是要生成四个字符,所以可以使用四重for循环,循环的内容就是ASCII码值。

        程序继续执行。接下来如果通过了字符合法性的验证,就来到了loc_41326B的位置:

图10

        图10也就是真正来到了算法运算的代码处。首先是将[ebp+var_B4]中的值与0x24相乘,该地址初始值为0,它也会保存着最终的运算结果。乘完之后,再加上[ebp+var_A8]中的值,该地址中保存有注册码的ASCII码值减去0x30(如果是数字)或者减去0x37(如果是大写字母)之后的值。一加一乘,还是很简单很好理解的。代码如下:

var_14 = 0;
for( a = 3; a >= 0; a-- )
{
var_14 *= 36;
var_14 += tmp[a];
}

        最后程序会无条件跳转到loc_4131CE处的代码,它在我们的图7中,作用是继续验证下一个注册码字符。

        那么当第一组的4个注册码都验证(运算)完毕后,结合图7的最后一句代码与图8的第一句代码可以知道,程序会跳转到loc_413291处的代码执行:


图11

        图11中的代码是对注册码第一组的四个字符的运算结果的验证,它是将运算结果与0x24相除,如果有余数,那么验证成功,并跳转到loc_4132CB处继续执行。至此,我们对程序验证机制的第一个循环分析完毕。

  

四、遇到瓶颈         以上分析还是比较顺利的,但是接下来我就遇到了一个瓶颈,继续分析:

图12

        这段代码的第一处反汇编语句出现了[ebp+var_60],这个在我之前的分析中并未出现,这里需要将[ebp+var_60]中保存的值与我们之前运算得出的结果即[ebp+var_14]进行相加,将求和结果当做地址,取该地址中的内容再进行运算。最后需要取余数,并保存在[ebp+var_20]中,再进行下一步的运算。所以首先应当搞清楚[ebp+var_60]里面究竟是什么,利用OD查看一下:


图13

        通过对数据窗口的分析可知,这里保存的似乎是一个PE文件,因为它很是符合一个PE文件的特征,因为接下来的验证过程需要从该PE文件中的特定偏移处([ebp+var_14])取数据进行运算,所以有必要搞清楚这个PE文件的来历。因为不同的验证码,它们位于第一重循环运算的结果,即[ebp+var_14]是不相同的,那么也就会获得不同的偏移值,获得不同的位于该偏移的数据,从而得出不同的验证结果。这么来看,这个PE文件有点像一个“密码本”,找到这个“密码本”的来历,为我所用,才能够成功编写出注册码生成器。分析至此,下一步的工作就是从头开始寻找。

        其实这个说着容易,我最初分析的时候可是花费了不少时间。这个PE文件的生成,应当是在第一重验证循环之前,那么有必要分析图1中最开始的那几个CALL语句,于是就找到了以下内容:


图14

        可见程序在此调用了CreateFile()这个API函数,打开了名为“cm4.epe”的文件,由于没有指定文件路径,所以可以肯定该文件就位于和游戏可执行文件同一个目录下。进入游戏目录查找,果然能够找到,利用十六进制编辑软件Hex Editor Neo打开查看一下:


图15

        图15和图13大致对比一下,其数据是一致的,那么就可以认为“密码本”就是这个“cm4.epe”文件。也许这个文件还有其它功用,但是这里不进行深究,只要知道需要用其来获取注册码的数据即可。

 

五、继续进行运算        接下来的代码和图12中的基本一致:


图16


图17

        以上两段代码同样需要“密码本”的帮助,不同的是每次的偏移值都不同,这里不再赘述。需要注意的是,图12、图16和图17中的[ebp+var_20]、[ebp+var_2C]和[ebp+var_38]中的值非常重要,直接影响下一步的验证过程,请大家通过截图搞明白这个三个数值的来历。这会在下一篇文章中进行分析。

 

六、小结        如果读者随着本文的分析过程,一步一步地看下来,可能会觉得这一切十分顺利,似乎没有什么困难,所谓的瓶颈也不过如此。但是事实上,由于我在分析的时候对很多事情是不知道的,一切都在摸着石头过河,总会卡在某处而不知所措,这些困难是在文章中难以体现的。也正是因为这样,如果想精通逆向分析,平时就需要大量的练习,才能在实际的分析过程中,一帆风顺。

推荐阅读
  • mysql字符集和表字符集_Mysql数据库表引擎与字符集
    Mysql数据库表引擎与字符集1.服务器处理客户端请求其实不论客户端进程和服务器进程是采用哪种方式进行通信,最后实现的效果都是:客户端进程向服务器进程发送一段文本(MySQL语句) ... [详细]
  • 本文介绍了Java工具类库Hutool,该工具包封装了对文件、流、加密解密、转码、正则、线程、XML等JDK方法的封装,并提供了各种Util工具类。同时,还介绍了Hutool的组件,包括动态代理、布隆过滤、缓存、定时任务等功能。该工具包可以简化Java代码,提高开发效率。 ... [详细]
  • C++字符字符串处理及字符集编码方案
    本文介绍了C++中字符字符串处理的问题,并详细解释了字符集编码方案,包括UNICODE、Windows apps采用的UTF-16编码、ASCII、SBCS和DBCS编码方案。同时说明了ANSI C标准和Windows中的字符/字符串数据类型实现。文章还提到了在编译时需要定义UNICODE宏以支持unicode编码,否则将使用windows code page编译。最后,给出了相关的头文件和数据类型定义。 ... [详细]
  • VG浏览器是免费的吗 VG浏览器常见问题汇总
    VG浏览器是免费的吗VG浏览器常见问题汇总。现在,不少人都在使用VG浏览器。但是,有些用户在使用VG浏览器过程中,也是碰到不少问题。今天,小编给大家带来VG浏览器常见问题大汇总。想 ... [详细]
  • 1关于字符串相邻的两个或多个字符串字面值(引号引起来的字符)将会自动连接到一起:str_catpython!str_cat输出:python!把很长 ... [详细]
  • 深入理解计算机系统之链接(一)
    程序是怎样运行的写好的c程序怎样运行的呢?答案是一个写好的程序要先经过语言预处理器,编译器,汇编器和链接器生成最后的可执行文件,然后加载器将可执行文件加载到内存中才能运行。这里以一 ... [详细]
  • c语言 怎么访问64位地址_C语言调动硬件的原理是什么?
    大家都知道我们可以使用C语言写一段程序来控制硬件工作,但你知道其工作原理吗?1c语言在实际运行中,都是以汇编指令的方式运行的,由编译器把C ... [详细]
  • 转自:http:www.phpweblog.netfuyongjiearchive200903116374.html一直对字符的各种编码方式懵懵懂懂,什 ... [详细]
  • 一,关于C语言的问卷调查【作业3】1.你对自己的未来有什么规划?做了哪些准备?没有明确的规划,在学习教材知识的同时准备了解其他知识。2.你认为什么是学 ... [详细]
  • 我从来没有学过c语言,学不会C语言
    本文目录一览:1、我从没学过计算机C语言,怎么准备考二级C? ... [详细]
  • 有没有一种方法可以在不继承UIAlertController的子类或不涉及UIAlertActions的情况下 ... [详细]
  • 如何在服务器主机上实现文件共享的方法和工具
    本文介绍了在服务器主机上实现文件共享的方法和工具,包括Linux主机和Windows主机的文件传输方式,Web运维和FTP/SFTP客户端运维两种方式,以及使用WinSCP工具将文件上传至Linux云服务器的操作方法。此外,还介绍了在迁移过程中需要安装迁移Agent并输入目的端服务器所在华为云的AK/SK,以及主机迁移服务会收集的源端服务器信息。 ... [详细]
  • ➜~tarzxfsuperset-0.18.2.tar.gzgzip:stdin:notingzipformattar:Childreturnedstatu ... [详细]
  • LINUX运行谷歌TTS,中文TTS 的简单实现(基于linux)之 语音库的实现
    语音库保存着常用汉字的发音(多音的汉字只记录其一种发音,这也是本系统的一个缺陷,需要以后完善),所以先要得到一汉字集,这个汉 ... [详细]
  • 我举个例子,C代码如下:#include<stdio.h>main(){charx;printf(本程序功能:任意输入一个字符,如果是字母字符,将其ASCII ... [详细]
author-avatar
怡智俊婷珮婷
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有