# IDA Pro

## Kiến thức cơ bản

**PE..d†** là một file 64-bit. **PE..L** là một file 32-bit.

**Options — General**, tích chọn **Line prefixes** để hiện thị thêm thông tin về địa chỉ bộ nhớ ở bên cạnh các lệnh trong chế độ đồ hoạ của IDA và tại **Number of opcode bytes**, nếu chúng ta thay đổi giá trị tại đó (mặc định là bằng 0), chúng ta sẽ thấy các opcodes hoặc các bytes xuất hiện tại mỗi lệnh.

<figure><img src="https://vietzettt.files.wordpress.com/2022/07/image-27.png?w=591" alt=""><figcaption></figcaption></figure>

Chúng ta có thể tìm kiếm tại các cửa sổ này bằng cách nhấn **CTRL + F**.

**View-Open Subview-Strings** hiển thị cho ta những chuỗi

**View-Open Subview-Imports** truy xuất thông tin về các hàm được chương trình import.

### **Hệ thống số** <a href="#he-thong-so" id="he-thong-so"></a>

Được sử dụng nhiều nhất là số nhị phân (**BINARY**), số thập phân (**DECIMAL**), số thập lục phân (**HEXADECIMAL**).

> **BINARY (Số nhị phân):** các số được biểu diễn bằng hai chữ số là 0 và 1, đó là lý do tại sao nó được gọi là nhị phân. Máy tính chỉ hiểu được số nhị phân.
>
> **DECIMAL (Số thập phân):** các số được biểu diễn bằng 10 chữ số (từ 0 đến 9), đó là lý do tại sao nó được gọi là số thập phân. Con người chúng ta từ lúc sinh ra mặc định sẽ được làm quen với hệ thống số này.
>
> **HEXADECIMAL (Số thập lục phân):** Tất cả các số được biểu diễn với các kí tự từ 0 đến F (từ 0 đến 9, cộng với A, B, C, D, E và F (*không phân biệt chữ hoa và chữ thường*), có nghĩa là sẽ có tổng cộng 16 kí tự). Trong đó: A = 10, B = 11, C = 12, D = 13, E = 14, F = 15.

> **Câu lệnh hỗ trợ chuyển đổi trong python**
>
> **hex(), bin()** để chuyển đổi các hệ số cho nhau
>
> **chr()** để chuyển thành ký tự

### **Số âm** <a href="#so-am-trong-he-thap-luc" id="so-am-trong-he-thap-luc"></a>

> Nếu máy tính yêu cầu biểu diễn số âm thì nó sẽ bỏ qua bit MSB – tức là bit đầu tiên để biểu diễn dấu của số. Nếu bit dấu là 0 là số dương, ngược lại nếu là 1 thì là số âm

<figure><img src="https://vietzettt.files.wordpress.com/2022/07/image-29.png" alt=""><figcaption><p>MSB – most significant bit ; LSB – least significant bit</p></figcaption></figure>

### **Bảng mã ASCII** <a href="#bang-ma-ascii" id="bang-ma-ascii"></a>

<figure><img src="https://vietzettt.files.wordpress.com/2022/07/image-30.png?w=614" alt=""><figcaption></figcaption></figure>

## **Tìm kiếm trong IDA** <a href="#chuc-nang-tim-kiem-trong-ida" id="chuc-nang-tim-kiem-trong-ida"></a>

* **Next Code** – tìm kiếm lệnh kế tiếp đã được hiểu là Code
* **Next Data** – tìm kiếm các địa chỉ tiếp theo, nơi được IDA hiểu là Data hoặc được xử lý dữ liệu tại bất kỳ section nào
* **Search Explored và Unexplored** – tìm code hoặc data đã được định nghĩa (lệnh hoặc dữ liệu) và tính năng thứ hai áp dụng với các vùng không được phát hiện là lệnh hoặc dữ liệu hợp lệ.
* **Search Immediate Value — Search Next Immediate Value** – tìm kiếm lệnh đầu tiên hoặc byte dữ liệu có chứa giá trị hằng số được chỉ định.
* **Search Text — Search Next Text** – Tìm kiếm các chuỗi mà chúng ta nhập vào, bao gồm cả biểu thức chính quy nếu chúng ta muốn.
* **Search Sequence Of Bytes** – tìm kiếm các chuỗi các bytes
* **Search Not Function** – tìm kiếm byte đầu tiên không thuộc về bất kỳ hàm nào.

## IDA Loader <a href="#ida-loader" id="ida-loader"></a>

<figure><img src="https://vietzettt.files.wordpress.com/2022/07/image-31.png?w=614" alt=""><figcaption></figcaption></figure>

### **Các thanh ghi dùng chung** <a href="#cac-thanh-ghi-dung-chung" id="cac-thanh-ghi-dung-chung"></a>

> **EAX (thanh ghi chứa — accumulator)**: được sử dụng nhiều nhất trong các lệnh số học, logic, và chuyển dữ liệu. Các thao tác nhân, chia sử dụng thanh ghi này. Với các hàm API của Windows, kết quả trả về của hàm thường sẽ lưu vào thanh ghi EAX.
>
> **EBX (thanh ghi cơ sở — base):** thanh ghi EBX có thể truy cập trực tiếp dữ liệu bộ nhớ và nó cũng là một thanh ghi dùng chung.
>
> **ECX (thanh ghi đếm — count):** ECX là một thanh ghi dùng chung có thể được sử dụng như là một bộ đếm cho các lệnh khác nhau. Nó cũng có thể chứa địa chỉ lệch của dữ liệu trong bộ nhớ. Các lệnh sử dụng bộ đếm là các lệnh liên quan lặp chuỗi, các lệnh chuyển, xoay và LOOP / LOOPD.
>
> **EDX (thanh ghi dữ liệu — data):** là một thanh ghi dùng chung dùng để chứa một phần kết quả của phép nhân hoặc một phần của phép chia. Nó cũng có thể truy cập địa chỉ dữ liệu trong bộ nhớ trực tiếp.
>
> **EDI (chỉ số đích — destination):** EDI thường được sử dụng trong các thao tác làm việc với chuỗi hoặc mảng. Thanh ghi này sẽ trỏ tới chuỗi đích. Bên cạnh đó nó cũng là một thanh ghi dùng chung.
>
> **ESI (chỉ số nguồn — source):** Giống như EDI, ESI cũng thường được sử dụng trong các thao tác làm việc với chuỗi hoặc mảng. Thanh ghi này sẽ trỏ tới chuỗi nguồn.
>
> **EBP (con trỏ cơ sở — base):** EBP trỏ tới vị trí bộ nhớ, bên cạnh mục đích dùng chung thì nó được sử dụng làm frame pointer để truy xuất các tham số và các biến cục bộ trong ngăn xếp của một hàm.
>
> **ESP (con trỏ ngăn xếp — stack):** thanh ghi này luôn trỏ đến đỉnh hiện thời của Stack. Theo nguyên tắc làm việc của Stack thì thanh ghi này sẽ hướng về phía địa chỉ thấp hơn.

