APIS32NT v1.4
http://www.biosys.net/apis32
这个软件加过壳,壳中似有检测调试器断点的反跟踪代码。真正入口如下:
001B:004185BD MOV ESP,[ESP+14]
001B:004185C1 POP ESI
001B:004185C2 MOV EDI,ESI
001B:004185C4 ADD ESI,000015D7
001B:004185CA PUSH 05
001B:004185CC POP ECX
001B:004185CD REPZ MOVSB
001B:004185CF POPAD
001B:004185D0 POPF
001B:004185D2 JMP 004045A0 //入口地址
这里不关心脱壳,主要是讲如何制作它的注册机。
先用bpx GetDlgItemTextA设断,会中断两次,分别读入UserName和UserKey。然后用BPM或BPR监视你输入的假注册码,发现它将注册码写入注册表之后未再对读入的注册码作进一步的判断就弹出了“错误的注册码”对话框。这说明它肯定判断的是从注册表中读出来的注册码,即先写入注册表再读出来进行判断,用RegMon也可以证实这一点。于是用bpx
RegQueryValueExA设断,中断后按两下F12,就看见了判断的代码。首先是判断长度:
:0041EEEC 83EC2C
sub esp, 0000002C
:0041EEEF 53
push ebx
:0041EEF0 55
push ebp
:0041EEF1 56
push esi
:0041EEF2 57
push edi
:0041EEF3 6A50
push 00000050
:0041EEF5 68C0AE4000 push 0040AEC0
* Possible StringData Ref from Data Obj ->"UserKey"
|
:0041EEFA 6898864000 push 00408698
:0041EEFF E868030000 call 0041F26C
//RegQueryValueExA( )
:0041EF04 83C40C
add esp, 0000000C
:0041EF07 83F811
cmp eax, 00000011 //strlen(UserKey) >= 0x11?
:0041EF0A 7D0A
jge 0041EF16
:0041EF0C 33C0
xor eax, eax //bad guy
:0041EF0E 5F
pop edi
:0041EF0F 5E
pop esi
:0041EF10 5D
pop ebp
:0041EF11 5B
pop ebx
:0041EF12 83C42C
add esp, 0000002C
:0041EF15 C3
ret
:0041EF16 6A2F
push 0000002F
:0041EF18 68C0BE4000 push 0040BEC0
* Possible StringData Ref from Data Obj ->"UserName"
|
:0041EF1D 6888864000 push 00408688
:0041EF22 E845030000 call 0041F26C
//RegQueryValueExA( )
:0041EF27 83C40C
add esp, 0000000C
:0041EF2A 83F805
cmp eax, 00000005 //strlen(UserName) >= 5 ?
:0041EF2D 7D0A
jge 0041EF39
:0041EF2F 33C0
xor eax, eax //bad guy
:0041EF31 5F
pop edi
:0041EF32 5E
pop esi
:0041EF33 5D
pop ebp
:0041EF34 5B
pop ebx
:0041EF35 83C42C
add esp, 0000002C
:0041EF38 C3
ret
:0041EF39 6A1E
push 0000001E
:0041EF3B 8D442418 lea
eax, dword ptr [esp+18]
:0041EF3F 68C0BE4000 push 0040BEC0
:0041EF44 50
push eax
:0041EF45 E812070000 call 0041F65C
:0041EF4A 83C40C
add esp, 0000000C
:0041EF4D 8D4C2414 lea
ecx, dword ptr [esp+14]
:0041EF51 51
push ecx
:0041EF52 E845FFFFFF call 0041EE9C
//strupr(UserName)
:0041EF57 8D7C2418 lea
edi, dword ptr [esp+18]
:0041EF5B 83C9FF
or ecx, FFFFFFFF
:0041EF5E 33C0
xor eax, eax //bad guy
:0041EF60 83C404
add esp, 00000004
:0041EF63 F2
repnz
:0041EF64 AE
scasb
:0041EF65 F7D1
not ecx
:0041EF67 49
dec ecx
:0041EF68 83F905
cmp ecx, 00000005 //strlen(UserName) >= 5?
:0041EF6B 7308
jnb 0041EF75
:0041EF6D 5F
pop edi
:0041EF6E 5E
pop esi
:0041EF6F 5D
pop ebp
:0041EF70 5B
pop ebx
:0041EF71 83C42C
add esp, 0000002C
:0041EF74 C3
ret
接下来它要判断注册码的第8位,即UserKey[8]。如下,显然要满足条件
(UserKey[8] ^ 0x20) + 0xF3 = 0 (溢出的进位位不考虑)
从而可知注册码的第8个字符是横杠字符"-"。
:0041EF75 8A1DC8AE4000 mov bl, byte
ptr [0040AEC8] //取出UserKey[8]
...............................................
:0041EF85 80F320
xor bl, 20
...............................................
:0041EFCB 80C3F3
add bl, F3
...............................................
:0041EFD0 740A
je 0041EFDC
:0041EFD2 33C0
xor eax, eax //bad guy
:0041EFD4 5F
pop edi
:0041EFD5 5E
pop esi
:0041EFD6 5D
pop ebp
:0041EFD7 5B
pop ebx
:0041EFD8 83C42C
add esp, 0000002C
:0041EFDB C3
ret
注意到此时bl寄存器的值应为0,这bl要作为下面这个循环的循环控制变量。该循环先将注册码的另外16个字符转换成8个字节,比如注册码是"11223344-55667788",则得到的8个字节是
0x11, 0x22,0x33,0x44,0x55,0x66,0x77,0x88
用数组a[ ]表示这8个字节,下面的循环对应的C语句就是:
char a[8];
for(k = 0; k < 8; k++)
{
a[k] = UserKey[k] ^ (k + 0x50);
}
由于异或运算可逆,所以已知a[k]是可以求出UserKey[k]的。
:0041EFDC BEC1AE4000 mov esi,
0040AEC1
:0041EFE1 BFD4AE4000 mov edi,
0040AED4
:0041EFE6 57
push edi
:0041EFE7 E8E0010000 call 0041F1CC
//将注册码中的两个字符转换成一个字节
:0041EFEC 8ACB
mov cl, bl //(cl) = k
:0041EFEE 83C404
add esp, 00000004
:0041EFF1 80C150
add cl, 50 //(cl) = k + 0x50
:0041EFF4 83C702
add edi, 00000002 //指向后续的两个字符
:0041EFF7 32C1
xor al, cl // a[k] ^= k + 0x50
:0041EFF9 FEC3
inc bl // k++
:0041EFFB 8846FF
mov byte ptr [esi-01], al
:0041EFFE C60600
mov byte ptr [esi], 00
:0041F001 46
inc esi
:0041F002 80FB08
cmp bl, 08
:0041F005 72DF
jb 0041EFE6 //继续循环
之后对a[ ]进行不可逆变换,得到新的8个字节,如下:
* Referenced by a CALL at Address:
|:0041F011
|
:0041F1FC 53
push ebx
:0041F1FD 55
push ebp
:0041F1FE 8B6C2410 mov
ebp, dword ptr [esp+10]
:0041F202 56
push esi
:0041F203 57
push edi
:0041F204 8B7C2414 mov
edi, dword ptr [esp+14]
:0041F208 33C9
xor ecx, ecx //外循环控制变量初值
:0041F20A 2BFD
sub edi, ebp
:0041F20C 897C2418 mov
dword ptr [esp+18], edi
:0041F210 EB04
jmp 0041F216
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0041F260(C)
|
:0041F212 8B7C2418 mov
edi, dword ptr [esp+18]
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0041F210(U)
|
:0041F216 8D3429
lea esi, dword ptr [ecx+ebp]
:0041F219 33D2
xor edx, edx
:0041F21B B801000000 mov eax,
00000001 //累乘器的初值
:0041F220 C744241407000000 mov [esp+14], 00000007
//内循环控制变量的初值
:0041F228 8A1437
mov dl, byte ptr [edi+esi]//取出a[k]
:0041F22B 8BFA
mov edi, edx
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0041F24C(C)
|
:0041F22D 8BD7
mov edx, edi
:0041F22F 0FAFC2
imul eax, edx //累乘器乘以a[k]
:0041F232 3D99880000 cmp eax,
00008899
:0041F237 7E0A
jle 0041F243
:0041F239 99
cdq
:0041F23A BB99880000 mov ebx,
00008899
:0041F23F F7FB
idiv ebx //对0x8899求余
:0041F241 8BC2
mov eax, edx
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0041F237(C)
|
:0041F243 8B542414 mov
edx, dword ptr [esp+14]
:0041F247 4A
dec edx //内循环控制变量减1
:0041F248 89542414 mov
dword ptr [esp+14], edx
:0041F24C 75DF
jne 0041F22D //内循环,求乘幂
:0041F24E 99
cdq
:0041F24F BFBB000000 mov edi,
000000BB
:0041F254 F7FF
idiv edi //对0xBB求余
:0041F256 41
inc ecx
:0041F257 83F908
cmp ecx, 00000008 //8个字节均处理完?
:0041F25A 8816
mov byte ptr [esi], dl //保存余数
:0041F25C C6042900 mov
byte ptr [ecx+ebp], 00
:0041F260 7CB0
jl 0041F212 //外循环,共处理8个字节
:0041F262 5F
pop edi
:0041F263 5E
pop esi
:0041F264 5D
pop ebp
:0041F265 5B
pop ebx
:0041F266 C3
ret
上述循环可等价表示如下:
for(k = 0; k < 8; k++)
{
temp = 1L;
for(j = 7; j > 0; j--)
{
temp *= a[k];
if (temp > 0x00008899L)
{
temp %= 0x00008899L;
}
}
a[k] = temp % 0x000000BBL;
}
对于这个不可逆的变换,我们可以根据变换之后的8字节的值来用穷举的方法猜出变换之前的8个字节的值,最坏的情况只需要猜(256 * 8)次即可,当然也可能无解。优化一下的话最坏只要猜256次。
紧跟着它要从UserName得到一个新的长为8的串UserString。下面的处理等价于C语句:
char index = 0;
for(k = 0; k < 8; k++)
{
UserString[k] = UserName[index++];
index %= NameLen;
}
即如果UserName的长度大于等于8,则新串UserString就是UserName的前8个字符,否则把UserName串重复多次,然后取整个串的前8个字符即可。
:0041F016 8D7C241C lea
edi, dword ptr [esp+1C]
:0041F01A 83C9FF
or ecx, FFFFFFFF
:0041F01D 33C0
xor eax, eax
:0041F01F 83C408
add esp, 00000008
:0041F022 F2
repnz
:0041F023 AE
scasb
:0041F024 F7D1
not ecx
:0041F026 2BF9
sub edi, ecx
:0041F028 33ED
xor ebp, ebp
:0041F02A 8BD1
mov edx, ecx
:0041F02C 8BF7
mov esi, edi
:0041F02E BFDEAE4000 mov edi,
0040AEDE
:0041F033 C1E902
shr ecx, 02
:0041F036 F3
repz
:0041F037 A5
movsd
:0041F038 8BCA
mov ecx, edx
:0041F03A 83E103
and ecx, 00000003
:0041F03D F3
repz
:0041F03E A4
movsb
:0041F03F 8D7C2414 lea
edi, dword ptr [esp+14]
:0041F043 83C9FF
or ecx, FFFFFFFF
:0041F046 F2
repnz
:0041F047 AE
scasb
:0041F048 F7D1
not ecx
:0041F04A 49
dec ecx
:0041F04B 80F908
cmp cl, 08 //判UserName的长度
:0041F04E 884C2410 mov
byte ptr [esp+10], cl
:0041F052 732F
jnb 0041F083 //大于等于8则取前8个字符
:0041F054 8B542410 mov
edx, dword ptr [esp+10] //否则拼也要拼8个字符出来
:0041F058 8D7C2414 lea
edi, dword ptr [esp+14]
:0041F05C 81E2FF000000 and edx, 000000FF
:0041F062 83C9FF
or ecx, FFFFFFFF
:0041F065 81C2DEAE4000 add edx, 0040AEDE
:0041F06B F2
repnz
:0041F06C AE
scasb
:0041F06D F7D1
not ecx
:0041F06F 2BF9
sub edi, ecx
:0041F071 8BC1
mov eax, ecx
:0041F073 8BF7
mov esi, edi
:0041F075 8BFA
mov edi, edx
:0041F077 C1E902
shr ecx, 02
:0041F07A F3
repz
:0041F07B A5
movsd
:0041F07C 8BC8
mov ecx, eax
:0041F07E 83E103
and ecx, 00000003
:0041F081 F3
repz
:0041F082 A4
movsb
最后就是利用UserString[ ]和UserKey[ ]进行判断。这也是一个循环。
:0041F028 33ED
xor ebp, ebp
......................................................
:0041F083 C605E6AE400000 mov byte ptr [0040AEE6],
00
:0041F08A B9D4AE4000 mov ecx,
0040AED4
:0041F08F BE08000000 mov esi,
00000008 //循环控制变量k = 8
:0041F094 8A01
mov al, byte ptr [ecx] //取出a[k]
:0041F096 3C20
cmp al, 20 //和0x20相比,分两种情况处理
:0041F098 730E
jnb 0041F0A8
:0041F09A 33D2
xor edx, edx
:0041F09C 25FF000000 and eax,
000000FF
:0041F0A1 8A510A
mov dl, byte ptr [ecx+0A] //取出UserString[k]
:0041F0A4 0BD0
or edx, eax //与a[k]相或
:0041F0A6 EB0C
jmp 0041F0B4
:0041F0A8 33D2
xor edx, edx
:0041F0AA 25FF000000 and eax,
000000FF
:0041F0AF 8A510A
mov dl, byte ptr [ecx+0A] //取出UserString[k]
:0041F0B2 33D0
xor edx, eax //与a[k]异或
:0041F0B4 03EA
add ebp, edx //累加到ebp中
:0041F0B6 41
inc ecx
//下一个字节
:0041F0B7 4E
dec esi
//k--;
:0041F0B8 75DA
jne 0041F094 //继续循环
:0041F0BA 33C0
xor eax, eax
:0041F0BC 5F
pop edi
:0041F0BD 85ED
test ebp, ebp //累加和为0吗?
:0041F0BF 5E
pop esi
:0041F0C0 5D
pop ebp
:0041F0C1 0F94C0
sete al //为0则OK,否则bad
guy
:0041F0C4 5B
pop ebx
:0041F0C5 83C42C
add esp, 0000002C
:0041F0C8 C3
ret
上面这个循环可等价为:
long ebp = 0
for(k = 0; k < 8; k++)
{
if (a[k] < 0x20)
{
ebp += UserString[k] | a[k];
}
else
{
ebp += UserString[k] ^ a[k];
}
}
注册码正确与否的标准为:
if (ebp)
{
printf("Wrong key");
}
else
{
printf("good guy");
}
最后这个循环初看之下也比较难求逆,因为其中的或运算是不可逆的。但实际上我们只要让(a[k] < 0x20)这个条件永远不会满足即可避开这个或运算,此时上面这个循环简化为可逆的异或变换:
for(k = 0; k < 8; k++)
{
ebp += UserString[k] ^ a[k];
}
这样要想让累加和ebp为0,只要满足a[k] = UserString[k](k = 0, ..., 7)即可,由于UserString[k]是由可显示的用户名得来的,所以肯定满足(a[k]
>= 0x20)。至此得到注册机如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void main(void)
{
char UserName[128];
char UserString[8];
char a[8];
int index, len;
char k, j;
signed long temp, guess;
char SuccessCounter;
do
{
printf("Input your name(at least 5 chars):");
gets(UserName);
len = strlen(UserName);
} while(len < 5);
strupr(UserName);
index = 0;
for(k = 0; k < 8; k++)
{
a[k] = UserString[k] = UserName[index++];
index %= len;
}
//下面用穷举求出变换前的8个字节
SuccessCounter = 0;
for(k = 0; k < 8; k++)
{
for(guess = 0; guess <= 255; guess++)
{
temp = 1L;
for(j = 7; j > 0; j--)
{
temp *= guess;
if (temp > 0x00008899L)
{
temp %=
0x00008899L;
}
}
if ((temp % 0x000000BBL) == a[k])
{
SuccessCounter++;
a[k] = guess;
break;
}
}
}
//判断8个字节是否全部穷举成功
if (SuccessCounter != 8)
{
printf("Guess failed.\n");
exit(-1);
}
for(k = 0; k < 8; k++)
{
a[k] ^= (k + 0x50);
}
printf("Your registration key is: ");
for(k = 0; k < 4; k++)
{
- 标 题:API Spy for NT v1.4 (16千字)
- 作 者:1212
- 时 间:2000-10-25 20:13:05
- 链 接:http://bbs.pediy.com