首页
社区
课程
招聘
fonge's crackme 10.1源码解析
发表于: 2007-2-16 15:12 5974

fonge's crackme 10.1源码解析

2007-2-16 15:12
5974

总的说来,fonge's crackme 10.1还是很成功的,每一部分思路都已基本实现了他的作用!
借这个机会,在这里公布他的delphi源码!

为什么是delphi源码而不是其他的呢?
不为什么,因为是用delphi写的,直接拿来看了!

本来,crackme的外观很漂亮的,虽然仅仅是用字和颜色来排列!
可能是因为你们没那种字体吧以至于看到你们贴出来的图片那么难看!


unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    GroupBox1: TGroupBox;
    Label2: TLabel;
    Label3: TLabel;
    Edit1: TEdit;
    Edit2: TEdit;
    GroupBox2: TGroupBox;
    Label4: TLabel;
    Button1: TButton;
    Button2: TButton;
    GroupBox3: TGroupBox;
    Label1: TLabel;
    Label5: TLabel;
    Label6: TLabel;
    Label7: TLabel;
    Label8: TLabel;
    Label9: TLabel;
    Label10: TLabel;
    procedure Button2Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

初始化就略过,那都是机器自己先成的,我们从我们开始键入代码的地方开

就这里,


procedure TForm1.Button2Click(Sender: TObject);      这里是清空按钮的代码
begin
edit1.Text:='';
edit2.Text:='';
end;

procedure TForm1.FormCreate(Sender: TObject);        这个位置的作用后面再详细介绍
begin
var
  isDebuggerPresent: function:Boolean;
  DllModule: THandle;
begin
  DllModule := LoadLibrary('kernel32.dll');
  isDebuggerPresent := GetProcAddress(DllModule, 'IsDebuggerPresent');
     if isDebuggerPresent then
  asm
mov eax,eax
mov eax,eax
mov eax,eax
mov eax,eax
mov eax,eax
  end;
end;

这个就是注册按钮功能的地方,也是最关键的地方
procedure TForm1.button1Click(Sender: TObject);      
begin
var
i,j,n,b,m,a,c,d,k,o,rt,v,l,s,p,q,r,t,t1,t2,t3:integer;
name,sn,wr,at,at1,at2,at3,at4,at5,inp,su,ln,ps1,ps2:string;
tt:TMsgDlgType;
label
cz,cz0;
begin
name:=edit1.text;
inp:=edit2.Text;
at:='abcdefghijklmnopqrstuvwxyz ';
tt:=mtError;
n:=0;
m:=0;
o:=0;
if length(inp)<5 then exit;
if length(name)<6 then  goto cz;
m:=m+1;
if length(name)>10 then goto cz;
b:=ord(name[4]) ;
            上面都是初始化
下面部分开始算主要注册码部分了

for i:=1 to length(name) do
begin
for j:=1 to length(name) do b:=b+ord(name[length(name)-i])*ord(name[j]) shr 6;
n:=n+b;
sn:=sn+inttostr(n);
end;
下面就是用来判断的了,也是本次crackme重点关注的地方,引入了用除0异常来判断的编程思想


begin
q:=0;
p:=0;
for l:=1 to 3 do
begin
if l<2 then
begin
for s:=1 to 5 do
begin
p:=p+ord(sn[s]);
q:=q+ord(inp[s]);
end;
end
else
begin
if l=2 then
begin
for r:=6 to 12 do
begin
p:=p+ord(sn[r])+1;
q:=q+ord(inp[r]);
end;
q:=q+7;
end
else
begin
for t:=16 to length(sn) do
begin
p:=p+ord(sn[t]);
q:=q+ord(inp[t]);
end;
end;
end;
begin
try
o:=m div (p xor q);
o:=o+1;
except
on EDivByZero do
m:=m+1;
end;
end;
end;
end;

goto cz0;
asm
mov eax,eax
nop
end;
exit;                        