<figure><img src="https://vietzettt.files.wordpress.com/2022/07/image-32.png?w=614" alt=""><figcaption><p><em>Các thanh ghi này còn có thể chia nhỏ thành các thanh ghi 16-bit và 8-bit</em></p></figcaption></figure>

<figure><img src="https://vietzettt.files.wordpress.com/2022/07/image-33.png?w=421" alt=""><figcaption></figcaption></figure>

### **Các thanh ghi đặc biệt** <a href="#cac-thanh-ghi-dac-biet" id="cac-thanh-ghi-dac-biet"></a>

> **EIP (con trỏ lệnh — instruction):** đây là một thanh ghi đặc biệt, nó luôn trỏ đến lệnh tiếp theo sẽ được thực hiện. Khác với các thanh ghi khác, EIP không thể bị tác động trực tiếp bởi các lệnh.
>
> **EFLAGS (thanh ghi cờ)** – mỗi bit của nó được dùng để phản ánh một trạng thái nhất định của phép toán

<figure><img src="https://vietzettt.files.wordpress.com/2022/07/image-34.png" alt=""><figcaption></figcaption></figure>

Tiếp theo là các thanh ghi đoạn, các thanh ghi này trỏ tới các phần khác nhau của file thực thi như CS = CODE, DS = DATA v..v…

<figure><img src="https://vietzettt.files.wordpress.com/2022/07/image-35.png?w=589" alt=""><figcaption></figcaption></figure>

Trong quá trình làm việc với thanh ghi và bộ nhớ thì có một chi tiết quan trọng là **kích thước** của các kiểu dữ liệu: **BYTE là 1 byte**, **WORD là 2 byte**, **DWORD 4 bytes** trong bộ nhớ.

<figure><img src="https://vietzettt.files.wordpress.com/2022/07/image-37.png" alt=""><figcaption></figcaption></figure>

<figure><img src="https://vietzettt.files.wordpress.com/2022/07/image-39.png?w=614" alt=""><figcaption></figcaption></figure>

* Các thanh ghi dùng chung 32 bit (4 byte) eax, ebx, ecx, edx, esi, edi, ebp và esp được mở rộng thành 64 bit (8 byte); các thanh ghi này được đặt tên là rax, rbx, rcx, rdx, rsi, rdi, rbp và rsp.
* 8 thanh ghi mới được bổ sung thêm là r8, r9, r10, r11, r12, r13, r14, và r15.
* Một chương trình có thể truy cập vào thanh ghi dưới dạng 64 bit (rax, rbx, v.v.), 32 bit (eax, ebx, v.v.), 16 bit (ax, bx, v.v.) hoặc 8 bit (al, bl, …).
* Truy cập các thanh ghi r8 — r15 dưới dạng byte, word, dword hoặc qword bằng cách bổ sung thêm b, w, d hoặc q vào sau tên thanh ghi.
* Trong kiến trúc x86, các tham số của hàm sẽ được đẩy vào ngăn xếp trước khi gọi hàm, trong khi ở kiến trúc x64, bốn tham số đầu tiên được truyền vào các thanh ghi rcx, rdx, r8 và r9 và nếu chương trình còn các tham số khác nữa, chúng sẽ được lưu vào stack. Điều này sẽ khiến cho khó xác định được xem địa chỉ bộ nhớ nào là biến cục bộ hay tham số của hàm.

## Các lệnh ASM cơ bản <a href="#cac-lenh-asm-co-ban" id="cac-lenh-asm-co-ban"></a>

### Chuyển dữ liệu <a href="#cac-lenh-chuyen-du-lieu" id="cac-lenh-chuyen-du-lieu"></a>

#### **MOV**

> **MOV dest, src**: Sao chép nội dung của toán hạng nguồn (src) tới đích (dest).
>
> Thao tác: dest <- src.
>
> Lệnh này được sử dụng để chuyển dữ liệu giữa các thanh ghi, giữa một thanh ghi và một ô nhớ hoặc chuyển trực tiếp một số vào một thanh ghi hay ô nhớ.
>
> Hiểu cơ bản thì lệnh mov này có thể tương ứng với lệnh gán ở ngôn ngữ bậc cao.
>
> Ví dụ: **MOV EAX, EDI; EAX** nhận giá trị của EDI; còn EDI giữ nguyên giá trị, không bị thay đổi.
>
> Tiền tố **offset** ở phía trước chỉ ra rằng phải lấy địa chỉ chứ không phải nội dung của ô nhớ đó

<figure><img src="https://vietzettt.files.wordpress.com/2022/07/image-38.png?w=479" alt=""><figcaption></figcaption></figure>

#### **MOVSX, MOVZX**

> Lệnh **MOVSX** và **MOVZX**, hai lệnh này đều lấy 1 byte và chuyển vào một thanh ghi.
>
> MOVZX sẽ điền 0 vào bytes cao. MOVSX sẽ xem xét bit dấu, nếu là số dương, nhỏ hơn hoặc bằng 0x77 thì nó sẽ điền 0; còn nếu là số âm, 0x80 hoặc lớn hơn, thì nó sẽ điền 0xFF.

#### **XCHG**

> **XCHG A, B;** Hoán đổi giá trị của A với giá trị của B. A và B có thể là hai thanh ghi, thanh ghi và ô nhớ, nhưng không được phép đồng thời là 2 ô nhớ.

**Edit > Patch Program > Assemble** để thay đổi lệnh

**View > Open subviews > Patched Bytes (Ctrl+Alt+P)** xem các địa chỉ nào có lệnh đã bị thay đổi và có thể khôi phục lại các giá trị ban đầu.

### Tương tác với Stack <a href="#cac-cau-lenh-tuong-tac-voi-stack" id="cac-cau-lenh-tuong-tac-voi-stack"></a>

