Tải crackme tại: http://crackmes.de/users/san01suke/somecrypto02/
Bài viết tham khảo write-up của Johannesbade , tại : http://johannesbader.ch/2014/07/crackmes-de-san01sukes-somecrypto02/
1.Patch file để lấy full code
Giao diện của crackme khi thực thi tương tự như SomeCrypto~01:
Load crackme vào OllyDBG. Tại EP có được code như sau:
Quan sát, nhận thấy có hai điểm khá thú vị:
- Vùng code trước
0x4014D3
không thể disassembly được và trông giống như đã bị làm rối (obfuscated).
- Vùng code sau EP rõ ràng sẽ làm thay đổi code của crackme. Bắt đầu tại vị trí
0x401000
, lệnh XOR được sử dụng để XOR chính xác 0x4D3
bytes (lưu tại ecx) với giá trị 0x20h
(lưu tại al).
Vùng code tại 0x401000
trước khi thay đổi:
Nhấn F8 để trace thử một vài lần sẽ thấy được các bytes đã được thay đổi:
Để có đầy đủ các byte sau khi XOR, đặt BP tại lệnh 004014EF . FFD0 call eax
, sau đó nhấn F9, break tại BP. Lúc này đã có toàn bộ code sau khi XOR, nhưng OllyDbg không tự động phân tích lại và vẫn hiển thị các bytes. Nhấn Ctrl + A để phân tích lại code sẽ có được đầy đủ thông tin:
Lưu lại bản patched của crackme sau khi các bytes từ 0x401000
tới 0x4014D2
đã được XOR với 20h
(Dump file và sửa lại EP của file mới thành 0x14B0
). Sau đó, sử dụng IDA Pro để load file mới:
2. Phân tích sub_4011E0 – lần 1
Mở Subviews Strings (Shift + F12), quan sát thấy có chuỗi “Success”:
Tìm tới đoạn code liên quan tới chuỗi này:
Theo đoạn code trong hình, ta sẽ nhận được thông báo “Success” nếu sub_4011E0
trả về một giá trị khác 0 tại thanh ghi eax. Tiến hành phân tích sub_4011E0
:
Theo thông tin cung cấp khi debug với OllyDBG, ta thấy rằng thanh ghi edi sẽ chứa chuỗi szSerial mà ta nhập vào:
Quan sát đoạn mã tại loc_401296
:
Với thông tin có được, chuyển sang mã giả ta như sau:
serial = edi
IF strlen(serial) == 0 THEN
RETURN 0 // loc_401296
END
Nếu có nhập serial (serial không phải là chuỗi rỗng) thì đoạn mã tiếp theo được thực hiện:
Đoạn mã trên thực hiện tính toán chiều dài của serial và trả về 0 nếu không phải là 7. Ta có mã giả như sau:
serial_length = strlen(serial)
IF serial_length != 7 THEN
RETURN 0 // loc_401296
END
Nếu chiều dài nhập vào lớn hơn 7, đoạn mã tiếp theo sẽ thực hiện lời gọi tới một subroutine khác:
eax là một biến cục bộ. Vùng bộ nhớ ebp+arg_0 = ebp+8
trỏ đến nội dung của Name textbox, kiểm tra bằng OllyDbg ta có được:
var_1C là biến mà chưa biết kiểu, mã giả có được như sau:
sub_401000(&var_1C, szName)
3. Phân tích sub_401000
Chuyển tới sub_401000
, toàn bộ code tại đây như sau:
.text:00401000 ; =============== S U B R O U T I N E =======================================
.text:00401000
.text:00401000
.text:00401000 ; int __usercall sub_401000@<eax>(int mapping@<eax>, int szName@<edx>)
.text:00401000 sub_401000 proc near ; CODE XREF: sub_4011E0+26p
.text:00401000 mov dword ptr [eax], 0 ; mapping[0] = 0x0
.text:00401006 mov dword ptr [eax+4], 1 ; mapping[4] = 0x1
.text:0040100D mov dword ptr [eax+8], 2 ; mapping[8] = 0x2
.text:00401014 mov dword ptr [eax+0Ch], 3 ; mapping[12] = 0x3
.text:0040101B mov dword ptr [eax+10h], 4 ; mapping[16] = 0x4
.text:00401022 mov dword ptr [eax+14h], 5 ; mapping[20] = 0x5
.text:00401029 mov dword ptr [eax+18h], 6 ; mapping[24] = 0x6
.text:00401030 mov cl, [edx]
.text:00401032 test cl, cl ; if szName[i] is null
.text:00401034 jz short locret_40108C ; then exit sub
.text:00401036 push esi ; save esi
.text:00401037 jmp short top_Loop
.text:00401037 ; ---------------------------------------------------------------------------
.text:00401039 align 10h
.text:00401040
.text:00401040 top_Loop: ; CODE XREF: sub_401000+37j
.text:00401040 ; sub_401000+89j
.text:00401040 movsx ecx, cl ; Temp = szName[i]
.text:00401043 and ecx, 80000001h ; Temp &= 0x80000001; set SF = 1 if result is 80000000h
.text:00401049 jns short swap_mapping ; if Temp != 0 then swap(mapping[0], mapping[4])
.text:0040104B dec ecx
.text:0040104C or ecx, 0FFFFFFFEh
.text:0040104F inc ecx
.text:00401050
.text:00401050 swap_mapping: ; CODE XREF: sub_401000+49j
.text:00401050 jz short rotate_left ; if Temp = 0 then rotate_left_mapping
.text:00401052 mov esi, [eax+4] ; \
.text:00401055 mov ecx, [eax] ; |<- swap(mapping[0], mapping[4])
.text:00401057 mov [eax], esi ; |
.text:00401059 mov [eax+4], ecx ; /
.text:0040105C
.text:0040105C rotate_left: ; CODE XREF: sub_401000:swap_mappingj
.text:0040105C mov ecx, [eax] ; ecx = mapping[0]
.text:0040105E mov esi, [eax+4]
.text:00401061 mov [eax], esi ; mapping[0] = mapping[4]
.text:00401063 mov esi, [eax+8]
.text:00401066 mov [eax+4], esi ; mapping[4] = mapping[8]
.text:00401069 mov esi, [eax+0Ch]
.text:0040106C mov [eax+8], esi ; mapping[8] = mapping[12]
.text:0040106F mov esi, [eax+10h]
.text:00401072 mov [eax+0Ch], esi ; mapping[12] = mapping[16]
.text:00401075 mov esi, [eax+14h]
.text:00401078 mov [eax+10h], esi ; mapping[16] = mapping[20]
.text:0040107B mov esi, [eax+18h]
.text:0040107E mov [eax+14h], esi ; mapping[20] = mapping[24]
.text:00401081 inc edx
.text:00401082 mov [eax+18h], ecx ; mapping[24] = ecx
.text:00401085 mov cl, [edx] ; cl = next char of szName
.text:00401087 test cl, cl ; if not null
.text:00401089 jnz short top_Loop ; continue Loop
.text:0040108B pop esi ; restore esi
.text:0040108C
.text:0040108C locret_40108C: ; CODE XREF: sub_401000+34j
.text:0040108C retn
.text:0040108C sub_401000 endp
.text:0040108C
Phân tích subroutine từ đầu tới cuối có được thông tin như sau:
- Khởi tạo một mảng, tạm đặt tên là
mapping[]
và gán các giá trị cho mảng: {0,1,2,3,4,5,6}
- Kiểm tra từng kí tự trong chuỗi szName nhập vào theo biểu thức:
Temp &= 0x80000001
(với Temp = szName[i]
)
- Nếu Temp != 0, thực hiện đổi vị trí hai giá trị đầu tiên của mảng. Ví dụ, sau khi đổi sẽ là
{0,1,2,3,4,5,6} -> {0,1,2,3,4,5,6}
- Nếu Temp = 0, thực hiện việc gán lại các giá trị trong mảng theo kiểu dịch trái quay vòng. Ví dụ, sau khi thực hiện sẽ có
{0,1,2,3,4,5,6} -> {0,1,2,3,4,5,6}
- Lặp lại cho đến khi hết chuỗi
szName
, kết quả trả về là mảng mapping[] sau khi thực hiện các phép hoán đối và gán lại.
Mã giả của sub_401000
có được như sau:
FUNCTION sub_401000(mapping<var_1C>, szName)
mapping = {0,1,2,3,4,5,6} // in eax = &var_1C
FOR character IN szName DO
IF character % 2 != 0 DO
swap(mapping[0], mapping[1])
ENDIF
circular_left_shift(mapping)
ENDFOR
RETURN mapping
END
4. Phân tích sub_4011E0 – lần 2
Phân tích xong sub_401000
, quay trở lại sub_4011E0
để phân tích tiếp các lệnh bên dưới:
Đoạn code này đơn giản thực hiện việc copy chuỗi mặc định của crackme tại địa chỉ byte_403010 (hardcoded_str)
tới byte_403140 (copy_hardcoded_str)
. Sau đó kiểm tra xem chuỗi sau khi copy có null hay không? (Điều kiện này hơi thừa vì đã copy vào rồi thì null thế nào được 🙂 ). Mã giả của đoạn code trên như sau:
STRCPY(byte_403140, byte_403010) // copy string byte_403010 to byte_403140
IF byte_403140 == NULL THEN
GOTO loc_40123A \\ should never happen
ENDIF
Chuỗi hardcoded ban đầu của crackme tại địa chỉ byte_403010
như hình bên dưới:
Tiếp theo là một vòng lặp nhỏ khác:
Vòng lặp này thực hiện duyệt toàn bộ chuỗi sau khi copy tại byte_403140 (copy_hardcoded_str)
cho tới khi gặp null và tăng dần thanh ghi esi. Kết thúc vòng lặp thì giá trị có được của thanh ghi esi chính là độ dài của chuỗi tại byte_403140
, ta có mã giả như sau:
esi = strlen(byte_403140)
Đoạn code tiếp theo sẽ là lời gọi tới một subroutine khác với hai tham số truyền vào là var_1C
và strlen(byte_403140)
:
Trong đó, tham số var_1C
chính là mảng mapping[]
đã phân tích ở trên, chứa các giá trị từ 0 đến 6 đã được trộn lại bởi sub_401000
.
5. Phân tích sub_401110
.text:00401110 ; =============== S U B R O U T I N E =======================================
.text:00401110 ; Attributes: bp-based frame
.text:00401110 ; _UNKNOWN *__userpurge sub_401110@<eax>(_UNKNOWN *result@<eax>, int len_copy_hardcoded)
.text:00401110 sub_401110 proc near ; CODE XREF: sub_4011E0+5Ep
.text:00401110 ; sub_4011E0+71p
.text:00401110
.text:00401110 var_1C = dword ptr -1Ch
.text:00401110 var_18 = word ptr -18h
.text:00401110 var_16 = byte ptr -16h
.text:00401110 var_14 = dword ptr -14h
.text:00401110 var_10 = dword ptr -10h
.text:00401110 var_C = dword ptr -0Ch
.text:00401110 var_8 = dword ptr -8
.text:00401110 var_4 = dword ptr -4
.text:00401110 len_copy_hardcoded= dword ptr 8
.text:00401110
.text:00401110 push ebp
.text:00401111 mov ebp, esp
.text:00401113 mov ecx, 7 ; ecx = 7
.text:00401118 sub esp, 1Ch
.text:0040111B cmp [ebp+len_copy_hardcoded], ecx ; If strlen(copy_hardcoded) < ecx
.text:0040111E jle exit_sub ; then exit_sub()
.text:00401124 mov edx, [eax+8]
.text:00401127 lea edx, [ebp+edx+var_1C]
.text:0040112B mov [ebp+var_4], edx ; v0 = mapping[8]
.text:0040112E mov edx, [eax+0Ch]
.text:00401131 lea edx, [ebp+edx+var_1C]
.text:00401135 mov [ebp+var_8], edx ; v1 = mapping[12]
.text:00401138 mov edx, [eax+10h]
.text:0040113B lea edx, [ebp+edx+var_1C]
.text:0040113F push ebx
.text:00401140 push esi
.text:00401141 mov esi, [eax]
.text:00401143 mov [ebp+var_C], edx ; v2 = mapping[16]
.text:00401146 mov edx, [eax+14h]
.text:00401149 push edi
.text:0040114A mov edi, [eax+4]
.text:0040114D mov eax, [eax+18h]
.text:00401150 lea edx, [ebp+edx+var_1C]
.text:00401154 mov [ebp+var_10], edx ; v3 = mapping[20]
.text:00401157 lea edx, [ebp+eax+var_1C]
.text:0040115B mov eax, offset copy_hardcoded ; j = 2
.text:00401160 lea esi, [ebp+esi+var_1C] ; v5 = mapping[0]
.text:00401164 lea edi, [ebp+edi+var_1C] ; v6 = mapping[4]
.text:00401168 mov [ebp+var_14], edx ; v4 = mapping[24]
.text:0040116B sub ecx, eax ; ecx -= eax
.text:0040116D lea ecx, [ecx+0] ; ecx = &ecx
.text:00401170
.text:00401170 top_Loop: ; CODE XREF: sub_401110+B6j
.text:00401170 movzx edx, byte ptr [eax-2]
.text:00401174 mov ebx, [ebp+var_4]
.text:00401177 mov [esi], dl ; temp_arr[v5] = copy_hardcoded[j-2]
.text:00401179 movzx edx, byte ptr [eax-1]
.text:0040117D mov [edi], dl ; temp_arr[v6] =copy_hardcoded[j-1]
.text:0040117F movzx edx, byte ptr [eax]
.text:00401182 mov [ebx], dl ; temp_arr[v0] = copy_hardcoded[j]
.text:00401184 movzx edx, byte ptr [eax+1]
.text:00401188 mov ebx, [ebp+var_8]
.text:0040118B mov [ebx], dl ; temp_arr[v1] = copy_hardcoded[j+1]
.text:0040118D movzx edx, byte ptr [eax+2]
.text:00401191 mov ebx, [ebp+var_C]
.text:00401194 mov [ebx], dl ; temp_arr[v2] = copy_hardcoded[j+2]
.text:00401196 movzx edx, byte ptr [eax+3]
.text:0040119A mov ebx, [ebp+var_10]
.text:0040119D mov [ebx], dl ; temp_arr[v3] = copy_hardcoded[j+3]
.text:0040119F movzx edx, byte ptr [eax+4]
.text:004011A3 mov ebx, [ebp+var_14]
.text:004011A6 mov [ebx], dl ; temp_arr[v4] = copy_hardcoded[j+4]
.text:004011A8 mov edx, [ebp+var_1C]
.text:004011AB mov [eax-2], edx
.text:004011AE mov dx, [ebp+var_18]
.text:004011B2 mov [eax+2], dx
.text:004011B6 movzx edx, [ebp+var_16]
.text:004011BA mov [eax+4], dl
.text:004011BD add eax, 7 ; j += 7
.text:004011C0 lea edx, [ecx+eax] ; edx = ecx + 7
.text:004011C3 cmp edx, [ebp+len_copy_hardcoded] ; If edx < strlen(copy_hardcoded)
.text:004011C6 jl short top_Loop ; then continue Loop
.text:004011C8 pop edi
.text:004011C9 pop esi
.text:004011CA pop ebx
.text:004011CB
.text:004011CB exit_sub: ; CODE XREF: sub_401110+Ej
.text:004011CB mov esp, ebp
.text:004011CD pop ebp
.text:004011CE retn 4
.text:004011CE sub_401110 endp
.text:004011CE ; ---------------------------------------------------------------------------
Toàn bộ đoạn code trên thực hiện việc hoán đổi kí tự trong chuỗi byte_403140 (copy_hardcoded_str)
dựa trên mảng mapping[]
đã được tạo ra bởi sub_401000
đã phân tích trước đó. Mỗi lần thực hiện sẽ xử lý 7 kí tự của chuỗi byte_403140
. Mã giả của đoạn code này được viết lại như sau:
FUNCTION sub_401110(mapping)
{
int i=0, j = 2, ecx = 7, edx=0, k=0
//create index for temp_arr[]
v0 = mapping[8]
v1 = mapping[12]
v2 = mapping[16]
v3 = mapping[20]
v5 = mapping[0]
v6 = mapping[4]
v4 = mapping[24]
do
{
temp_arr[v5] = copy_hardcoded[j-2]
temp_arr[v6] = copy_hardcoded[j-1]
temp_arr[v0] = copy_hardcoded[j]
temp_arr[v1] = copy_hardcoded[j+1]
temp_arr[v2] = copy_hardcoded[j+2]
temp_arr[v3] = copy_hardcoded[j+3]
temp_arr[v4] = copy_hardcoded[j+4]
for (i=0; i<=6; i++)
{
copy_hardcoded[k+i] = temp_arr[i];
}
j+=7
edx = edx + ecx
k = k+i
}while (edx <= strlen(copy_hardcoded))
return copy_hardcoded //after permute
}
Tổng kết lại, sub_401110
thực hiện phép hoán đối vị trí của kí tự tại byte_403140
. Nó xây dựng index cho mảng temp_array[]
dựa trên mảng mapping[]
đã được tạo bởi sub_401000
và thực hiện hoán đổi từng khối 7 kí tự.
6. Phân tích sub_4011E0 – lần 3
Sau khi thực hiện xong sub_401110
ta sẽ quay trở về sub_4011E0
, ta có đoạn code sau:
.text:00401243 mov ecx, edi ; ecx = edi = szSerial
.text:00401245 lea eax, [ebp+var_1C] ; eax = &mapping[]
.text:00401248 call sub_401090
Ta thấy sẽ sub_401090
xử lý trên hai tham số truyền vào : [ebp+var_1C]
như đã biết chứa thông tin về mảng mapping[]
, còn thanh ghi ecx
và edi
lưu thông tin về szSerial
mà ta nhập vào. Mã giả của đoạn code trên như sau:
sub_401090(mapping, serial)
7. Phân tích sub_401090
Toàn bộ code của sub như dưới đây:
.text:00401090 ; =============== S U B R O U T I N E =======================================
.text:00401090 sub_401090 proc near ; CODE XREF: sub_4011E0+68p
.text:00401090 movsx edx, byte ptr [ecx] ; edx = szSerial[i]
.text:00401093 add edx, 0FFFFFFD0h ; edx = edx - 30h
.text:00401096 push esi ; save esi
.text:00401097 xor esi, esi ; esi = 0
.text:00401099 mov [eax], edx ; mapping[0] = edx
.text:0040109B cmp edx, 7 ; if edx >= 7
.text:0040109E jb short loc_4010A2
.text:004010A0 mov [eax], esi ; then mapping[0] = esi
.text:004010A2
.text:004010A2 loc_4010A2: ; CODE XREF: sub_401090+Ej
.text:004010A2 movsx edx, byte ptr [ecx+1] ; else edx = szSerial[i+1]
.text:004010A6 add edx, 0FFFFFFD0h ; edx = edx - 30h
.text:004010A9 mov [eax+4], edx ; mapping[4] = edx
.text:004010AC cmp edx, 7 ; if edx >= 7
.text:004010AF jb short loc_4010B4
.text:004010B1 mov [eax+4], esi ; then mapping[4] = esi
.text:004010B4
.text:004010B4 loc_4010B4: ; CODE XREF: sub_401090+1Fj
.text:004010B4 movsx edx, byte ptr [ecx+2] ; else edx = szSerial[i+2]
.text:004010B8 add edx, 0FFFFFFD0h ; edx = edx - 30h
.text:004010BB mov [eax+8], edx ; mapping[8] = edx
.text:004010BE cmp edx, 7 ; if edx >= 7
.text:004010C1 jb short loc_4010C6
.text:004010C3 mov [eax+8], esi ; then mapping[8] = esi
.text:004010C6
.text:004010C6 loc_4010C6: ; CODE XREF: sub_401090+31j
.text:004010C6 movsx edx, byte ptr [ecx+3] ; else edx = szSerial[i+3]
.text:004010CA add edx, 0FFFFFFD0h ; edx = edx - 30h
.text:004010CD mov [eax+0Ch], edx ; mapping[12] = edx
.text:004010D0 cmp edx, 7 ; if edx >= 7
.text:004010D3 jb short loc_4010D8
.text:004010D5 mov [eax+0Ch], esi ; then mapping[12] = esi
.text:004010D8
.text:004010D8 loc_4010D8: ; CODE XREF: sub_401090+43j
.text:004010D8 movsx edx, byte ptr [ecx+4] ; else edx = szSerial[i+4]
.text:004010DC add edx, 0FFFFFFD0h ; edx = edx - 30h
.text:004010DF mov [eax+10h], edx ; mapping[16] = edx
.text:004010E2 cmp edx, 7 ; if edx >= 7
.text:004010E5 jb short loc_4010EA
.text:004010E7 mov [eax+10h], esi ; then mapping[16] = esi
.text:004010EA
.text:004010EA loc_4010EA: ; CODE XREF: sub_401090+55j
.text:004010EA movsx edx, byte ptr [ecx+5] ; else edx = szSerial[i+5]
.text:004010EE add edx, 0FFFFFFD0h ; edx = edx - 30h
.text:004010F1 mov [eax+14h], edx ; mapping[20] = edx
.text:004010F4 cmp edx, 7 ; if edx >= 7
.text:004010F7 jb short loc_4010FC
.text:004010F9 mov [eax+14h], esi ; then mapping[20] = esi
.text:004010FC
.text:004010FC loc_4010FC: ; CODE XREF: sub_401090+67j
.text:004010FC movsx ecx, byte ptr [ecx+6] ; esle ecx = szSerial[i+6]
.text:00401100 add ecx, 0FFFFFFD0h ; ecx = ecx - 30h
.text:00401103 mov [eax+18h], ecx ; mapping[24] = ecx
.text:00401106 cmp ecx, 7 ; if ecx >= 7
.text:00401109 jb short loc_40110E
.text:0040110B mov [eax+18h], esi ; then mapping[24] = esi
.text:0040110E
.text:0040110E loc_40110E: ; CODE XREF: sub_401090+79j
.text:0040110E pop esi ; restore esi
.text:0040110F retn
.text:0040110F sub_401090 endp
.text:00401110 ; =============== S U B R O U T I N E =======================================
Nhìn thì thấy code khá dài, nhưng thuật toán của nó khá đơn giản. Mục đích cuối cùng là xây dựng lại mảng mapping[]
dựa trên Serial đã nhập vào. Chuỗi Serial nhập vào bao gồm 7 chữ cái, được chuyển đổi thành 7 số nguyên và được lưu vào mảng mapping[]
(nếu số đó nhỏ hơn 7) Mã giả của sub_401090
như sau:
FUNCTION sub_401090(mapping, serial)
FOR i = 0 TO 6 DO
temp = serial[i] - '0'
IF nr >= 7 THEN
mapping[i] = 0
ELSE
mapping[i] = temp
ENDIF
ENDFOR
END
8. Phân tích sub_4011E0 – lần 4
Sau khi sub_401090
nạp xong serial vào mảng mapping[]
, trở về sub_4011E0
và thấy lại tiếp tục có lời gọi tới sub_401110
để thực hiện hoán đổi kí tự một lần nữa:
.text:0040124D push esi ; len_copy_hardcoded
.text:0040124E lea eax, [ebp+var_1C] ; eax = &mapping
.text:00401251 call sub_401110
sub_4011E0
kết thúc bằng một đoạn code thực hiện tính toán hash cho chuỗi byte_403140 (copy_hardcoded_str)
. Nếu như hash sau khi tính toán có giá trị tại eax là 0B45D7873h
thì sẽ nhận được thông báo “Success”.
.text:00401256 or eax, 0FFFFFFFFh ; eax = 0xFFFFFFFF
.text:00401259 mov ecx, esi ; ecx = strlen(copy_hardcoded_str)
.text:0040125B mov edx, offset copy_hardcoded_str
.text:00401260 test esi, esi
.text:00401262 jz short loc_40127D
.text:00401264
.text:00401264 create_Hash: ; CODE XREF: sub_4011E0+9Bj
.text:00401264 movzx esi, byte ptr [edx] ; esi = copy_hardcoded_str[i]
.text:00401267 xor esi, eax ; esi = esi ^ eax
.text:00401269 and esi, 0FFh ; esi = esi & 0xFF
.text:0040126F shr eax, 8 ; eax = eax / 100h (100h = 256)
.text:00401272 xor eax, ds:dword_402058[esi*4] ; eax = eax ^ (esi*4+402058)
.text:00401279 inc edx ; i++ (next char of copy_hardcoded_str)
.text:0040127A dec ecx ; ecx--
.text:0040127B jnz short create_Hash ; Loop until ecx = 0
.text:0040127D
.text:0040127D loc_40127D: ; CODE XREF: sub_4011E0+82j
.text:0040127D not eax ; eax = ~eax
.text:0040127F pop esi
.text:00401280 cmp eax, 0B45D7873h ; If eax = 0xB45D7873h
.text:00401285 jnz short return_al_0
.text:00401287 mov eax, [ebp+arg_4]
.text:0040128A mov dword ptr [eax], offset copy_hardcoded_str
.text:00401290 mov al, 1 ; then set al = 1
.text:00401292 mov esp, ebp
.text:00401294 pop ebp
.text:00401295 retn
.text:00401296 ; ---------------------------------------------------------------------------
.text:00401296 return_al_0: ; CODE XREF: sub_4011E0+Aj
.text:00401296 ; sub_4011E0+1Aj ...
.text:00401296 xor al, al ; else set al = 0
.text:00401298 mov esp, ebp
.text:0040129A pop ebp
.text:0040129B retn
.text:0040129B sub_4011E0 endp
.text:0040129B ; ---------------------------------------------------------------------------
Mã giả của đoạn code trên như sau:
cal_hash = cal_hash_routine(message)
IF cal_hash = '0B45D7873h' THEN
RETURN 1 // success
ELSE
RETURN 0 // failure
ENDIF
9. Pseudo-Code
Tổng kết lại toàn bộ quá trình phân tích trên ta có được mã giả tương đối tường minh như sau:
FUNCTION name_mapping(szName)
mapping = {0,1,2,3,4,5,6} // in eax = &var_1C
FOR character IN szName DO
IF character % 2 != 0 DO
swap(mapping[0], mapping[1])
ENDIF
circular_left_shift(mapping)
ENDFOR
RETURN mapping
END
FUNCTION serial_mapping(szSerial)
FOR i = 0 TO 6 DO
temp = serial[i] - '0'
IF nr >= 7 THEN
mapping[i] = 0
ELSE
mapping[i] = temp
ENDIF
ENDFOR
RETURN mapping
END
FUNCTION permutation(mapping)
{
int i=0, j = 2, ecx = 7, edx=0, k=0
//create index for temp_arr[]
v0 = mapping[2]
v1 = mapping[3]
v2 = mapping[4]
v3 = mapping[5]
v5 = mapping[0]
v6 = mapping[1]
v4 = mapping[6]
do
{
temp_arr[v5] = copy_hardcoded[j-2]
temp_arr[v6] = copy_hardcoded[j-1]
temp_arr[v0] = copy_hardcoded[j]
temp_arr[v1] = copy_hardcoded[j+1]
temp_arr[v2] = copy_hardcoded[j+2]
temp_arr[v3] = copy_hardcoded[j+3]
temp_arr[v4] = copy_hardcoded[j+4]
for (i=0; i<=6; i++)
{
copy_hardcoded[k+i] = temp_arr[i];
}
j+=7
edx = edx + ecx
k = k+i
}while (edx <= strlen(copy_hardcoded))
return copy_hardcoded //after permute
}
FUNCTION CHECK_SERIAL(szSerial, szName)
IF strlen(serial) != 7 THEN
RETURN 0
END
mapping = name_mapping(szName)
STRCPY(copy_hardcoded_str, hard_coded_str) // clone hard coded message
IF copy_hardcoded_str == NULL THEN
GOTO loc_40123A \\ should never happen
ENDIF
copy_hardcoded_str = permutation(mapping)
mapping = serial_mapping(szSerial)
copy_hardcoded_str = permutation(mapping)
cal_hash = cal_hash_routine(copy_hardcoded_str)
IF cal_hash = '0B45D7873h' THEN
RETURN 1 // success
ELSE
RETURN 0 // failure
ENDIF
END
CHECK_SERIAL(szSerial, szName)
10. Tìm Key
Qua quá trình phân tích ở trên, ta đã nắm được cơ chế hoạt động của thuật toán. Việc tiếp theo là cần phải tìm ra quá trình hoán đối vị trí (permutation) sẽ tạo ra thông điệp plaintext (bản rõ) như thế nào. Ta có thông tin về thông điệp được mã hóa lưu tại byte_403010
là:
prncyI haorptge ,apy iamttru onbxo bPo -(r ix so)ot ehami fbdofu-hftss igulnpodt e trueemnrrtao beps sorat cisbSs -osn xsioer,us ptnntiieauf ifgdh inwsoatl rienssoinpg.
Để giải mã, chỉ cần tìm ra phép hoán vị cho một khối 7 kí tự. 7 chữ cái đầu tiên của bản mã là (bao gồm cả dấu cách):
prncyI
Nhìn vào 7 chữ cái này, ta có thể đoán chữ hoa “I” phải là kí tự đầu tiên, phần còn lại không quá khó để đoán, ta có được:
In cryp
Từ thông tin này ta suy luận ra được mảng mapping[]
chuẩn phục vụ cho việc giải mã như sau:
Như trên hình, chữ cái đầu tiên sẽ nằm ở vị trí thứ 6, chữ cái thứ hai nằm ở vị trí thứ 4, v..v… Từ đó, ta có bản đồ giải mã đúng là: 6 4 1 3 5 0 2
11. Viết Keygen sinh Serial theo Name nhập vào
Theo như quá trình phân tích ở trên, ta thấy crackme sử dụng hai lần phép hoán vị đối với bản mã (cipher text) để có được bản rõ (plain text). Lần đầu tiên thực hiện trên chuỗi Name, lần thứ hai thực hiện trên chuỗi Serial. Để viết được thuật toán sinh Serial cho chuỗi Name nhập vào, chúng ta cần phải tính toán kết quả mapping bằng việc thực hiện đoạn code name_mapping. Sau đó, ta có thể xác định mapping thứ hai sau khi kết hợp name mapping với kết quả mapping chính xác là 6 4 1 3 5 0 2
.
Keygen code bằng C:
#include <stdio.h>
#include <string.h>
int main()
{
char szName[20];
char szSerial[8]="";
int correct_key[] = {6, 4, 1, 3, 5, 0, 2};
int mapping[] = {0, 1, 2, 3, 4, 5, 6};
int i, temp=0;
printf("Please enter your name: ");
scanf("%s", szName);
for (i=0; i&lt;strlen(szName); i++)
{
if (szName[i] % 2 != 0)
{
temp = mapping[0];
mapping[0] = mapping[1];
mapping[1] = temp;
}
temp = mapping[0];
mapping[0] = mapping[1];
mapping[1] = mapping[2];
mapping[2] = mapping[3];
mapping[3] = mapping[4];
mapping[4] = mapping[5];
mapping[5] = mapping[6];
mapping[6] = temp;
}
for (i=0; i&lt;7; i++)
{
szSerial[mapping[i]] = (correct_key[i] + 0x30);
}
printf("Serial : %s", szSerial);
return 0;
}
Keygen code bằng Python:
import argparse
from collections import deque
parser = argparse.ArgumentParser(description="SomeCrypto~02 keygen")
parser.add_argument('name')
args = parser.parse_args()
name = args.name #get Name args
correct_key = [6, 4, 1, 3, 5, 0, 2]
cypher = deque(list(range(7)))
#create mapping array based on Name.
for c in name:
if ord(c) % 2:
cypher[0], cypher[1] = cypher[1], cypher[0]
cypher.rotate(-1)
serial = 7*[None] #create serial array and set None
for c, k in zip(cypher, correct_key):
serial[c] = k
print('Right serial: ' + ''.join(str(s) for s in serial))
Kiểm tra key:
Kết quả kiểm tra keygen có được như sau:
Toàn bộ bài viết đến đây là kết thúc. Happy Reversing!!
Best Regards,
m4n0w4r