本帖最后由 pk8900 于 2018-1-13 22:44 编辑
经过几个月的断断续续的研究 【适合破解新手的160个crackme练手】已经进行到第80个,正好是一半,现在的速度已经比开始时快了好几倍,这期间C++的语法和应用也熟练了不少,今天用这个例程来介绍一下Delphi程序快速分析定位关键代码找出算法的一点心得,也可以说是相关工具综合应用的心得。
【crackme简介】
下载地址:http://pan.baidu.com/share/link?shareid=541269&uk=4146939145
Delphi编写,有壳,是一个用户名+序列号验证方式,界面两个文本框,一个底图。
【初试】在Crackme中输入任意用户名和序列号,没有按钮,点任何地方没有反应,脱壳就不介绍了,专用工具或调试器脱都行。
分析工具:Ldr + IDA + X64dbg
【crackme截图】
【算法分析过程】
这个fireworx是一个系列的Crackme,都是Delphi语言编写,这个079,应该是系统里比较典型的一个例子。综合运用Ldr + IDA + X64dbg三大杀器,将会快速搞定他。
【步骤1:Ldr静态分析】:将程序拖入Crackme中,显示为Delphi-4语言。
Forms(f5)页面,Form选项下,程序总共有两个窗体,双击Form1,我们看到了注册窗口,Form2应该是注册成功窗口了,看起来有点恐怖,右下角有succed字样,应该是注册成功会出现这个窗口。在Form1中找到唯一的事件Unit1.TForm1.Image1Click,看来关键代码就在这里了。
还有三个标签,应该被设置了隐藏属性,运行界面上看不到。查看窗体代码方法:选Forms(f5)页面,Text选项后双击来查看窗体属性代码, object Label1: TLabel。。。Caption = 'Wrong Serial' object Label2: TLabel。。。 Caption = 'VL -3585903..46119.-208490.' object Label3: TLabel。。。 Caption = 'VL -',看来这些标签应该是作者编写时用来测试的,在发布的时修将属性Visble设为False了。VL -3585903..46119.-208490.看起来像注册码,尝试无效,'Wrong Serial' 为注册错误提示,因为是标签的属性文字,在调试器里可能搜索不到。还是看一下关键事件Unit1.TForm1.Image1Click代码吧,点击CodeViewer(F6)窗口下方的“Src”按钮,SourceCode(F10)页会显示反编译的Delphi代码,将代码复制到NotePad++中,对流程进行分析:
[Delphi] 纯文本查看 复制代码 EAX := Name;
EAX := Length(Name);
if (EAX{Length(Name)} >= 0) then
begin//2
//00455545
EAX := EAX + 1;//EAX
lvar_18 := EAX;
EDI := 0;
for lvar_18 := 0 to do
begin//3
//0045554B
ESI := EDI;
ESI := ESI Shr 14;//EDI Div 16384
ESI := ESI Xor EDI;//EDI Shr 14 Xor 0
ESI := ESI + $2F21A0{3088800};//ESI
ESI := ESI + $795CE{497102};//ESI
EAX := ESI;//ESI
lvar_8 := IntToStr(ESI);
EAX := ESI;//ESI
ECX := $49{73};
EAX := EAX Div ECX;//(ESI) Div $49{73}
EAX := EAX - $BBA{3002};//EAX
lvar_14 := EAX;
EAX := lvar_14;
lvar_C := IntToStr(lvar_14);
EAX := ESI;//ESI
ECX := $130{304};
EAX := EAX Div ECX;//(ESI) Div $130{304}
EAX := EAX Shl 2;//(ESI) Div $130{304} * 4
EAX := EAX + EAX * 4;
EAX := EAX Xor lvar_14;//EAX + EAX * 4 Xor lvar_14
EAX := EAX + $10F{271};//EAX
EAX := EAX - 0;//EAX
ESI := EAX;//EAX
EAX := ESI;//EAX
lvar_10 := IntToStr(EAX);
EAX := Edit2;
lvar_1C := Edit2.GetText{Caption};
EAX := lvar_1C;
EAX := Label3;
lvar_24 := Label3.GetText{Caption};
EDX := 8;
lvar_20 := lvar_24 + ' -' + lvar_8 + '..' + lvar_C + '.-' + lvar_10 + '.';
EDX := lvar_20;
if (lvar_1C{EAX} = lvar_20) then
begin//4
//00455604
EAX := Edit2;
lvar_1C := Edit2.GetText{Caption};
EAX := lvar_1C;
EAX := Label2;
lvar_20 := Label2.GetText{Caption};
EDX := lvar_20;
if (lvar_1C{EAX} = lvar_20) then
begin//5
//0045562F
EAX := Label1;
lvar_1C := Label1.GetText{Caption};
EAX := lvar_1C;
ShowMessage(lvar_1C);
end//5
经分析发现,Edit1(用户名)文本框未被引用,也就是说这个文本框与注册算法无关,最终注册码是 lvar_20 := lvar_24 + ' -' + lvar_8 + '..' + lvar_C + '.-' + lvar_10 + '.';这些字符连接组成最终注册码,而lvar_24 := Label3.GetText{Caption};,也就是lvar_24=“VL -”,而 lvar_8 、lvar_C 、 lvar_10是上面的算法中生成的,这里代码看来有些乱,还是看看IDA中分析的结果。
【步骤2:IDA静态分析】
在IDA中打开程序,按G转到Unit1.TForm1.Image1Click事件地址:00455504,F5分析伪代码,在这段代码中找到关键算法部分:
[C] 纯文本查看 复制代码 v4 = (v3 ^ (v3 >> 14)) + 3585902;
Sysutils::IntToStr(v4);
v18 = v4 / 73 - 3002;
Sysutils::IntToStr(v18);
Sysutils::IntToStr((v18 ^ 20 * (v4 / 304)) + 271);
TControl::GetText(*(TControl **)(v1 + 716));
v5 = v16;
TControl::GetText(*(TControl **)(v1 + 728));
System::__linkproc__ LStrCatN(&v15, 8, v6, &str___[1], v21, &str___[1], v20, &str___[1], v19, &str___2[1]);
System::__linkproc__ LStrCmp(v5, v15);
根据IDA中的伪代码可以分析出:
行01:v4<即lvar_8> = (v3<循环变量> ^ (v3 >> 14)) + 3585902;
行03:v18<即lvar_C> = v4 / 73 - 3002;
行05:lvar_10=(v18 ^ 20 * (v4 / 304)) + 271;
不得不说,IDA真的很强大,通过对LDR和IDA中的代码进行分析,加上在X64DBG中的动态验证:程序的循环流程是:取TForm1的Name属性('Form1')字符串长度为循环次数,计数器从0开始循环到5,最后一个循环生成的值拼接后为真正注册码。
[Asm] 纯文本查看 复制代码 00455535 mov eax,dword ptr [ebx+8];TForm1.Name:TComponentName
00455538 call @LStrLen
0045553D test eax,eax
0045553F> jl 00455660
00455545 inc eax
00455546 mov dword ptr [ebp-18],eax
eax取TForm1.Name后@LStrLen,得到长度,inc eax:从1开始循环。
【步骤3:X64DBG动态验证】
动态验证的过程就不详写了,提供一个窍门,可以在LDR中的Tools菜单下,点击 MAP Generator生成MAP文件,然后在X64DBG中用插件导入。这样代码会一目了然。
附添加MAP数据后的对比图:
未导入MAP文件:
导入MAP文件后:
这个CRACKME的算法就分析完成了,注册机的C++代码如下:
[C++] 纯文本查看 复制代码 #define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include <string>
using namespace std;
void main()
{
string serial = "VL -";
long v4 = 0;
long v18 = 10;
long v10 = 0;
for (int x = 0; x <= 5; x++)
{
v4 = (x ^ (x >> 0xE)) + 0x36B76E;
v18 = v4 / 0x49 - 0xBBA;
v10 = (v18 ^((v4 / 304)*20)) + 0x10F;
}
serial = serial + " -" + to_string(v4) + ".." + to_string(v18) + ".-" + to_string(v10) + ".";
cout << "your serial is:" << serial << endl;
system("pause");
}
注册码为:VL - -3585907..46120.-208483.
对于这一系列CRACKME(071-080)都适用于本方法快速破解,当然也同样用于其它DELPHI(代码无加密混淆)的程序,教程未尽之处或不对的地方,还请路过的大神指正。
|