API Spy for NT v1.4 (16千字)
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 Windows 95/98/NT/2000 《=寫得不好,初學者看一看吧
(4千字)2001-07-03APIWindows
- ePublisher Gold v1.4 (9千字)2001-01-15Go
- Steam Spy:2016年Steam新遊戲比例高達38%2016-12-02遊戲
- wince remote spy 原理2008-03-22REM
- WINDOWS NT2011-08-08Windows
- Spy工具到底如何使用2022-06-13
- 初學者(16) (2千字)2000-07-04
- NT安全指南2017-11-09
- Windows NT 核心2015-01-07Windows
- What's new of dubbogo v1.42020-03-26Go
- nt高可用部署2024-06-15
- 編寫驅動攔截NT的API實現隱藏檔案目錄 (轉)2007-08-17API
- 瞭解1688API介面測試 | 1688 API介面測試指南2023-11-22API
- 我的破解心得(5) (16千字)2001-03-13
- 1688詳情api介面2023-04-12API
- SSL/TLS部署最佳實踐v1.42015-07-31TLS
- Windows NT 是什麼?2015-06-18Windows
- 1688 API介面測試指南2023-11-22API
- network
spy eval 1.6破解教程【原創】2004-12-26
- 破解 密碼監聽器 v1.4 註冊碼,順便向BCG組織的各位兄弟問好! (12千字)2001-10-25密碼
- 把NT“趕盡殺絕”攻擊NT的一些技術(轉)2007-09-19
- 自然碼輸入系統2000 for NT/2K破解(PECOMPACT殼),見笑啦:)
(3千字)2000-06-24
- 安裝informix for nt時報錯2006-04-10ORM
- WallPaper Changer 2.5 for Windows 98/NT2000-07-04Windows
- (16) SpringCloud-Eureka的REST API及API擴充套件2022-10-24SpringGCCloudRESTAPI套件
- Django REST framework API 指南(16):過濾2018-03-18DjangoRESTFrameworkAPI
- 1688 API分享:抓取1688商品詳情頁資料2023-02-28API
- 尋找最快的Debian源 apt-spy2008-09-24APT
- Hasher for Mac(雜湊值生成器)v1.42021-12-04Mac
- Win98、NT和Linux的共存 NT的OS Loader ntosknl.exe (轉)2007-12-08Linux
- EmEditor v3.16破解過程 (9千字)2001-07-22
- 20 Differences Between Oracle on NT and Oracle on Unix2005-10-10Oracle
- Api32 keygen: learn how to use RSA
(4千字)2015-11-15API
- react16常見api以及原理剖析2019-09-24ReactAPI
- Java16的Vector API更好支援機器學習2021-12-09JavaAPI機器學習
- Python學習之路16-使用API2018-05-29PythonAPI
- 備忘錄四:Spring Boot + P6Spy2019-08-10Spring Boot
- 破解spy312.exe實戰! (953字)2000-06-02