下面是字符串简单解码,是可以复杂的,但这毕竟是试验型crackme,点到为止
cz0:
begin
t1:=ord(inp[13])-31;
t2:=ord(inp[15])-36;
t3:=ord(inp[14])-49;
ps1:=at[t1]+at[5]+at[7]+at[9]+at[19]+at[t2];
ps2:=at[t3]+at[t1]+at[5]+at[m];
m:=m+7;
end;
cz:
begin
at1:=at[4]+at[5]+at[7]+at[8]+at[9];
at2:=at[15]+at[18]+at[19]+at[20]+at[14]+at[12];
at3:=at[19]+at[14]+' ';
at4:=at1[5]+at2[3]+' ';
at5:=at[23]+at[18]+at[15]+at[14]+at[7];
ln:=at2[6]+at2[1]+at2[5]+at1[3];
最后的确认,是否输出正确结果
if m<1 then wr:=at2[3]+at1[4]+at2[1]+at2[2]+at[20] else
begin
if length(name)>10 then wr:=ln else
begin
if m>10 then
begin
wr:=ps1+ps2+at[27]+at[20]+at[15]+at[27]+name;
tt:=mtInformation;
end else
wr:=at5;
end;
end;
if o>20 then wr:=ps1+ps2;
messageDlg(at3+at4+wr,tt,[mbOK],0) ;

exit;
end;
end;
end.


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 7
支持
分享
最新回复 (13)
雪    币: 263
活跃值: (10)
能力值: ( LV9,RANK:210 )
在线值:
发帖
回帖
粉丝
2
主体算法的实现很简单,会编程(not delphi)的都很容易就看明白,所以就不再讨论主体算法

下面我们着重分析判断部分,包括下面的对话框,以及isDebuggerPresent部分在本次crackme中的意义(虽然好像作用并不理想)

q:=0;
p:=0;
for l:=1 to 3 do 设置循环3次,因为里面加入了三次的比较,公用一个异常比较
begin
if l<2 then 第一次比较
begin
for s:=1 to 5 do 长度为1-5
begin
p:=p+ord(sn[s]);
q:=q+ord(inp[s]);
end;
end
else
begin
if l=2 then 第二次比较部分,6-12
begin
for r:=6 to 12 do
begin
p:=p+ord(sn[r])+1;
q:=q+ord(inp[r]);
end;
q:=q+7;
end
else
begin
for t:=16 to length(sn) do 最后的比较部分,16-
begin
p:=p+ord(sn[t]);
q:=q+ord(inp[t]);
end;
end;
end;
begin
try      用到了try --except的除0异常来作判断,而本次中仅仅都是P=Q式,并且主体算法在外围,所以固此crackme强

度不大

o:=m div (p xor q);
o:=o+1;        这特殊计数器,用来记录特针,刚开始编程时也是为了测试try的执行情况
except
on EDivByZero do 指定0异常处理
m:=m+1;                参与解码,在本次crackme中意义不大,还是因为主体算法被放在外围
end;
end;
end;
end;
……
if o>20 then wr:=ps1+ps2;        为什么要用这段呢,我也不知道,反正除0的那段必须是后面要调用的,不然,他不产生

0异常,所以就在后面加了这一段,让delphi以为后面有用到这个值,能生产可以正常运行的除0异常

……

这部分总结:通过异常来实现比较的方法得以初步使用,不停的调用异常来参与运算对动态跟踪加大的一定难度,因本次crackme是试验型产品

,仅使用3比较,而且实现过程内容统一,加之主体算法代码放在外围,促进了crackme加速破出的进程!很理想!
2007-2-16 15:13
0
雪    币: 263
活跃值: (10)
能力值: ( LV9,RANK:210 )
在线值:
发帖
回帖
粉丝
3
这部分是对话框,公用一个对话框!参数随过程的变化而变化。KAN曾争论过这个问题!
目的是尽可能的隐藏信息!
由于OD修改版的功能问题,让bqjxhw捡了个大便宜,这一部分功能没用上!
这个便宜将会在更下面的isDebuggerPresent部分提到,

……
tt:=mtError;                                        初始化提示框的TMsgDlgType   
m:=0;
if length(name)<6 then  goto cz;
m:=m+1;                                    

……
cz0:
begin
t1:=ord(inp[13])-31;
t2:=ord(inp[15])-36;
t3:=ord(inp[14])-49;
ps1:=at[t1]+at[5]+at[7]+at[9]+at[19]+at[t2];
ps2:=at[t3]+at[t1]+at[5]+at[m];
m:=m+7;
end;                                                这一部分是查表得出正确的提示
cz:
begin
at1:=at[4]+at[5]+at[7]+at[8]+at[9];
at2:=at[15]+at[18]+at[19]+at[20]+at[14]+at[12];
at3:=at[19]+at[14]+' ';
at4:=at1[5]+at2[3]+' ';
at5:=at[23]+at[18]+at[15]+at[14]+at[7];                         这一部分是查表得出错误注册码提示   

                    
ln:=at2[6]+at2[1]+at2[5]+at1[3];                             这一部分是查表得出long普遍字串

