# Windows Reversing Intro

## 1. Introductions

Có ba chức năng chính mà các công cụ của chúng ta sẽ cung cấp, đó là gỡ lỗi (debugging), hợp dịch (disassembling) và dịch ngược (decompiling). **Trình hợp dịch (Disassemblers)** sẽ dịch chương trình từ các **byte** của nó trên đĩa hoặc trong bộ nhớ thành mã **Assembly** tương đương và trình bày nó một cách thông tin. **Trình dịch ngược (Decompilers)** tương tự như trình hợp dịch, ngoại trừ thay vì cung cấp cho chúng ta **Assembly**, nó cố gắng tạo lại mã bằng C/C++. Nhược điểm của trình dịch ngược là chúng có thể không chính xác hoặc thiếu thông tin. Vì điều này, nếu bạn đang sử dụng trình dịch ngược, thì nên có mã đã hợp dịch bên cạnh mã đã dịch ngược để kiểm tra tính không chính xác. **Trình gỡ lỗi (Debuggers)**, cùng với trình hợp dịch và trình dịch ngược, sẽ cho phép chúng ta đặt điểm dừng (breakpoints) trong chương trình khi nó đang chạy và phân tích **thanh ghi (registers)**, bộ nhớ, trạng thái và hơn thế nữa. Chúng cũng cho phép thay đổi dữ liệu trong bộ nhớ khi chương trình đang chạy.

**Biểu tượng gỡ lỗi (Debug Symbols)**

Các biểu tượng gỡ lỗi cực kỳ hữu ích và bạn nên sử dụng chúng khi có thể. Thật không may, bạn thường sẽ chỉ có các biểu tượng gỡ lỗi cho các thư viện phổ biến và hiếm khi cho **file thực thi** mà bạn quan tâm. Ngoài ra, hầu hết các công cụ **đảo ngược** đều tải xuống các biểu tượng cho các thư viện phổ biến, vì vậy nếu bạn không có kết nối internet, bạn sẽ không nhận được chúng.

**Nhập/Xuất (Imports/Exports)**

Các tab này khá dễ hiểu. Tab imports hiển thị tất cả các hàm được chương trình hiện tại nhập từ các nguồn khác. Tab exports hiển thị tất cả các hàm được chương trình hiện tại hiển thị. Lưu ý rằng các export của một **file thực thi** thường chỉ chứa điểm nhập (entry point) (nơi chương trình bắt đầu thực thi).

**Hàm (Functions)**

Ở bên trái, bạn có thể thấy cửa sổ Functions hiển thị các hàm được xác định cho chương trình hiện tại. Tùy thuộc vào những biểu tượng bạn có quyền truy cập, bạn có thể có nhiều hơn hoặc ít hàm hơn với tên thực tế. Nếu không có biểu tượng nào để xác định một hàm, nó sẽ được đặt một tên chung chung, chẳng hạn như sub\_140001000 trong đó 140001000 là địa chỉ của hàm.

