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:
Post a Comment