环境
看雪论坛的附件,其中序列号的第一题
IDA PRO 破解版 Version 6.8.150423 (64-bit)
含有下文注释的ida pro数据文件
过程
(跳过UPX脱壳过程)
首先在界面中输入Name,和Serial,发现错误的情况并不会有弹窗
拖入IDA,可见Functions View
导入表中含有GetDlgItemTextA
,那么在此处DLL设下断点
输入Name: woshi; Serial : 1234567
运行触发
触发点断后,返回上一层CR-F7
或者 Run until Return
seg019:00401539 call GetDlgItemTextA
seg019:0040153E mov ebx, eax
seg019:00401540 or ebx, ebx ; 验证Name填入不可为空
seg019:00401542 jnz short loc_401548
seg019:00401544 xor eax, eax
seg019:00401546 jmp short loc_401598 ; 如果为空,结束. 可以把Loc_401598重命名成 Loc_End_Function
按空格键,显示流程图样式
看图中代码,就是代码继续执行下来的流程,其中部分补充解释如下
//其中N.len 代表 Name.length
//(h)代表16进制,(d)代表10进制
当输入名字长度满足
190(h) <= (2BC(h) - ( (30(h) - 48(h)) / N.len ) * 5 ) * 6B(h) <= 2300(h)
代码进入loc_40157E
, 如下图
这里注意我标记了1 , 2 , 3
1处
代码末端判断决定流程走不走2处
,但是最终都会走向3处
2处
代码很明显,是将edx = 1
以此反推1处
末端代码要想走向2处
,必须eax != 0
,也就是函数sub_401305
执行结果
那么此时再进入sub_401305
看看
sub_401305
的前面部分的代码异常复杂,全是申请内存,初始化,跳过,直接看第一个判断跳转的地方
4处
末端代码很明显是取Serial
值判空,很明显期望走向5处
代码
5处代码
注释如下
// serial[0] 代表serial的首尾字符ASSIC数值
seg020:004013BC mov eax, 11CFh ; eax = 11CF(h)
seg020:004013C1 movzx ecx, [ebp+String] ; ecx = Serial[0]
seg020:004013C8 cdq
seg020:004013C9 idiv ecx ; eax = 11CF(h) / serial[0]
seg020:004013C9 ; edx = 11CF(h) % serial[0]
seg020:004013CB cmp edx, 17h
seg020:004013CE jz short loc_4013D7
seg020:004013D0 xor eax, eax
seg020:004013D2 jmp loc_401504
意思就是需满足11CF(h) % serial[0] >= 17(h)
,否则 5处
红色又是直接走向终结
这里需要计算一下,先前Serial输入的是 1234567
,那么计算如下
11CF(h) % (1的ASSIC) = 11CF(h) % 31(h) = 4559(d) % 49(d) = 2(d) = 2(h) < 17(h)
所以首位是1
是不可以的,可以尝试如下构造(也可以直接写个程序遍历ASSIC)
//此处^代表乘方,不是异或
11CF(h) - 17(h) = 11B8(h) = 4536(d) = 4536 = 2^3 * 3^4 * 7
// 任意取因子
2^3 * 3^2 = 72(d) = 48(h)
// 48(h) 刚好为可见字符`H`
重新输入
通过上面分析,这里修改Serial输入为H234567
,重新断点
流程到6处
,这里其实是个循环,6处
是初始条件,7处
是循环体,8处
是判断条件
ebx
从零开始,直到大于等于Name.length
,本质上遍历求name assic
的和,结果存在栈上[ebp + __Name_Assic_Sum]
求和结算后,进入9处
初始化ebx
,此处又是个大循环,依然遍历Name
12处
是循环体,注解如下
seg020:004013F2 mov edx, [ebp+arg_8]
seg020:004013F5 movsx edi, byte ptr [edx+ebx] ; edi = Name[i]
seg020:004013F9 mov esi, [ebp+__Name_ASSIC_SUM] ; esi = NSum
seg020:004013FC mov ecx, ebx
seg020:004013FE shl ecx, 2
seg020:00401401 mov edx, ebx
seg020:00401403 inc edx
seg020:00401404 sub ecx, edx
seg020:00401406 movzx ecx, [ebp+ecx+var_11F] ; ecx = [ABCD...][3 * i - 1]
seg020:0040140E mov edx, edi
seg020:00401410 xor edx, ecx ; edx = N[i] xor [ABCD...][3 * i - 1]
seg020:00401412 mov ecx, esi
seg020:00401414 imul ecx, ebx ; ecx = NSum * i
seg020:00401417 sub ecx, esi ; ecx = NSum * i - NSum
seg020:00401419 mov esi, ecx ; esi = NSum * i - NSum
seg020:0040141B xor esi, 0FFFFFFFFh ; esi = (NSum * i - NSum) xor 0FFFFFFFF(h)
seg020:0040141E lea esi, [edx+esi+14Dh] ; esi = (N[i] xor [ABCD...][3 * i - 1]) + ((NSum * i - NSum) xor 0FFFFFFFF(h)) + 14D(h)
seg020:00401425 mov ecx, [ebp+__NAME_Length] ; ecx = NLen
seg020:00401428 mov edx, ebx ; edx = i
seg020:0040142A add edx, 3 ; edx = i + 3
seg020:0040142D imul ecx, edx ; ecx = NLen * (i + 3)
seg020:00401430 imul ecx, edi ; ecx = N[i] * NLen * (i + 3)
seg020:00401433 mov eax, esi ; eax = (N[i] xor [ABCD...][3 * i - 1]) + ((NSum * i - NSum) xor 0FFFFFFFF(h)) + 14D(h)
seg020:00401435 add eax, ecx ; eax = (N[i] xor [ABCD...][3 * i - 1]) + ((NSum * i - NSum) xor 0FFFFFFFF(h)) + 14D(h) + (N[i] * NLen * (i + 3))
seg020:00401437 mov ecx, 0Ah ; ecx = 0A(h)
seg020:0040143C xor edx, edx ; edx = 0
seg020:0040143E div ecx ; eax = ( N[i] xor [ABCD...][3 * i - 1] + ((NSum * i - NSum) xor 0FFFFFFFF(h)) + 14D(h) + N[i] * NLen * (i + 3) ) / 0A(h)
seg020:0040143E ; edx = ( N[i] xor [ABCD...][3 * i - 1] + ((NSum * i - NSum) xor 0FFFFFFFF(h)) + 14D(h) + N[i] * NLen * (i + 3) ) % 0A(h)
seg020:00401440 add edx, 30h ; edx = ( N[i] xor [ABCD...][3 * i - 1] + ((NSum * i - NSum) xor 0FFFFFFFF(h)) + 14D(h) + N[i] * NLen * (i + 3) ) % 0A(h) + 30(h)
seg020:00401443 mov [ebp+ebx+var_104], dl ; 取 edx的低8位,记为Low
seg020:0040144A movzx edi, [ebp+ebx+var_104] ; edi = low
seg020:00401452 xor edi, 0ADACh ; edi = low xor 0ADAC(h)
seg020:00401458 mov esi, ebx ; esi = i
seg020:0040145A add esi, 2 ; esi = i + 2
seg020:0040145D mov eax, edi ; eax = low xor 0ADAC(h)
seg020:0040145F imul eax, esi ; eax = (low xor 0ADAC(h)) * (i + 2)
seg020:00401462 mov ecx, 0Ah ; ecx = 0A(h)
seg020:00401467 cdq
seg020:00401468 idiv ecx ; eax = ((low xor 0ADAC(h)) * (i + 2)) / 0A(h)
seg020:00401468 ; edx = ((low xor 0ADAC(h)) * (i + 2)) % 0A(h)
seg020:0040146A add edx, 30h ; edx = ((low xor 0ADAC(h)) * (i + 2)) % 0A(h) + 30(h)
seg020:0040146D mov [ebp+ebx+var_104], dl ; 取edx的低8位,记为Low_Low
seg020:00401474 inc ebx ; ebx = i + 1 ;即循环次数+1
流程继续到11处
代码,该处是将求出结果进行格式化,先跳过过程直接看求出最终结果是什么
ecx保存着数据地址 ,结果是T86856-118
,流程继续走到13处
是一个循环
循环变量是eax
,起始值语句or eax 0FFFFFFFF(h)
本质上就是赋值了-1
(为什么不用mov
,因为这样机器码会少)
循环直到不为零,很显然13处
是求上叙T86856-118
的长度,保存在eax
寄存器
流程继续往下走到14处
,这里已经可以猜测出
T86856-118
就是对应的Name: whoshi
的序列号,14处
代码会把生成的序列号跟输入的序列号作比较
//14处代码
seg021:004014DE push eax
seg021:004014DF lea eax, [ebp+String]
seg021:004014E5 push eax
seg021:004014E6 lea eax, [ebp+var_21F]
seg021:004014EC push eax
seg021:004014ED call sub_4012C2 //这里就是比较函数
seg021:004014F2 add esp, 0Ch //释放栈内存
seg021:004014F5 cmp eax, 0 //这里可以看出eax保存了比较结果
seg021:004014F8 jnz short loc_401501
14处
代码有两处出口 => 15处
、16处
=> 17处
15处
和16处
的差异仅仅在于eax
一个取值为0,一个取值为1
回想起前面2处
的结果,也就明白了,就是把结果eax = 1
一层一层往上面传
那么这里下断点,修改寄存器eax
的值为1,答案可以弹出了
注册机
序列号计算分为两部分,例如T86856-118
第一部分86856
的计算方式在12处
代码,上面已经给出注释了
第二部分118
的计算部分在格式的地方,即14处
的部分代码,以下是注解
seg020:0040149A mov edi, [ebp+__NAME_Length] ; edi = N.len
seg020:0040149D mov eax, edi ; eax = N.len
seg020:0040149F imul eax, [ebp+__Name_ASSIC_SUM] ; eax = N.len * N_Assic_Sum
seg020:004014A3 mov ecx, 64h ; ecx = 64(h)
seg020:004014A8 cdq
seg020:004014A9 idiv ecx ; eax = N.len * N_Assic_Sum / 64(h)
seg020:004014A9 ; edx = N.len * N_Assic_Sum % 64(h)
seg020:004014AB mov edi, edx ; edi = N.len * N_Assic_Sum % 64(h)
seg020:004014AD add edi, 30h ; edi = N.len * N_Assic_Sum % 64(h) + 30(h)
seg020:004014B0 push edi
//此处edi即为第二部分
但是仅仅这两部分求出来的字符串并不是对的,在比较函数sub_4012C2里面还有层转换,比较简单 注解如下
//注释中R就是上面求得的字符串,注意 循环变量 i 的是从 1 开始取值的,也就是说第一个字符并非一定是它代码中写死的'T',利用我们上面求法,第一个字符是`H`也是可以的
seg011:004012D3 movsx edi, byte ptr [edx+esi] ; edi = R[i]
seg011:004012D7 mov eax, edi ; eax = R[i]
seg011:004012D9 xor eax, 20h ; eax = R[i] xor 20(h)
seg011:004012DC mov ecx, 0Ah ; ecx = 0A(h)
seg011:004012E1 cdq
seg011:004012E2 idiv ecx ; eax = (R[i] xor 20(h)) / 0A(h)
seg011:004012E2 ; edx = (R[i] xor 20(h)) % 0A(h)
seg011:004012E4 mov edi, edx
seg011:004012E6 add edi, 30h ; edx = (R[i] xor 20(h)) % 0A(h) + 30(h)
注册机代码
#include <string.h>
#include <stdio.h>
#define MAXN 10000
char name[MAXN + 1];
char Serial[MAXN + 1];
char resFirst[MAXN + 1], fLen;
int resSecond;
char res[MAXN + 1];
//[ABCD..]
int getAlpha(int index) {
if(index < 0 || index >= 24) {
return 0;
}
return 'A' + index;
}
int main() {
while(gets(name)) {
int NLen = (int)strlen(name);
if(0 == NLen || '\n' == name[0]) {
break;
}
int NSum = 0;
fLen = 0;
for(int i = 0;i < NLen;i++) {
NSum += name[i];
}
//the first part of result
for(int i = 0;i < NLen;i++) {
int tmp = 0;
//(N[i] xor [ABC..][3 * i - 1])
tmp += name[i] ^ getAlpha(3 * i - 1);
//((Sum * i - Sum) xor FFFFFFFF) + (14D(h))
tmp += ((NSum * i - NSum) ^ 0xFFFFFFFF) + 0x14d;
//(NLen * (i + 3) * N[i])
tmp += NLen * (i + 3) * name[i];
//low
int low = tmp % 0xa + 0x30;
//res
int curRes = (low ^ 0xadac) * (i + 2) % 0xa + 0x30;
//restore the first part of result
resFirst[fLen++] = curRes;;
}
resFirst[fLen++] = '\0';
//the second part of result
resSecond = NLen * NSum % 0x64 + 0x30;
//join
sprintf(res, "T%s-%d\0", resFirst, resSecond);
//change
int RLen = strlen(res);
for(int i = 1;i < RLen;i++) {
res[i] = (res[i] ^ 0x20) % 0xa + 0x30;
}
printf("%s\n",res);
}
return 0;
}