* basic\_ostream - C++ template for output streams. [More info here](https://en.cppreference.com/w/cpp/io/basic_ostream).

**Mảng (Arrays)**

Mảng lưu trữ nhiều phần dữ liệu có cùng loại tuần tự trong bộ nhớ. Giả sử bạn có một mảng gồm 5 số nguyên bắt đầu tại địa chỉ 0x4000. Kích thước của mảng là 20 **byte** vì mỗi số nguyên là 4 **byte**. Số nguyên đầu tiên ở 0x4000+0x0, số nguyên thứ hai ở 0x4000+0x04, v.v. Mảng thường dễ phân tích, giống như mảng ký tự (chuỗi) mà chúng ta đã gặp trong nhiệm vụ vòng lặp.

**Lớp (Classes)**

Các lớp (classes) có thể có nhiều phần dữ liệu có các kiểu khác nhau. Đây là điều khiến chúng khó làm việc hơn khi **đảo ngược (reversing)**. Để tìm ra bố cục của một lớp, bạn sẽ phải thực hiện một số phân tích. Bạn cần tìm ra không chỉ có bao nhiêu thứ trong lớp, mà còn cả các kiểu dữ liệu của chúng. Có khá nhiều cách để làm điều này, nhưng nó thường chỉ đơn giản là xem xét các hàm sử dụng lớp và chú ý kỹ đến cách chúng sử dụng nó.

Giả sử chúng ta có lớp sau:

```cpp
class Human {
public:
	int age;
	float height;
	char* name;
	Human(char* newName, int newAge, float newHeight)
		: age(newAge), height(newHeight), name(newName) {}
};
```

Dữ liệu của lớp này sẽ chiếm 16 **byte**. 4 **byte** cho tuổi, 4 **byte** cho chiều cao và 8 cho tên (con trỏ giữ địa chỉ và địa chỉ trong x64 là 8 **byte**). Điều này sẽ được xác định như thế nào trong **Assembly**? Giả sử một con trỏ đến lớp được chứa trong RAX và địa chỉ của lớp là 0x4000. Dưới đây là một số **pseudo-assembly**:

```nasm
mov RAX, 0x4000     ; RAX = Địa chỉ của lớp và biến tuổi (độ lệch 0)
lea RBX, [RAX+0x4]  ; RBX = Địa chỉ của chiều cao
lea RCX, [RAX+0x8]  ; RCX = Địa chỉ của tên
mov [RAX], 0x32     ; tuổi = 50
mov [RBX], 0x48     ; chiều cao = 72
mov [RCX], 0x424F42 ; tên = "BOB"
```

Như bạn có thể thấy, chúng ta có địa chỉ cơ sở được lưu trữ trong RAX và các mục của lớp được truy cập thông qua **độ lệch (offsets)**. Một trong những điều cần chú ý khi xử lý các lớp là việc sử dụng địa chỉ/con trỏ và lệnh `lea`. Điều này được thực hiện vì bạn thường muốn truy cập dữ liệu trong lớp, không phải bản sao của nó.

Xử lý các lớp thường không quá tệ, nhưng đôi khi bạn không được cung cấp toàn bộ lớp. Ví dụ: thường có một số phần của một lớp chỉ được tham chiếu một lần, chẳng hạn như một số thông tin tiêu đề, vì vậy bạn phải cẩn thận.

**Running DLL**

Khi một DLL được tải, hàm DllMain() được thực thi trong ngữ cảnh của tiến trình tải DLL. Tại đây, DLL có thể chạy bất kỳ mã nào nó muốn.

Bạn có thể đã nghe nói về DLL injection (tiêm DLL), đây là một cách nó có thể được thực hiện. Bạn có thể khiến tiến trình mục tiêu gọi LoadLibrary() trên DLL của bạn, điều này sẽ khiến DllMain() trong DLL của bạn được thực thi trong ngữ cảnh của tiến trình mục tiêu.

Có một chương trình đi kèm với Windows có tên là rundll32.exe, chương trình này thực hiện khá nhiều điều đó, nó chỉ tải DLL của bạn làm cho DllMain() thực thi. Với tư cách là một nhà phát triển, bạn có thể làm cho rundll32.exe thực thi các hàm trong DLL, nhưng tôi không nghĩ ai làm điều này.

Đối với các chuyên gia **kỹ thuật đảo ngược**, điều gì sẽ xảy ra nếu chúng ta muốn phân tích động một hàm trong DLL? Cách tốt nhất để làm điều này là viết mã của riêng bạn để gọi hàm bạn quan tâm. Nếu không có thư viện hoặc file tiêu đề, đây là một cách (có lẽ có một cách thanh lịch hơn) mà bạn có thể gọi một hàm trong DLL chỉ với file .dll. Điều này không bao gồm bất kỳ xử lý lỗi nào, nó chỉ là mã quan tâm. Đó là một cuộc gọi đến một hàm có tên là Add() trong DLL có tên là DLL.DLL:

```cpp
HMODULE dll = LoadLibraryA("DLL.DLL");
typedef void(WINAPI* Add_TypeDef)(int, int); // Add(int x, int y)
Add_TypeDef Add = (Add_TypeDef)GetProcAddress(dll, "Add_MangledName");
Add(1, 2);
```

Trừ khi bạn có file tiêu đề hoặc thư viện, bạn có thể sẽ cần phải **đảo ngược (reverse)** hàm để tìm ra những tham số nào được truyền cho nó.

## Interacting with Windows Internals

Chúng ta sẽ tiêm một message box vào quy trình cục bộ của mình để chứng minh một bằng chứng về khái niệm (proof-of-concept) để tương tác với bộ nhớ.

Các bước để viết một message box vào bộ nhớ được vạch ra dưới đây,

1. Phân bổ bộ nhớ quy trình cục bộ cho message box.
2. Ghi/sao chép message box vào bộ nhớ đã phân bổ.
3. Thực thi message box từ bộ nhớ quy trình cục bộ.

Ở bước một, chúng ta có thể sử dụng OpenProcess để lấy handle của quy trình được chỉ định.

```c
HANDLE hProcess = OpenProcess(
	PROCESS_ALL_ACCESS, // Xác định quyền truy cập
	FALSE, // Handle mục tiêu sẽ không được kế thừa
	DWORD(atoi(argv[1])) // Quy trình cục bộ được cung cấp bởi các đối số dòng lệnh 
);
```

Ở bước hai, chúng ta có thể sử dụng VirtualAllocEx để phân bổ một vùng bộ nhớ với payload buffer.

```c
remoteBuffer = VirtualAllocEx(
	hProcess, // Quy trình mục tiêu đã mở
	NULL, 
	sizeof payload, // Kích thước vùng của việc phân bổ bộ nhớ
	(MEM_RESERVE | MEM_COMMIT), // Dự trữ và commit các trang
	PAGE_EXECUTE_READWRITE // Cho phép thực thi và truy cập đọc/ghi vào các trang đã commit
);
```

Ở bước ba, chúng ta có thể sử dụng WriteProcessMemory để ghi payload vào vùng bộ nhớ đã phân bổ.

```c
WriteProcessMemory(
	hProcess, // Quy trình mục tiêu đã mở
	remoteBuffer, // Vùng bộ nhớ đã phân bổ
	payload, // Dữ liệu để ghi
	sizeof payload, // Kích thước byte của dữ liệu
	NULL
);
```

Ở bước bốn, chúng ta có thể sử dụng CreateRemoteThread để thực thi payload của mình từ bộ nhớ.

```c
remoteThread = CreateRemoteThread(
	hProcess, // Quy trình mục tiêu đã mở
	NULL, 
	0, // Kích thước mặc định của ngăn xếp
	(LPTHREAD_START_ROUTINE)remoteBuffer, // Con trỏ đến địa chỉ bắt đầu của luồng
	NULL, 
	0, // Chạy ngay sau khi tạo
	NULL
);
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://viettaliii.gitbook.io/home/education/reverse-engineering/windows-reversing-intro.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