<figure><img src="https://vietzettt.files.wordpress.com/2022/07/image-40.png?w=301" alt=""><figcaption></figcaption></figure>

* **Stack (Ngăn xếp)** là một phần của bộ nhớ và là cấu trúc dữ liệu một chiều (các phần tử được cất vào và lấy ra từ cùng một đầu của cấu trúc)
* Theo quy ước, Stack hướng về phía địa chỉ bộ nhớ thấp hơn.
* Cho phép lưu trữ và khôi phục lại dữ liệu
* Có 2 thao tác lệnh cơ bản đối với xử lý dữ liệu trên stack: PUSH and POP

#### **PUSH**

<figure><img src="https://vietzettt.files.wordpress.com/2022/07/image-41.png?w=304" alt=""><figcaption></figcaption></figure>

* Lệnh này được dùng để thêm/ lưu dữ liệu vào trong ngăn xếp. Toán hạng nguồn có thể là các thanh ghi dùng chung hoặc ô nhớ. Sau mỗi lần thực hiện lệnh Push thì giá trị của **thanh ghi ESP sẽ được giảm đi.**

**sz** hoặc **a** là các ký hiệu nhận biết đó là một chuỗi ASCII

#### **POP**

<figure><img src="https://vietzettt.files.wordpress.com/2022/07/image-42.png?w=313" alt=""><figcaption></figcaption></figure>

* Lệnh này được dùng để lấy ra giá trị từ đỉnh của ngăn xếp, sau khi thực hiện lệnh thì giá trị của thanh ghi ESP sẽ được tăng lên để trỏ tới phần tử tiếp theo.

#### **LEA**

* **LEA A, B**
* **Lệnh LEA** (nạp địa chỉ hiệu dung vào thanh ghi) thực hiện chuyển một địa chỉ được chỉ định trong B (nguồn) vào A (đích). Nội dung của B không bao giờ được truy cập, nó sẽ luôn là một địa chỉ hoặc là kết quả của phép tính toán nằm trong dấu ngoặc vuông \[] của toán hạng thứ hai. Lệnh LEA được sử dụng rất nhiều để lấy địa chỉ bộ nhớ của các biến hoặc các tham số.
* *Về bản chất, quá trình thực hiện thực ra chỉ là **cộng** hoặc **trừ** thanh ghi **EBP** với một giá trị hằng số để trỏ tới một địa chỉ nằm trên Stack mà thôi, tuy nhiên do IDA là công cụ hỗ trợ khả năng tương tác cao, nên nó sẽ đánh nhãn cho từng biến để ta có thể dễ dàng đặt lại tên khi phân tích.*
* Chốt lại vấn đề, điều rất quan trọng giúp chúng ta nhận ra sự khác biệt giữa lệnh LEA và lệnh MOV là:
  * **Lệnh LEA thực hiện lấy địa chỉ biến. Tương ứng mã giả là a = \&b**
  * **Lệnh MOV thực hiện lấy giá trị được lưu tại địa chỉ biến. Tương ứng với mã giả là a = \*b**

### Tính toán <a href="#cac-lenh-tinh-toan" id="cac-lenh-tinh-toan"></a>

#### **ADD**

* **ADD A, B** ; A = A + B
* Câu lệnh ADD thực hiện cộng giá trị của B với A, kết quả tính toán sẽ lưu vào A. Tức là A = A + B. A ở đây có thể là một thanh ghi hoặc là nội dung của một ô nhớ, B có thể là một thanh ghi, một hằng số hoặc nội dung của một ô nhớ. Tuy nhiên, trong câu lệnh ADD thì cả A và B **không thể đồng thời là nội dung của ô nhớ.**

#### **SUB**

* **SUB A, B** ; A = A — B
* Lệnh SUB cũng tương tự như lệnh ADD, ngoại trừ thay vì thực hiện cộng thì nó thực hiện trừ số nguyên và lưu kết quả vào A.

#### **INC và DEC**

* **INC A; A++**
* **DEC A; A–**
* Các lệnh trên thực hiện tăng hoặc giảm giá trị thanh ghi hoặc nội dung của một địa chỉ bộ nhớ đi 1.
* Cả hai lệnh này thường hay được sử dụng trong các vòng lặp để tăng hoặc giảm biến đếm.

#### **IMUL**

* **IMUL A, B** ; A = A \* B
* **IMUL A, B, C** ; A = B \* C
* Câu lệnh đầu tiên sẽ thực hiện nhân A với B, kết quả được bao nhiêu sẽ được lưu lại vào A.
* Câu lệnh thứ hai thì B và C được nhân với nhau và kết quả được lưu vào A.
* Trong cả hai trường hợp A chỉ có thể là một thanh ghi, B chỉ có thể là một thanh ghi hoặc nội dung của một vị trí bộ nhớ và C chỉ có thể là một hằng số.
* Với trường hợp lệnh chỉ có **một toán hạng** (ví dụ: *imul ecx*), thì tùy theo độ dài của toán hạng mà sẽ lấy giá trị trong các thanh ghi AL, AX, hoặc EAX để nhân và kết quả của phép nhân sẽ được lưu vào AX, DX:AX, hoặc EDX:EAX.

#### **DIV/ IDIV**

* **DIV/ IDIV A**
* Trong câu lệnh này, A được hiểu là số chia. Số bị chia và thương số không được chỉ định bởi vì chúng luôn giống nhau. Tức là có 3 dạng như sau:
  * Nếu A có kiểu byte, lấy giá trị của thanh ghi AX chia cho A, kết quả thương số lưu vào thanh ghi AL, phần dư lưu vào thanh ghi AH.
  * Nếu A có kiểu word, lấy giá trị của cặp thanh ghi DX:AX chia cho A, kết quả thương số lưu vào thanh ghi AX, phần dư lưu vào thanh ghi DX.
  * Nếu A có kiểu dword, lấy giá trị của cặp thanh ghi EDX:EAX chia cho A, kết quả thương số lưu vào thanh ghi EAX, phần dư lưu vào thanh ghi EDX.
* Xem ví dụ:

<figure><img src="https://vietzettt.files.wordpress.com/2022/07/image-43.png?w=571" alt=""><figcaption></figcaption></figure>

