【软件名称】:CyberBlade.2.exe
【软件大小】:61.0 KB
【下载地址】:94eK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6f1L8$3&6&6b7$3S2W2L8U0f1$3i4K6u0r3x3e0j5H3i4K6u0V1b7%4u0S2j5$3E0E0k6b7`.`.
【加壳方式】:未加壳
【保护方式】:Name/Serial
【编译语言】:VB P-Code
【调试环境】:W10 x64
【使用工具】 OD,VBExplorer,VB Decompiler
【破解日期】:2019-5-1
【破解目的】:学习分析P-Code类型的程序,理解P-Code虚拟机的解释过程
【目标程序】:

这一次的目标程序的这个Crackme,是160个Crackme里面的第38个。运行时需要 Visual Basic 5.0 运行库支持。这个Crackme的分析,用到了三个工具,每个工具都有各自的用途:
想要分析这个Crackme,首先需要了解VB变量类型在内存中的存储方式。
VB中的variant类型属于一种结构体,该结构体的前两个字节表示变量的类型,后面有3个WORD是保留的,接下来才是其真正的值,如下图:

也就是说VB变量中真正的数据是存储在首地址+8的位置处,下图显示了VB的所有的变量类型及含义

首先用 VB Decompiler反编译目标程序,找到Check按钮的点击事件,分析整个点击事件的校验过程

这个程序的校验过程分为五个部分,下面讲解每一个部分的校验过程

首先根据静态分析的结果可以看到,该程序首先会校验用户名和序列号是否为空,然后判断序列号长度是否小于5个字节,否则提示错误。即使看不懂反汇编后的VB代码,也可以通过字符串知道整个过程。

第二部分的校验过程看的就不那么清晰了,需要利用OD动态跟踪每一个伪指令的具体操作流程。

用VBExplorer对目标程序进行反编译,一直往下拉,根据字符串直接忽略第一部分的基础校验,来到0040E380的位置


我们可以看到第一个被执行的伪指令是0040E380处的0D,接下来将程序载入OD,数据窗口跟随->0040E380

然后给第一个字节0D下内存访问断点,F9运行

然后随便输入一个用户名和序列号,点击Check

程序首先会读取一个字节的操作码到AL,


来看下VB Explorer中显示的操作码,后面的注释提示这是在调用一个函数,0D后面的是操作码的参数,接着esi自增1,指向操作码的参数

接着通过一个jmp跳转去执行操作码,0x741BED94是地址跳转表的首地址,eax保存下一条指令的操作码,由于每一个跳转地址是一个DWORD,所以用eax乘以4的值加上跳转表的基地址来索引下一条伪指令的解释单元,我们跟随这个jmp

首先把[ebp-0x4C]赋值给eax,然后将eax压栈。我们需要知道eax的含义。数据窗口跟随之后,发现是一个指针,再次选中前四个字节,数据窗口跟随DWORD,然后将数据显示方式切换为长型->地址

这其实是一个函数的跳转表,再接着把esi的内容A0赋值给edi,而esi始终执行的是操作码


可以看到这一步实际上是在取操作码的参数A0了

接着取出eax的内容,然后将eax加上edi,eax实际是跳转表的首地址,那么参数A0,就是跳转表的偏移

接着call eax,我们不需要跟进这个函数,只需要关注栈中的第二个参数0x19F134即可。直接步过这个函数

可以看到栈中的参数显示出了我们刚才输入的用户名,再接着单步到xor eax,eax的地址

这里把eax的值清零了。也就是说第一条伪指令已经执行完成了。
这个就是P-Code虚拟机的解释过程,其中esi始终指向要解释的伪指令,eax保存的是将要解释的伪指令。

接着看下一条伪指令,6C是操作码,64FF是参数。后面的注释告诉我们这条伪指令是在将某个DWORD值入栈。继续跟踪

首先取出操作码6C,然后esi+5执向伪指令参数,接着跳转执行这条伪指令,直接跟进jmp

首先取出参数FF64放到eax中,FF64是一个负数,

也就是十进制的9C,这也是为什么VBExplorer里会显示LOCAL_009C的原因,这个9C代表[ebp-9C],是个局部变量


