18/09/2018, 15:12

Lưu Ý Khi Phân Tích Ứng Dụng Windows (Phần 2)

Để có thể phân tích được các ứng dụng Windows hiệu quả bạn hãy chú ý những lưu ý trong bài viết của Securitybox nha. Tiếp nối chủ đề những lưu ý khi phân tích ứng dụng windows chuyên gia an ninh mạng Nguyễn Việt Anh sẽ tiếp tục gửi đến bạn đọc những nghiên cứu của mình. Các Networking API Mã ...

Để có thể phân tích được các ứng dụng Windows hiệu quả bạn hãy chú ý những lưu ý trong bài viết của Securitybox nha. Tiếp nối chủ đề những lưu ý khi phân tích ứng dụng windows chuyên gia an ninh mạng Nguyễn Việt Anh sẽ tiếp tục gửi đến bạn đọc những nghiên cứu của mình.

Các Networking API

Mã độc thường dựa vào các hàm network để thực hiện các hành vi xấu, trong khi Windows API cung cấp rất nhiều hàm phục vụ giao tiếp mạng. Hiểu về các hàm giao tiếp mạng thông dụng sẽ giúp ta xác định được chương trình độc hại đang làm gì khi sử dụng các hàm này.

Các Berkeley socket

Trong các tùy chọn mạng của Windows, mã độc thường dùng các socket Berkeley nhất. Các chức năng mạng của Berkeley socket được triển khai trong các thư viện Winsock, chủ yếu trong ws2_32.dll. Các hàm phổ biến là connect, bind, listen, accept, send recv.

berkeley

Hàm WSAStartup phải được gọi đến trước khi gọi bất cứ một hàm networking nào khác để cấp phát tài nguyên cho các thư viện networking. Khi debug code, muốn xác định khởi đầu của một kết nối mạng, ta nên đặt một breakpoint tại hàm WSAStartup, thường thì điểm bắt đầu của kết nối sẽ ở ngay sau đó.

XEM THÊM: Lưu Ý Khi Phân Tích Ứng Dụng Windows (Phần 1)

Server side và Client side

  • Server side: Duy trì một socker mở và lắng nghe các kết nối tới nó.
  • Client side: Kết nối tới các socket lắng nghe kể trên.

Một mã độc có thể là chương trình server side hoặc client sideTrong trường hợp ứng dụng client-side kết nối tới một socket, ta sẽ thấy lời gọi hàm socket và sau đó là hàm connect, sau hàm connect có thể là send hoặc recv,… Đối với một ứng dụng dịch vụ lắng nghe các kết nối đến nó, các hàm socket, bind, listenaccept sẽ được gọi theo đúng trình tự liệt kê, sau đó sẽ là các hàm send, recv hoặc hàm tương tự tùy trường hợp cần thiết. Khuôn mẫu này phổ biến đối với cả các chương trình độc hại và chương trình bình thường.

xử lý lỗi

Ví dụ này bỏ qua mọi thao tác thiết lập tham số và xử lý lỗi. Trên các mẫu mã độc thực tế sẽ có các lời gọi hàm WSAGetLastError và các hàm xử lý lỗi khác nằm rải rác.

Đầu tiên, hàm WSAStartup khởi tạo các socket Win32 và một socket sẽ được tạo bởi hàm socket. Hàm bind gán socket vừa tạo tới một port, hàm listen được gọi để thiết lập socket lắng nghe trên cổng vừa được gán và hàm accept chấp nhận kết nối, từ địa chỉ là giá trị của tham số addr (dòng 0x401082), tới socket.

Các hàm WinINet API

Ngoài Winsock API, WinINet API là một API mức cao hơn. Các hàm của WinINet API được lưu trong Wininet.dll.

WinINet API triển khai các giao thức trên tầng ứng dụng trong chồng giao thức TCP/IP, ví dụ: HTTP và FTP. Ta có thể suy luận hành vi của mã độc dựa trên các kết nối mà nó mở.

  • InternetOpen được dùng để khởi tạo một kết nối Internet.
  • InternetOpenUrl được dùng để kết nối tới một URL (có thể là một trang HTTP hoặc một nguồn FTP).
  • InternetReadFile hoạt động tương tự hàm ReadFile, cho phép chương trình đọc dữ liệu trong một file được tải xuống từ Internet.

Mã độc thường gọi các hàm WinINet API để kết nối tới máy chủ điều khiển và lấy các lệnh điều khiển thực thi.

Theo dõi mã độc khi chúng đang được thực thi

Mã độc có thể sử dụng nhiều cách thức truyền lệnh thực thi ngoài các lệnh được biểu diễn bởi IDA Pro. Một chuyên gia phân tích mã độc cần phải nắm được các kỹ thuật mã độc có thể sử dụng để thực thi các code khác. Kỹ thuật đầu tiên và phổ biến nhất là để truy cập code trong những file khác là sử dụng các DLL.

Các DLL

Windows sử dụng các DLL để chia sẻ code giữa nhiều ứng dụng. Một DLL là một file thực thi không thể chạy một mình mà trích xuất các hàm trong nó để các ứng dụng khác gọi đến.