* Với lệnh trên, ví dụ nếu EAX = 5, EDX = 0 và ECX = 2, nó sẽ thực hiện phép chia số nguyên. Kết quả của phép chia 5 / 2 sẽ được 2 và dư 1. Khi đó kết quả là 2 được lưu vào thanh ghi EAX và số dư 1 sẽ được lưu vào thanh ghi EDX.
* Bên lề: Thông thường khi thực hiện phép chia, do thanh ghi EDX được sử dụng để lưu phần dư nên nó sẽ được thiết lập về 0 trước khi thực hiện phép tính. Để xóa EDX về 0 có hai cách:
  * Sử dụng câu lệnh **XOR** (chi tiết bên dưới): XOR EDX, EDX
  * Sử dụng câu lệnh **CDQ** (như trên hình minh họa): Câu lệnh này thực hiện mở rộng bit dấu (bit 31) của thanh ghi EAX sang thanh ghi EDX. Nếu bit này có giá trị 0 thì EDX sẽ bằng 0.

### Logic <a href="#cac-lenh-logic" id="cac-lenh-logic"></a>

#### **AND, OR và XOR**

* **AND A, B** ; A = A & B
* **OR A, B** ; A = A | B
* **XOR A, B** ; A = A ^ B
* Lệnh đầu tiên thực hiện phép AND giữa hai giá trị và lưu lại kết quả vào A, tương tự với các lệnh OR hoặc XOR. Mỗi phép tính đều sử dụng một bảng thật tương ứng của nó. A và B có thể là thanh ghi hoặc nội dung của địa chỉ bộ nhớ, tuy nhiên các thao tác giữa hai ô nhớ là không hợp lệ.

<figure><img src="https://vietzettt.files.wordpress.com/2022/07/image-44.png" alt=""><figcaption></figcaption></figure>

* Lệnh hay được sử dụng nhiều nhất là **XOR** cùng một thanh ghi để dễ dàng xóa thanh ghi đó về 0. Ví dụ: **XOR EAX,EAX**.
* Trong bảng trên chúng ta thấy rằng nếu **XOR** một số với chính nó thì kết quả sẽ luôn bằng không. Các phép tính này được thực hiện ở chế độ nhị phân (binary):
  * Lệnh **AND** có thể sử dụng để che đi/ giữ lại các bit nhất định của toán hạng đích. Bit 0 của mặt nạ sẽ xóa bit tương ứng, còn bit 1 của mặt nạ sẽ giữ nguyên bit tương ứng của toán hạng đích.
  * Lênh **OR** có thể được sử dụng để thiết lập các bit xác định của toán hạng đích trong khi vẫn giữ nguyên các bit còn lại. Bit 1 cua mặt nạ sẽ thiết lập bit tương ứng còn bit 0 của mặt nạ sẽ giữ nguyên bit tương ứng của toán hạng đích.
  * Lệnh **XOR** dùng để đảo các bit xác định của toán hạng đích trong khi vẫn giữ nguyên các bit còn lại. Bit 1 của mặt nạ làm đảo bit tương ứng còn bit 0 giữ nguyên bit tương ứng của toán hạng đích.

#### **NOT**

* **NOT A**
* Lệnh NOT thực hiện đảo ngược tất cả các bit của A và lưu lại kết quả vào A.
* Trong Python không có lệnh NOT, nhưng nó rất đơn giản nếu bạn có một số nhị phân. ví dụ 0101 và bạn áp dụng lệnh NOT với số này. Kết quả có được sau khi thực hiện đảo ngược từng bit một. Toàn bộ các bit 0 sẽ được thay bằng 1 và ngược lại.

<figure><img src="https://vietzettt.files.wordpress.com/2022/07/image-50.png?w=418" alt=""><figcaption></figcaption></figure>

#### **NEG**

* **NEG A** ; chuyển đổi A thành –A (reg = 0 — reg). Trên thực tế, lệnh neg là kết quả của một lệnh not và add 1.
* Nó không giống như cú pháp \~ trong Python vì lệnh này chỉ là phép trừ đi 1.
* Nói cách khác, để thực hiện lệnh NEG bằng Python, bạn cần cộng thêm 1 vào kết quả.

<figure><img src="https://vietzettt.files.wordpress.com/2022/07/image-51.png?w=221" alt=""><figcaption></figcaption></figure>

### Dịch bit SHL, SHR <a href="#cac-lenh-dich-bit-shl-shr" id="cac-lenh-dich-bit-shl-shr"></a>

* **SHL A, B**; Dịch trái A đi B bit
* **SHR A, B**; Dịch phải A đi B bit
* A có thể là một thanh ghi hoặc một vị trí bộ nhớ và B là một hằng số hay một thanh ghi 8-bit. Các lệnh này thực hiên phép dịch bit sang trái (**SHL**) và sang phải (**SHR**), các bit bên phải/trái được thay thế bằng các số 0.
* Khi di chuyển các bit sang trái, mỗi lần dịch thì MSB sẽ được đưa qua cờ CF và 0 đưa vào LSB. Vì dịch đi 2, nên hai bit cuối cùng ở phía bên phải nhất sẽ được thay thế bằng 0.

<figure><img src="https://vietzettt.files.wordpress.com/2022/07/image-52.png?w=433" alt=""><figcaption></figcaption></figure>

* Tương tự khi ta thực hiện lệnh SHR. Các bit sẽ di chuyển sang phải, sau mỗi lần dịch thì LSB sẽ được đưa qua cờ CF còn 0 đưa vào MSB.

<figure><img src="https://vietzettt.files.wordpress.com/2022/07/image-53.png?w=441" alt=""><figcaption></figcaption></figure>

* Lưu ý: Việc dịch bit trái (phải) tương ứng với phép nhân (chia) cho lũy thừa 2.
  * shl eax, 0x2 à EAX << 2 or EAX = EAX \* 4
  * shr eax, 0x2 à EAX >> 2 or EAX = EAX / 4

#### Lệnh nhảy <a href="#lenh-nhay" id="lenh-nhay"></a>

**Không điều kiện**

* **JMP A**; giống như lệnh goto trong lập trình bậc cao Lệnh **JMP** là một lệnh nhảy không phụ thuộc vào điều kiện và **A** sẽ là một địa chỉ bộ nhớ mà chúng ta muốn chương trình nhảy tới.
* **JMP SHORT** là một lệnh nhảy ngắn gồm có 2 bytes, có khả năng nhảy về phía trước và ngược lại. Hướng nhảy được chỉ định bởi giá trị của byte thứ hai vì byte đầu tiên là opcode (0xEB) của lệnh. Lệnh này không thể nhảy quá xa.

<figure><img src="https://vietzettt.files.wordpress.com/2022/07/image-54.png?w=614" alt=""><figcaption></figcaption></figure>

