# [RE011] Unpack crypter của malware Netwire bằng x64dbg **[blog.vincss.net/2020/03/re011-unpack-crypter-cua-malware-netwire-bang-x64dbg.html](https://blog.vincss.net/2020/03/re011-unpack-crypter-cua-malware-netwire-bang-x64dbg.html)** _Gần đây, chúng tôi có tiếp cận một mẫu malware lạ. Kết quả trả về của sandbox cho thấy_ _đây là một biến thể của dòng malware Netwire được pack bằng một loại crypter viết bằng_ _VB6. Chúng tôi đã tiến hành phân tích mẫu này nhằm hiểu sâu về kỹ thuật của malware,_ _đồng thời xây dựng thành tài liệu tham khảo cho những mẫu malware sử dụng kĩ thuật tương_ _tự._ ## 1. Công cụ sử dụng 2. Thông tin cơ bản về sample **_File Hash (SHA-_** **_256): d381c5a5eeb46759bb5ebce67eb50cc61f91a75c204d6ec1c7750937f7f4c3f1_** [Nguồn:Any.run](http://any.run/) ----- Phân tích sơ bộ bằng các chương trình PE Scanner cho thấy mã độc được viết bằng **Visual Basic 6.0** **_Hình 1. Kết quả scan bằng Exeinfo_** ## 3. Phân tích crypter Một mẹo khi dịch ngược chương trình được biên dịch từ VB6 là sẽ có một loạt các lệnh jump nhảy đến các function được viết trong chương trình. Như trong trường hợp này, các lệnh jump nằm ở địa chỉ 0x40DF3C. Có thể tìm các lệnh jump này bằng mẫu “81 6C 24 04 **?? ?? ?? ?? E9”** **_Hình 2. Các lệnh jump đến function của VB6_** Có thể thấy mã độc chỉ có hai function chính. Chọn function tại địa chỉ 0x40E9DE để phân tích vì function này có kích thước lớn nhất. Tiếp tục trace dần sẽ tới một hàm call như sau: ----- **_Hình 3. Lệnh call đến shellcode_** Với những ai đã quen với việc dịch ngược các chương trình được viết bằng VB6 sẽ nhận thấy điểm bất thường khi trong code của chương trình VB6 gọi thẳng đến một thanh ghi. Tiếp tục debug vào lệnh call sẽ tới đoạn shellcode: **_Hình 4. Đoạn code cấp phát memory và chạy shellcode_** Toàn bộ đoạn shellcode này đã được obfuscated. Tuy nhiên, kích thước của nó cũng khá nhỏ nên nếu trace dần từng bước và tổng hợp thông tin, chúng ta sẽ thấy shellcode thực hiện những bước sau: - Gọi hàm VirtualAlloc để cấp phát bộ nhớ. - Giải mã shellcode và copy vào vùng bộ nhớ vừa được cấp phát. - Tiến hành gọi shellcode mới. ----- Tới đây, có thể dùng x64dbg để dump toàn bộ shellcode mới và tiến hành phân tích. Để dump shellcode, lựa chọn đoạn cần dump trong cửa sổ Hex, chuột phải và chọn Binary > **Save To a File:** **_Hình 5. Dump shellcode với x64dbg_** Với shellcode đã dump, có nhiều công cụ hỗ trợ để phân tích. Trong bài này, chúng tôi sử dụng jmp2it để load shellcode lên và debug bằng x64dbg. Chạy jmp2it với command như sau: “Jmp2it.exe shellcode.bin 0x0 pause” Tiến hành attach x64dbg vào process jmp2it.exe để debug shellcode. Shellcode lúc này được load ở địa chỉ 0x30000. ----- **_Hình 6. Shellcode thực hiện tác vụ độc hại_** Như trên hình, shellcode mới này cũng bị obfuscate. Lúc này, có hai lựa chọn: - Trace từng lệnh để debug. - Viết tool/script để deobfuscate đoạn shellcode. Vì shellcode là khá lớn nên ở đây chúng tôi lựa chọn phương án viết script để thực hiện deobfuscate đoạn shellcode này. ### 3.1. Deobfuscate shellcode Để viết được script thực hiện deobfuscate đoạn shellcode, chúng ta cần hiểu một số pattern của đoạn shellcode. Ví dụ như sau: - Pattern Push Reg/Pop Reg - Pattern Inc Reg/Dec Reg - Pattern Add Reg, Const/ Sub Reg, Const - Pattern Push Reg/Xor Reg, Const/ Pop Reg - Pattern CLD, CLC - … Các pattern trên tương ứng với lệnh NOP. Sau khi xác định được các pattern này, dùng [python để viết script deobfuscate. Chi tiết script xem tại đây. Để sử dụng, lựa chọn đoạn](https://drive.google.com/file/d/1gc5Cs2zxElj13TJHc9Krgw1ERMkPLj0V/view) mã cần deobfuscate và chạy script. ----- **_Hình 7. Trước và sau khi deobfuscated_** *Lưu ý: _script trên tạm thời bỏ qua tìm kiếm pattern 2 bytes vì pattern ngắn, không có hiệu_ _quả nhiều trong việc obfuscate và rất dễ nhầm lẫn khi deobfuscate bằng cách_ _search/replace byte pattern. Để xác định pattern 2 bytes một cách chính xác thì nên dùng_ _pattern bằng asm. Hiện tại x64dbg chưa support tốt các pattern bằng asm._ ### 3.2. Heaven’s gate Shellcode áp dụng kỹ thuật heaven’s gate[1] để làm rối trong quá trình debug. Đây là kỹ thuật thực thi mã từ x86 sang x64 bằng lệnh far jmp. Bằng cách đơn giản check ở địa chỉ **FS:[0xC0] để xem hệ thống có phải là x64 hay không? Nếu là x64, shellcode dùng kỹ** thuật heaven’s call. Để debug tiếp được trơn tru trên x86, chúng tôi tiến hành patch lệnh nhảy sau lệnh so sánh để "lừa" shellcode thực thi trên x86. **_Hình 8. Đoạn code thực thi Heaven’s gate_** ### 3.3. Resolve API Có thểnói GetProcAddress là một API quan trọng đểshellcode có thểtìm và gọi các ----- hàm API khác. Để tìm địa chỉ của hàm GetProcAddress cần có địa chỉ của kernel32.dll. Shellcode ở trên sẽ tiến hành truy xuất tới PEB->Ldr->InMemoryOrderModuleList và lấy địa chỉ của module tương ứng với tên kernel32.dll. **_Hình 9. Truy cập vào PEB lấy base tương ứng_** Sau khi có địa chỉ của kernel32.dll, shellcode tiếp tục tìm địa chỉ của API bằng API hash. Ở đây hash được sử dụng là DJB hash và giá trị hash của hàm “GetProcAddress” là **0xCF31BB1F[2].** **_Hình 10. Đoạn code hash tên API và tiến hành so sánh_** Dựa vào địa chỉ của kernel32.dll và API GetProcAddress, shellcode tiến hành resolve một loạt các API sau: - LoadLibraryA - TerminateProcess - EnumWindows ----- ZwProtectVirtualMemory - DbgBreakPoint - DbgUIRemoteBreakin ### 3.4. Anti attach Shellcode gọi hàm ZwProtectVirtualMemory để đặt quyền **PAGE_EXECUTE_READWRITE cho section .text của ntdll, sau đó tiến hành patch các** hàm API DbgBreakPoint và DbgUIRemoteBreakin để anti attach. **_Hình 11. Code patch hàm DbgBreakPoint thành NOP_** **_Hình 12. Code patch hàm DbgUIRemoteBreakin_** ----- **_Hình 13. Trước và sau khi patch hàm DbgUIRemoteBreakin_** ### 3.5. Restore hook/breakpoint tại các hàm Zw*/Nt* Shellcode thực hiện scan pattern “B9 ?? ?? ?? ?? 8D 54 24 04” và “33 C9 8D 54 24 04” trong code của thư viện ntdll. Đây là pattern nằm trong các hàm Zw*/Nt* gọi đến system call. Sau khi tìm thấy pattern này, shellcode sẽ khôi phục lại 5 bytes đầu tiên của các hàm: ----- **_Hình 14. Đoạn code search pattern_** **_Hình 15. Đoạn code khôi phục lại 5 byte đầu tiên của API_** Một điểm hay cần học ở kỹ thuật này là các hàm Zw*/Nt* có các system call number tăng dần theo luồng từ trên xuống dưới. Nhờ đặc điểm này, shellcode có thể khôi phục lại lệnh “mov eax, system call number” một cách chính xác: **_Hình 16. Pattern được khoanh màu đỏ và system call number được khoanh màu xanh_** _Bug: Nếu hàm API với system call number là 0 hoặc dòng lệnh “lea edx, dword ptr ss:_ **_[esp+4]” bị thay đổi (hook, breakpoint,…), shellcode sẽ hủy toàn bộ system call number_** ----- _của ntdll._ ### 3.6. Set hidden thread Shellcode gọi hàm ZwSetInformationThread với tham số 0x11 để hide thread[3] trước debugger: **_Hình 17. Đoạn mã shellcode set hidden thread_** ### 3.7. Kiểm tra breakpoint Shellcode sử dụng một hàm check trước khi gọi API. Nội dung hàm check như sau: - Gọi NtGetThreadContext để check hardware breakpoint[3]. - Kiểm tra opcode 0xCC (int 3) để phát hiện software breakpoint[3]. ----- **_Hình 18. Code phát hiện hardware breakpoint_** **_Hình 19. Code phát hiện software breakpoint_** ### 3.8. Create process và code injection Cuối cùng shellcode thực hiện code injection bằng cách: - Gọi CreateProcessInternalW với flag là CREATE_SUSPENDED. - Gọi ZwUnmapViewOfSection với base là 0x400000. - Sử dụng ZwCreateSection/NtMapViewOfSection để cấp phát bộ nhớ. - Nếu ZwCreateSection/NtMapViewOfSection bị lỗi, chuyển sang dùng API **ZwAllocVirtualMemory để cấp phát bộ nhớ.** - Gọi NtWriteVirtualMemory để inject shellcode mới vào bộ nhớ. - Cuối cùng gọi hàm NtSetThreadContext/NtSetThreadContext/ NtResumeThread để chạy shellcode và TerminateProcess để exit. ### 3.9. Phân tích shellcode thứ hai Đoạn shellcode thứ hai thực chất là đoạn shellcode thứ nhất với config khác. Thay vì inject shellcode như đoạn shellcode thứ nhất, mục đích của đoạn shellcode này là tải về payload, decrypt và thực thi. Công việc chính nó thực hiện bao gồm: - Sử dụng hàm của thư viện wininet.dll để download payload từ địa chỉ **https://drive[.]google[.]com/uc?** **export=download&id=1zEuX2HZcVvTYp7wzGtD1IXOSVLTBWVUe** - Giải mã payload và map vào memory để thực thi. Payload này có kích thước **0x1AA40, 40 bytes đầu tiên là phần header của payload.** ----- **_Hình 20. Payload được decrypt trong memory_** Dump toàn bộ với size là 0x1AA00 sẽ thu được payload cuối cùng là Netwire. ### 3.10. Bonus Khi load payload Netwire đã dump bằng x64dbg và đặt breakpoint tại 0x409E8A, sẽ thu được config đã decrypted: **_Hình 21. Config được decrypt trong memory_** ### 4. Tài liệu tham khảo Các nguồn tham khảo được sử dụng làm tư liệu cho bài viết: ---------------------------------**Để tiện theo dõi, chúng tôi cung cấp bài phân tích dưới dạng PDF:** **File Name: CSS-RD-ADV-200304-011_Unpack crypter của malware Netwire bằng** x64dbg.pdf **File Hash (SHA-** **256): 717efd6b8dd9a8a40ee34386311ab0f5689eb1f5f8fbd6df30b9cfdd8abe02c0** **Dang Dinh Phuong** **R&D Center - VinCSS (a member of Vingroup)** -----