CrackMe2逆向分析
系统:Windows10 企业版 1709(部分测试在Win7虚拟机)
OD版本:吾爱专版(包含:API断点设置插件)
IDA版本:7.0
截图工具:Snipaste
文本编辑器:Typora
题目下载:链接:https://pan.baidu.com/s/15tqCH-Uj8YScej-whvv9Fg 密码:api7
一、脱壳
-
查壳
看到yoda的,大部分都是PEiD误报,又用Exeinfo PE查了一下,也没有找到特征壳,看来不是市场上常见壳。
-
尝试脱壳
00401830 > $- E9 B71D0100 jmp crackme2.004135EC 00401835 . 9C pushfd 00401836 . 873424 xchg dword ptr ss:[esp],esi ; crackme2.<ModuleEntryPoint> 00401839 . 8B3424 mov esi,dword ptr ss:[esp] ; kernel32.749E8654 0040183C . 8B3424 mov esi,dword ptr ss:[esp] ; kernel32.749E8654
第一行程序入口便是一个jmp大跳,尝试直接F4在00401835下断,程序直接执行了,无法调试。用单步法往下执行,没用(没有往上jump)。ESP大法下硬断,直接跳到程序逻辑了。那就先不脱壳吧,先看看程序验证算法。
二、程序算法分析
-
根据程序运行逻辑下API断
看到有用户名、密码输入框,这两个字符串都要拷贝到一个缓存区内,猜测用了
GetWindowText
或GetDlgItemTextA
,于是下了下面四个断:
然后F9,随便输入用户名密码:
再F9之后,四个断都用上了,然后Ctrl+F9跳到retn
,回到程序领空之后一直单步看逻辑。 -
获取用户名长度
004165E1 F2:AE repne scas byte ptr es:[edi]
执行到这一步的时候,看到
repne scas
可知在获取用户名字串长度,猜测应该是后面对用户名操作,需要根据用户名长度加循环。
ecx从FFFFFFFF开始,每循环一次ecx-1
,edi左移以删除第一个字符,最终edi的值为00停止循环,图中通过ecx的值可以看出字符串长度为7。 -
分析循环过程
调了半天,最后定位密码生成算法在
004064B3——004074F5
内存空间里,主要步骤:-
操作1:
-
操作2:
-
操作3:
-
操作1:
-
密码验证分析
-
循环过后,跳出密码生成循环,能看到密码,很惊讶密码没有加密:
-
经过几个大跳之后,找到关键比较:
-
比较完密码第一个字符串是错误之后,就到了关键跳:
-
循环过后,跳出密码生成循环,能看到密码,很惊讶密码没有加密:
三、写注册机
python实现
#-*- coding: utf-8 -*-
list = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMTVMPROTECT310"
name = raw_input("Please input username: ")
password = ""
for i in range(len(name)):
num = ord(name[(i+1)%len(name)]) >> 2
num = num | (0x4*int(hex(ord(name[i])[-1])))
num = num ^ 0x15
password += list[num]
print "Password is: ",password
C++实现(还没调试好,不建议使用)
#include<iostream>
#include<string>
using namespace std;
int strToHex(char *ch, char *hex);
int hexToStr(char *hex, char *ch);
int hexCharToValue(const char ch);
char valueToHexCh(const int value);
int main()
{
char s[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMTVMPROTECT310";
int i,len;
char ch[1024];
char hex[1024];
char result[1024];
char strin,*str=hex;
char *p_ch = ch;
char *p_hex = hex;
char *p_result = result;
while (true)
{
cout << "Please input username: " << endl;
cin >> str;
len = strlen(str);
cout << s[5];
cout << "Password is: ";
for (i = 0; i < len; i++)
{
*p_ch = str[(i + 1) % len];
strToHex(p_ch, p_hex);
strin = *p_hex >> 2;
strin = (strin | (0x4 * hexCharToValue(str[i]))) ^ 0x15;
cout << s[strin];
}
cout << endl;
}
}
int strToHex(char *ch, char *hex)
{
int high, low;
int tmp = 0;
if (ch == NULL || hex == NULL) {
return -1;
}
if (strlen(ch) == 0) {
return -2;
}
while (*ch) {
tmp = (int)*ch;
high = tmp >> 4;
low = tmp & 15;
*hex++ = valueToHexCh(high);
*hex++ = valueToHexCh(low);
ch++;
}
*hex = '\0';
return 0;
}
int hexToStr(char *hex, char *ch)
{
int high, low;
int tmp = 0;
if (hex == NULL || ch == NULL) {
return -1;
}
if (strlen(hex) % 2 == 1) {
return -2;
}
while (*hex) {
high = hexCharToValue(*hex);
if (high < 0) {
*ch = '\0';
return -3;
}
hex++;
low = hexCharToValue(*hex);
if (low < 0) {
*ch = '\0';
return -3;
}
tmp = (high << 4) + low;
*ch++ = (char)tmp;
hex++;
}
*ch = '\0';
return 0;
}
int hexCharToValue(const char ch) {
int result = 0;
if (ch >= '0' && ch <= '9') {
result = (int)(ch - '0');
}
else if (ch >= 'a' && ch <= 'z') {
result = (int)(ch - 'a') + 10;
}
else if (ch >= 'A' && ch <= 'Z') {
result = (int)(ch - 'A') + 10;
}
else {
result = -1;
}
return result;
}
char valueToHexCh(const int value)
{
char result = '\0';
if (value >= 0 && value <= 9) {
result = (char)(value + 48);
}
else if (value >= 10 && value <= 15) {
result = (char)(value - 10 + 65);
}
else {
;
}
return result;
}
四、总结
解这题应该可以直接patch,hook出00405E92
的密码,或者可以直接改程序流,在关键跳那里nop,可惜时间不够。最近在学安卓逆向,很久没调OD了有点吃力。还是渣渣,要好好学习。