* Nếu thiết lập tùy chọn trong IDA để hiển thị opcode của các lệnh, chúng ta sẽ thấy opcode EB tương ứng với lệnh JMP và lệnh này sẽ nhảy 5 bước về phía trước kể từ vị trí kết thúc lệnh. Nghĩa là địa chỉ đích của lệnh được tính như sau:

<figure><img src="https://vietzettt.files.wordpress.com/2022/07/image-55.png?w=241" alt=""><figcaption></figcaption></figure>

* Lấy địa chỉ bắt đầu của lệnh cộng với 2 là số bytes chiếm bởi lệnh và sau đó cộng thêm 5 (byte thứ hai — bước nhảy). Rõ ràng, việc nhảy tới hoặc lùi với một byte duy nhất không cho chúng ta đạt được bước nhảy xa. Bước nhảy cao nhất sẽ là 0x7f.
* Trong Python không biết đay là bước nhảy tiến hay lùi từ các giá trị đã cho nên sau khi cộng giá trị ở *byte thứ hai* của bước nhảy thì ta thực hiện thêm **AND** kết quả tính toán được với **0xFFFFFFFF** nhằm xóa toàn bộ các bit lớn hơn một số 32bit. Từ đó ta nhận được địa chỉ sẽ nhảy tới:

<figure><img src="https://vietzettt.files.wordpress.com/2022/07/image-56.png?w=486" alt=""><figcaption></figcaption></figure>

**Lưu ý: 2 bytes “0xEB 0xFE” được gọi là 2 bytes “thần thánh”.** Bởi vì nó được sử dụng để lặp đi lặp lại chính nó và không thể thoát được. (**Infinite Loop**)

* **Đối với bước nhảy dài** – **Khoảng cách** sẽ được tính bằng công thức lấy địa chỉ cuối cùng trừ địa chỉ ban đầu – 5 (là chiều dài của lệnh) (**Final address – start address -5)**, kết quả nhận được chính là **dword** đứng cạnh **opcode** của bước nhảy dài.

<figure><img src="https://vietzettt.files.wordpress.com/2022/07/image-57.png?w=296" alt=""><figcaption></figcaption></figure>

<figure><img src="https://vietzettt.files.wordpress.com/2022/07/image-58.png?w=418" alt=""><figcaption></figcaption></figure>

* Chuyển sang hexa là **FFFFD94D**, đó là các bytes đứng cạnh opcode 0xe9, được bố trí theo kiểu **Little-endian:**

<figure><img src="https://vietzettt.files.wordpress.com/2022/07/image-59.png?w=614" alt=""><figcaption></figcaption></figure>

> **Little-endian** is an order in which the “little end” (least significant value in the sequence) is stored first.

<figure><img src="https://vietzettt.files.wordpress.com/2022/07/image-60.png?w=850" alt=""><figcaption></figcaption></figure>

**Cách tạo \_snapshot**\_\*\* cơ sở dữ liệu nhằm giúp ta quay lại trạng thái trước khi thay đổi:\*\*

* **File > Take database snapshot** > đặt tên cho snapshot là gì > ok
* Có thể quản lý snapshot thông qua **View > Database Snapshot Manager**. Tại đây chúng ta có thể xem danh sách tất cả các snapshot và ngày tạo nó, cùng với nút **Restore** cho phép chúng ta có thể trở về trạng thái chúng ta muốn từ những bản snapshot mà chúng ta đã lưu.

**Có điều kiện**

Thông thường các chương trình phải đưa ra quyết định rẽ nhánh thực thi chương trình, điều này sẽ căn cứ vào việc so sánh các giá trị để chuyển hướng thực hiện chương trình sang một điểm khác.

Ta có lệnh so sánh:

* **CMP A, B** ; so sánh toán hạng thứ nhất với toán hạng thứ 2 và bật các cờ trên thanh ghi **EFLAGS** dựa theo kết quả tính toán (*việc tính toán tương tự như lệnh **SUB** nhưng khác ở chỗ kết quả tính toán không đươc lưu lại*).

<figure><img src="https://vietzettt.files.wordpress.com/2022/07/image-62.png?w=614" alt=""><figcaption></figcaption></figure>

* Ngoại trừ các lệnh **JMP** và **NOP** được liệt kê trong bảng, các lệnh còn lại đều là các lệnh nhảy có điều kiện. Các lệnh nhảy này đều căn cứ vào kết quả của câu lệnh so sánh trước đó.
  * Các lệnh nhảy Above / Below được sử dụng cho so sánh số không dấu (**unsinged comparison**)
  * Các lệnh nhảy Greater than / Less than được sử dụng cho so sánh số có dấu (**singed comparison**)

<figure><img src="https://vietzettt.files.wordpress.com/2022/07/image-63.png?w=243" alt=""><figcaption></figcaption></figure>

<figure><img src="https://vietzettt.files.wordpress.com/2022/07/image-64.png?w=855" alt=""><figcaption></figcaption></figure>

Bên lề: bên cạnh việc so sánh sử dụng câu lệnh CMP, một câu lệnh khác cũng rất hay được sử dụng là **TEST**. Bản chất của lệnh **TEST** là tính toán logic thông qua việc **AND** hai toán hạng, căn cứ trên kết quả để bật cờ. Kết quả tính toán sẽ không được lưu lại.

**CALL và RET**

* **CALL** dùng để gọi một hàm
* **RET** dùng để quay trở về lệnh tiếp theo sẽ được thực hiện sau lệnh **CALL**.
* Lệnh **CALL** thực hiện lưu vào đỉnh của ngăn xếp (**Stack**) địa chỉ trở về sau khi thực hiện xong hàm, tức là địa chỉ bên dưới của kệnh **CALL**. Sau đó, nó sẽ thay đổi đĩa chỉ thanh ghi **EIP** bằng địa chỉ đã chỉ định trong câu lệnh. Ví dụ: trực tiếp **0x401da8**, thanh ghi dùng chung **eax**, bị trí bộ nhớ **call dword ptr \[0x402202c]**.
* Khi kết thúc hàm, sẽ thực hiện một lệnh **RET**, lệnh này có nhiệm vụ lấy địa chỉ trở về được lưu lại đỉnh của **stack** đưa vào thanh ghi **EIP** và nhảy tới địa chỉ này để tiếp tục thực hiện lệnh sau **CALL**.
* *Bên lề: liên quan đến hai lệnh **Call & Ret** chúng ta cần biết thêm về tập quán gọi hàm (hay từ chuyên môn là calling convention), bởi vì việc gọi hàm có thể khác nhau trong mã lệnh asm. Nó bao gồm thứ tự các tham số được đẩy vào ngăn xếp hay các thanh ghi, hàm gọi (caller) hay hàm được gọi (callee) chịu trách nhiệm dọn dẹp ngăn xếp khi hoàn tất hàm. Tùy thuộc vào trình biên dịch, có hai tập quán gọi hàm hay gặp nhất là cdecl (C declaration) và và stdcall.*
* **cdecl — tập quán gọi hàm phổ biến nhất:**
  * Tham số của hàm được đẩy vào stack theo chiều từ phải qua trái.
  * Kết quả trả về của hàm thường lưu vào thanh ghi EAX.
  * Hàm gọi (caller) chịu trách nhiệm dọn dẹp stack.
    * Ví dụ: \_cdecl int function(int arg1, int arg2, int arg3)

