#3 18-08-2006, 12:54 PM kienmanowar Offline Junior Member Posts: 19
Join Date: Mar 2006
Phân tích Asm và Code Keygen Phân tích ASM và code Keygen Lời nói đầu : Sau khi có được cái Keygen Form như trong bài viết trước đã hướng dẫn xây dựng. Trong bài viết tiếp theo này chúng ta sẽ tiến hành việc phân tích ASM của một soft để từ đó tiến hành công việc code keygen :”đầy gian nan nhưng cũng vô cùng thú vị” . Trong bài viết này, chúng ta sẽ sử dụng soft Wallpaper v1.2 mà chú đèn đã viết trong box Newbie (http://www.reaonline.net/forum/showthread.php?t=884) để phục vụ cho việc Keygen. Bài viết này sẽ đối chiếu từng đoạn code tương ứng được nói trong bài viết của chú đèn để mọi người tiện theo dõi. Oki , giờ chúng ta bắt tay vào làm việc. 1. Phân tích ASM của soft trong Ollydbg : - Để có thể phân tích được ASM của một soft chúng ta cần một chương trình Debug mà cụ thể ở đây là Ollydbg. Chúng ta mở Olly lên và load file Wallpaper.exe vào trong Olly. Tìm và set BP như trong bài viết của chú đèn đã nói. Sau khi đã có được điểm đặt BP tại 00404A35 , chúng ta Run chương trình bằng cách nhấn F9. Tiếp theo chúng tiến hành công việc nhập FU và FS vào trong màn hình Register của chương trình. Ở đây mình nhập như sau : FU : kienmanowar và FS : 11111982. Sau đó nhấn OK , chúng ta sẽ Ice tại điểm mà chúng ta đã set BP. Bước tiếp theo đây sẽ liên quan chặt chẽ tới bài của chú đèn. Chúng ta chú ý tới đoạn code sau : Code: 00404A35 00404A37 00404A3C 00404A3E 00404A40 00404A42 00404A43
. . . . . . .
8B3B MOV EDI,DWORD PTR DS:[EBX] ; <== FU B9 FFFFFFFF MOV ECX,-1 2BC0 SUB EAX,EAX F2:AE REPNE SCAS BYTE PTR ES:[EDI] F7D1 NOT ECX 49 DEC ECX ; <== Length(FU) 0F84 08010000 JE Wallpape.00404B51 ; <== If Length (FU) = 0 then Jump to Nag
/* Đoạn code này sẽ kiểm tra xem chúng ta có nhập Username không, nếu như không nhập (tức là Length = 0) thì sẽ nhảy tới Nag */ - Đoạn code tiếp theo : Code: 00404A49 00404A4C 00404A51 00404A53 00404A55 00404A57 00404A58
. . . . . . .
8B7E 5C MOV EDI,DWORD PTR DS:[ESI+5C] ; <== FS B9 FFFFFFFF MOV ECX,-1 2BC0 SUB EAX,EAX F2:AE REPNE SCAS BYTE PTR ES:[EDI] F7D1 NOT ECX 49 DEC ECX ; <== Length (FS) 0F84 F3000000 JE Wallpape.00404B51 ; <== If Length(FS) = 0 then Jump to Nag
/* Cũng tương tự đoạn code trên , đoạn này dùng để kiểm tra xem chúng ta có nhập Serial vào không */ - Oki , chúng ta đã nhập FU và FS vào rùi nên chúng ta sẽ vượt qua được hai đoạn kiểm tra này. Chúng ta sẽ đến một đoạn code hết sức quan trọng . Đó chính là đoạn Calculation : Code: 00404A5E 00404A60 00404A62 00404A67 00404A68 00404A6A
. . . . . .
6A 00 PUSH 0 8BCB MOV ECX,EBX E8 24D40200 CALL Wallpape.00431E8B 50 PUSH EAX 8BCE MOV ECX,ESI E8 21010000 CALL Wallpape.00404B90
; /Arg1 = 00000000 ;| ; \Wallpape.00431E8B ; /Arg1 ;| ; \Wallpape.00404B90 <==Calculation(trace into)
- Sau khi Trace Into vào trong hàm trên chúng ta sẽ găp một đoạn code như sau : Code:
00404B90 /$ 83EC 2C SUB ESP,2C 00404B93 |. C74424 00 050>MOV DWORD PTR SS:[ESP],5 ; <== Default1 = 5 00404B9B |. C74424 04 030>MOV DWORD PTR SS:[ESP+4],3 ; <== Default2 = 3 00404BA3 |. C74424 08 070>MOV DWORD PTR SS:[ESP+8],7 ; <== Default3 = 7 00404BAB |. C74424 0C 010>MOV DWORD PTR SS:[ESP+C],1 ; <== Default4 = 1 00404BB3 |. 53 PUSH EBX 00404BB4 |. 56 PUSH ESI 00404BB5 |. 57 PUSH EDI 00404BB6 |. BB 88583422 MOV EBX,22345888 ; <== Temp = 0x22345888 00404BBB |. C74424 20 020>MOV DWORD PTR SS:[ESP+20],2 ; <== Default5 = 2 00404BC3 |. C74424 24 060>MOV DWORD PTR SS:[ESP+24],6 ; <== Default6 = 6 00404BCB |. 55 PUSH EBP 00404BCC |. 6A 09 PUSH 9 00404BCE |. C74424 30 040>MOV DWORD PTR SS:[ESP+30],4 ; <== Default7 = 4 00404BD6 |. 33ED XOR EBP,EBP ; <== i = 0 00404BD8 |. 896C24 24 MOV DWORD PTR SS:[ESP+24],EBP ; <== Default8 = 0 /* Chúng ta thấy rằng đây chính là một quá trình khởi tạo giá trị ban đầu sẽ sử dụng để tính toán về sau. Việc đầu tiên nó sẽ khởi tạo các giá trị mặc định là 5 , 3, 7, 1, 0, 2, 6, 4 vào trong một mảng. Để cho tiện chúng ta sẽ gọi mảng chứa những phần tử này là reaDefault (int reaDefault[9] = {0x5, 0x3, 0x7, 0x1, 0x0, 0x2, 0x6, 0x4}; ). Tiếp theo chúng ta sẽ thấy chương trình khởi tạo một giá trị khác là 0x22345888 được cất giữ trong thanh ghi EBX , vậy chúng ta gọi một biến là Temp sẽ chứa giá trị này (unsigned long int Temp = 0x22345888 . Cuối cùng chúng ta để ý thấy có một lệnh XOR EBP, EBP , lệnh này làm cho giá trị của thanh ghi EBP = 0x0, cho nên ta khai báo một biến i và khởi gán là 0 (i = 0) . Việc tại sao lại đặt tên biến là i sẽ nói ngay dưới đây thôi */ - Sau khi trace qua đoạn code này, chúng ta sẽ đến một đoạn code tiếp theo: Code: 00404BE1 00404BE5 00404BE8 00404BEA 00404BEC 00404BF1 00404BF3 00404BF5 00404BF7 00404BF8
|. |. |. |. |. |. |. |. |. |.
8B5424 44 MOV EDX,DWORD PTR SS:[ESP+44] ; <== FU 83C4 04 ADD ESP,4 8BF0 MOV ESI,EAX 8BFA MOV EDI,EDX ; <== FU B9 FFFFFFFF MOV ECX,-1 2BC0 SUB EAX,EAX F2:AE REPNE SCAS BYTE PTR ES:[EDI] F7D1 NOT ECX 49 DEC ECX ; <== Length (FU) 74 1A JE SHORT Wallpape.00404C14
/* Đoạn code này chỉ là việc tính toán lại chiều dài của chuỗi FU mà chúng nhập vào , Length(FU) này sẽ được sử dụng trong quá trình lặp để tính toán tiếp phía dưới */ - Qua đoạn tính toán Length ở trên chúng ta sẽ đến đoạn code tính toán như sau : Code: 00404BFA 00404BFB 00404BFD 00404C02 00404C04 00404C09 00404C0B 00404C0D 00404C0F 00404C10 00404C12
|> /45 /INC EBP ; <== i = i + 1 |. |8BFA |MOV EDI,EDX ; <== FU |. |0FBE442A FF |MOVSX EAX,BYTE PTR DS:[EDX+EBP-1] ; <== FU[i] |. |03D8 |ADD EBX,EAX ; <== Temp = Temp + Fu[i] |. |B9 FFFFFFFF |MOV ECX,-1 |. |2BC0 |SUB EAX,EAX |. |F2:AE |REPNE SCAS BYTE PTR ES:[EDI] |. |F7D1 |NOT ECX |. |49 |DEC ECX ; <== Length (FU) |. |3BCD |CMP ECX,EBP ; <== While (Length(FU) > i) |.^\77 E6 \JA SHORT Wallpape.00404BFA ; <== Continue Loop
/* Tại đoạn code trên, chúng ta quan sát thấy quá trình tăng dần của thanh ghi EBP , sau đó giá trị này sẽ được đem đi so sánh với với chiều dài của FU đã đựơc cất trong ECX trong đoạn code tính Length ở trên.Đó chính là lí do tại sao chúng ta lại khai báo một biến i . Vòng lặp này sẽ thực hiện công việc như sau , nó sẽ lấy từng kí tự trong FU sau đó thực hiện một phép cộng dồn với biến Temp (Biến Temp lúc này đang lưu giá trị là 0x22345888). Vòng lặp này sẽ thực hiện bao nhiêu lần là tùy thuộc vào vào chiều dài của chuỗi FU. Kết quả sau khi tính toán được sẽ được biến Temp lưu giữ */ Code: Trong VC++ vòng lặp tính toán cộng dồn trên được viết như sau :
//Caculation i = 0; while ( i < LenUser ) { Temp = Temp + (reaName[i] & 0xFF); i++; } - Sau khi qua khỏi vòng lăp này chúng ta sẽ đến đoạn code sau : Code: 00404C18 00404C19 00404C1E 00404C1F
|. |. |. |.
53 PUSH EBX ; <== Temp 68 D0044400 PUSH Wallpape.004404D0 ; ASCII "%08X" 50 PUSH EAX E8 AC6B0100 CALL Wallpape.0041B7D0 ; <== TempString = Convert(Temp) to String
/* Đoạn code trên đây làm công việc chuyển đổi giá trị chứa trong biến Temp . Cụ thể là nó sẽ chuyển giá trị trong biến Temp sang chuỗi ở dạng Hexa như định dạng mà nó đã đưa ra trong lệnh PUSH (ASCII "%08X") . Ví dụ như sau : với chuỗi FU là kienmanowar , sau vòng lặp cộng dồn chúng ta có được biến Temp chứa giá trị sau : 0x22345D24 , sau quá trình chuyển đổi này ta sẽ có một chuỗi là “22345D24”. Chuỗi này chúng ta lưu trữ trong một biến là TempString (char reaTempString[64] = {0}; ) */ Code: Đoạn code chuyển đổi được viết lại trong VC++ như sau : //Convert Temp value to Hex String wsprintf(reaTempString, "%08X",Temp); - Trace tiếp chúng ta sẽ đến đoạn code dưới đây : Code: 00404C27 00404C29 00404C2D 00404C2E 00404C31 00404C35 00404C39
|. 33C0 XOR EAX,EAX ; <== i = 0 |> 8B4C84 10 /MOV ECX,DWORD PTR SS:[ESP+EAX*4+10]; <== j = Default [i] |. 40 |INC EAX ; <== i = i + 1 |. 83F8 08 |CMP EAX,8 |. 8A4C0C 30 |MOV CL,BYTE PTR SS:[ESP+ECX+30] ; <== TempString[j] |. 884C06 FF |MOV BYTE PTR DS:[ESI+EAX-1],CL ; <== reaTempSerial[i] = TempString[j] |.^ 72 EE \JB SHORT Wallpape.00404C29
/* Trong đoạn code trên chúng ta nhận thấy lại có quá trình tăng dần của EAX cho nên ta lại khởi gán biến i = 0 . Vòng lặp này làm nhiệm vụ như sau, nó sẽ lấy các giá trị trong biến reaTempString theo những vị trị đã được khai báo trong biến reaDefault. Mà để lấy được các giá trị trong biến reaDefault chúng cần một biến j . Do đó ta khai báo thêm một biến j . Vòng lặp này sẽ được thực hiện 8 lần , để cho ra một chuỗi chứa trong reaTempSerial , mà thực chất chuỗi này chỉ là xáo trộn các vị trí trong reaTempString mà thôi. Thực ra sau quá trình này thì reaTempSerial chính là Real Serial , nhưng để dễ hiểu ta cứ gọi là reaTempSerial */ Code: Đoạn code tương ứng được viết lại trong VC++ : //Creat reaTempSerial i = 0; while (i < 8) { j = reaDefault[i]; reaTempSerial[i] = reaTempString[j]; i++; } - Cuối cùng sau khi chúng ta qua khỏi vòng lặp này chúng ta sẽ đi đến đích cuối cùng : Code: 00404C3B |. 8BC6
MOV EAX,ESI
; <== Right Serial
/* Chúng ta sẽ thấy được Right Serial ngay tại câu lệnh trên */ Code:
Đoạn code tương ứng trong VC++ : wsprintf(reaSerial,reaTempSerial); 2. Code Keygen trong VC++ : Dựa vào tất cả những gì đã phân tích rất chi tiết ở trên , chúng ta chỉ việc ghép lại để có một đoạn code Keygen hoàn chỉnh như sau : Code: void CKEYGENDlg::OnGenerate() { // TODO: Add your control notification handler code here char reaName[64]={0}; char reaSerial[64]={0}; char reaTempString[64] = {0}; char reaTempSerial[64] = {0}; unsigned long int Temp = 0x22345888; int reaDefault[9] = {0x5, 0x3, 0x7, 0x1, 0x0, 0x2, 0x6, 0x4}; int i=0,j=0, LenUser=0; LenUser=GetDlgItemText(IDC_NAME,reaName,128); if (LenUser < 1 || LenUser > 64) { MessageBox(" ----------===== Your name atleast 1 chart =====---------- \n\n---------===== But not over than 64 charts =====---------- ","Hey !! Please input your name again !! "); } else { // Calculation i = 0; while ( i < LenUser ) { Temp = Temp + (reaName[i] & 0xFF); i++; } //Convert Temp value to Hex String wsprintf(reaTempString, "%08X",Temp); //Creat reaTempSerial i = 0; while (i < 8) { j = reaDefault[i]; reaTempSerial[i] = reaTempString[j]; i++; } wsprintf(reaSerial,reaTempSerial); } SetDlgItemText(IDC_SERIAL,reaSerial); } Trên đây một trong số các cách thức để một cracker thực hiện công việc code keygen . Sau khi bạn đọc xong bài viết này , bạn có cảm thấy dễ dàng hơn không? . Thực sự Keygen nhiều lúc không đơn giản , nó đòi hỏi cả một nghệ thuật để làm sao đoạn code ngắn gọn , trong sáng nhất. Trong 4room này thì anh Moon là người có khả năng đó (và còn nhiều người khác nữa trong đội ngũ BQT của REA :wub: ). Em chỉ là Super Newbie học đòi theo anh Moon mà thôi. Hi vọng với bài viết với tinh thần truyền tải những gì hết sức basic như trên , các bạn sẽ định hình được phần nào công việc code Keygen của mình. @Moon : Có thể em nói lời cảm ơn anh quá nhiều nhưng thực sự không có anh , em chắc chẳng bao giờ biết đến cái Keygen nó là cài gì. Thanx anh đã góp ý , động viên và ủng hộ em :wub: . 03/03/2005 __________________
Có những con người mới đôi ba tuổi đời..... Cuộc sống mới bắt đầu đã cho rằng mình thấu hết .. Làm được gì ? Và đã có gì ? Chỉ học đòi thói chê bai.... Cần nhìn lại về chính thân mình .... Hình hài chỉ là con số 0 ....... Hình hài chỉ là con số 0..... Con số 0..!!! http://img.photobucket.com/albums/v5...nmanowar/6.gif kienmanowar View Public Profile Send a private message to kienmanowar Find all posts by kienmanowar Add kienmanowar to Your Buddy List
Kĩ thuật Internal Keygen!!
#4 18-08-2006, 01:00 PM kienmanowar Offline Junior Member Posts: 19
Join Date: Mar 2006
Kĩ thuật Internal Keygen!! Kĩ thuật Internal Keygen
I. Lời giới thiệu : Nếu như các bạn đã từng đọc qua loạt tut về Olly của ARTEAM thì chắc chắn sẽ bắt gặp cụm từ “Internal Keygen” . Trong một số tài liệu khác cụm từ này có thể xuất hiện dưới các tên khác là “Keygen Injection” hay “Code Injection”. Vậy cụm từ này có nghĩa là gì ? Thực chất thì “Internal Keygen” là một kĩ thuật giúp Cracker lợi dụng chính chương trình mà họ muốn crack , biến chương trình trở thành một Keygen. Nói như vậy vẫn có vẻ hơi mơ hồ và xa lạ !! Như các bạn đã biết , trong quá trình crack soft mỗi User Name nhập vào thì chương trình mà chúng ta crack sẽ lại sinh ra một số Serial khác nhau ứng với User Name đó. Do đó, nếu như Serial mà chúng ta tìm được là chính xác thì việc đăng kí diễn ra hoàn toàn bình thường. Nhưng do mỗi Serial là khác nhau với mỗi User Name , do đó nếu như có một người nào đó muốn sử dụng chương trình thì bản thân họ phải tự tìm lấy Serial tương ứng với User Name của họ nhập vào. Điều này là rất bất tiện cho những người chẳng biết gì về Crack. Khi bạn là một Cracker, thì tôi tin rằng các bạn cũng đã biết Keygen là cái gì và làm thế nào để có một Keygen. Keygen thường áp dụng cho các chương trình sử dụng cơ chế bảo vệ Name/Serial. Đối với các Cracker thì việc Keygen hoàn chỉnh được một soft đỏi hỏi họ phải biết phân tích thuật toán mà coder sử dụng để cho ra Serial (đôi khi cũng phải đoán xem ý đồ của coder, nhắm xem coder định làm gì để chặn đầu).Nhưng để phân tích và code ra một Keygen sẽ phải tốn khá nhiều thời gian và công sức, vì mỗi chương trình sẽ có những cơ chế tính toán khác nhau từ dễ đến khó để làm nản lòng các Cracker. Do đó, kĩ thuật “Internal Keygen” ra đời và được các Cracker sử dụng để tiết kiệm thời gian và công sức nhưng vẫn đem lại hiệu quả nhất định. Họ sẽ dùng kĩ thuật này để giải quyết bài toán với mỗi User Name sẽ có một Serial (tương đương với việc code một Keygen) bằng cách lợi dụng quá trình tính toán của chương trình để biến chương trình trở thành một Keygen. Như đã nói ở trên, Cracker sẽ lợi dụng chương trình để biến nó thành Keygen ! Vậy họ sẽ lợi dụng cái gì ? Và làm thế nào để lợi dụng ? Trong quá trình đăng kí sử dụng hợp pháp một chương trình , thì theo thường lệ chương trình sẽ yêu cầu chúng ta nhập User Name và Serial vào các text box . Khi chúng ta nhấn Register / OK thì có nghĩa là chúng ta muốn chương trình sẽ chấp nhận cho phép chúng ta đăng kí với những gì chúng ta đã nhập vào. Nếu như những gì chúng ta nhập vào là đúng thì một Message Box sẽ hiện ra thông báo đại loại như “Thank You” or “Successful” thông báo cho chúng ta biết là chương trình đồng ý với những gì chúng ta nhập vào và dĩ nhiên chúng ta đã trở thành người dùng hợp pháp : ). Nhưng trong trường hợp ngược lại , khi những gì chúng ta nhập vào là sai, thì lẽ thường tình đã có “Thank you” thì chắc chắn sẽ có một Message Box khác làm công việc ngược lại là thông báo “ Wrong Code” or “ Invalid Serial” bắn ra , báo cho ta biết đừng có mà cố gắng nữa làm gì . Càng cố càng sai thôi!! : ). Vậy cái mà Cracker lợi dụng ở đây chính là cái Message Box thông báo khi chúng ta nhập sai đó, thay vì việc nó Pop up ra thông báo “Wrong Code” or “Invalid Serial” thì Cracker sẽ cho chương trình Pop up ra cái Real Serial của chương trình sau quá trình tính toán. Mà cái Real Serial này dùng để làm gì tiếp theo chì chắc chắn không cần nói thì ai cũng biết phải làm gì . Vậy để lợi dụng được nhược điểm này, không còn một cách nào khác là Cracker phải dùng một chương trình Debugger để Debug chương trình . Họ sẽ tìm xem chương trình sẽ sinh ra Serial ở đâu và lưu vào vị trí nào trong bộ
nhớ. Song song với quá trình này thì họ cũng phải biết cái hàm Message Box thông báo sai nằm ở đâu. Khi đã có được 2 điều kiện cần ở trên , họ sẽ tiến hành sửa code để cho chương trình sẽ không bắn ra thông báo sai mà sẽ bắn ra cái Real Serial mà lẽ ra phải nhập vào tương ứng với User Name chứ không phải là một Fake Serial nào đó mà chúng ta nhập vào ... Để dễ hình dung hơn, các bạn hãy quan sát một đoạn code đơn giản sau đây : Original code: Code: CMP EAX, EBX JNZ WrongCode .... .... WrongCode: ..
; EAX=our serial | EBX=correct serial ; Not the same? Then display "Wrong Code" ; This is where the "Thank You" stuff is.
; Shoot Nag “Wrong Code”
Code after change: Code: CMP EAX, EBX JNZ WrongCode .. .. .. .. WrongCode: .. .. Call xxxx ... ...
; EAX=our serial | EBX=correct serial ; Not the same? Then run the built-in keygen. ; This is where the "Thank You" stuff is. ; All the other code of the program. ; We place our keygen at a convenient location. ; ; Push Right serial ; Display the RIGHT serial. ; Continue the program Normal
Như các bạn đã thấy trên đây , mọi việc diễn ra rất đơn giản không có gì là phức tạp cả. Vậy tóm lại để có được một “Internal Keygen” chúng ta sẽ phải trải qua các bước sau đây : Quote: 1. Chúng ta phải biết được Real Serial được tạo ra ở đâu và được lưu vào vị trí nào trong bộ nhớ. 2. Chúng ta phải biết vị trí của hàm Message Box bắn ra thông báo sai ở đâu. 3. Cuối cùng chúng ta phải chỉnh sửa hàm Message Box thế nào khiến cho nó hiện ra Real Serial thay vì hiện ra thông báo sai. Oki , tổng quát về kĩ thuật “Internal Keygen” là như thế, không có gì là “cao siêu” cả. Bước tiếp theo trong bài viết này tôi sẽ hướng dẫn các bạn một làm một ví dụ nho nhỏ để các bạn hiểu rõ hơn về kĩ thuật này. II. Thực hành : Trong phần giới thiệu ở trên, tôi đã chỉ cho các bạn các bước cơ bản nhất để tiến hành áp dụng kĩ thuật “Internal Keygen”. Trong phần tiếp theo này, tôi sẽ cùng với các bạn làm một ví dụ nho nhỏ để áp dụng kĩ thuật này. Một phần là để thay đổi không khí, một phần là để các bạn có cái nhìn trực quan hơn về cách thực hiện cũng như hiểu rõ thêm về các bước phải làm. Oki, chúng ta bắt đầu nhé!. Ví dụ mà tôi sẽ sử dụng sau đây chính là file Wallpaper.exe (Down load here : http://www.reaonline.net/forum/showthread.php?t=884) đã được đề cập trong tut “Phân tích ASM và Code Keygen”(Link to this tut : http://www.reaonline.net/forum/showthread.php?t=921). Oki, chúng ta sẽ tiến hành phân tích chương trình này. Đầu tiên chúng ta chạy thử chương trình, ngay lập tức chương trình sẽ hiện ra một hộp thoại “Wallpaper Registration” yêu cầu chúng ta nhập User Name và Serial vào. Chúng ta chưa có gì trong tay cả nên cứ việc nhập đại vào hai text box đó ( Vd : kienmanowar & 11111982 ). Sau đó nhấn OK, khi chúng ta nhấn vào nút này thì ngay lập tức chương trình sẽ tính toán lại Serial dựa trên User Name mà chúng ta đưa vào , sau đó so sánh Serial mà nó tính toán được với cái Serial mà chúng ta nhập vào (quá trình này xảy ra rất nhanh). Nếu trùng thì Ok , còn không nó sẽ Pop up ra thông báo sau :
Như những gì tôi đã nói với các bạn ở trên về kĩ thuật “Internal Keygen”, chúng ta sẽ biến chương trình này thành một Keygen để thay vì việc nó hiện ra cái thông báo sai như các bạn nhìn thấy ở trên, nó sẽ phải hiện ra cái Serial thật mà nó tạo ra cho chúng ta thấy. Oki , mục đích và ý tưởng là như thế , tiếp theo chúng ta sẽ hiện thực hóa ý tưởng của chúng ta. Đầu tiên mở Olly lên , load file Wallpaper.exe vào Olly, việc tìm ra điểm đặt BP của chương trình này đã được tôi nói tới trong tut “Phân tích ASM và Code Keygen” rồi , các bạn có thể xem lại hoặc tự làm. Việc tìm ra điểm đặt BP của soft này hết sức dễ dàng. Oki, khi có được BP, nhấn F9 để run chương trình, tiến hành nhập thông tin đăng kí gồm FU & FS và nhấn Ok . Olly sẽ Ice tại điểm mà chúng ta đặt BP. Tiến hành Trace(F8) chúng ta sẽ có như sau : Code: 00404A35 00404A37 00404A3C 00404A3E 00404A40 00404A42 00404A43
. . . . . . .
8B3B MOV EDI, DWORD PTR DS:[EBX] ; <== Fake User B9 FFFFFFFF MOV ECX, -1 2BC0 SUB EAX, EAX F2:AE REPNE SCAS BYTE PTR ES:[EDI] F7D1 NOT ECX 49 DEC ECX ; <== Length(FU) 0F84 08010000 JE Wallpape.00404B51 ; <== If Length(FU) == 0 then Jump to Nag
. . . . . . .
8B7E 5C MOV EDI, DWORD PTR DS:[ESI+5C] ; <== Fake Serial B9 FFFFFFFF MOV ECX, -1 2BC0 SUB EAX, EAX F2:AE REPNE SCAS BYTE PTR ES:[EDI] F7D1 NOT ECX 49 DEC ECX ; <== Length(FS) 0F84 F3000000 JE Wallpape.00404B51 ; <== If Length(FS)==0 then Jump to Nag
Tiếp tục : Code: 00404A49 00404A4C 00404A51 00404A53 00404A55 00404A57 00404A58
Oki, do chúng ta đã nhập đầy đủ các thông số mà chương trình yêu cầu , nên chúng ta đã vượt qua được đoạn code này. Tiếp theo chúng ta sẽ đến một đoạn code rất quan trọng , đó chính là đoạn code tạo ra Real Serial của chương trình : Code: 00404A5E 00404A60 00404A62 00404A67 00404A68 00404A6A
. . . . . .
6A 00 PUSH 0 ; /Arg1 = 00000000 8BCB MOV ECX, EBX ;| E8 24D40200 CALL Wallpape.00431E8B ; \Wallpape.00431E8B 50 PUSH EAX ; /Arg1 8BCE MOV ECX, ESI ;| E8 21010000 CALL Wallpape.00404B90 ; \Wallpape.00404B90 ;<==(Calculation)
Do đặc thù của kĩ thuật Internal Keygen cho nên chúng ta không cần phải biết và không cần quan tâm tới giải thuật hay thuật toán mà chương trình dùng để tính toán ra Real Serial, mà chúng ta chỉ cần biết vị trí của hàm tính toán ra Real Serial mà thôi. Như vậy , theo yêu cầu này thì chúng ta đã tìm ra được vị trí của hàm này rồi đó, chính là ở 00404A6A. Nhấn F8 tiếp tục cho tới hàm này và Trace qua hàm này. Sau khi Trace qua hàm này thì ngay lập tức chúng ta sẽ nhìn thấy Real Serial hiện ra trong cửa sổ Registers :
Cụ thể ở đây , chúng ta sẽ thấy Real Serial mà chương trình sinh ra sau hàm CALL ở 00404A6A được lưu vào thanh ghi EAX, cụ thể là tại địa chỉ 008E1210. Right Click lên thanh ghi EAX và chọn Follow In Dump chúng ta có được như sau :
Khà khà ...Như vậy bước đầu tiên trong 3 bước của kĩ thuật “Internal Keygen” đã được chúng ta thực hiện xong. Bước thứ 2 là chúng ta phải tìm ra chỗ của hàm Message Box “Invalid Key” ở đâu ? Để thực hiện được điều này, chúng ta nhìn xuống dưới đoạn code tính toán Serial : Code: 00404A6A . E8 21010000 CALL
Wallpape.00404B90
; \Wallpape.00404B90 ;<==(Calculation)
chúng ta sẽ thấy như sau : Code: 00404A97 > \85C0 TEST EAX, EAX 00404A99 . 0F85 B2000000 JNZ Wallpape.00404B51 Click chuột vào hàm JNZ này , là lần theo hướng mũi tên mà nó chỉ chúng ta sẽ tới được vị trí mà chúng ta cần tìm. (Thực ra khi các bạn đi tìm điểm đặt BP thì các bạn cũng đã phải thông qua bước tìm thông báo này rồi) : Code: 00404B5A 00404B5F 00404B64 00404B69 00404B6E 00404B70
. 68 09040000 PUSH . 68 00000400 PUSH 68 B8024400 PUSH 68 C4044400 PUSH > 6A 00 PUSH 0 . FF15 84994400 CALL
409 40000 Wallpape.004402B8 Wallpape.004404C4
; ASCII "ERROR" ; ASCII "Invalid key" ; |hOwner = NULL NEAR DWORD PTR DS:[<&USER32.>; \MessageBoxExA
Vậy là bước thứ 2 trong cũng đã được thực hiện xong một cách rất dễ dàng . Tiếp theo tôi muốn nói với bạn một chút về hàm MessageBox: Code: int MessageBox( HWND hWnd, // handle of owner window LPCTSTR lpText, // address of text in message box LPCTSTR lpCaption,// address of title of message box UINT uType // style of message box ); Hàm MessageBox là một hàm nội tại của Windows. Điều này có nghĩa là , khi bạn là người lập trình , bạn viết một chương trình và thay vì việc bạn phải tự thân vận động để ngồi code ra một MessageBox của riêng chúng ta thì chúng ta có thể dùng hàm MessageBox sẵn có của Windows . Hàm MessageBox này sẽ tạo , hiển thị và thi hành một message box. Hàm MessageBox sẽ được gọi giống như bất cứ một hàm nào , và để sử dụng được hàm này thì điều đầu tiên chúng ta cần phải truyền biến cho nó hay còn gọi là đối số. Các đối số này đã được liệt kê như các bạn đã thấy ở trên. Việc tìm hiểu thêm về việc làm thế nào một MessageBox được tạo ra sẽ giúp cho chúng ta có những hiểu biết cơ bản để vận dụng nó vào công việc. (Để biết thêm chi tiết về hàm này các bạn có thể tham khảo trong tài liệu MSDN hoặc trong file win32.hlp ). Như trên đã nói , chúng ta vừa trải qua hai bước : bước thứ nhất là tìm vị trí của hàm tạo ra Serial và vị trí của Serial được lưu tại thanh ghi EAX, bước thứ 2 là tìm ra vị trí của hàm MessageBox thông báo sai. Công việc tiếp theo và cũng quan trọng không kém 2 bước trước là bước thứ 3 : Edit code. Như các bạn thấy, trong hàm MessageBox ở trên các đối số sẽ lần lượt được truyền vào theo cơ chế của Stack . Tức là đối số nào ở vị trí đầu tiên thì sẽ được truyền vào cuối cùng để khi lời gọi hàm MessageBox xuất hiện thì các đối số này sẽ được lấy ra lần lượt theo thứ tự ngược lại. Và trong ví dụ trên các bạn sẽ quan sát rất rõ điều này thông qua các lệnh Push. Để có cái nhìn thực tế hơn nữa các bạn có thể Trace để tới đoạn code bắt đầu của quá trình truyền đối số cho hàm MessageBox, trong cửa sổ Stack Window các bạn sẽ thấy như sau :
Khà khà.... các bạn có thấy gì không , khi chúng ta nhập thông tin đăng kí vào. Chương trình tiến hành tính toán và sẽ kiểm tra Serial ta nhập vào với Serial mà nó tính toán. Do chúng ta nhập Serial sai, nên sau vài bước kiểm tra chương trình sẽ Jump tới đoạn code để Pop up ra thông báo như chúng ta thấy . Các đối số được đưa vào ở đây cho phép chúng ta hiểu được có những thông tin như sau : Tiêu đề của hộp thoại là : “ERROR” Dòng text sẽ hiển thị trên hộp thoại là : “Invalid key” v...v..
Vậy chúng ta sẽ phải làm gì ? Việc chúng ta phải làm là ép hàm MessageBox này thay vì nó sẽ hiện ra thông báo “Invalid key” thì ta buộc nó phải hiện Real Serial. Chúng ta đã biết địa chỉ của hàm tính toán Serial là tại 00404A6A và khi Trace qua hàm này ta nhận được Real Serial nắm ở thanh ghi EAX . Ta cũng đã biết được điểm bắt đầu của hàm MessageBox là ở địa chỉ 00404B5A. Vậy ta sẽ sửa câu lệnh ngay phía dưới của hàm tính toán để cho chương trình sẽ nhảy tới điểm bắt đầu của hàm MessageBox. Do đó tại câu lệnh : Code: 00404A6F
8B56 5C
MOV
EDX, DWORD PTR DS:[ESI+5C] ; <== Fake Serial
chúng ta nhấn Space hoặc Right Click và chọn Assemble. Olly sẽ hiện ra hộp thoại Assemble cho phép chúng ta Edit lại đoạn code. Chúng ta sẽ thay lệnh trên bằng lệnh nhảy tới điểm bắt đầu của hàm MessageBox như sau, trong hộp thoại chúng ta gõ : Code: JMP
00404B5A
nhấn Enter , màn hình Olly sẽ hiện như sau :
__________________ Có những con người mới đôi ba tuổi đời..... Cuộc sống mới bắt đầu đã cho rằng mình thấu hết .. Làm được gì ? Và đã có gì ? Chỉ học đòi thói chê bai.... Cần nhìn lại về chính thân mình .... Hình hài chỉ là con số 0 ....... Hình hài chỉ là con số 0..... Con số 0..!!! http://img.photobucket.com/albums/v5...nmanowar/6.gif kienmanowar View Public Profile Send a private message to kienmanowar Find all posts by kienmanowar Add kienmanowar to Your Buddy List
#5 18-08-2006, 01:01 PM kienmanowar Offline Junior Member Posts: 19
Join Date: Mar 2006
Nhấn F8 để Trace đến hàm gọi MessageBox, quan sát cửa sổ Stack các bạn sẽ thấy như sau :
Ke..Ke.. Nhìn vào đây các bạn có thể biết được khi chúng ta Trace qua hàm MessageBox thì nó sẽ hiển thị cho chúng ta cái gì rồi phải không. Đó chính là thứ mà từ đầu đến giờ chúng ta đã phải tốn nhiều giấy mực để thực hiện. Thật đáng đồng tiền bát gạo !!
Cuối cùng một công việc không thể thiếu đó là chúng ta hãy lưu lại tất cả những gì chúng ta vừa mới thực hiện . Ngay tai màn hình Olly , Right Click vào một vị trí bất kì trên màn hình Code Window và chọn Copy to Executable
--> All modifications. Một hộp thọai hiện ra , ta chọn Copy All. Một màn hình mới xuất hiện , tại màn hình này chúng ta Right Click và chọn Save file. Đặt một cái tên bất kì mà chúng ta muốn và nhấn Save. Vậy là chúng ta đã có được một Internal Keygen rồi đó , đem phân phát cho mọi người thôi. : ) PS : Tại sao lại phải lưu vào , là vì trong trường hợp file chúng ta crack và save lại có kích thước nhỏ thì việc phát tán nó cho người khác rất dễ dàng. Nhưng nếu như file đó có dung lượng lớn thì chúng ta sẽ dùng file gốc và file đã được chỉnh sửa này để tạo ra một Patch file có kích thước nhỏ hơn để phân phát cho User. III. Tổng kết : Qua tất cả những gì tôi vừa trình bày ở trên , các bạn đã hiểu được phần nào kĩ thuật Internal Keygen là gì? Thực chất nó là kĩ thuật cho phép Cracker không phải ngồi lọ mọ nghiền ngẫm thuật toán để code ra một Keygen hoàn chỉnh , nó cho phép Cracker lợi dụng chính hàm MessageBox thông báo sai của chương trình , biến hàm này thành một hàm phục vụ cho việc hiển thị Real Serial mà chương trình đã tính toán ra. Mà để thực hiện được điều đó thì chúng ta phải biết Real Serial nằm ở đâu và hàm gọi thông báo sai nằm ở đâu để mà áp dụng kĩ thuật này một cách hợp lý và hiệu quả nhất. Hi vọng với những gì cơ bản nhất mà tôi đã trình bày ở trên sẽ giúp ích cho các bạn phần nào trên con đường trở thành Craker 23/04/2005 -== kienmanowar ==__________________ Có những con người mới đôi ba tuổi đời..... Cuộc sống mới bắt đầu đã cho rằng mình thấu hết .. Làm được gì ? Và đã có gì ? Chỉ học đòi thói chê bai.... Cần nhìn lại về chính thân mình .... Hình hài chỉ là con số 0 ....... Hình hài chỉ là con số 0..... Con số 0..!!! http://img.photobucket.com/albums/v5...nmanowar/6.gif kienmanowar View Public Profile Send a private message to kienmanowar Find all posts by kienmanowar Add kienmanowar to Your Buddy List
Ví dụ 2!
#6 18-08-2006, 01:02 PM kienmanowar Offline Junior Member Posts: 19
Join Date: Mar 2006
Ví dụ 2! b. Ví dụ 2 : Phần tiếp theo , chúng ta sẽ làm thêm một ví dụ nữa. Ở ví dụ này chúng ta sẽ thực hành trên một crackme. Cách thức thực hiện trên crackme cũng hoàn toàn tương tự như khi chúng ta thực hiện đối với Software ở trên. Oki, chúng ta bắt đầu : ). Chạy thử Crackme, tiến hành nhập thông tin và nhấn nút “Check It!”. Chằng thấy có Nag nào bắn ra cả.
Nhấn nút “Give Up!” để exit .Mở Ollydbg lên và load crackme vào (crackme này được code bằng Microsoft Visual C++ 6.0). Sau khi load xong , dùng chức năng Search For \ All referenced text strings để tìm kiếm thông tin. Chúng ta có được kết quả như sau :
Double Click tại 00401639 chúng ta sẽ quay trở lại màn hình chính của Ollydbg. Dịch lên một chút , ta đặt BP tại 004015BC. Nhấn F9 để Run , tiến hành nhập lại Fake Information. Sau đó nhấn “Check It!”, Ollydbg sẽ Ice tại điểm đặt BP. Nhấn F8 để Trace, chúng ta sẽ có như sau :
Như mọi người nhìn thấy thì Crackme này sẽ tiến hành một số quá trình kiểm tra như sau : Độ dài của UserName mà chúng ta nhập vào phải thỏa mãn điều kiện là 5 < Length(FakeUser) < 20. Bên cạnh đó thì chiều dài của FakeSerial mà chúng ta nhập vào cũng phải bằng với chiều dài của FakeUser. Nếu như chúng ta nhập đúng các thông tin thỏa mãn tất cả các điều kiện trên thì chúng ta sẽ vượt qua được quá trình check này. Sau đoạn check ở trên chúng ta sẽ gặp một vòng lặp tính toán ra Real Serial của Crackme . Cụ thể như sau :
Do đặc thù của kĩ thuật Internal Keygen như đã nói ở trên , ở đây ta không cần quan tâm đến quá trình tính toán Serial như thế nào. Ta chỉ cần biết rằng đây chính là vòng lặp dùng cho việc tính ra Real Serial và giá trị trong thanh ghi AL tại địa chỉ 00401629 chính là giá trị RealSerial sau mỗi quá trình tính toán của vòng lặp . Vòng lặp này có số lần lặp phụ thuộc vào chiều dài của chuỗi UserName mà chúng ta nhập vào. Nhưng mọi người để ý rằng, ngay tại lần lặp đầu tiên giá trị thứ nhất của RealSerial sẽ được tính toán , sau đó giá trị này được lưu vào thanh ghi AL. Tiếp theo , tại địa chỉ 00401629 là quá trình so sánh kí tự đầu tiên trong chuỗi Fake Serial mà chúng ta nhập vào với giá trị Real Serial lưu trong thanh ghi AL. Nếu 2 giá trị này khác nhau thì ngay lập tức sẽ có một lệnh nhảy thoát ra khỏi vòng lặp tính toán. Vậy tức là nếu như ngay từ đầu kết quả so sánh này đã sai thì chúng ta luôn bị out khỏi vòng lặp và sẽ không thể biết hểt được giá trị tiếp theo sẽ sinh ra của chuỗi Real Serial. Đây là điều mà chúng ta không muốn! Vậy hướng giải quyết ở đây là thế nào? Để ý rằng dưới câu lệnh : Code: 00401629 . 38040A
CMP
BYTE PTR DS:[EDX+ECX], AL
; <== FakeSerial[i] = Temp ?
là một lệnh nhảy làm cho chúng ta out ra khỏi vòng lặp tính toán : Code: 0040162C . /75 1C
JNZ
SHORT Rith_Cra.0040164A
; <== If not, Jump to Bad boy
nhưng chúng ta thấy rằng, chuỗi FakeSerial của chúng ta được đưa vào thanh ghi EDX. Mà giá trị trong BYTE PTR DS:[EDX+ECX] chính là kí tự đầu tiên của chuỗi Fake Serial(lúc đầu ECX = 0). Quan sát thấy , ngay dưới lệnh nhảy ở trên là lệnh : Code: 0040162E . 41
INC
ECX
; <== i ++
Vậy ta có thể kết luận một điều rất quan trọng như sau : Nếu như giá trị trong BYTE PTR DS:[EDX+ECX] = AL (tức là giá trị đầu tiên của chuỗi FakeSerial mà chúng ta nhập vào bằng với giá trị RealSerial của quá trình tính toán) thì chúng ta sẽ không bị out khỏi vòng lặp tại lệnh nhảy , do đó giá trị ECX sẽ tăng lên để trỏ tới vị trí tiếp theo trong chuỗi FakeSerial. Vậy thì ngay tại chỗ này , chúng ta nảy ra một ý tưởng là , chúng ta sẽ dùng chính chuỗi FakeSerial này để lưu chuỗi Serial của quá trình tính toán. Để thực hiện được điều này chúng ta chỉ việc thay đổi câu lệnh so sánh tại 00401629. Tại địa chỉ này, nhấn Space để mở cửa sổ Assemble , chúng ta edit lại như sau :
Tiếp theo chúng ta sẽ NOP câu lệnh nhảy tại 0040162C. Ta sẽ có được như sau :
Oki , tiến hành Run thử và Dump tại thanh ghi EDX , chúng ta sẽ có được kết quả mà chúng ta mong muốn . Vậy là qua bước thứ nhất tìm ra nơi tính toán RealSerial và nơi lưu trữ giá trị RealSerial :
Bước tiếp theo chúng ta sẽ cho hiện cái RealSerial này thông qua cái MessageBox. Công việc này hết sức đơn giản. Chúng ta chỉ việc trỏ tới vị trí của dòng : Code: 0040163E
68 20304000 PUSH
Rith_Cra.00403020
; ASCII "Well done cracker!"
sau đó nhấn Space và sửa thành : PUSH EDX Cuối cùng chúng ta Save thành quả của chúng ta thành một file exe. Chạy file này lên, nhập thông tin vào nhớ là chiều dài UserName phải bằng với chiều dài Serial nhập vào : ). Chúng ta sẽ có được kết quả như sau :
Vậy là thành công rồi !!! __________________ Có những con người mới đôi ba tuổi đời..... Cuộc sống mới bắt đầu đã cho rằng mình thấu hết .. Làm được gì ? Và đã có gì ? Chỉ học đòi thói chê bai.... Cần nhìn lại về chính thân mình .... Hình hài chỉ là con số 0 ....... Hình hài chỉ là con số 0..... Con số 0..!!! http://img.photobucket.com/albums/v5...nmanowar/6.gif Last edited by kienmanowar : 18-08-2006 at 05:40 PM. kienmanowar View Public Profile Send a private message to kienmanowar Find all posts by kienmanowar Add kienmanowar to Your Buddy List
#7 18-08-2006, 05:41 PM kienmanowar Offline Junior Member Posts: 19 Best Regards _[Kienmanowar]_
Join Date: Mar 2006
--++--==[ Greatz Thanks To ]==--++-My family, Computer_Angel, Moonbaby , Zombie_Deathman, Littleboy, Benina, QHQCrker, the_Lighthouse, Merc, Hoadongnoi, Nini ... all REA‘s members, TQN, HacNho, RongChauA, Deux, tlandn, light.phoenix, dqtln, ARTEAM .... all my friend, and YOU. --++--==[ Thanks To ]==--++-iamidiot, WhyNotBar, trickyboy, dzungltvn, takada, hurt_heart, haule_nth, hytkl v..v.. I want to thank Teddy Roggers for his great site, Reversing.be folks(especially haggar), Arteam folks(Shub-Nigurrath, MaDMAn_H3rCuL3s) and all folks on crackmes.de, thank to all members of unpack.cn (especially fly and linhanshi). Great thanks to lena151(I like your tutorials). And finally, thanks to RICARDO NARVAJA and all members on CRACKSLATINOS. >>>> If you have any suggestions, comments or corrections email me: kienbigmummy[at]gmail.com __________________ Có những con người mới đôi ba tuổi đời..... Cuộc sống mới bắt đầu đã cho rằng mình thấu hết .. Làm được gì ? Và đã có gì ? Chỉ học đòi thói chê bai.... Cần nhìn lại về chính thân mình .... Hình hài chỉ là con số 0 ....... Hình hài chỉ là con số 0..... Con số 0..!!! http://img.photobucket.com/albums/v5...nmanowar/6.gif kienmanowar View Public Profile Send a private message to kienmanowar Find all posts by kienmanowar Add kienmanowar to Your Buddy List
« Previous Thread | Next Thread »