Saturday 8 November 2014

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

4.2.3 Đoạn và hàm
Như đã được đề cập ở phần trước, lớp segment_t và func_t được mở rộng từ lớp area_t. Điều này có nghĩa rằng tất cả những thành viên trong area_t được bao gồm cùng với những hàm riêng biệt trong những lớp kế thừa được thêm vào.
4.2.3.1 Đoạn
Lớp segment_t được định nghĩa trong segment.hpp. Đây là khai báo vắn tắt của nó.
class segment_t : public area_t
{
public:
..............

#define SEG_NORM 0 // Unknown type, no assumptions
#define SEG_XTRN 1 // * segment with 'extern' definitions
// no instructions are allowed
#define SEG_CODE 2 // code segment
#define SEG_DATA 3 // data segment
#define SEG_IMP 4 // java: implementation segment
#define SEG_GRP 6 // * group of segments
#define SEG_NULL 7 // zero-length segment
#define SEG_UNDF 8 // undefined segment type (not used)
#define SEG_BSS 9 // uninitialized segment
#define SEG_ABSSYM 10 // * segment with definitions of absolute symbols
#define SEG_COMM 11 // * segment with communal definitions
#define SEG_IMEM 12 // internal processor memory & sfr (8051)
..............
}; // total 95 bytes



SEG_XTRN là một kiểu đoạn đặc biệt (không tồn tại trong lưu trữ vật lý) được tạo bởi IDA khi đảo mã một tập tin, cùng với những mô tả về các phần tử vật lý khác của tập tin được nạp. Ví dụ với hầu hết tập tin thực thi được nạp vào IDA, kiểu của phân đoạn .text sẽ là SEG_CODE và giá trị của quyền hạn sẽ là SEGPERM_EXEC | SEGPERM_READ.
Để duyệt qua tất cả các đoạn trong một tập tin nhị phân, in ra tên và địa chỉ của mỗi một đoạn vào của sổ Log của IDA, bạn có thể sử dụng đoạn code sau.
#include <segment.hpp>
// Đoạn mã này sẽ chỉ chạy trong IDA 4.9+ bởi vì get_segm_name() được thay đổi
// trong bản 4.9. Xem Chương 5 để biết thêm thông tin
// get_segm_qty() trả về số lượng của tổng số phân đoạn
// của tập tin được nạp.
for (int i = 0; i < get_segm_qty(); i++)
{
char segmName[MAXSTR];
// getnseg() trả về một cấu trúc segment_t cho đoạn có số
// được cung cấp
segment_t *curSeg = getnseg(i);
// get_segm_name() lấy tên của đoạn
// msg() hiển thị thông điệp ra cửa sổ Log của IDA
get_segm_name(curSeg, segmName, sizeof(segmName)-1);
msg("%s @ %a\n", segmName, curSeg->startEA);
}



Kết xuất sẽ trông như thế này khi bạn chạy một tập tin thực thi Windows cơ bản.
_idata @ 1001000 text @ 1001388 data @ 100A000


Hiểu những gì mà những hàm ở trên làm ở giai đoạn này không quan trọng – chúng sẽ được mô tả chi tiết trong Chương 5 – Hàm
4.2.3.2 Hàm
Một hàm được đại diện bởi lớp func_t, được định nghĩa trong funcs.hpp. Trước khi đi vào chi tiết của lớp func_t, chúng ta cần phải biết những khái niệm về khối hàm, khối cha và khối con.
Hàm hầu hết là những khối liên tục của mã trong một tập tin nhị phân được phân tích, và thường được mô tả bởi một khối đơn. Tuy nhiên, có nhiều trường hợp mà khi tối ưu, trình dịch di chuyển mã lộn xộn, và bởi vậy, một hàm bị chia thành nhiều khối mà mã bị chia cắt bởi những hàm khác. Những khối như vậy được biết như là “khối con”, và những khối tham chiếu đến những khối này (bởi lệnh JMP hoặc một thứ gì đó tương tự) được biết như là những khối cha. Điều dễ nhầm lẫn là tất cả các khối trên đều thuộc kiểu func_type, và bởi vận cần phải kiểm tra những biến cờ của func_t để xác định một thực thể của func_t là khối con hay khối cha.
Bên dưới là một phần vắn tắt của lớp, func_t, cùng với một vài chú tích ngắn gọn được lấy từ funcs.hpp
//------------------------------------------------------------------------
// A function is a set of continuous ranges of addresses with characteristics:

class func_t : public area_t
{
public:
ushort flags;
#define FUNC_NORET 0x00000001 // Function doesn't return
#define FUNC_FAR 0x00000002 // Far function
#define FUNC_LIB 0x00000004 // Library function
................
};



Bởi vì những hàm cũng là những vùng giống như đoạn, duyệt qua mỗi hàm là một tiến trình tương tự như làm việc với các đoạn. Ví dụ tiếp theo sẽ liệt kê tất cả các hàm và địa chỉ của chúng trong một tập tin được đảo mã, hiển thị kết quả ra cửa sổ Log của IDA.
#include <funcs.hpp>

// get_func_qty() trả về số lượng hàm trong tập tin được nạp
for (int f = 0; f < get_func_qty(); f++)
{
// getn_func() trả về cấu trúc func_t cho hàm có số thứ tự
// được cung cấp
func_t *curFunc = getn_func(f);
char funcName[MAXSTR];
// get_func_name lấy tên của hàm và lưu trữ kết quả vào
// funcName
get_func_name(curFunc->startEA, funcName,
sizeof(funcName)-l);
msg("%s:\t%a\n", funcName, curFunc->startEA);
}