<figure><img src="https://vietzettt.files.wordpress.com/2022/07/image-65.png?w=614" alt=""><figcaption></figcaption></figure>

* **stdcall — thường được sử dụng bởi Microsoft C++, đặc biệt là Win32 API functions:**
  * Tham số của hàm được đẩy vào stack theo chiều từ phải qua trái.
  * Kết quả trả về của hàm thường lưu vào thanh ghi **EAX**.
  * Hàm được gọi (**callee**) chịu trách nhiệm dọn dẹp stack.
    * Ví dụ: \_stdcall int function(int a, int b, int c)

<figure><img src="https://vietzettt.files.wordpress.com/2022/07/image-66.png?w=614" alt=""><figcaption></figcaption></figure>

## Làm quen LOADER IDA <a href="#lam-quen-voi-loader-ida" id="lam-quen-voi-loader-ida"></a>

* **View > Open Subview > Segments**, chúng ta sẽ thấy có các **segments** được nạp tự động bởi LOADER.
  * các cột **RWX** thông báo cho ta biết section đó có quyền đọc (**READ** (R)), quyền ghi (**WRITE**) hay quyền thực thi (**Execute** (X)).
  * hai cột có tên **D** và **L** tương ứng với **DEBUGGER** và **LOADER**.
    * **D** cho ta thông tin khi chúng ta tải chương trình **DEBUGGER**
    * **L** cho ta thông tin là các **segments** này được nạp bởi trình **LOADER** của IDA
* Nếu muốn nạp thêm **HEADER** thì khi mở chương trình bằng IDA ta cần lựa chọn **Manual Load** và nhấn **OK**.
* Để tìm kiếm các strings trong IDA, chọn **View > Open Subview > Strings** (phím tắt là **Shift+F12**)
* *Bên lề: Như các bạn đã thực hành theo bài viết, toàn bộ quá trình thực hiện về bản chất là thay đổi luồng thực thi của chương trình hay nhánh thực hiện trong chương trình. Việc phân nhánh thực hiện này được quyết định vào điều kiện so sánh trước đó. Các bạn có kiến thức về lập trình cũng biết có hai cấu trúc lệnh rẽ nhánh phổ biến hay dùng là if và if-else:*
* *Với các khối lệnh trên thì ở mức bên dưới sẽ sử dụng các câu lệnh assembly là **test** và **cmp** để kiểm tra điều kiện, từ đó bật các cờ của thanh ghi **EFLAGS**. Dựa vào trạng thái của cờ thì các lệnh nhảy jcc sẽ nhảy tới các block code tương ứng. **Tuy nhiên có một lưu ý quan trọng khi các bạn đọc mã asm là điều kiện sẽ được đảo ngược lại**. Tức là, ở mã nguồn của chương trình, bạn viết lệnh kiểm tra một biến **var\_1** bằng **0**, nhưng sau khi biên dịch chương trình thì trình compiler sẽ đảo ngược lại điều kiện này, do đó lệnh điều kiện (ở đây là lệnh nhảy) sẽ kiểm tra biến **var\_1** khác **0**.*

<figure><img src="https://vietzettt.files.wordpress.com/2022/07/image-67.png?w=614" alt=""><figcaption></figcaption></figure>

<figure><img src="https://vietzettt.files.wordpress.com/2022/07/image-68.png?w=643" alt=""><figcaption></figcaption></figure>

* Từ khóa “**dup**” có nghĩa là lặp lại các dữ liệu trong ngoặc
* *Bên lề: thông tin bổ sung thêm dành cho những ai chưa biết, đó là về giá trị cookie mà trong code chương trình không hề có. Đây là cơ chế bảo vệ của chương trình tránh khỏi lỗi stack overflow hay còn được gọi theo thuật ngữ chuyên ngành là Stack Canary. Giá trị này được lưu ở đầu của mỗi hàm và sẽ được kiểm tra trước khi thoát khỏi hàm. Chúng ta sẽ đặt tên cho nó là SECURITY\_COOKIE:*

<figure><img src="https://vietzettt.files.wordpress.com/2022/07/image-69.png?w=614" alt=""><figcaption></figcaption></figure>

### Debugger <a href="#debugger" id="debugger"></a>

* **Debugger > Select Debugger**
* có thể lựa chọn trình debugger mặc định mỗi khi load một file mới bằng các tích chọn **Set as default debugger**
* Trong **Debuggers > Debuggers options** chúng ta có các thiết lập như sau:
  * Lựa chọn **Suspend on process entry point** để trình **debugger** dừng lại tại **entry point**
* đặt một **Breakpoint** bằng cách nhấn **F2** tại địa chỉ này

<figure><img src="https://vietzettt.files.wordpress.com/2022/07/image-70.png?w=614" alt=""><figcaption></figcaption></figure>

* Đây là các thông tin về **File Offset** (Offset của file thực thi khi lưu trên disk) và địa chỉ bộ nhớ (*Virtual Address khi file được load vào memory*). Nếu chúng ta mở crackme bằng một trình HexEditor, ví dụ như HxD, ta sẽ thấy tại File Offset là 0x600 có cùng opcode như đã thấy tại màn hình disassembly:

<figure><img src="https://vietzettt.files.wordpress.com/2022/07/image-71.png?w=614" alt=""><figcaption></figcaption></figure>

