Saturday 8 November 2014

Xây dựng trình cắm cho IDA Pro - Phần 7

4.2.5.3 Dữ liệu
Nếu iscode trong cấu trúc xrefblk_t được đặt giá trị 0, thì tham chiếu thuộc kiểu tham chiếu dữ liệu. Đây là những giá trị kiểu thành viên có thể khi bạn làm việc với tham chiếu dữ liệu. Những ghi chú trong kiểu liệt kê này cũng được lấy từ xref.hpp
// The following DATA xref types are defined:

enum dref_t
{
dr_U, // Unknown -- for compatibility with old
// versions. Should not be used anymore.
dr_O, // Offset
// The reference uses 'offset' of data
// rather than its value
// OR
// The reference appeared because the "OFFSET"
// flag of instruction is set.
// The meaning of this type is IDP dependent.
dr_W, // Write access
dr_R, // Read access
dr_T, // Text (for forced operands only)
// Name of data is used in manual operand
dr_I, // Informational
// (a derived java class references its base
// class informatonally)
};


Ghi nhớ rằng khi bạn thấy một đoạn như sau trong mã dissasm, bạn thật sự là đang thấy một tham chiếu dữ liệu, với 712D9BD9 tham chiếu đến 712C119C:
.idata:712C119C extrn wsprintfA:dword
.text:712D9BD9 call ds:wsprintfA


Trong trường hợp ở trên, kiểu thành viên của xrefblk_t sẽ mang giá trị dr_R, bởi ví nó đơn giản là đọc giá trị tại địa chỉ được mô tả bởi ds:wsprintfA. Một tham chiếu dữ liệu khác bên dưới, nơi mà chỉ thị push tại địa chỉ 712EABE2 tham chiếu đến chuỗi ở địa chỉ 712C255C:
.text:712C255C aversion:
.text:712C255C Unicode 0, <Version>,0
.text:712EABE2 push offset aversion


Kiểu thành viên của xrefblk_t sẽ là dr_o trong trường hợp này, bởi vì nó truy cập dữ liệu thông qua độ lệch (offset).
4.3 Những byte cờ
Với mỗi byte trong tập tin nhị phân, IDA ghi lại tương ứng 4 byte (32-bit) cờ, tất cả được lưu trữ trong tập tin *.idl. Byte cuối cùng của 4 byte cờ là byte thật sự tại địa chỉ trong tập tin nhị phân.
Ví dụ, chỉ thị bên dưới cần một byte (0x55) trong tập tin đang được đảo mã:
.text:010060FA push ebp
Cờ của IDA cho địa chỉ trên trong file được đảo mã là 0x00010755:0001007 là thành phần của cờ và 55 là giá trị byte ở tại địa chỉ của tập tin. Ghi nhớ rằng địa chỉ không liên quan đến cờ, vì vậy không thể dẫn xuất cờ từ địa chỉ hoặc từ chính byte đó – bạn cần phải sử dụng get_glags_novalue() để lấy giá trị flag cho một địa chỉ (sẽ nói rõ hơn trong phần sau)
Hiển nhiên, không phải tất cả các chỉ thị đều có kích thước một byte; ví dụ chi thị bên dưới, nó sẽ chiếm 3 byte (0x83 0xEC 0x14). Chỉ thị bên dưới vì vậy sẽ được trải dài qua 3 địa chỉ 0x010011DE, 0x010011DF and 0x010011E0:
.text:010011DE sub esp, 14h
.text:010011E1 ...
Đây là những cờ tương ứng cho mỗi byte của chỉ thị này:
010011DE: 41010783
010011DF: 001003EC
010011E0: 00100314


Bởi vì ba byte này thuộc về một chỉ thị, byte đầu tiên của chỉ thị được xem như là đầu, và 2 byte sau được xem là đuôi. Một lần nữa, hãy chú ý đến byte cuối cùng của mỗi cờ được đặt tương ứng với những byte của chỉ thị (0x83, 0xEC, 0x14).
Tất cả các cờ được định nghĩa trong tập tin bytes.hpp, và bạn có thể kiểm tra những cờ đó có được bật hay không bằng việc sử dụng tập cờ được trả về từ hàm get_flags_novalue(ea_t ea) như là một tham số tương ứng với hàm kiểm tra cờ. Sau đây là một vài cờ cùng với những hàm kiểm tra của nó mà sẽ kiểm tra nó có tồn tại. Một vài hàm được mô tả trong Chương 5 – Hàm và những thứ khác bạn có thể xem trong tập tin bytes.hpp
Flag Name
Flag
Indication
Wrapper function
FF_CODE
0x00000600L
Is the byte code?
isCode()
FF_DATA
0x00000400L
Is the byte data?
isData()
FF TAIL
0x00000200L
Is this byte a part (non-head) of an instruction data chunk?
isTail()
FF UNK
0x00000000L
Was IDA unable to classify this byte?
isUnknown()
FF COMM
0x00000800L
Is the byte commented?
has cmt()
FF REF
0x00001000L
Is the byte referenced elsewhere?
hasRef()
FF NAME
0x00004000L
Is the byte named?
has name()
FF FLOW
0x00010000L
Does the previous instruction flow here?
isFlow()


