目标:riijj的keygenme1.
这个是前段时间弄的, 因为打算把riijj的cm和km都分析一遍,当时分析的时候
因为没看原帖子, 所以花了很多时间(真的是好多时间
), 现在看来可能有些手法比较笨拙, 也不大算改了.
原原本本贴上来算了.
00401777 |> \50 push eax ; /Arg4
00401778 |. FF75 9C push dword ptr [ebp-64] ; |Arg3
0040177B |. 56 push esi ; |Arg2
0040177C |. 56 push esi ; |/pModule
0040177D |. FF15 48904000 call [<&KERNEL32.GetModuleHandleA>] ; |\GetModuleHandleA
00401783 |. 50 push eax ; |Arg1
00401784 |. E8 77F8FFFF call riijj_km.00401000 ; \这里是WinMain, 跟进
00401000 /$ 83EC 1C sub esp, 1C ; Winmain
00401003 |. 56 push esi
00401004 |. 8B7424 24 mov esi, [esp+24]
00401008 |. 57 push edi
00401009 |. 8B3D E8904000 mov edi, [<&USER32.LoadStringA>] ; USER32.LoadStringA
0040100F |. 6A 64 push 64 ; /Count = 64 (100.)
00401011 |. 68 A4CD4000 push riijj_km.0040CDA4 ; |Buffer = riijj_km.0040CDA4
00401016 |. 6A 67 push 67 ; |RsrcID = STRING "Riijj Keygenme 1 30112007"
00401018 |. 56 push esi ; |hInst
00401019 |. FFD7 call edi ; \LoadStringA
0040101B |. 6A 64 push 64 ; /Count = 64 (100.)
0040101D |. 68 3CCD4000 push riijj_km.0040CD3C ; |Buffer = riijj_km.0040CD3C
00401022 |. 6A 6D push 6D ; |RsrcID = STRING "Riijj Keygenme 1 30112007"
00401024 |. 56 push esi ; |hInst
00401025 |. FFD7 call edi ; \LoadStringA
00401027 |. 56 push esi
00401028 |. E8 43030000 call riijj_km.00401370 ; 光标,图标及窗体的注册.
0040102D |. 8B4424 38 mov eax, [esp+38]
00401031 |. 50 push eax
00401032 |. 56 push esi
00401033 |. E8 28040000 call riijj_km.00401460 ; 这个call里面会创建对话框.跟进.
004014B8 |. 6A 00 push 0 ; /lParam = 0
004014BA |. 68 00144000 push riijj_km.00401400 ; |pDlgProc = riijj_km.00401400
004014BF |. 56 push esi ; |hOwner
004014C0 |. 6A 65 push 65 ; |pTemplate = 65
004014C2 |. 57 push edi ; |hInst
004014C3 |. FF15 C8904000 call [<&USER32.CreateDialogParamA>] ; \CreateDialogParamA
得到DialogFunc. 跳转至之.
00401400 . 817C24 08 110>cmp dword ptr [esp+8], 111 ; DialogFunc
00401408 . 75 4D jnz short riijj_km.00401457
0040140A . 817C24 0C EA0>cmp dword ptr [esp+C], 3EA
00401412 . 75 43 jnz short riijj_km.00401457
00401414 . A1 38CD4000 mov eax, [40CD38]
00401419 . 56 push esi
0040141A . 8B35 D4904000 mov esi, [<&USER32.GetDlgItemTextA>] ; USER32.GetDlgItemTextA
00401420 . 6A 14 push 14 ; /Count = 14 (20.)
00401422 . 68 0CCE4000 push riijj_km.0040CE0C ; |Buffer = riijj_km.0040CE0C
00401427 . 68 E8030000 push 3E8 ; |ControlID = 3E8 (1000.)
0040142C . 50 push eax ; |hWnd => 016E0CBC (class='#32770',parent=00380C10)
0040142D . FFD6 call esi ; \GetDlgItemTextA
0040142F . 8B0D 38CD4000 mov ecx, [40CD38]
00401435 . 68 FF000000 push 0FF ; /Count = FF (255.)
0040143A . 68 24CE4000 push riijj_km.0040CE24 ; |Buffer = riijj_km.0040CE24
0040143F . 68 E9030000 push 3E9 ; |ControlID = 3E9 (1001.)
00401444 . 51 push ecx ; |hWnd => 016E0CBC (class='#32770',parent=00380C10)
00401445 . FFD6 call esi ; \GetDlgItemTextA
00401447 . E8 74FCFFFF call riijj_km.004010C0 ; 这个是数据转换Call. :记为keycall1
0040144C . E8 4FFEFFFF call riijj_km.004012A0 ; 这个是比较及弹信息Call. :记为keycall2
00401451 . 33C0 xor eax, eax
00401453 . 5E pop esi
00401454 . C2 1000 retn 10
00401457 > 33C0 xor eax, eax
00401459 . C2 1000 retn 10
DialogFunc就这么少了, 从中可知,用户名和注册码保存在全局变量中.
到这里,我们用由下至上的思想去跟踪,先去keycall逆着推.
//keycall2:
004012A0 /$ 83EC 40 sub esp, 40
004012A3 |. B8 2D000000 mov eax, 2D
004012A8 |. 56 push esi ; 现场保护.
004012A9 |. 894424 04 mov [esp+4], eax
004012AD |. 894424 10 mov [esp+10], eax
004012B1 |. B8 55000000 mov eax, 55
004012B6 |. C74424 08 620>mov dword ptr [esp+8], 62
004012BE |. 894424 14 mov [esp+14], eax
004012C2 |. 894424 2C mov [esp+2C], eax
004012C6 |. C74424 0C 240>mov dword ptr [esp+C], 24
004012CE |. C74424 18 570>mov dword ptr [esp+18], 57
004012D6 |. C74424 1C 170>mov dword ptr [esp+1C], 17
004012DE |. C74424 20 560>mov dword ptr [esp+20], 56
004012E6 |. C74424 24 530>mov dword ptr [esp+24], 53
004012EE |. C74424 28 0B0>mov dword ptr [esp+28], 0B
004012F6 |. C74424 30 5E0>mov dword ptr [esp+30], 5E
004012FE |. C74424 34 480>mov dword ptr [esp+34], 48
00401306 |. C74424 38 540>mov dword ptr [esp+38], 54
0040130E |. C74424 3C 1A0>mov dword ptr [esp+3C], 1A
00401316 |. C74424 40 580>mov dword ptr [esp+40], 58 ; 从此往上是对局部DWORD m[16]填充常量.
0040131E |. 33C0 xor eax, eax
00401320 |. BA 01000000 mov edx, 1
00401325 |> 8B88 00F04000 /mov ecx, [eax+40F000] ; 我们吧40f000记为DWORD g[16], 这里是g[i]
0040132B |. 8B7404 04 |mov esi, [esp+eax+4]
0040132F |. 2BCE |sub ecx, esi ; m[i] - g[i],且置标志位.
00401331 |. 8988 00F04000 |mov [eax+40F000], ecx
00401337 |. 79 08 |jns short riijj_km.00401341
00401339 |. F7D9 |neg ecx ; 小于0则求补,这里实际是在求绝对值.
0040133B |. 8988 00F04000 |mov [eax+40F000], ecx
00401341 |> 3990 00F04000 |cmp [eax+40F000], edx ; edx就是常数1.
00401347 |. 7F 20 |jg short riijj_km.00401369 ; 这里不能跳.
00401349 |. 83C0 04 |add eax, 4
0040134C |. 83F8 40 |cmp eax, 40
0040134F |.^ 7C D4 \jl short riijj_km.00401325
00401351 |. A1 08CE4000 mov eax, [40CE08]
00401356 |. 6A 00 push 0 ; /Style = MB_OK|MB_APPLMODAL
00401358 |. 68 54A04000 push riijj_km.0040A054 ; |Title = "Riijj Keygenme 1"
0040135D |. 68 38A04000 push riijj_km.0040A038 ; |Text = "Registration successful."
00401362 |. 50 push eax ; |hOwner => NULL
00401363 |. FF15 E4904000 call [<&USER32.MessageBoxA>] ; \MessageBoxA
00401369 |> 5E pop esi
0040136A |. 83C4 40 add esp, 40
0040136D \. C3 retn
从00401347这处可以看出, 这里的判断就是|m[i] - g[i]| <= 1 . 只要满足这个条件就OK.
现在m[16]为常量, 关键是看g[16]的生成过程. 我们下写断点于0040F000处. 结果断在这里:
00401288 |. 8907 |mov [edi], eax
这句就是在keycall1栈帧里面了.
那我们就去看看KeyCall1, 依然是倒着看:
00401261 |> /DD8434 A80000>/fld qword ptr [esp+esi+A8]
00401268 |. |DCA434 280100>|fsub qword ptr [esp+esi+128]
0040126F |. |83EC 08 |sub esp, 8
00401272 |. |DC05 08914000 |fadd qword ptr [409108]
00401278 |. |DD1C24 |fstp qword ptr [esp]
0040127B |. |E8 C0020000 |call riijj_km.00401540
00401280 |. |83C4 08 |add esp, 8
00401283 |. |E8 0C040000 |call riijj_km.00401694
00401288 |. |8907 |mov [edi], eax
0040128A |. |83C7 04 |add edi, 4
0040128D |. |83C6 08 |add esi, 8
00401290 |. |81FF 40F04000 |cmp edi, riijj_km.0040F040
00401296 |.^\72 C9 \jb short riijj_km.00401261
00401298 |> 5F pop edi
00401299 |. 5E pop esi
0040129A |. 5D pop ebp
0040129B |. 5B pop ebx
0040129C |. 8BE5 mov esp, ebp
0040129E |. 5D pop ebp
0040129F \. C3 retn
我们把esp+A8处的记为double m1[16], esp+128处的记为double m2[16];
则这里是: g[i] = m1[i] - m2[i] + 0.4 ; qword ptr [409108]就是double型的0.4
所以我们目前要知道m1[16]和m2[16]的生成过程.
我们在OD往上看看,看哪个循环是在对m1和m2生成数据.
很明显, 向上翻5行, 就看到这句:
0040124F |> \DD9CD4 A80000>|fstp qword ptr [esp+edx*8+A8]
这是在对吗m1就行写入了. 这里是个双重循环, 我们来看看:
004011F2 |> \33D2 xor edx, edx ; 计数器,16次.
004011F4 |> DD05 30914000 /fld qword ptr [409130] ; 这里是double型的0,貌似没用到.
004011FA |. 895424 18 |mov [esp+18], edx
004011FE |. 897424 1C |mov [esp+1C], esi
00401202 |. DF6C24 18 |fild qword ptr [esp+18] ; 64位型整数push进STx.
00401206 |. 8D4C24 28 |lea ecx, [esp+28]
0040120A |. B8 01000000 |mov eax, 1 ; 内部一个小循环.
0040120F |> 894424 20 |/mov [esp+20], eax
00401213 |. 897424 24 ||mov [esp+24], esi
00401217 |. DF6C24 20 ||fild qword ptr [esp+20]
0040121B |. 83C0 02 ||add eax, 2
0040121E |. 83C1 08 ||add ecx, 8
00401221 |. 83F8 21 ||cmp eax, 21
00401224 |. D8C9 ||fmul st, st(1)
00401226 |. DC0D 28914000 ||fmul qword ptr [409128]
0040122C |. DC0D 20914000 ||fmul qword ptr [409120]
00401232 |. D9FF ||fcos
00401234 |. DC49 F8 ||fmul qword ptr [ecx-8] ; 这里在乘m1_1[i],作为引入参数.
00401237 |. DEC2 ||faddp st(2), st
00401239 |.^ 72 D4 |\jb short riijj_km.0040120F
0040123B |. 3BD6 |cmp edx, esi ; esi始终为0.
0040123D |. DDD8 |fstp st
0040123F |. 75 08 |jnz short riijj_km.00401249 ; 第一次不会跳,以后都跳.
00401241 |. DC0D 18914000 |fmul qword ptr [409118] ; 0.25
00401247 |. EB 06 |jmp short riijj_km.0040124F
00401249 |> DC0D 10914000 |fmul qword ptr [409110] ; 0.3535...
0040124F |> DD9CD4 A80000>|fstp qword ptr [esp+edx*8+A8] ; 写入m1[i]
00401256 |. 42 |inc edx
00401257 |. 83FA 10 |cmp edx, 10
0040125A |.^ 72 98 \jb short riijj_km.004011F4
上面这段生成m1依靠的其实是: 循环计数(i,j)和上面的m1_1[16] ( 也即esp+28处).这个嵌套循环很简单, 现在关键要知道m1_1如何来的,
继续上溯:
004011B5 |. BB 24CE4000 mov ebx, riijj_km.0040CE24 ; ASCII "999999999"
004011BA |. C1E9 03 shr ecx, 3
004011BD |. 897424 14 mov [esp+14], esi
004011C1 |. 74 2F je short riijj_km.004011F2
004011C3 |. 8D7C24 28 lea edi, [esp+28] ; 取m1_1地址.
004011C7 |. 8BE9 mov ebp, ecx
004011C9 |> 8D5424 14 /lea edx, [esp+14] ; 这是sscanf的第三个参数.记为dw.
004011CD |. 52 |push edx
004011CE |. 68 30A04000 |push riijj_km.0040A030 ; ASCII "%08x"
004011D3 |. 53 |push ebx
004011D4 |. E8 36040000 |call riijj_km.0040160F ; 调用sscanf.
004011D9 |. DB4424 20 |fild dword ptr [esp+20] ; 这里就是dw. +20是因为前面push了3次.
004011DD |. 83C4 0C |add esp, 0C
004011E0 |. 83C3 08 |add ebx, 8
004011E3 |. 83C7 08 |add edi, 8
004011E6 |. 4D |dec ebp
004011E7 |. DC0D 38914000 |fmul qword ptr [409138] ; 乘以0.00001
004011ED |. DD5F F8 |fstp qword ptr [edi-8] ; 对m1_1写入.
004011F0 |.^ 75 D7 \jnz short riijj_km.004011C9
现在m1就很明了了, 就是这样的:
注册码:--变形-->>m1_1 ----再变形-->>m1.
再来看看m2.m2相对很简单, 就不贴反汇编了,免得占地方.
所以最后是: name --变1--> m2[16]
reg --变2--> m1_1 ----变22---m1[16].
这里的"变22"是最复杂的一个. 因为每个m1[i]都与所有的m1_1有关系.
其实是一个16元一次方程.
我想起了大一的线性代数又讲解法的,不过我的书扔了, 室友的没扔, 借来看看,可以用Cramer定律做.
于是求行列式, 开始我用递归去求, 结果发现太慢, 最后反倒后面有讲高斯消去法的, 于是用高斯法,
结果速度很快.(高斯消去的代码是在网上找的.)
我调了好久,算是凑了个注册机了,代码实在惨不忍睹
#include <stdio.h>
#include <conio.h>
#include <windows.h>
#include <math.h>
#define PI 3.1415926535897932384626433832795
#define JK
int m[16] = {0x2d,0x62,0x24,0x2d,0x55,0x57,0x17,0x56,0x53,0xb,0x55,0x5e,0x48,0x54,0x1a,0x58};
char name[256] = {0};
char reg[1024] = {0};
double rec_name[16] = {0}; //就是上面分析的m2.
double rec_reg2[16] = {0}; //注册名第二次变化后. 就是上面分析的m1
double rec_reg1[16] = {0}; //注册名第一次变换后.就是m1_1.
double b[16] ;
double a[16][16];
double x[16] ;
void mk_name();
void mk_reg2();
void mk_reg1();
const int n = 16;
//double aa[n][n] = {{2,1,-5,1},{1,-3,0,-6},{0,2,-1,2},{1,4,-7,6}};
//double bb[n] = {8,9,-5,0};
//5个高斯消去相关函数.
int find_line(int i,double a[n][n]);
void chang_line(int line,int other_line,double a[n][n]);
void clear_num(int i,double a[n][n]);
double calculate_det(double a[n][n]);
void JGus(double a[n][n],double b[n],double x[n]);
DWORD f2dw(double x)
{
DWORD dw =(DWORD)x;
double d = (double)dw;
if (x-d>d+1-x)
{
dw++;
}
return dw;
}
void main(void)
{
int i;
DWORD dw[n];
char _t[10];
printf("Enter your name : \n");
scanf("%s",name);
mk_name();
mk_reg2();
mk_reg1(); //得x[16]
FILE *fp = fopen("d:\\dd.txt","w+");
for(i =0;i<n;i++)
{
x[i] /= 0.00001;
fprintf(fp,"x[%d] = %f \n",i,x[i]);
dw[i] = f2dw(x[i]);
fprintf(fp,"dw[%d] = %08X \n\n",i,dw[i]);
memset(_t,0,sizeof(_t));
sprintf(_t,"%08X",dw[i]);
strcat(reg,_t);
}
printf("Your RegCode is :\n%s\n",reg);
printf("Press any key to exit ...");
getch();
}
void mk_name()
{
int i,len;
int t,total = 1;
double f;
char ch;
len = strlen(name);
for (i=0;i<16;i++)
{
f = (double)name[(i+1)%len];
f *= 10.0;
f = sin(f);
f *= 5527;
total += (int)fabs(f);
t = (int)name[i%len];
t *= 37;
total += t + 0xB;
ch = (char)total;
total = (int)ch;
rec_name[i] = (double)total;
}
}
void mk_reg2()
{
int i;
for (i=0;i<16;i++)
{
rec_reg2[i] = rec_name[i] - 0.4 + m[i];
}
}
void mk_reg1()
{
FILE *fp;
int i,j;
for (i = 0;i<16;i++)
{
for (j = 0;j<16;j++)
{
a[i][j] = (double)cos(PI * i * (2*j + 1) / 32);
}
if (0 == i)
{
b[i] = (double)rec_reg2[i] / (double)0.25;
}
else
{
b[i] = (double)rec_reg2[i] / (double)0.35355339;
}
}
#ifdef JK
fp= fopen("d:\\dd.txt","a+");
fprintf(fp,"a[16][16]:\n\n");
// for(i=0;i<16;i++)
// {
// for (j=0;j<16;j++)
// {
// fprintf(fp," a[%d][%d] :%f\n",i,j,a[i][j]);
// }
// }
fprintf(fp,"b[16]:\n\n");
for(i=0;i<16;++i)
{
fprintf(fp," b[%d] :%f\n",i,b[i]);
}
fprintf(fp,"generating ... \n\n");
#endif
JGus(a,b,x);
#ifdef JK
fprintf(fp,"\n\n== done ! x[16]: \n\n");
for(i=0;i<16;++i)
{
fprintf(fp," x[%d] :%f\n",i,x[i]);
}
fclose(fp);
#endif
}
//这后面都是高斯消去法解方程相关.网上找的.
void JGus(double a[n][n],double b[n],double x[n])
{
double a2[n][n];
int i,j,k;
double D = calculate_det(a);
double D2;
for (i=0;i<n;i++)
{
////copy
for (j=0;j<n;j++)
for (k = 0;k<n;k++)
{
if (k == i)
a2[j][k] = b[j];
else
a2[j][k] = a[j][k];
}
D2 = calculate_det(a2);
x[i] = D2/D;
}
}
int find_line(int i,double ar[n][n])
{
int max_a_line=i; //max_a_line为最大元素所在的行
double max_a=ar[i][i]; //max_a为最大元素的值
int out_i=i; //out_i为i的初始值
for(i=i+1;i<n;i++)
{
if(ar[i][out_i]<0)
if(max_a<-ar[i][out_i])
{
max_a=-ar[i][out_i];
max_a_line=i;
};
if(max_a<ar[i][out_i])
{
max_a=ar[i][out_i];
max_a_line=i;
};
}
return max_a_line;
}
void chang_line(int line,int other_line,double ar[n][n])
{
double m; //m为中间变量
for(int j=line;j<n;j++)
{
m=ar[line][j];
ar[line][j]=ar[other_line][j];
ar[other_line][j]=m;
}
}
void clear_num(int i,double ar[n][n])
{
int line=i,j;
double m;
for(i=i+1;i<n;i++)
{
m=-ar[i][line]/ar[line][line];
for(j=line;j<n;j++)
{
ar[i][j]=ar[i][j]+m*ar[line][j];
}
}
}
double calculate_det(double ar[n][n])
{
double det=1;
int i=0,j;
//----------copy-----------
double copy_ar[n][n];
for (i=0;i<n;i++)
for (j=0;j<n;j++)
copy_ar[i][j] = ar[i][j];
//--------------------------
i=0;
do{
//print_num(a);
j=find_line(i,copy_ar);
if(i!=j)
{
chang_line(i,j,copy_ar);
det=-det;
};
if(copy_ar[i][i]==0)
return 0;
det=det*copy_ar[i][i];
if(i<n-1)
clear_num(i,copy_ar);
i++;
}while(i<n);
return det;
}
各位不要笑哦, 我代码一直都写的乱七八糟的.
也没怎么改,直接copy了到这里.
总结:
riijj这个km应该来说用的是F(U,R)= 0 这种方法.
对于最后的比较, 如果直接弄成g[i] == m[i] 的. 这样就又要加一点
代码了.
第一次弄注册机, 弄的很烂, 大家别笑话我.
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!