Các thư viện tĩnh (static library) vẫn được sử dụng nhưng không còn phổ biến. Ưu điểm chính của việc sử dụng các DLL so với thư viện tĩnh là không gian nhớ được sử dụng bởi DLL có thể được chia sẻ với các tiến trình đang chạy. Ví dụ, nếu một thư viện được dùng đồng thời bởi hai tiến trình khác nhau, code của thư viện tĩnh sẽ cần nhiều không gian nhớ gấp hai lần so với code từ DLL vì chúng cần được nạp hai lần vào hai vùng nhớ khác nhau.

Một ưu điểm lớn nữa khi sử dụng DLL là những hàm thực hiện các chức năng phổ biến được cung cấp từ các DLL có sẵn, lập trình viên và người viết mã độc không cần lập trình lại và build các hàm đó trong file thực thi của mình, nhờ đó tiết kiệm công sức và giảm kích thước chương trình.

Các kỹ thuật sử dụng DLL

Người viết mã độc có thể sử dụng các DLL theo ba cách:

Sử dụng DLL để lưu code độc hại

Đôi khi lưu code độc hại trong một DLL sẽ tiện lợi hơn là trong một file .exe. Một số mã

độc cần được kèm với các tiến trình khác, nhưng mỗi tiến trình chỉ chứa được một file .exe. Mã độc có thể phải sử dụng các DLL để tải chúng vào một tiến trình khác.

Sử dụng các DLL của Windows

Hầu hết mã độc đều sử dụng các DLL cơ bản có thể tìm thấy trong mọi hệ thống

Windows. Các DLL này cung cấp các hàm chức năng cần thiết để tương tác với OS.

Sử dụng các DLL từ bên thứ ba

Khi phát hiện mã độc import các hàm từ một DLL của bên thứ ba, ta có thể suy đoán

rằng mã độc phụ thuộc vào một chương trình khác (thường là chương trình sử dụng DLL đặc biệt đó) để thực hiện các chức năng của nó. Ví dụ, mã độc có thể sử dụng DLL của Mozilla Firefox để kết nối tới máy chủ. Trong một số trường hợp thực hiện chức năng mã hóa, mã độc cũng có thể phải dùng đến các DLL tùy chỉnh cũng cấp hàm mã hóa đặc biệt do các hàm này thường không được cài đặt sẵn trên máy nạn nhân.

Cấu trúc chung của DLL

Các file DLL gần giống với các file .exe. Chúng sử dụng PE file format và chỉ có một trường trong PE header có thể chỉ ra một file là DLL chứ không phải .exe. Các DLL thường có nhiều export và ít import hơn so với .exe.

(https://hshrzd.wordpress.com/2016/07/21/how-to-turn-a-dll-into-a-standalone-exe/ ;

https://stackoverflow.com/questions/6309179/how-to-tell-if-a-file-is-an-exe-or-a-dll )

Hàm chính của DLL là DllMain. Hàm này không có nhãn (label) và không phải là một export của DLL nhưng nó được quy định là entry point của DLL trong PE header. DllMain được gọi để thông báo DLL khi một tiến trình nạp hoặc giải phóng thư viện, tạo một luồng mới hoặc kết thúc một luồng đang tồn tại. Thông báo này giúp DLL quản lý tài nguyên đối với từng tiến trình và từng luồng.

Đa số DLL không có tài nguyên riêng cho mỗi luồng và chúng bỏ qua các lời gọi tới DllMain thực hiện bởi các hoạt động luồng. Khi tài nguyên của một DLL được quản lý theo luồng, các tài nguyên này có thể là manh mối để suy luận mục đích của DLL.

Các tiến trình

Mã độc có thể thực thi code bên ngoài chương trình hiện tại bằng cách tạo một tiến trình mới hoặc sửa đổi một tiến trình đã có. Một tiến trình là một chương trình đang được thực thi. Mỗi tiến trình quản lý tài nguyên riêng của nó, ví dụ như các handle đang mở và không gian nhớ. Một tiến trình chứa một hoặc nhiều luồng thực thi. Trước đây, các mã độc chỉ sử dụng tiến trình của riêng chúng, nhưng các mã độc gần đây có xu hướng thực thi code của chúng như thành phần của các tiến trình khác.

Windows sử dụng các tiến trình như là những vỏ bọc để quản lý tài nguyên và giữ cho các chương trình riêng biệt không xung đột nhau. Thường có trên 30 tiến trình chạy đồng thời trên một hệ thống Windows tại bất cứ thời điểm nào, tất cả đều chia sẻ tài nguyên như CPU, hệ thống file, bộ nhớ và các thiết bị phần cứng. Hệ điều hành cho phép một tiến trình có thể truy cập tới những tài nguyên này mà không xung đột với các tiến trình khác. Các tiến trình cũng giúp nâng cao tính ổn định hệ thống bằng cách giới hạn lỗi và crash trong một chương trình không thể gây ảnh hưởng tới các chương trình khác.

Một tài nguyên đặc biệt quan trọng mà hệ điều hành cần quản lý giữa các tiến trình là bộ nhớ hệ thống. Mỗi tiến trình được cấp phát một không gian nhớ riêng và chỉ được sử dụng các ô nhớ trong giới hạn không gian nhớ đó.

Khi một tiến trình yêu cầu không gian nhớ, hệ điều hành thực hiện phân vùng bộ nhớ và cấp phát cho tiến trình một địa chỉ để bắt đầu truy cập bộ nhớ. Các tiến trình có thể chia sẻ các địa chỉ nhớ logic. Ví dụ, nếu một tiến trình lưu dữ liệu tại địa chỉ 0x00400000, một tiến trình khác cũng có thể lưu dữ liệu khác tại cùng địa chỉ 0x00400000 đó mà không gây xung đột giữa hai tiến trình. Hai địa chỉ logic này là một nhưng trên bộ nhớ vật lý, chúng ở hai vùng khác nhau.

Cũng giống như địa chỉ gửi thư, địa chỉ nhớ chỉ có giá trị trong đúng ngữ cảnh. Nếu chỉ có địa chỉ 404 Phố Chợ, ta không thể biết khu vực chính xác cần đến, ta cần phải có cả ZIP code; địa chỉ 0x00400000 không cho ta biết dữ liệu đang nằm ở đâu trừ khi ta biết địa chỉ đó thuộc tiến trình nào. Một mã độc hại truy cập tới địa chỉ nhớ 0x00400000 chỉ gây ảnh hưởng tới tiến trình chứa đoạn mã độc hại đó; các chương trình khác trong hệ thống cùng sử dụng địa chỉ nhớ logic đó đều không bị ảnh hưởng.

Tạo một tiến trình mới

Hàm thường được mã độc sử dụng để tạo tiến trình mới là CreateProcess. Hàm này sử dụng nhiều tham số và hàm gọi đến nó có nhiều quyền kiểm soát cách gọi nó. Ví dụ, một mã độc có thể gọi hàm này để tạo một tiến trình vượt qua các host-based firewall, các cơ chế bảo mật và thực thi mã độc hại; hoặc mã độc có thể tạo một tiến trình Internet Explorer mới và sử dụng IE để truy cập nội dung độc hại.

Mã độc thường sử dụng hàm CreateProcess để tạo một remote shell chỉ trong một lời gọi hàm. Tham số STARTUPINFO là một struct bao gồm một handle cho đầu vào chuẩn, đầu ra chuẩn và dòng báo lỗi chuẩn cho một tiến trình. Mã độc có thể thiết lập các giá trị này với một socket, khi tiến trình ghi đầu ra chuẩn, nó sẽ ghi vào socket, từ đó cho phép kẻ tấn công thực thi remote shell mà không cần chạy thêm gì khác ngoài lời gọi hàm CreateProcess.

Ở ví dụ phía dưới, handle của socket được lưu trong stack và được truyền vào struct STARTUPINFO. Khi hàm CreateProcess được gọi, mọi dữ liệu đầu vào và đầu ra của tiến trình mới đều được điều hướng qua socket.

ảnh 1

Ở dòng đầu tiên, biến SocketHandle được chuyển vào EAX. Socket handle được khởi tạo từ bên ngoài hàm này. Struct lpStartupInfo giữ thông tin đầu vào chuẩn (0x004010EC), đầu ra chuẩn (0x004010E8) và thông báo lỗi chuẩn (0x004010E4) của tiến trình sẽ được tạo. SocketHandle đang lưu trong EAX được truyền vào cả ba giá trị này bằng 3 lệnh mov tại (1), (2), (3). Lệnh thực thi remote shell lưu trong dword_403098 được truyền vào EAX (0x004010F0) và push vào stack như một tham số của hàm CreateProcess (0x00401109).

Lời gọi hàm CreateProcess truyền vào 10 tham số, nhưng ngoài lpCommandLine, lpProcessInformation lpStartupInfo thì các tham số khác hoặc chứa giá trị 0, 1, NULL hoặc thể hiện giá trị cờ và ta không cần quan tâm tới các tham số đó khi phân tích mã độc.

Lời gọi hàm CreateProcess trong ví dụ trên thực hiện tạo một tiến trình với mọi dữ liệu vào và ra được điều hướng qua một socket. Để tìm ra remote host, ta cần xác định vị trí khởi tạo socket (nằm ngoài hàm này). Để xác định chương trình nào sẽ được chạy, ta cần phân tích giá trị string lưu tại dword_403098.

Mã độc thường tạo tiến trình mới để thực thi code trong resource section của PE file. Resource section của PE file có thể chứa bất kì file gì. Khi chạy một chương trình, nó sẽ trích xuất code thực thi bổ sung, ghi phần code đó lên đĩa và gọi hàm CreateProcess để thực thi đoạn code đó. Code thực thi bổ sung cũng có thể là các DLL. Khi phân tích một mã độc sử dụng kỹ thuật này, ta cần mở chương trình bằng công cụ Resource Hacker và lưu file được nhúng trong chương trình vào đĩa, sau đó có thể tiến hành phân tích trên file vừa lưu.

Theo chuyên gia Nguyễn Việt Anh

0