Trở về với ví dụ đầu tiên “push ebp” bên trên, nếu chúng ta tuần tự kiểm tra mỗi cờ được trả về từ hàm get_flags_novalue(0x010060FA) qua một loạt các cờ ở trên, chúng ta sẽ có được những kết quả sau:
0x00010755 & 0x00000600 (ff_code) = 0x00000600. Chúng ta thấy đây là mã.
0x00010755 & 0x00000800 (ff_comm) = 0x00000000. Không phải là ghi chú.
Ví dụ bên trên chỉ có mục đích mô tả - không nên làm theo cách ở trên trong plugin của bạn. Như đã được khuyến cáo, bạn luôn luôn nên sử dụng những hàm trợ giúp để kiểm tra một giá trị của cờ có được bật hoặc tắt. Ví dụ tiếp theo sẽ trả về những cờ cho một loạt các địa chỉ đầu tại vị trí con trỏ của bạn trong IDA.
#include <bytes.hpp>
#include <kernwin.hpp>
msg("%08x\n", get_flags_novalue(get_screen_ea()));


4.4 Trình gỡ rối
Một trong những chức năng mạnh của IDA là khả năng tương tác với trình gỡ rối IDA, và nếu bạn không cài đặt trình plugin gỡ rối nào, nó sẽ trở thành một plugin gỡ rối với IDA. Một số lớn plugin gỡ rối được mặc định kèm theo IDA, cùng với những trình khác, và bạn có thể tìm thấy chúng trong thư mục plugins của IDA.
Tên tập tin
Mô tả
win32 user.plw
Windows local debugger
win32 stub.plw
Windows remote debugger
linux user.plw
Linux local debugger (only when running IDA in Linux)
linux stub.plw
Linux remote debugger
bochs user.plw
Bochs local debugger
windbg user.plw
WinDbg local debugger
mac stub.plw
Mac remote debugger


Chúng được tự động nạp bởi IDA và sẵn sàng khởi động qua menu Debugger > Start Process. Kể từ đây, thuật ngữ “debugger” sẽ thể hiện cho bất cứ công cụ nào bạn sử dụng ở trên (IDA sẽ chọn một trình gỡ rối phù hợp nhất cho bạn mặc định)
Như đã đề cập từ trước, việc viết modun gỡ rối cho IDA có thể hoàn toàn thực hiện được, nhưng đừng nên lẫn lộn với việc viết những modun plugin tương tác với trình gỡ rối. Kiểu plugin thứ 2 sẽ được mô tả bên dưới.
Bên cạnh tất cả các hàm được cung cấp để tương tác với trình gỡ rối, mà sẽ được trình bày trong Chương 5 – Hàm, có một vài cấu trúc dữ liệu và lớp quan trong mà bạn cần phải hiểu trước khi tìm hiểu sâu hơn.
4.4.1 Cấu trúc debugger_t
Cấu trúc debugger_t, được định nghĩa trong tập tin idd.hpp và được export dạng *dbg, đại diện cho một trình gỡ rối đang được kích hoạt, và nó sẵn sàng khi trình gỡ rối được nạp ( ngay lúc khởi động, không phải là lúc bạn chạy trình gỡ rối).
struct debugger_t
{
int version; // Expected kernel version,
// should be IDD_INTERFACE_VERSION
const char *name; // Short debugger name like win32 or linux
int id; // Debugger API module id
#define DEBUGGER_ID_X86_IA32_WIN32_USER 0 // Userland win32 procs (w32 debugging APIs)
#define DEBUGGER_ID_X86_IA32_LINUX_USER 1 // Userland linux processes (ptrace())
#define DEBUGGER_ID_ARM_WINCE_USER 2 // Windows CE ARM
#define DEBUGGER_ID_X86_IA32_MACOSX_USER 3 // Userland MAC OS X processes
#define DEBUGGER_ID_ARM_EPOC_USER 4 // Symbian OS
#define DEBUGGER_ID_ARM_IPHONE_USER 5 // iPhone 1.x
#define DEBUGGER_ID_X86_IA32_BOCHS 6 // BochsDbg.exe 32
#define DEBUGGER_ID_6811_EMULATOR 7 // MC6812 emulator (beta)
#define DEBUGGER_ID_GDB_USER 8 // GDB remote
#define DEBUGGER_ID_WINDBG 9 // WinDBG using Microsoft Debug engine
#define DEBUGGER_ID_X86_DOSBOX_EMULATOR 10 // Dosbox MS-DOS emulator
#define DEBUGGER_ID_ARM_LINUX_USER 11 // Userland arm linux

register_info_t *registers; // Array of registers
int registers_size; // Number of registers

};