Hàm này sẽ hiển thị như thế này :
WinMain@16: 100138D
memset: 1002379
memcpy: 1002E50


4.2.4 Mô tả mã
Những chỉ thị hợp ngữ bao gồm, trong hầu hết trường hợp, những từ gợi nhớ (MOV, PUSH, CALL, vv) và toán tử (EAX, [EBP+0xAh], 0x0Fh), vv). Một vài toán tử có thể có nhiều hình thức, và một vài chỉ thị thậm chí không có toán tử. Tất cả điều này được mô tả rất tường minh trong IDA SDK.
Để bắt đầu, hãy xem xét kiểu insn_t, kiểu này đại diện cho toàn bộ các chỉ thị, ví dụ “MOV EAX, 0x0A”. insn_t bao gồm các biến thành viên, lên đến 6 op_t (cho mỗi toán tử được cung cấp cho chỉ thị), và mỗi toán tử có thể là một kiểu optype_t (thanh ghi chung, thanh ghi tức thời, etc).
Hãy xem xét từ dưới lên mỗi thành phần. Chúng được định nghĩa tất cả trong ua.hpp.
4.2.4.1 Kiểu toán tử
optype_t mô tả kiểu của toán tử được sử dụng cho một chỉ thị. Đây là những kiểu toán tử thông dụng. Những mô tả được lấy từ định nghĩa của lớp optype_t trong tập tin ua.hpp
4.2.4.2 Toán tử
Lớp op_t thể hiện một toán tử riêng lẻ được truyền cho một chỉ thị. Bên dưới là một đoạn mô tả của lớp.
class op_t
{
public:
.............
uchar n; // number of operand (0,1,2)
optype_t type; // type of operand
uchar flags;
.............
char specflag1;
char specflag2;
char specflag3;
char specflag4;
};



Ví dụ, toán tử của biểu thức [esp + 8] sẽ cho kết quả là o_displ, reg có giá trị 4 (là giá trị mà thanh ghi ESP chứa) và addr là 8, bởi vì bạn truy cập vào bất cứ thứ gì nằm tại vị trí 8 byte kể từ vị trí hiện tại của con trỏ stack, nên nó sẽ là dạng tham chiếu bộ nhớ. Bạn có thể sử dụng những đoạn mã ngắn sau để lấy giá trị của op_t của toán tử đầu tiên của chỉ thị tại vị trí con trỏ với IDA:
Toán tử Mô tả Ví dụ minh họa (toán tử tương ứng được tô đậm)
o_void Không có toán tử pusha
o_reg Bất cứ thanh ghi nào (ESI, EAX, CS, …) dec eax
o_mem Tham chiếu dữ liệu trực tiếp trên bộ nhớ, được xác định vào lúc biên dịch mov eax, ds:1001h
o_phrase Tham chiếu bộ nhớ thông qua nội dung thanh ghi
push dword ptr [eax]
lea esi, [esi+eax*2]
o_displ Tham chiếu bộ nhớ dựa vào nội dung thanh ghi cộng với khoảng dịch chuyển.
push [esp+8]
movzx eax, word ptr [eax+5Ch]
o_imm Giá trị tức thời
add ebx, 10h
push offset myVar
o_near Tham chiếu bộ nhớ mã trực tiếp, được xác định vào lúc biên dịch.
call fprintf
call sub_401B60


#include <kernwin.hpp>
#include <ua.hpp>
// Đảo mã chỉ thị tại vị trí con trỏ, lưu kết quả vào cấu trúc
// truy suất toàn cục 'cmd'
ua_out(get_screen_ea(), false);
// Hiển thị thông tin về toán tử đầu tiên
msg("n = %d type = %d reg = %d value = %a addr = %a\n",
cmd.0perands[0].n,
cmd.0perands[0].type,
cmd.0perands[0].reg,
cmd.0perands[0].value,
cmd.0perands[0].addr);



Với vị trí con trỏ được đặt tại chỉ thị “xor ebx, ebx”, đoạn mã trên sẽ cho kết quả:
n = 0 type = 1 reg = 3 value = 0 addr = 0
Với con trỏ được đặt tại chỉ thị “jmp loc_10322c”, đoạn mã trên sẽ cho kết quả:
n = 0 type = 7 reg = 0 value = 0 addr = 100322C
4.2.4.3 Từ gợi nhớ
Từ gợi nhớ (push, mov, etc) trong một chỉ thị đại diện bởi biến itype trong lớp insn_t (xem phần kế). Tuy nhiên, nó có giá trị dạng số nguyên, và hiện tại không có một thể hiện bằng văn bản rõ ràng cho những chỉ thị này sẵn có cho người dùng trong bất kỳ cấu trúc dữ liệu nào. Thay vào đó, nó có thể được lấy ra thông qua hàm ua_mnem(), sẽ được trình bày trong Chương 5 – Hàm.
Có một kiểu liệt kê tên là instuc_t (allins.hpp) giữ tất cả các định danh gợi nhớ (tiền tố bằng NN_). Nếu bạn biết những chỉ thị nào bạn đang kiểm tra, bạn có thể sử dụng nó thay vì làm việc với văn bản. Ví dụ, để kiểm tra nếu chỉ thị đầu tiên dạng nhị phân là PUSH, bạn có thể làm như sau:
#include <ua.hpp>
#include <allins.hpp>

// Cap nha 'cmd' (xem phan 4.2.4.4) voi ma duoc lay tu diem nhap
// cua tap tin nhi phan.
decode_insn(inf.startlP);
// Kiem tra xem chi thi dau tien co phai la PUSH
if (cmd.itype == NN_push)
msg("First instruction is a PUSH");
else
msg("First instruction isn't a PUSH");
return;



No comments: