练习笔记之160Crackme-033
本帖最后由 xiaoyu2032 于 2022-8-18 21:42 编辑# 160CM-033
## 1. 爆破
首先拖入PE看一下,应该是个汇编程序(ExeInfoPE检测为unknown,DetectItEasy检测为delphi(delhi反汇编工具无法反汇编),PEID检测为汇编)。直接拖进OD中,查找关键字符串也可以找到。这里我们用F12暂停法,打开注册界面,输入用户名和密码,然后确定,弹窗错误提示对话框后,在OD中按F12暂停,然后Alt+K进入堆栈调用列表中,再最后一个调用处双击,就找到了弹窗的调用程序,然后F8单步跟踪到返回,就可以到401245位置,该处调用就是错误弹窗函数。
往上看,可以看到一个比较和一个判断跳转,这个跳转就是关键跳转,将`je`该为`jmp`,爆破就完成了。
## 2. 算法分析
从图1中的代码可以看出,程序验证过程一目了然:先获取用户名字符串,然后调用校验算法1计算得到结果1;再获取密码字符串,然后调用校验算法2计算得到结果2,最后比较结果1和结果2是否相等,相等则提示成功,否则提示失败。
先分析用户名校验算法,用IDA打开程序,找到算法子程序`40137E`,按F5得到伪代码,如下图:
从伪代码中可以看出,算法先一次判断用户名字符串中的字符是否在A~Z之间,小于A,则弹窗报错,大于Z,将其ASCII值减32(即转换成大写字母),全部转换成大写字母后,再调用子程序`4013C2`,将所有字母的ASCII码值相加,最后在和`0x5678`进行异或运算(上述过程如果功力不够静态分析不大确定,在OD中跟踪一下程序执行过程也就很容易确定下来)。
接着分析密码校验算法,同样用IDA打开程序,找到算法子程序`4013D8`,按F5得到伪代码,如下图:
从伪代码中可以发现,IDA生成的伪代码有点问题,漏掉了与`0x1234`进行异或的计算过程。`*i-48`也就是字符的ASCII码减去数字0的ASCII码值,也即得到输入数字所代表的数值,`v2=(*i-48)+10*v2`,整个过程起始就是将数字字符串转化为其对应的数值。最后再与`0x1234`进行异或计算。
将上述校验过程编写成校验算法如下,输入数据与程序本身的计算结果进行验证,可以证明算法无误。
```cpp
// 160CrackMe-033.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include <Windows.h>
#include <stdio.h>
#include <math.h>
int _tmain(int argc, _TCHAR* argv[])
{
char name;
char code;
int i,j;
int k;
printf("请输入用户名(长度≤20):");
scanf_s("%s", name, 20);
printf("请输入密码(长度≤20):");
scanf_s("%s", code, 20);
//未做用户名输入校验,只允许输入字母
for (i = 0; i<strlen(name); i++)
{
name = toupper(name);
}
k = 0;
for (i = 0; i<strlen(name); i++)
{
k = k + name;
}
printf("用户名字符和:%d \n",k);
k = k ^ 0x5678;
printf("用户名校验输出:%x \n", k);
j = 0;
for (i = 0; i<strlen(code); i++)
{
j = (code-48) + 10 * j;
}
printf("密码异或前的结果:%d \n", j);
j = j ^ 0x1234;
printf("密码校验输出:%x \n", j);
system("pause");
return 0;
}
```
至于注册机的算法,由于密码的校验算法相当于输入数字字符串的数值与`0x1234`的异或结果,因此注册码的求解过程可以直接用用户名校验算法得到的结果,与`0x1234`进行异或,得到的数值就是注册码。具体算法程序如下:
```cpp
// 160CrackMe-033.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include <Windows.h>
#include <stdio.h>
#include <math.h>
int _tmain(int argc, _TCHAR* argv[])
{
char name;
char code;
int i,j;
int k;
printf("请输入用户名(长度≤20):");
scanf_s("%s", name, 20);
//未做用户名输入校验,只允许输入字母
for (i = 0; i<strlen(name); i++)
{
name = toupper(name);
}
k = 0;
for (i = 0; i<strlen(name); i++)
{
k = k + name;
}
k = k ^ 0x5678;
k = k ^ 0x1234;
printf("注册码为:%d \n", k);
system("pause");
return 0;
}
```
输入用户名`abc`,可以计算得到注册码为`17546`,输入程序中,验证成功。
## 3. 总结
这道题验证过程清晰简单,算法也不难,借助IDA、OD这两个软件,分析起来还是比较顺利的。
其中关于注册码的计算,默认注册码为数字字符串的话,可以如上直接异或后得出,但由于程序没有限制只能输入数字字符串,因此将其中的数字用字母代替,也是可以算得注册码的,比如最后一个数字用字母`h`替代,则可以得到`1749h`这样一个注册码,有很多个解。
我大体看了一下,很想了解,仔细又看了一下,还不如大体看一下。总结,看不懂! 这是啥?看不到{:1_909:} 我大体看了一下,很想了解,仔细又看了一下,还不如大体看一下。总结,{:1_907:}看不懂! 看不懂,先灌个水! 这个可以啊 谢谢分享谢谢你的坚持 写到好详细 写的真好!!! 看了楼主的帖子。打算去自己练一下。