Như một modun plugin, bạn hầu như cần phải truy cập đến một biến được đặt tên, có thể là để kiểm tra những trình gỡ rối nào mà plugin bạn đang làm việc. Biến registers và register_size cũng hữu dụng để lấy danh sách của những thanh ghi sẵn sàng (xem ở phần tiếp theo), cũng như một vài phương thức khác sẽ được trình bày trong Chương 5.
4.4.2 Thanh ghi
Nhiệm vụ chung được đảm nhiệm bởi trình gỡ rối là để truy cập và thao tác với giá trị các thanh ghi. Trong IDA SDK, một thanh ghi được mô tả bởi một cấu trúc register_info_t, và giá trị được chứa trong một thanh ghi được mô ta bởi cấu trúc regval_t. Bên dưới là một đoạn vắn tắt về cấu trúc register_info_t, được định nghĩa trong tập tin idd.hpp.
struct register_info_t {
const char *name; // Register full name (EBX, etc)
ulong flags; // Register special features,
// which can be any conbination
// of the below.
#define REGISTER_READONLY 0x0001 // The user can’t modify
// the current value of this
//register
#define REGISTER_IP 0x0002 // instruction pointer (EIP)
#define REGISTER_SP 0x0004 // stack pointer (ESP)
#define REGISTER_FP 0x0008 // frame pointer (EBP)
#define REGISTER_ADDRESS 0x0010 // register can contain an address
};
Thực thể duy nhất của cấu trúc này có thể truy cập là một thành viên registers của biến *dbg (một thực thể của lớp debugger_t), bởi vậy, nó tùy thuộc vào trình gỡ rối mà bạn làm việc mà danh sách thanh ghi có thể khác nhau trên hệ thống của bạn.
Để lấy giá trị của bất cứ thanh ghi nào, hiển nhiên là trình gỡ rối phải đang chạy. Những chức năng để đọc và thao tác với giá trị các thanh ghi sẽ được mô tả chi tiết trong Chương 5 – Hàm, nhưng bây giờ, tất cả bạn cần biết để lấy giá trị của thanh ghi là sử dụng biến thành viên ival của lớp regval_t, hoặc sử dụng fval nếu bạn làm việc với kiểu số thực. Bên dưới là cấu trúc regval_t, được định nghĩa trong idd.hpp.
struct regval_t {
ulonglong ival; // Integer value
ushort fval[6]; // Floating point value in the internal
// representation (see ieee.h)
};


ival/fval sẽ phụ thuộc trực tiếp vào những gì được lưu trữ trong thanh ghi, bởi vậy nếu EBX chứa 0X, ival (một khi được lấy giá trị thông qua get_reg_val()), cũng sẽ chứa 0xD
Ví dụ sau đây sẽ lặp qua tất cả các thanh ghi, và hiển thị giá trị của mỗi thanh ghi. Nếu bạn chạy nó ngoài chế độ dò lỗi, giá trị mỗi thanh ghi sẽ là 0xFFFFFFF:
#include <dbg.hpp>
// Loop through all registers
for (int i = 0; i < dbg->registers size; i++) { regval_t val;
// Get the value stored in the register
get reg val((dbg->registers+i)->name, &val);
msg("%s: %08a\n", (dbg->registers+i)->name, val.ival);
}


Sau khi đặt một điểm dừng và chạy chương trình thực thi của tôi trong trình dò lỗi, tôi gọi plugin và thấy kết quả được hiển thị như bên dưới:
EAX
00000001
EBX
00000000
ECX
00000022
EDX
00000004
ESI
00251E50
EDI
0093A21C
EBP
0006F9FC



No comments: