160CM-009
008太简单了,固定明码字符串,直接一看就知道了,因此跳到009。
1.爆破
先拖到PE中查一下,无壳,VB,请出VB Decompiler,很容易找到函数入口地址以及关键跳转地址(4022CB)。
OD中将4022CB行的je改成jnz即可爆破成功。
2.算法
根据VB Decompiler反编译出来的VB代码,其中有的VB函数不是很确定的话,百度一下也就搞定了,可以很方便的得出算法的C代码。
char str1[30];
int i,m,code;
double f;
printf("请输入用户名:");
scanf_s("%s",str1,30);
m=0;
for (i=0;i<=strlen(str1);i++)
{
m=m+str1[i];
}
printf("m为:%d \n",m);
code=m*1234567890;
printf("注册码为:%n \n",code);
输入用户名abc,发现输出结果为-2109260500。呃。。。好像溢出了,把这个结果输入到程序中,发现失败。取出中间值m(294),用计算器算一下结果是362962959660,输入进去还是失败。
在OD中F8跟踪了一下程序,发现算法中的中间值m计算是没有问题的,后面好像也是乘1234567890的计算,但是这个函数调用过程好像很奇怪,根据函数的参数变量地址,根本找不到对应的运算结果。
在VB Decompiler中,去掉“程序分析器和优化器”选项,重新反编译一下,得到的反编译代码如下图所示。
其中可以发现和前面的反汇编结果比较,代码可读性更差了,多出来很多看起来没有用处的变量赋值,而且还有两行代码40222F和402254是前面的反汇编代码里面没有的,好像是前一种反汇编方式有问题,漏掉了部分有用的代码。
本来想用IDA来分析一下算法,结果IDA中没法直接识别到401FF0这个函数,手动创建函数后,按F5,结果显示“Call analysid failed”,百度了半天,也没找到解决办法,只能放弃。
重新下载了一个最新的VB Decompiler Pro v11.5,同样反汇编一下,发现和第一次反汇编的结果相比,多出来402254这一行。按这个代码去算一个注册码,发现还是不对。看来还是要到OD里面看看汇编代码,看看到底是怎么回事。
进入OD里面F8跟踪运行了一遍,发现有点懵圈(懵了整整一上午……)。以前调试跟踪的时候,汇编的函数调用之前,要么通过寄存器、要么通过指针传值,而这个程序里面,虽然套路也差不多,函数调用前push一堆地址进去,调用完成后有一堆数据更新了,但是查查对应地址的数据,前前后后没有一个对得上。
比如vbaVarMul函数,百度得到的信息是:
__vbaVarMul ;变体变量相乘
lea eax,var1
push eax ;被乘数
lea ecx,var2
push ecx ;乘数
lea edx,var3
push edx ;结果
call __vbaVarMul ;变量相乘,在eax中返回
在OD中,对应的代码也类似,而且OD中的右侧的注释中已经自动将var1、var2和SaveTo识别出来了,该函数应该是将var1和var2相乘,结果放入SaveTo位置。但是F8跟踪的时候发现,函数调用前,var1为指针地址12F4AC,指向的变量数值为0x2,var2为指针地址12F434,指向的变量数值为0x3,SaveTo为指针地址12F474,指向的变量数值为0。函数调用后,var1的数值为仍0x2,var2的数值为仍0x3,SaveTo的数值变成了0x5。
呃……,这哪哪都不对啊。。。
在整整懵逼了一上午后,终于找到了一篇介绍VB变量体数据类型的文章,文章地址为https://blog.csdn.net/vbsourcecode/article/details/18662089。
根据介绍,一个VB简单Variant(变量体数据)的表示至少需要12个字节。通常前2个字节是表示类型信息的。从第5个字节到第8个字节并不总是使用到,实际上很少被使用。我们不妨先叫它辅助类型信息。从第9个字节开始就是真正的变量的值了。这里有可能存储一个指针值,也可能是数据,具体是什么取决于变量类型。另一个值得注意的事实是VB的内存是以4个字节对齐的。即使你使用一个字节,那至少也要4个字节来表示。而且编译器只初始化它需要的那些字节,剩余的字节可能是随机数据。
==Variant变量的内部表示==
符号常量 |
函数值 |
数值类型 |
V_Empty |
0 |
Empty(未初始化) |
V_Null |
1 |
Null(无有效数据) |
V_Integer |
2 |
整数 |
V_Long |
3 |
长整数 |
V_Single |
4 |
单精度浮点数 |
V_Double |
5 |
双精度浮点数 |
V_Currency |
6 |
货币值 |
V_Date |
7 |
日期 |
V_String |
8 |
字符串 |
V_Object |
9 |
对象 |
V_Error |
10 |
错误值 |
V_Boolean |
11 |
布尔值 |
V_Variant |
12 |
Variant(只与变体中的数组一起使用) |
V_Object |
13 |
数据访问对象 |
V_Byte |
17 |
位值 |
V_Array |
8192 |
数组 |
根据上述内容,实际抓了两个数据案例如下:
地址 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
0012F4AC |
05 |
00 |
00 |
00 |
E8 |
F4 |
12 |
00 |
00 |
40 |
77 |
92 |
5A |
73 |
65 |
42 |
这表示一个双精度浮点数,1-2字节05表示类型的数值,9-16字节为4265735A92774000,表示浮点数737037030330。3-4和5-8意义不明。
地址 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
0012F434 |
03 |
00 |
00 |
00 |
01 |
00 |
00 |
00 |
D2 |
02 |
96 |
49 |
这表示一个长整型,1-2字节03表示类型的数值,9-12字节为499692D2,表示整型1234567890。3-4和5-8意义不明。
回头再看一下VB Decompiler去掉“程序分析器和优化器”选项后反汇编的结果,终于明白那些看起来没有任何意义的赋值语句怎么来的了,OD调试也终于可以跟踪到正确的数据了。根据vbaVarMul后面的指令,可以看出是执行了两次vbaMidStmtVar函数,分别将字符串中第4个字符和第9个字符替换成“-”,这样就得到了最终的注册吗。
最终得到注册码计算程序如下:
#include "stdafx.h"
#include <Windows.h>
#include<stdio.h>
int _tmain(int argc, _TCHAR* argv[])
{
char str1[30],code[30];
int i,m;
double f;
printf("请输入用户名:");
scanf_s("%s",str1,30);
m=0;
for (i=0;i<=strlen(str1);i++)
{
m=m+str1[i];
}
printf("m为:%d \n",m);
f=double(m)*double(1234567890);
sprintf_s(code,"%.0f",f);
code[3]= 45; //ASCII "-"
code[8]= 45; //ASCII "-"
printf("注册码为:%s \n",code);
system("pause");
return 0;
}
运行后,输入用户名abc,得到注册码
将结果输入程序,注册成功!
3.总结
这次破解碰到了一个未接触过的变量体数据类型,在没搞清楚这种数据类型的特点时,完全不知道如何下手。搞清楚以后问题迎刃而解,但是这种数据类型相对来说还是会增加一些跟踪难度(无法直接看到变量数据,需要自己去内存中查找)。
- 变量体数据类型可以表示各种数据类型,使用变量体函数进行处理时,不需要手动转换数据类型。比如vbaVarMul的运算结果是双精度浮点数,vbaMidStmtVar运算时就自动变成了字符串(整型转字符串)。
- 逆向时碰到的一些常用的VB函数如下:
函数 |
功能 |
rtcMidCharVar |
从字符串中取相应字符,对应VB中的MID函数,用法为:MID("字符串","开始的位置","取几个字符") |
rtcAnsiValueBstr |
取字符的ASCII码数值,对应函数 Asc(string) |
vbaLenVar |
获得一个字符串的长度,与vbaLenBstr功能类似,只是对应的字符串为变量体数据类型 |
vbaVarForInit |
for循环起始,参数为变量体数据类型 |
vbaVarForNext |
循环结构的Next,每次执行时计数加步长,参数为变量体数据类型 |
vbaStrVarVal |
从变量体字符串指定位置上获取字符串(非变量体类型),从str1中取arg开始的字符串,结果返回eax |
vbaVarAdd |
变量体类型的两数相加,var1+var2,结果返回var3 |
vbaVarMul |
变量体类型的两数相乘,var1 * var2,结果返回var3 |
vbaVarTstEq |
变量体类型的比较,结果返回ax |
vbaMidStmtVar |
从字符串中取相应字符,与rtcMidCharVar类似,不过操作对象为变量体 |
vbaVarMove |
变量体类型的赋值,将var1赋值给var2 |
- 第一次碰到IDA中F5无法生成伪代码的情况,没有找到解决办法。
- 发现VB Decompiler反编译有时会出现问题,有时候要把“程序分析器和优化器”选项去掉去排查。另外,不同版本的VB Decompiler结果也有不同,比如VB Decompiler V11.5去掉“程序分析器和优化器”选项后反编译的结果如下图,其中存在var_A4先使用再赋值“-”的情况。这个是编译器翻译的问题,就后续机器翻译一样,有时候翻出来的句子比较奇怪的时候,还是需要取查看一下原文(汇编代码)去确认一下准确含义。