* Trong cửa sổ **View > Open Subview > Segments**, chúng ta thấy có ba segments được nạp bởi Loader, trong đó **CODE segment** được nạp vào **0x401000**, tiếp theo là **DATA segment** và **.idata** segment. Bất kỳ sự thay đổi nào chúng ta thực hiện với ba segments này đều được lưu lại vì chúng được nạp bởi IDA Loader, nhưng các thay đổi bên ngoài ba segments này sẽ bị mất bởi vì chúng chỉ là các mô-đun nạp bởi Debugger và sẽ không được lưu vào cơ sở dữ liệu của IDA. Như vậy, các mô-đun mà chúng ta đang phân tích và muốn debug phải nằm trong Loader hoặc có xuất hiện chữ L ở đó, rõ ràng chúng cũng sẽ được tải trong Debugger, nhưng có L đồng nghĩa là chúng sẽ ở trong cả hai chế độ và ta vừa có thể phân tích tĩnh tại Loader và vừa debug được bằng Debugger mà không bị mất thông tin.

Ta có thể sự dụng trình debugger remote sever chạy và lắng nghe tại một địa chỉ và cổng tương ứng.

> Ta chép file **win32/win64\_remote.exe** từ thư mục đã cài đặt IDA trên máy **Main ({IDA\_path}/dbgsrv)** vào máy **target.**
>
> Ta thiết lập cấu hình: ở *Hostname* nhập vào **địa chỉ IP** của **IDA remote debug server** và **port** tương ứng và *parameters* thì ta chỉ cần thiết lập thư mục giống *Directory* mặc định.

### FLAGS <a href="#flags" id="flags"></a>

#### CARRY FLAG (CF) <a href="#carry-flag-cf" id="carry-flag-cf"></a>

* cờ này được kích hoạt trong quá trình tính toán của các số. Khi kết quả là số âm hoặc vượt quá mức biểu diễn tối đa trong trường hợp phép cộng. Hay nói cách khác, cờ CF được thiết lập là 1 khi có nhớ từ bit MSB trong phép cộng hoặc có vay vào bit MSB trong phép trừ.
* Như vậy quy tắc để bật cờ CF trong phép toán nhị phân/số nguyên là:
  * Cờ carry được bật nếu phép cộng 2 số dẫn đến bit có trọng số lớn nhất bị đẩy ra ngoài – vượt quá khả năng biểu diễn. Ví dụ: 1111 + 0001 = 0000 (CF được bật)
  * Cờ carry được bật nếu phép trừ 2 số dẫn tới việc cần phải vay vào bit có trọng số lớn nhất để trừ. Ví dụ: 0000 – 0001 = 1111 (CF được bật).

#### OVERFLOW FLAG (OF) <a href="#overflow-flag-of" id="overflow-flag-of"></a>

* Cờ **OF (cờ tràn)** cũng tương tự như cờ **CF**, nhưng đối với các tính toán liên quan đến số có dấu (). Cờ **OF** được thiết lập 1 khi xảy ra tràn, ngược lại nó bằng 0. Hiện tượng tràn gắn liền với một sự thật là phạm vi của các số biểu diễn trong máy tính có giới hạn. Ví dụ, phạm vi của các số thập phân có dấu có thể biểu diễn bằng *một word 16 bit là từ-32768 đến 32767*, với *một byte 8 bit thì phạm vi là từ-128 đến 127.* Đối với các *số không dấu thì phạm vi từ 0 tới 65535 cho một word* và *từ 0 đến 255 cho một byte.* Nếu kết quả của một phép tính nằm ngoài phạm vi thì hiện tượng tràn sẽ xảy ra và kết quả nhận được bị cắt bớt sẽ không phải là kết quả đúng.
* Cờ OF được kích hoạt khi có lỗi xảy ra trong quá trình tính toán với dấu.
* Như vậy, một số quy tắc để bật OF (overflow) trong phép toán nhị phân/số nguyên là:
  * Nếu tổng của hai số với bit dấu tắt tạo ra kết quả là một số với bit dấu bật, cờ “overflow” sẽ được bật. Ví dụ: 0100 + 0100 = 1000 (OF được bật)
  * Nếu tổng của hai số với bit dấu bật tạo ra kết quả là một số với bit dấu tắt, cờ “overflow” sẽ được bật. Ví dụ: 1000 + 1000 = 0000 (OF được bật)

#### SIGN FLAG (SF) <a href="#sign-flag-sf" id="sign-flag-sf"></a>

* Nó được kích hoạt khi kết quả của việc tính toán là **số âm**, trong mọi trường hợp.
* Hay nói cách khác, cờ **SF (cờ dấu)** được thiết lập 1 khi bit msb của kết quả bằng 1, có nghĩa là kết quả là âm nếu ta làm việc vơi số có dấu.

> Nếu đang làm việc với số có dấu thì chỉ có cờ OF đáng quan tâm trong khi cờ CF có thể bỏ qua, ngược lại khi làm việc với số không dấu thì cờ quan trọng là CF chứ không phải là OF.

#### ZERO FLAG (ZF) <a href="#zero-flag-zf" id="zero-flag-zf"></a>

* Cờ này không phụ thuộc vào dấu
* Nó được kích hoạt khi:
  * Phép so sánh (sử dụng một phép trừ) khi cả hai toán hạng đều bằng nhau
  * Khi tăng hoặc giảm và kết quả bằng 0, hoặc trong một phép trừ mà kết quả có được bằng 0.

> Chúng ta chỉ cần nhớ rằng, nếu hai toán hạng bằng nhau, lệnh JZ sẽ thực hiện. Nếu toán hạng đầu nhỏ hơn và là unsigned, thì sẽ nhảy nếu nó là lệnh JB. Còn nếu toán hạng đầu nhỏ hơn nhưng ở kiểu signed thì sẽ nhảy nếu là lệnh JL.

### IpyIDA <a href="#ipyida" id="ipyida"></a>

> sử dụng nó thông qua **Edit > Plugins > IpyIDA**, cửa sổ **Ipython Console** xuất hiện
>
> Để **tra cứu các tính năng của plugin** này, ta nhấn phím **?**
>
> Để biết thông tin của đối tượng ta nhập tên đối tượng kèm dấu **hỏi chấm** rồi nhấn enter. Ví dụ **object\_name?**
>
> Hoàn thành **namespace local** ta sử dụng phím **Tab**
>
> **Ctrl +p / Ctrl +n** dùng để chuyển qua lại các lệnh trước và sau
>
> **%hist** để hiện thị lịch sử các index
>
> **ESC** để xóa thông tin hiện thị trên màn hình
>
> **%edit** sẽ mở ứng dụng notepad của windows
>
> **%edit x-y** sẽ mở một notepad chứa các lệnh đã gõ nằm trong khoảng đó x-y (có thể sử dụng để lấy lại các lệnh đã dùng trong quá trình dùng để tạo 1 file sau sử dụng lại cho mục đích tương tự

### Unpacked <a href="#unpacked" id="unpacked"></a>

Khi chúng ta làm việc với một chương trình bị packed, ta sẽ không biết **OEP** của nó ở đâu bởi ta không có file gốc ban đầu, do đó chúng ta sẽ phải áp dụng các kĩ thuật để tìm ra **OEP** (**Original Entry Point**).

* Khi ta biết địa chỉ OEP thì ta sẽ đặt bp tại lệnh JMP tới địa chỉ đó rồi debugger rồi F8 để trace qua lệnh này
* Sau đó ta nhận được stub được giải nén code và nhảy tới vị trí thực thi.
* Sau đó để có thể hiện thị dạng đồ họa thì ta cần chuyển dạng **loc\_** sang **sub\_** bằng cách nhấp chuột phải ở góc bên trái của màn hình IDA và chọn **Reanalyze program**.
* Dump file: Hành động này có thể hiểu là sau khi tới được OEP thì toàn bộ code gốc cùng các hàm APIs đã được bung ra đầy đủ trên memory lúc này ta cần thực hiện thao tác dump để lưu thành một file.
* Ở đây sử dụng IDC Script để thực hiện công việc này:

```c
// dump file sử dụng IDC Script
#include<ida.idc>

static main()
{
auto fp, ea;

fp = fopen("dumped.bin", "wb");

// địa chỉ ban đầu và địa chỉ cuối được load (tùy thuộc vào mỗi chương trình)
for (ea = 0x400000; ea < 0x40b200; ea++) 

fputc(Byte(ea), fp);
}
```

```python
# Sử dụng Python Script
import idaapi
import idc
import struct

bin =""
file=open("dumped.bin", "wb")

for ea in range(0x400000,0x46e000,4):
    bin+=struct.pack("<L", idc.Dword(ea))

file.write(bin)
file.close()
```

* Tiếp theo ta sử dụng trình PE Editor để fix lại file dump này.
* Sau đó ta sd trình Scylla để rebuild lại IAT là xong.

Đối với các trình packer đơn giản, hầu hết chúng đều bắt đầu với lệnh **PUSHAD** để lưu trạng thái ban đầu của các thanh ghi khi bắt đầu và sử dụng **POPAD** để khôi phục lại các giá trị đã lưu, trước khi nhảy tới **OEP** để thực hiện code của chương trình đã “rã code” hoàn toàn trong bộ nhớ.

> Vậy phương pháp là ta sẽ đặt Breakpoint tại lệnh bên dưới lệnh PUSHAD sau đó thực hiện chương trình.
>
> Vào trình debugger quan sát giá trị các thanh ghi đã được lưu vào cửa sổ Stack ta đặt một bp ở dòng đầu tiên để dừng lại ở đó vì nơi các giá trị của các thanh ghi lưu bởi **PUSHAD** sẽ được khôi phục lại bằng **POPAD** (*On Read and Write* chứ không phải *On execution*)
>
> Tiếp tục F9 để tiếp tục chương trình
>
> Sau đó sẽ thấy chương trình dừng lại ngay sau lệnh **POPAD** khi nói khôi phục các thanh ghi và cũng thấy rằng Stub này sẽ nhảy tới địa chỉ **OEP** tại địa chỉ xác định thông qua lệnh **PUSH & RET** (tương tự như lệnh **JMP**)

### **Các phím tắt thông dùng trong IDA pro** <a href="#cac-phim-tat-thong-dung-trong-ida-pro" id="cac-phim-tat-thong-dung-trong-ida-pro"></a>

| Tổ hợp           | Tên công cụ             | Chức năng                                   |
| ---------------- | ----------------------- | ------------------------------------------- |
| **?**            | Calculator              | Tính toán các giá                           |
| **Alt+C**        | next code               | Tìm kiếm lệnh kế tiếp đã được hiểu là code  |
| **Ctrl+ D**      | next data               | Tìm kiếm địa chỉ tiếp theo là data          |
| **Ctrl +U**      | next unexplored         |                                             |
| **Q**            | hexadecimal             | Chuyển toán hạng hiện tại thành hệ 16 hex   |
| **O**            | offset (data segment)   | chuyển về dạng offset                       |
| **G**            | Jump to address         | nhảy đến địa chỉ nào đó                     |
| **X**            | Jump to xref to operand | hiện thị các lệnh sử dụng tới địa chỉ đó    |
| **Spaceber**     | Graph                   | Chuyển sang chế độ đồ họa                   |
| **Ctrl+Alt+P**   | Patched Bytes           | hiện thị các địa chỉ có lệnh đã bị thay đổi |
| **A**            | strings                 | chuyển đổi thành chuỗi                      |
| **D**            | Data                    | để thay đổi kiểu dữ liệu sang data          |
| **Shift+F12**    | View > Strings          | mở cửa sổ chứa strings trong chương trình   |
| **Shift+F7**     | Segments                | mở cửa sổ hiện thị các segments của ct      |
| **Alt+T**        | text                    | tìm text mà ta nhập vào                     |
| **F2**           | Breakpointtoggle        | Tạo 1 điểm ngắt chương trình khi debug      |
| **F9**           | Start process           | bắt đầu ct debugger                         |
| **F8**           | Step over               | Bỏ qua lệnh (debbuger)                      |
| **F7**           | Step into               | Bước vào câu lệnh (debugger)                |
| **Ctrl+F**       | Find                    | tìm kiếm                                    |
| **N**            | Rename                  | Đổi tên biến hàm …                          |
| **Ctrl+Alt+B**   | Breakpoints list        | hiện thị danh sách các bp                   |
| **Alt+M**        | Mark position           | đánh dấu vị trí                             |
| **Ctrl+M**       | Jump to marked position | nhảy tới địa chỉ đã được đánh dấu           |
| **Alt+F7**       | Script file             | mở file                                     |
| **Ctrl+W**       | Save                    |                                             |
| **Ctrl+Shift+W** | Take database snapshot  | tạo 1 snapshot mới                          |

### References <a href="#references" id="references"></a>

> <https://kienbigmummy.medium.com/>


---

# 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/tools/ida-pro.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.