都是查表法,用来隐藏字符串,实现起来比跟踪容易了

if m<1 then wr:=at2[3]+at1[4]+at2[1]+at2[2]+at[20] else       M是个特征,在m小于1时,直接给出了错误的提示short


begin
if length(name)>10 then wr:=ln else                           判断用户名长度
begin
if m>10 then
begin
wr:=ps1+ps2+at[27]+at[20]+at[15]+at[27]+name;
tt:=mtInformation;                                         这一段就是改变提示框的TMsgDlgType,     
end else
wr:=at5;
end;
end;
if o>20 then wr:=ps1+ps2;                                 花指令,留给delphi的,此时是无意义的     

                  
messageDlg(at3+at4+wr,tt,[mbOK],0) ;                     这调用messagedlg,不停的变化参数,以此尽可能地隐藏信

   
exit;
end;
end;

总结:简单地处理字符信息,公用对话框。以止轻松地达到隐藏字符串或错误提示框的目的!
2007-2-16 15:13
0
雪    币: 263
活跃值: (10)
能力值: ( LV9,RANK:210 )
在线值:
发帖
回帖
粉丝
4
PEDiy 及SMC
先看delphi 代码


begin
var
  isDebuggerPresent: function:Boolean;
  DllModule: THandle;
begin
  DllModule := LoadLibrary('kernel32.dll');
  isDebuggerPresent := GetProcAddress(DllModule, 'IsDebuggerPresent');
     if isDebuggerPresent then
  asm
mov eax,eax
mov eax,eax
mov eax,eax
mov eax,eax
mov eax,eax
                           这么多mov eax,eax用来占位置的,一共10节
  end;
end;
……
goto cz0;                           
asm
mov eax,eax
nop
                                     这一段是5节                  
cz0:
……
cz1:         
……

下面是反汇编出来的结果

这里就是那五个mov eax,eax 共长10节

00454166   . /74 0A         je      short 00454172
00454168   . |89C0          mov     eax, eax
0045416A   . |89C0          mov     eax, eax
0045416C   . |89C0          mov     eax, eax
0045416E   . |89C0          mov     eax, eax
00454170   . |89C0          mov     eax, eax
00454172   > \5B            pop     ebx
00454173   .  C3            retn

goto cz0;                           
asm
mov eax,eax
nop
这个反汇编结果:
00454A96  ^ 0F85 20FFFFFF   JNZ fonge's_.004549BC
00454A9C    EB 03           JMP SHORT fonge's_.00454AA1
            89 C0           MOV EAX,EAX
            90              NOP
00454AA1    8B45 BC         MOV EAX,DWORD PTR SS:[EBP-44]
但是这些并不是想要的
所以要改

改成了这样
0045486E   . /75 0A         jnz     short 0045487A               
00454870   . |C705 9C4A4500>mov     dword ptr [454A9C], C08903EB    SMC,修改关键跳转      
0045487A   > \5B            pop     ebx
0045487B   .  C3            retn

++++++++++++++++++++++++++++++++++++++++++++++++++++++
第二段
++++++++++++++++++++++++++++++++++++++++++++++++++++++
00454A96   .^\0F85 20FFFFFF jnz     004549BC
00454A9C   .  E9 A0010000   jmp     00454C41                         这里直接跳过解正确码的地方,直接显示错误

对话框
         
00454AA1   .  8B45 BC       mov     eax, dword ptr [ebp-44]

+++++―――――――――――――――――――――――+++

这样就显得有个性了
整体结构就是通过PEB检测有没调试器
没有的话 就修改00454a9c处的代码,让他等于EB 03
正如我们的汇编goto cz0
如有,SMC将不再实现
那么下面就是绝对跳转向失败的地方,goto cz1
那样跟踪就会很失败,怎么都找不到正确的对话框形式!
当然,此次被修改版的OD捡了个大便宜  :)
_______________________________________________________

