Processes
Một chương trình có thể có một hoặc nhiều tiến trình. Một tiến trình duy trì và đại diện cho sự thực thi của một chương trình.
Attacker thường nhắm đến các tiến trình để tránh né sự phát hiện và giấu mã độc ở trong các process hợp lệ. Một vài attack vector:
Một tiến trình sẽ bao gồm các thành phần sau:
Process Component | Purpose |
---|---|
Private Virtual Address Space | Virtual memory addresses that the process is allocated. |
Executable Program | Defines code and data stored in the virtual address space. |
Open Handles | Defines handles to system resources accessible to the process. |
Security Context | The access token defines the user, security groups, privileges, and other security information. |
Process ID | Unique numerical identifier of the process. |
Threads | Section of a process scheduled for execution. |
Các thành phần của tiến trình được lưu trong bộ nhớ:
Component | Purpose |
---|---|
Code | Code to be executed by the process. |
Global Variables | Stored variables. |
Process Heap | Defines the heap where data is stored. |
Process Resources | Defines further resources of the process. |
Environment Block | Data structure to define process information. |
Minh họa:
Thông tin của từng tiến trình trong Task Manager bao gồm:
Value/Component | Purpose | Example |
---|---|---|
Name | Define the name of the process, typically inherited from the application | conhost.exe |
PID | Unique numerical value to identify the process | 7408 |
Status | Determines how the process is running (running, suspended, etc.) | Running |
User name | User that initiated the process. Can denote privilege of the process | SYSTEM |
Info
Một vài công cụ giúp quan sát các tiến trình dễ dàng hơn: Process Hacker 2, Process Explorer, và Procmon.
Threads
Một luồng là một đơn vị thực thi bên trong một tiến trình và được lập lịch dựa trên một vài yếu tố của thiết bị. Đơn giản hơn, nó là thành phần kiểm soát luồng thực thi của một tiến trình. Do đó, nó thường là mục tiêu tấn công.
Các luồng chia sẻ các tài nguyên mà tiến trình cấp với các luồng khác. Một thread đồng thời cũng có các giá trị và dữ liệu độc nhất như trong bảng bên dưới:
Component | Purpose |
---|---|
Stack | All data relevant and specific to the thread (exceptions, procedure calls, etc.) |
Thread Local Storage | Pointers for allocating storage to a unique data environment |
Stack Argument | Unique value assigned to each thread |
Context Structure | Holds machine register values maintained by the kernel |
Virtual Memory
Cho phép các thành phần khác tương tác với bộ nhớ chính mà không cần lo về rủi ro bị chồng chéo giữa các ứng dụng.
Bộ nhớ ảo cung cấp cho mỗi tiến trình một không gian địa chỉ ảo riêng tư (private virtual address space). Sẽ có một trình quản lý bộ nhớ dịch các địa chỉ ảo sang địa chỉ vật lý. Nhờ vào việc có một không gian địa chỉ ảo riêng tư và không ghi dữ liệu trực tiếp lên bộ nhớ vật lý nên các thiệt hại gây ra bởi tiến trình được giảm thiểu.
Minh họa:
Theo lý thuyết, không gian địa chỉ ảo riêng tư tối đa là 4GB trên một hệ thống 32-bit x86. Không gian địa chỉ này được chia thành hai phần. Phần bên dưới (0x00000000
- 0x7FFFFFFF
) được cấp phát cho các tiến trình còn phần bên trên (0x80000000
- 0xFFFFFFFF
) được cấp phát cho hệ điều hành. Administrator có thể chỉnh sửa bố cục cấp phát này cho các ứng dụng cần không gian địa chỉ lớn hơn thông qua các cài đặt (increaseUserVA) hoặc AWE (Address Windowing Extensions).
Bố cục cấp phát này cũng được sử dụng trong các hệ thống 64-bit.
Minh họa:
Dynamic Link Libraries (DLL)
Tài liệu của Microsoft mô tả một DLL là một thư viện có chứa code và dữ liệu mà có thể được sử dụng bởi nhiều hơn một chương trình cùng một lúc. Các DLL giúp module hóa code, tái sử dụng code, sử dụng hiệu quả bộ nhớ và giảm thiểu không gian ổ đĩa.
Về bản chất, DLL được tạo ra không khác gì các ứng dụng thông thường nhưng có thêm một vài thay đổi nhỏ. Bên dưới là một ví dụ của một DLL từ dự án Visual C++ Win32 Dynamic-Link Library:
#include "stdafx.h"
#define EXPORTING_DLL
#include "sampleDLL.h"
BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved
)
{
return TRUE;
}
void HelloWorld()
{
MessageBox( NULL, TEXT("Hello World"), TEXT("In a DLL"), MB_OK);
}
Bên dưới là header của DLL, nó sẽ định nghĩa các hàm được import và export:
#ifndef INDLL_H
#define INDLL_H
#ifdef EXPORTING_DLL
extern __declspec(dllexport) void HelloWorld();
#else
extern __declspec(dllimport) void HelloWorld();
#endif
#endif
DLL có thể được nạp vào một chương trình bằng cách sử dụng load-time dynamic linking hoặc run-time dynamic linking.
Với load-time dynamic linking, ứng dụng sẽ dùng các câu lệnh gọi sử dụng DLL tường minh. Để sử dụng kiểu nạp DLL này thì ta cần cung cấp cho ứng dụng một file header và một file thư viện (.lib
). Bên dưới là ví dụ cho việc gọi đến một exported DLL function trong một ứng dụng:
#include "stdafx.h"
#include "sampleDLL.h"
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
HelloWorld();
return 0;
}
Với run-time dynamic linking, ứng dụng sẽ dùng một hàm riêng (LoadLibrary
hoặc LoadLibraryEx
) để nạp DLL lúc run time. Khi load lên thì ứng dụng cần phải dùng hàm GetProcAddress
để xác định exported DLL function cần gọi. Bên dưới là ví dụ của việc nạp và import một DLL function trong một ứng dụng:
...
typedef VOID (*DLLPROC) (LPTSTR);
...
HINSTANCE hinstDLL;
DLLPROC HelloWorld;
BOOL fFreeDLL;
hinstDLL = LoadLibrary("sampleDLL.dll");
if (hinstDLL != NULL)
{
HelloWorld = (DLLPROC) GetProcAddress(hinstDLL, "HelloWorld");
if (HelloWorld != NULL)
(HelloWorld);
fFreeDLL = FreeLibrary(hinstDLL);
}
...
Khi DLL được nạp lên, nó sẽ được xem như là một dependency. Bởi vì một chương trình phụ thuộc vào một DLL nên các attacker có thể nhắm vào các DLL thay vì các ứng dụng để kiểm soát một phần nào đó quá trình thực thi. Một số loại tấn công:
Trong các đoạn mã độc, attacker sử dụng run-time dynamic linking nhiều hơn load-time dynamic linking. Lý do là vì một mã độc có thể cần phải trao đổi tập tin giữa nhiều vùng của bộ nhớ và việc trao đổi một file DLL duy nhất thì dễ kiểm soát hơn là import nhiều file.
Portable Executable Format
Định dạng PE (Portable Executable) là một định dạng tập tin cho các tập tin executable, object và DLL. Một tập tin có định dạng PE sẽ bao gồm một các header và section giúp dynamic linker biết cách map file vào bộ nhớ chính.
Định dạng PE gồm các thành phần sau:
DOS Header
Giúp định nghĩa loại tập tin. Giá trị DOS header nếu là MZ
(0x5A4D
) thì file đó có định dạng .exe
.
DOS header trong hex dump:
Offset(h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
00000000 4D 5A 90 00 03 00 00 00 04 00 00 00 FF FF 00 00 MZ..........ÿÿ..
00000010 B8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 ¸.......@.......
00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000030 00 00 00 00 00 00 00 00 00 00 00 00 E8 00 00 00 ............è...
00000040 0E 1F BA 0E 00 B4 09 CD 21 B8 01 4C CD 21 54 68 ..º..´.Í!¸.LÍ!Th
DOS Stub
Là một chương trình được chạy mặc định giúp in ra thông báo về tính tương thích (This program cannot be run in DOS mode
). Chương trình này không làm ảnh hưởng đến các chức năng của file thực thi.
DOS stub trong hex dump:
00000040 0E 1F BA 0E 00 B4 09 CD 21 B8 01 4C CD 21 54 68 ..º..´.Í!¸.LÍ!Th
00000050 69 73 20 70 72 6F 67 72 61 6D 20 63 61 6E 6E 6F is program canno
00000060 74 20 62 65 20 72 75 6E 20 69 6E 20 44 4F 53 20 t be run in DOS
00000070 6D 6F 64 65 2E 0D 0D 0A 24 00 00 00 00 00 00 00 mode....$.......
NT Headers
File Header
Chứa các header cung cấp thông tin về file thực thi.
Chúng ta có thể tìm vị trí của file header ở chỗ có signature PE
(0x50450000
) trong hex dump như sau:
000000E0 00 00 00 00 00 00 00 00 50 45 00 00 64 86 06 00 ........PE..d†..
000000F0 10 C4 40 03 00 00 00 00 00 00 00 00 F0 00 22 00 .Ä@.........ð.".
00000100 0B 02 0E 14 00 0C 00 00 00 62 00 00 00 00 00 00 .........b......
00000110 70 18 00 00 00 10 00 00 00 00 00 40 01 00 00 00 p..........@....
00000120 00 10 00 00 00 02 00 00 0A 00 00 00 0A 00 00 00 ................
00000130 0A 00 00 00 00 00 00 00 00 B0 00 00 00 04 00 00 .........°......
00000140 63 41 01 00 02 00 60 C1 00 00 08 00 00 00 00 00 cA....`Á........
00000150 00 20 00 00 00 00 00 00 00 00 10 00 00 00 00 00 . ..............
00000160 00 10 00 00 00 00 00 00 00 00 00 00 10 00 00 00 ................
00000170 00 00 00 00 00 00 00 00 94 27 00 00 A0 00 00 00 ........”'.. ...
00000180 00 50 00 00 10 47 00 00 00 40 00 00 F0 00 00 00 .P...G...@..ð...
00000190 00 00 00 00 00 00 00 00 00 A0 00 00 2C 00 00 00 ......... ..,...
000001A0 20 23 00 00 54 00 00 00 00 00 00 00 00 00 00 00 #..T...........
000001B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
000001C0 10 20 00 00 18 01 00 00 00 00 00 00 00 00 00 00 . ..............
000001D0 28 21 00 00 40 01 00 00 00 00 00 00 00 00 00 00 (!..@...........
Optional Header
Là một phần quan trọng của NT headers.
Data Dictionaries
Là một phần của optional header và chúng trỏ đến image data directory structure.
Section Table
Định nghĩa các section và thông tin khả dụng trong image. Các section chứa nội dung của file, bao gồm: code, các import và dữ liệu.
Có thể thấy từng section trong hex dump bên dưới:
000001F0 2E 74 65 78 74 00 00 00 D0 0B 00 00 00 10 00 00 .text...Ð.......
00000200 00 0C 00 00 00 04 00 00 00 00 00 00 00 00 00 00 ................
00000210 00 00 00 00 20 00 00 60 2E 72 64 61 74 61 00 00 .... ..`.rdata..
00000220 76 0C 00 00 00 20 00 00 00 0E 00 00 00 10 00 00 v.... ..........
00000230 00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 40 ............@..@
00000240 2E 64 61 74 61 00 00 00 B8 06 00 00 00 30 00 00 .data...¸....0..
00000250 00 02 00 00 00 1E 00 00 00 00 00 00 00 00 00 00 ................
00000260 00 00 00 00 40 00 00 C0 2E 70 64 61 74 61 00 00 ....@..À.pdata..
00000270 F0 00 00 00 00 40 00 00 00 02 00 00 00 20 00 00 ð....@....... ..
00000280 00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 40 ............@..@
00000290 2E 72 73 72 63 00 00 00 10 47 00 00 00 50 00 00 .rsrc....G...P..
000002A0 00 48 00 00 00 22 00 00 00 00 00 00 00 00 00 00 .H..."..........
000002B0 00 00 00 00 40 00 00 40 2E 72 65 6C 6F 63 00 00 ....@..@.reloc..
000002C0 2C 00 00 00 00 A0 00 00 00 02 00 00 00 6A 00 00 ,.... .......j..
000002D0 00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 42 ............@..B
Thông tin về một vài section:
Section | Purpose |
---|---|
.text | Contains executable code and entry point |
.data | Contains initialized data (strings, variables, etc.) |
.rdata or .idata | Contains imports (Windows API) and DLLs. |
.reloc | Contains relocation information |
.rsrc | Contains application resources (images, etc.) |
.debug | Contains debug information |
Minh họa cho định dạng PE:
Seealso
Chi tiết về định dạng PE: TryHackMe - Dissecting PE Headers
Interacting with Windows Internals
Cách dễ nhất để tương tác với hệ điều hành Windows là thông qua Windows API, bao gồm Win32 API và Win64 API.
Đa số các thành phần nội bộ bên trong Windows đều cần tương tác với phần cứng thông qua kernel. Một ứng dụng thông thường không thể tương tác với kernel mà cần sử dụng các processor mode (gồm user mode và kernel mode) và các access level. Việc chuyển đổi giữa hai chế độ thường được hỗ trợ bởi Windows API.
Bảng so sánh hai chế độ:
User mode | Kernel Mode |
---|---|
No direct hardware access | Direct hardware access |
Creates a process in a private virtual address space | Ran in a single shared virtual address space |
Access to “owned memory locations” | Access to entire physical memory |
Một ứng dụng chạy với user mode sẽ chuyển sang kernel mode khi có system call (syscall) được gọi từ ứng dụng đó.
Sau đây là các bước dùng để inject một message box (popup window) vào một process:
- Cấp phát vùng nhớ cục bộ trong tiến trình cho message box.
- Ghi/sao chép message box đến vùng nhớ đã được cấp phát.
- Thực thi message box từ bộ nhớ cục bộ của tiến trình.
Ở bước một, chúng ta có thể sử dụng hàm OpenProcess
để mở một handle với tiến trình cần inject message box:
HANDLE hProcess = OpenProcess(
PROCESS_ALL_ACCESS, // Defines access rights
FALSE, // Target handle will not be inhereted
DWORD(atoi(argv[1])) // Local process supplied by command-line arguments
);
Ở bước hai, chúng ta dùng VirtualAllocEx
để cấp phát một vùng nhớ với payload buffer:
remoteBuffer = VirtualAllocEx(
hProcess, // Opened target process
NULL,
sizeof payload, // Region size of memory allocation
(MEM_RESERVE | MEM_COMMIT), // Reserves and commits pages
PAGE_EXECUTE_READWRITE // Enables execution and read/write access to the commited pages
);
Ở bước ba, dùng hàm WriteProcessMemory
để ghi payload vào vùng nhớ đã được cấp phát:
WriteProcessMemory(
hProcess, // Opened target process
remoteBuffer, // Allocated memory region
payload, // Data to write
sizeof payload, // byte size of data
NULL
);
Cuối cùng, ta dùng hàm CreateRemoteThread
để thực thi payload:
remoteThread = CreateRemoteThread(
hProcess, // Opened target process
NULL,
0, // Default size of the stack
(LPTHREAD_START_ROUTINE)remoteBuffer, // Pointer to the starting address of the thread
NULL,
0, // Ran immediately after creation
NULL
);
Related
list
from outgoing([[TryHackMe - Windows Internals]])
sort file.ctime asc