接着将[eax+ebp]入栈,压栈的是刚刚输入的用户名[eax]的值是-98,这里其实是将[ebp-98]局部变量压入栈,然后eax清零,表示这条伪指令结束。
继续看下一条,伪代码解释的求长度

首先取出伪指令,然后直接跟进jmp


这里调用vbaLenBster,参数是之前输入的用户名,接着将用户名长度入栈后,eax清零,接着看下一条FD6934EF

这条伪指令后面并没有给出解释,但是没有关系,我们可以根据伪指令的执行过程猜测指令含义,这里其实是一个双操作码的指令,

首先取出操作码FD,直接跟进jmp,什么都没有做,直接将eax清零。之后再次取出操作码69,继续跟进jmp

这里将bx赋值为0x3,然后跳转,继续跟进

接着取出参数FF34,FF34也是个负数,然后再将FF34加上ebp,代表这是一个局部变量

接下来将ecx赋值给[eax+0x8]的地址处,我们数据窗口跟随eax,然后将bx赋值给eax,赋值完成后eax值如下:

还记得VB的变量类型吗?前两个字节是变量类型,03代表是Long,中间是6个字节的保留位,首地址+8的位置才是真正的数值。
这个07是之前通过vbaLenBstr获取到的用户名长度,在这里转成了变量,并将变量首地址压栈。

现在我们就能通过实际的跟踪结果来得出这条指令的含义了。就是将int值转成变量类型。由于整个跟踪过程实在是复杂,我这里只贴出算法的关键部分


在40E3C1处截取用户名的第一个字符串

接着在40E3CD处将截取的用户名每一位转成ASCII值



接着将用户名每一位的ASCII值转为十进制后进行字符串拼接

整个过程循环,循环次数为用户名的长度,可以直接在这个地方下断点看到最后的结果

即拼接用户名的ASCII十进制字符串,第二部分的算法就完成

在VB伪代码中,var_94就是最后拼接的结果
接下来是第三部分,直接来看VB Decompiler中的伪代码

这一部分的逻辑也很清晰,如果用户名拼接的字符串长度大于9的话,就将这个结果转为浮点数除以圆周率,一直除到结果的长度小于9。这个部分我也用OD详细跟过每一条伪指令,确实和静态反汇编的逻辑是一样的
但是在loc_40E449的位置,将var_94和一个值进行了异或,并且还减去了另外一个值。这两个数值我们无从得知,只能跟踪OD

根据伪指令的助记符XorVar和SubVar快速定位到这两个地址,下内存访问断点,很快就能找到这两个值

可以看到这里实际上是将0x30F85678和用户名的结果进行异或

然后减去0xD8B3,找到了这两个数,第三部分也就结束了

这个是最有意思的,你会发现代码初始化了10次循环,循环将一个变量和Key值进行比较,但问题在于Then分支没有任何代码。这也就是说不管这个循环中的比较成立与否 都对我们没有任何影响

最后一部分,比较序列号减去var_94是否等于用户名长度,也就是说用户名计算的结果再加上用户名的长度就是真正的序列号。最后对这个程序的校验过程做一个总结
总结:
接着我们根据已经分析的算法写出这个程序的注册机,这个注册机用C++写还是太费劲了 直接用python快


P-Code的程序并非如传言一样不可战胜,只要你有足够的耐心,配合VB Decompiler+VBExplorer+OD的黄金组合,剩下的就是纯体力活了。
P-Code类的程序用OD跟踪伪指令虽然能看到每一处实现细节,但是毕竟还是太费力了。这个时候如果能总结出一套相对比较完整的P-Code的伪指令及每个参数的具体含义再配合WKTVBDebugger,调试P-Code就显得游刃有余了。如果有大佬总结出来了还请发我一份 哈哈。
最后附上分析过程和相关文件
需要分析记录和相关文件可以到我的Github下载:0c3K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6f1L8$3&6&6b7$3S2W2L8U0f1$3i4K6u0r3x3e0j5H3i4K6u0V1b7%4u0S2j5$3E0E0k6b7`.`.
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课