总结:
问:为什么编程的时候不直接用goto cz1呢?
答:delphi有优化指令的功能,如果直接用got0 cz1,后面生成EXE里CZ0段的内容都会因为没被调用而被清除!
类似刚才调用除0异常时一样!
2007-2-16 15:14
0
雪    币: 263
活跃值: (10)
能力值: ( LV9,RANK:210 )
在线值:
发帖
回帖
粉丝
5
mov     dword ptr [454A9C], C08903EB 这个代码直接保存后的EXE这一句将会执行有问题!那是因为保护模式下CODE段可读可执行不可写的

原因!所以要修改CODE段属性!

关于块属性的

总长度 20,编码方式,高位在后低位在前
块名.text
___________________________
2e74 6578 7400 0000 9a01 0000
0010 0000 0002 0000 0004 0000
0000 0000 0000 0000 0000 0000
2000 0060
___________________________
1.name 长度为8的块名:2e74 6578 7400 0000 .text
2.virtualsize 该块的真实长度 9a01 0000 19ah
3.virtualaddress 载入内存后的RVA 0010 0000 1000h
4.sizeofrawdata 磁盘中所占大小 0002 0000 200h
5.pointertorawdata 块在磁盘中的偏移 0004 0000 400h
6.pointertorelocations 0000 0000 6.7.8.9不要也罢
7.pointertolinenumbers 0000 0000
8.numberofrelocations  0000
9.numberoflinenumbers  0000
10.characteristics 0000 0000块属性(如代码/数据/可读/可写等)

――――――――――――――――――――――――――――――――
字段属性为多个属性值求或

0000 0020 含可执行代码
0000 0040 含已初始化数据
0000 0080 含未初始化数据
0200 0000 可丢弃
1000 0000 共享块
2000 0000 可执行块
4000 0000 可读块
8000 0000 可写块

――――――――――――――――――――――――――――――――
常用块属性: 文件中形式
e000 0020h 2000 00e0 包含可执行代码,可读可写可执行
c000 0040h 4000 00c0 包含初始化数据,可读可写
6000 0020h 2000 0060 包含可执行代码,可读可执行
+++++++++++++++++++++++++++++++++++++++++++++++++++++

OK

所以,改coDe段块属性直接是找到.code字样,找到最后那4节属性值2000 0060,改成2000 000e0
2007-2-16 15:14
0
雪    币: 263
活跃值: (10)
能力值: ( LV9,RANK:210 )
在线值:
发帖
回帖
粉丝
6
可能有些地方解析得不合理
主要是过年了……


顺祝大家新年快乐!

新年新气象!
2007-2-16 15:17
0
雪    币: 263
活跃值: (10)
能力值: ( LV9,RANK:210 )
在线值:
发帖
回帖
粉丝
7
附件:
fonge's crackme 10.1 and keygen
fonge's crackme 10.1源码 and keygen 源码
上传的附件:
2007-2-16 15:19
0
雪    币: 2319
活跃值: (565)
能力值: (RANK:300 )
在线值:
发帖
回帖
粉丝
8
支持 open source
2007-2-16 16:32
0
雪    币: 313
活跃值: (250)
能力值: ( LV9,RANK:650 )
在线值:
发帖
回帖
粉丝
9
解析的太全面了,学习,支持开源
2007-2-16 19:45
0
雪    币: 263
活跃值: (10)
能力值: ( LV9,RANK:210 )
在线值:
发帖
回帖
粉丝
10
最初由 hbqjxhw 发布
解析的太全面了,学习,支持开源

那就在过年之前给个精
2007-2-16 19:52
0
雪    币: 721
活跃值: (350)
能力值: ( LV9,RANK:1250 )
在线值:
发帖
回帖
粉丝
11
支持你开源!
2007-2-16 21:01
0
雪    币: 615
活跃值: (1212)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
12
Free and Open
2007-2-16 21:04
0
雪    币: 263
活跃值: (10)
能力值: ( LV9,RANK:210 )
在线值:
发帖
回帖
粉丝
13
最初由 happytown 发布
支持你开源!

也支持你开源
2007-2-16 21:10
0
雪    币: 333
活跃值: (116)
能力值: ( LV9,RANK:570 )
在线值:
发帖
回帖
粉丝
14
楼主分析的真详细!
而且源代码中的几处'cz'让偶倍感亲切,嘿嘿!
...
label
cz,cz0;
...
if length(name)<6 then  goto cz;
...
2007-2-24 00:01
0
游客
登录 | 注册 方可回帖
返回
//