PART 3: GET ADDRESSES OF
APIs
Tranz by : Benina (5/4/2006)
(fixed 2008)
Sau
khi có được Base của Kernel, chúng ta sẽ tiếp tục tìm các addr của các hàm API
mà code độc lập cần dùng. Bài này tôi chỉ dịch lại từ 2 bài tuts rất nổi tiếng
trong giới Vxer. Các tác giả của 2 bài tuts này là Billy Belceb£ và
LethalMind/29A trong team 29A. Như
thông lệ, cuối tut, tôi sẽ đưa ra ví dụ được code trong Masm32.
Một
lời khuyên, để hiểu được tut này cặn kẻ, bạn nên đọc trước lọat tuts
PE của Iczlion. Đặc biệt là tut 7 trong lọat tut nói trên. Và ít nhất
phải hiểu được định dạng file PE format (xem tut của Kienmanowar có trên
site Benina’s)
Và
cuối cùng, vì là bài dịch, nên văn phong ko được mượt mà cho lắm do
cố gắng giữ nguyên ý nghĩa từ bản gốc, mong các bạn thông
cảm.
1.Bài
tut của Billy Belceb£
% Get those crazy APIs!!!
%
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
Ring-3
, như
tôi đã nói trong chương introduction, là
lớp người dùng (user level), vì
vậy chúng ta có thể chỉ truy xuất các đặc quyền trong giới hạn của
nó. Nghĩa là chúng ta ko thể dùng các ports, read hay write đến các
vùng memory đã xác định dành cho kernel, vân vân. Micro$oft đã đưa ra các
tuyên bố khi phát triển Win95 (đã được nói rất nhiều, đó là "Win32
platformz are uninfectable"), trong thực tế nếu họ đã “tịch thu” tất cả
những gì mà virus trước đó trong DOS đã dùng để “sản sinh” ra,thì họ
có thể defeat (đánh bại) chúng ta (các vxers) . Trong giấc mơ của họ,
họ nghĩ rằng chúng ta ko thể dùng các hàm APIs của họ, và hơn nữa,
họ ko thể hình dung chúng ta có thể jmp đến Ring-0, nhưng việc nhảy
đến ring0 là một câu chuyện khác sẽ đề cập trong các tut
sau.
Well,
như tôi đã nói trước đó, chúng ta sử dụng tên hàm API name kết hợp với chỉ lệnh extern lúc biên
dịch, do vậy import32.lib đã cho chúng ta address of function, và nó đã
được assembled một các chính xác trong code, nhưng chúng ta có một vấn
đề khi writing virus. Nếu chúng ta
hardcode (ứng với name của hàm API mà chúng ta cần dùng khi viết virus,
chúng ta dùng một fixed offset cố định để call hàm API đó),thì điều
hầu như chắc chắn có thể xảy ra là addr đó ko làm việc trong các
Win32 version kế tiếp. Bạn có một ví dụ trong virus Bizatch. Vậy chúng
ta sẽ làm gì bây giờ? Well, chúng ta có một function được gọi là
GetProcAddress, mà nó returns cho chúng ta offset của API mà chúng ta
muốn có nó. Bạn là người thông minh phải ko, bạn phải chú ý rằng
GetProcAddress lại là một hàm API , vì vậy khốn thật, mèo lại hòan
mèo, làm thế nào mà chúng ta có thể dùng một hàm API cho việc search
các hàm APIs nếu chúng ta ko có
hàm API đó?. Như điều tương tự trong cuộc sống, chúng ta chắc rằng sẽ
có nhiều khả năng khác nhau để làm được điều mình muốn, và tôi sẽ
đưa ra 2 cách mà tôi nghĩ là tốt nhất để lấy offset hàm API mong muốn:
1. Search hàm GetProcAddress API
trong Exports
table.
2. Khi
chúng ta infect một
file,
hảy
tìm trong các hàm nhập khẩu (imported
functions)
hàm
GetProcAddress có
trong chúng không.
Cách
dễ nhất là cách đầu tiên, hảy
đón xem những gì tôi sẽ giải thích ngay bây giờ cho bạn :)Ok, chúng
ta hảy bắt đầu với các bài học lý thuyết , và sau đó là sum
coding.
Nếu
bạn nhìn vào PE header format,
chúng
ta có trong offset 78h (of PE header,
ko
phải của file) là
RVA of exports table. Ok, chúng
ta cần lấy addr của exports of kernel. Đối
với Windows
95/98, kernel định
trú tại offset
0BFF70000h,và
trong Windows
NT,
kernel
dường
như ở tại 077F00000h. Trong
Win2k
chúng
ta có nó ở tại offset
077E00000h. Vì
vậy, trước tiên chúng ta load address của nó trong một thanh ghi
(register) mà chúng ta sẽ dùng như một con trỏ pointer. Tôi mạnh dạng
đề xuất dùng thanh ghi ESI, chủ yếu bởi vì chúng ta có thể tối ưu
hóa vài code bằng cách dùng lệnh LODSD sử dụng đến thanh ghi ESI. Well,
chúng ta check xem có hay ko tại addr đầu tiên của kernel signal "MZ" word
(well, signal là "ZM" cho lọai CPU “goddamn intel processor
architecture” :), bởi vì kernel
là một library (.DLL), và các libraries có một PE header, và như chúng
ta thấy trước đó, trước PE header,
là thành phần của DOS-compatible stuff. Sau khi so sánh , chúng ta đã
check addr kernel có đúng là một base của PE hay ko rồi, nếu mọi thứ
đều OK, thì chúng ta làm tiếp là tìm giá trị tại header offset image_base+[3Ch] (= offset of nơi mà kernel được định vị + address
được chỉ ra bởi offset tại 3Ch của KERNEL's PE header), và so sánh với
signal "PE\0\0" chính là PE signature.
Nếu
tất cả đều đúng, thì chúng ta sẽ đi tiếp. Chúng ta cần RVA của export
table. Như bạn có thể thấy, nó ở trong offset 78h of PE header.
Vậy
chúng ta lấy nó. Nhưng như bạn biết , RVA (Relative Virtual Address), như
cái tên nó đã nói, liên quan đến một offset cơ sở, trong trường hợp
này là image base of kernel, đó là
nơi nó định vị, như tôi nói trước đó . Đơn giản ở đây chính là, chỉ
cần add kernel offset với giá trị đã tìm được trong Export Table RVA. Ok.
Bây giờ chúng ta đang ở trong export
table
:)
Chúng
ta hảy xem định dạng của nó :
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ÄÄÄ
+00000000h
³
Export Flags ³ Size : 1 DWORD
ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ÄÄÄ
+00000004h
³
Time/Date stamp ³ Size : 1 WORD
ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ÄÄÄ
+00000006h
³
Major version ³ Size : 1 WORD
ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ÄÄÄ
+00000008h
³
Minor version ³ Size : 1 DWORD
ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ÄÄÄ
+0000000Ch
³
Name RVA ³ Size : 1 DWORD
ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ÄÄÄ
+00000010h
³
Number Of Exported Functions
³ Size : 1
DWORD
ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ÄÄÄ
+00000014h
³
Number Of Exported Names
³ Size : 1
DWORD
ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ÄÄÄ
+00000018h
³
Export Address Table RVA
³ Size : 1
DWORD
ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ÄÄÄ
+0000001Ch
³
Export Name Pointers Table RVA
³ Size : 1
DWORD
ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ÄÄÄ
+00000020h
³
Export Ordinals RVA ³ Size : 1 DWORD
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ÄÄÄÄÄÄÄÄÄ
Total
Size : 24h BYTES
Các
vùng quan trọng đối với chúng ta là 6 fields sau
cùng.
Như
bạn có thể hình dung, các giá trị trong: Address Table RVA, Name
Pointers RVA và Ordinals RVA liên
quan hòan tòan với địa chỉ cơ sở Kernel (KERNEL32's base
address). Vậy,
bước đầu tiên cho việc lấy một địa chỉ của hàm API là
phải biết vị trí mà hàm API này đang ở đâu trong các vùng, và cách
dễ nhất để biết nó là duyệt tìm trong Name
Pointers'
offset tên
hàm API ta cần, so
sánh từng string trong vùng đó với string tên hàm API mà
chúng ta muốn lấy addr, và nếu 2 string giống nhau một cách chính xác,
chúng ta sẽ thử tính tóan API's offset.
Well,coi
như chúng ta đã đi tới đây, và bây giờ chúng ta có một giá trị trong
biến counter,do
bới chúng ta dùng biến này tăng lên mỗi lần chúng ta duyệt tìm tên
hàm API's
name
trong giai đọan trên đã đề cập. Well, biến counter
này,
như
bạn có thể hình dung, đã nhảy qua các tên hàm API names mà
chúng ta đã duyệt qua và các hàm đó ko phù hợp với tên hàm API ta
cần. Counter
có
thể là một
word hay
một dword,
nhưng
chưa bao giờ là một byte. Bởi vì chúng ta có rất nhiều hàm APIs , hơn
255 hàm lận:)
NOTE: Tôi
cho rằng bạn đã chứa địa chỉ VA (RVA+kernel
image base) of
Address, Name
và Ordinal
tables
trong các biến tương ứng là AddressTableVA, NameTableVA và
OrdinalTableVA
Ok,
hảy
tưởng tượng chúng ta đã lấy được name của API mà
chúng ta mong muốn trong vùng Name Pointers table, vậy
chúng ta có trong counter là
vị trí nó chiếm giữ trong Name Pointers table.
Well, bây
giờ có lẽ sẽ phiền phức cho bạn đây, những người mới bắt đầu code
trên Win32
coding. Hmm,
chúng ta hảy tiếp tục. Chúng
ta đã có counter, và
bây giờ chúng ta phải search trong
Ordinal Table
(một array of
dwords) tìm
ordinal of
API mà
chúng ta muốn lấy addr. Khi chúng ta có được số thứ tự vị trí của
hàm API cần tìm trong array Name (giá trị trong biến counter) chúng ta
chỉ phải nhân nó với 2 ( hảy nhớ, array of Ordinals được tạo ra bới
các thành phần là words, vậy chúng ta phải tính tóan với words, mà
mỗi word là 2 bytes...), và tất nhiên, phải cộng nó với offset đầu tiên
của array Ordinal Table. Nhắc lại những gì tôi đã giải thích,
chúng tôi cần một word được tính bởi công thức sau:
API's Ordinal location: (
counter * 2 ) + Ordinal Table VA
Đơn
giản đúng ko?. Well, bước kế tiếp (và là bước sau cùng) là get (lấy)
địa chỉ hàm API đã được định sẳn trong mảng Address Table. Chúng ta có
API's
ordinal
rồi,
đúng
ko?
Có
nó ,công việc chúng ta sẽ dễ dàng hơn. Chúng ta chỉ phải nhân ordinal
cho 4 (mảng Addresses array được định
dạng bởi các thành phần là dwords thay vì words, và một dword có size
là 4) và cũng cộng nó với offset of điểm bắt đầu of Address Table, chúng ta có nó dễ dàng phải ko.
Hehe,
bây
giờ chúng ta có API Address RVA. Vậy
chúng ta phải normalize
(chuẩn hóa) nó, cộng
tiếp thêm kernel offset, và
đúng như thế. Chúng ta đã có được nó!!!! Chúng
ta hảy xem công thức của nó như
sau:
API's Address: ( API's Ordinal * 4 ) + Address
Table VA + KERNEL32 imagebase
ÚÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
Tóm
lại, khi chúng ta nhận được vị trí
³ EntryPoint ³ Ordinal ³ Name ³ string
xuất hiện trong Name table,
ÆÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͵
chúng
ta cần biết ordinal của nó
³
00005090 ³ 0001 ³ AddAtomA ³ (mỗi
name có một ordinal ở vị trí
ÆÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͵
tương
xứng với API name), và
³
00005100 ³ 0002 ³ AddAtomW ³ biết
được ordinal, chúng ta có
ÆÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͵
thể
biết addr của nó , đó là,
³
00025540 ³ 0003 ³
AddConsoleAliasA ³ entrypoint
RVA của nó.
ÆÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͵
Chúng
ta chuẩn hoá nó và cuối cùng
³
00025500 ³ 0004 ³
AddConsoleAliasW ³ bạn
có những gì cần thiết về
\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
API address đã
yêu cầu.
[...] Các
tables này có nhiều các entries, nhưng với các thành phần liệt kê như
trên là đủ xài rồi ...
Tôi hy vọng bạn hiểu những gì tôi đã
giải thích. Tôi đã cố trình bày đơn giản với những gì tôi có thể ,
nếu bạn chưa hiểu nó , đừng bỏ qua các dòng này , và hảy đọc lại
nó từng bước từng bước một . Hảy kiên nhẫn. Tôi chắc bạn sẽ hiểu
thôi. Hmmm, có lẻ những gì bạn cần bây giờ là vài đọan code để thấy
nó thực hiện như thế nào .Sau đây bạn có các dòng lệnh do tôi viết,
được sử dụng làm ví dụ, trong Iced Earth virus của
tôi.
;ÄÄÄ[ CUT HERE
]ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
;
;
GetAPI & GetAPIs procedures
;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
;
;
Đây
là procedures dùng để tìm tất cả các APIs yêu
cầu...
Chúng
được chia làm 2
;
parts. GetAPI procedure chỉ
lấy một API
mà
chúng ta cần,
và
GetAPIs
proce-
;
dure để
searches
tất
cả các hàm APIs nào
cần dùng bởi virus.
;
GetAPI
proc
;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;
; Ok, let's rock. Các
tham số cần thiết cho hàm này và kết quả returns ;
; như
sau : ;
;
;
; INPUT
ESI : Con
trỏ đến API
name (chú
ý chữ Hoa và chử thường ) ;
; OUTPUT
EAX : API address
;
;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;
mov
edx,esi ;
Save ptr to name
@_1:
cmp byte ptr [esi],0 ; Null-terminated
char?
jz
@_2 ;
Yeah, we got it.
inc
esi ;
Nopes, continue searching
jmp
@_1 ;
bloooopz...
@_2:
inc esi ; heh, don't
forget this ;)
sub
esi,edx ;
ESI = API Name size
mov
ecx,esi ;
ECX = ESI =
size of name
;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;
; Well, well, thưa
các bạn , ở đây dễ dàng để hiểu, chúng ta có ;
; ESI trỏ
đến điểm bắt đầu của API's name. chúng
ta hảy tưởng tượng chúng
;
; ta
tìm hàm "FindFirstFileA":
;
;
;
; FFFA
db "FindFirstFileA",0
;
; ÀÄ Pointer đang
ở đây
;
; ;
; và
chúng ta cần lưu trử pointer
này,
và
để biết API's
name size,
chúng ;
; ta
lưu trử pointer
khởi đầu đến
API name trong
1 register
là EDX mà ;
; chúng
ta chưa dùng làm gì và rồi tăng pointer trong
ESI cho
đến khi ;
;[ESI] = 0.
;
;
;
; FFFA
db "FindFirstFileA",0
;
; ÀÄ
Pointer bây
giờ đang ở đây ;
;
;
; Đó
là, null
terminated :) Rồi
bằng cách trừ pointer đầu tiên với pointer ;
; mới
chúng ta có API Name size,size
này cần cho search engine. Và ;
; tôi
chứa nó trong ECX, register khác
ko thích hợp dùng cho công việc này ;
;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;
xor
eax,eax ; EAX = 0
mov
word ptr [ebp+Counter],ax ;
Counter set to 0
mov
esi,[ebp+kernel] ;
Get kernel's PE head. offset
add
esi,3Ch
lodsw ; in
AX
add
eax,[ebp+kernel] ; Normalize
it
mov
esi,[eax+78h] ;
Get Export Table RVA
add
esi,[ebp+kernel] ;
Ptr to Address Table RVA
add
esi,1Ch
;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;
; Well, trước
tiên xóa EAX,
và
rồi tạo cho biến counter bằng 0, ;
; để
tránh lỗi ko mong đợi. Nếu u nhớ offset 3Ch là
gì of một
;
; PE file (đếm
từ image base,
MZ mark), u sẽ
hiểu ở đây. Chúng ta đang cần ;
; điểm
có điểm đầu of KERNEL32 PE header
offset. Well, nhưng
nó là một
RVA,;
; chúng
ta normalize
nó
và xong xuôi,
chúng ta có nó là PE headeroffset. ;
; Những gì chúng ta làm bây giờ là
get Export
Table address ;
; (in PE
Header+78h),và
sau đó chúng ta bỏ qua các data ko quan tâm of ;
; structure, và
get
một
cách trực tiếp Address Table
RVA.( add
với 1Ch) ;
;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;
lodsd ; EAX =
Address Table RVA
add
eax,[ebp+kernel] ;
Normalize
mov
dword ptr [ebp+AddressTableVA],eax ; Store it in VA
form
lodsd ; EAX = Name
Ptrz Table RVA
add
eax,[ebp+kernel] ;
Normalize
push
eax ;
mov [ebp+NameTableVA],eax
lodsd ; EAX = Ordinal
Table RVA
add
eax,[ebp+kernel] ;
Normalize
mov
dword ptr [ebp+OrdinalTableVA],eax ; Store in VA
form
pop
esi ;
ESI = Name Ptrz Table VA
;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;
; Nếu
u nhớ lại, chúng ta có trong ESI con
trỏ đến Address Table RVA,
vậy ;
; để
lấy được address
đó, ta
thực thi lệnh LODSD, đó
chính là ta puts ;
; DWORD được định vị bởi ESI vào trong
EAX. Vì nó là 1 RVA, chúng ta cần
;
; chuẩn hóa nó
;
;
;
; Chúng
ta xem những gì Matt Pietrek nói
về vùng đầu tiên này:
;
;
;
; "Vùng
này là một RVA và
trỏ đến một array các
địa chỉ hàm. Địa
chỉ ;
; hàm
là các thành phần trỏ đến (RVA) mỗi
hàm exported
function ;
; trong
module này”. ;
;
;
; Và
tất nhiên, chúng ta chứa nó trong biến của nó. Sau đó,kế đến chúng ta
;
; nhận
được Name
Pointers Table, Matt Pietrek mô
tả như sau : ;
;
;
; "Vùng
này là một RVA và
points
đến
một array of
string pointers. Các
;
; strings là
các names of
các
hàm exported
trong module
này".
;
;
;
; Nhưng
tôi ko chứa nó trong một biến, tôi đã pushed nó, chỉ
vì tôi sẽ dùng;
; nó
rất sớm. Well, và
cuối cùng chúng ta nhận được tiếp array Ordinals ;
; và
ở đây Matt
Pietrek mô
ta về nó: ;
; ;
; "Vùng
này là một RVA và
points
đến
1 array of
WORDs. WORDs là
các ;
; export ordinals of tất
cả các
exported functions trong
module
này". ;
; ;
; Well, đó
là những gì tôi đã làm ;
;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;
@_3:
push esi ; Save ESI for l8r
restore
lodsd ; Get value
ptr ESI in EAX
add
eax,[ebp+kernel] ;
Normalize
mov
esi,eax ;
ESI = VA of API name
mov
edi,edx ;
EDI = ptr to wanted API
push
ecx ;
ECX = API size
cld ; Clear
direction flag
repe cmpsb ; Compare both API
names
pop
ecx ;
Restore ECX
jz
@_4 ;
Jump if APIs are 100% equal
pop
esi ;
Restore ESI
add
esi,4 ; And get next value of
array
inc
word ptr [ebp+Counter] ;
Increase counter
jmp
@_3 ;
Loop again
;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;
; Heh, tôi
ko phải là loại người put nhiều
code mà ko chú thích gì, như
tôi;
; vừa
làm,
nhưng
hảy hiểu rằng block of code này
ko thể tách ra để giải
;
; thích nó. Những gì chúng ta làm trước
tiên là push ESI(do nó thay đổi bên;
; trong code bởi chỉ thị CMPSB) để restore
sau này. Sau đó, chúng ta lấy
;
; DWORD được trỏ bởi ESI (Name Pointerz
Table) trong accumulator (EAX),tất
;
; cả điều này thực hiện bởi chỉ thị LOSD.
Chúng ta chuẩn hóa nó bằng
cách ;
; add
với kernel
base address. Well, bây giờ chúng ta có trong EAX một ;
; pointer
trỏ đến một name of một hàm API, nhưng chúng ta vẫn ko biết API ;
; nào . Ví dụ, EAX có thể trỏ đến hàm nào đó giống như
"CreateProcessA"
;
; và hàm API này ko cần cho virus chúng ta
...
;
; Well, để so sánh string đó với string hàm
API chúng ta cần tìm (được trỏ ;
; đến bởi EDX) chúng ta dùng chỉ thị CMPSB.
Vậy chúng ta sửa chửa lại các
;
; parameters của nó: trong
ESI chúng ta put pointer đến điểm đầu API ngay
;
; bây giờ trong Name Pointerz Table, và trong
EDI chúng ta put pointer đến ;
; API mong muốn. Trong ECX chúng ta put size
của nó,và rồi chúng ta so
sánh;
; byte với byte. Nếu các
string giống nhau hòan tòan,cờ
zero
flag được set,;
; và chúng ta nhảy
đến routine để
lấy address of API đó, nhưng nếu sai
khác;
; ,chúng ta phục hồi lại ESI, và add nó với
size of một DWORD để lấy giá
;
; trị kế tiếp trong Name Pointerz Table array.
Chúng ta tăng biến đếm ;
; counter(RẤT
QUAN TRỌNG)lên
và chúng ta
tiếp tục searching.
;
;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;
@_4:
pop esi ; Avoid shit in
stack
movzx
eax,word ptr [ebp+Counter] ;
Get in AX the counter
shl
eax,1 ;
EAX = AX * 2
add
eax,dword ptr [ebp+OrdinalTableVA] ; Normalize
xor
esi,esi ;
Clear ESI
xchg
eax,esi ;
EAX = 0, ESI = ptr to Ord
lodsw ; Get
Ordinal in AX
shl
eax,2 ;
EAX = AX * 4
add
eax,dword ptr [ebp+AddressTableVA] ; Normalize
mov
esi,eax ;
ESI = ptr to Address RVA
lodsd ; EAX =
Address RVA
add
eax,[ebp+kernel] ;
Normalize and all is done.
ret
;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;
; Pfff, 1
block
code lớn khác,
và dường như khó hiểu, đúng ko?. Heh đừng lo;
; lắng, tôi sẽ chú thích ngay đây ;)
;
; Ehrm, lệnh pop chỉ đơn giản clear stack, nếu
như API names phù hợp,chúng
;
; ta có 1
shit trong
đó
(stack). Chúng
ta mov
phần lower of
EAX với
giá ;
; trị
của biến counter(là một WORD) và make
zero phần high register EAX. ;
; Chúng ta nhân nó với 2,vậy
chúng ta có một
số nó đang
lưu giữ, và
array ;
; mà chúng ta sẽ search là một
array of WORDs. Bây giờ chúng ta add cho
nó ;
; một pointer đến beginning of array
mà chúng ta muốn search,
và trong ;
; EAX chúng ta có một pointer trỏ
đến ordinal of
API mà chúng ta cần.
Vậy ;
; chúng ta put EAX vào trong ESI để dùng con
trỏ đó lấy giá trị đang trỏ
;
; đến, đó là, Ordinal trong EAX, sử dụng
lệnh LODSW . Heh, chúng ta có
;
; Ordinal, nhưng những gì
chúng ta muốn là EntryPoint of code of API, vậy
;
; chúng ta nhân ordinal (nó
nắm giử vị trí
mà EntryPoint of hàm API chúng ;
; ta
cần trong
Address Table) cho 4, đó là DWORD size, và chúng ta
có ;
; một giá trị RVA, liên quan đến AddressTable
RVA, vậy chúng ta chuẩn ;
; hóa nó, và bây giờ chúng ta có trong EAX con
trỏ trỏ
đến giá trị
của ;
; EntryPoint of hàm API trong Address Table.
Chúng ta put EAX trong ESI, và;
; chúng ta có giá trị EAX
trỏ đến . Vậy
chúng ta có trong EAX ;
; EntryPoint RVA of hàm API mong muốn. Heh,
ở
đây chúng ta
cần
phải làm ;
; bây giờ là chuẩn hóa (normalize) address với
KERNEL32's image base, và
;
; cuối
cùng, ta
đã thực hiện
xong, chúng ta có API
address
gốc
trong
EAX!!
;
;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;
GetAPI
endp
;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;
;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;
GetAPIs
proc
;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;
; Ok, đây là code cho việc lấy ALL hàm APIs
mong muốn bởi dùng procedure
;
; trước đó. Các tham số của nó là :
;
;
;
; INPUT
ESI : Pointer trỏ
đến tên hàm API (ASCIIz)
mong muốn đầu tiên ;
;
EDI : Pointer trỏ
đến biến lưu giữ hàm API mong
muốn đầu tiên ;
; OUTPUT
Nothing.
;
;
;
; Well, structure tôi xem như get all các giá
trị này là như sau:
;
; one:
;
;
;
; ESI points to ÄÄ db "FindFirstFileA",0
;
; db "FindNextFileA",0
;
; db "CloseHandle",0
;
; [...]
;
; db 0BBh ; Marks the end of this
array ;
;
;
; EDI points to ÄÄ dd 00000000h ; address
tương lai of
FFFA ;
; dd 00000000h ; address tương
lai of
FNFA ;
; dd 00000000h ; address tương
lai of CH ;
; [...]
;
;
;
; Tôi hy vọng bạn đủ thông minh và bạn nắm bắt
được nó ;
;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;
@@1:
push esi
push
edi
call
GetAPI
pop
edi
pop
esi
stosd
;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;
; Chúng ta push các giá trị mà chúng ta xử
lý
trong procedure
này,để tránh
;
; thay đổi chúng, và chúng ta call thủ tục
GetAPI. Chúng ta giả sử tại
đây ;
; trỏ đến ESI như một ptr
trỏ đến tên
hàm API mong muốn, và EDI như con
;
; trỏ trỏ
đến biến mà sẽ
xử lý tên
hàm API. Khi hàm return cho chúng ta API;
; offset trong EAX, chúng ta save nó trong
biến chịu trách nhiệm cho
nó ;
;
được
trỏ
đến bởi EDI bằng
lệnh
STOSD đơn
;
;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;
@@2:
cmp byte ptr
[esi],0
jz
@@3
inc
esi
jmp
@@2
@@3:
cmp byte ptr
[esi+1],0BBh
jz
@@4
inc
esi
jmp
@@1
@@4:
ret
GetAPIs
endp
;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;
; Có thể tối
ưu hóa thêm code trên, tôi biết thế, nhưng chủ
yếu ở
đây là ;
; để làm công việc giải thích cho tôi. ;
; Well, những gì tôi làm trước tiên trong code
trên là hướng đến cuối của
;
; string of những gì chúng ta đã hỏi addr
trước đó, và bây giờ nó trỏ đến
;
; API kế tiếp.
;
; Nhưng chúng ta muốn biết có phải là API
sau cùng ko, vì vậy chúng ta
;
; check bytes đánh dấu của chúng ta, đó
là 0BBh (đoán xem tại sao là 0BBh).;
; Nếu OK, chúng ta đã lấy tất cả các APIs
cần thiết, và nếu ko, chúng ta ;
; tiếp tục công việc search của chúng ta. ;
;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;
;ÄÄÄ[
CUT HERE ]ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
Heh, tôi đã cố
gắng viết thủ
tục này thật
dễ hiểu, và tôi chú thích rất nhiều
với mong muốn rằng bạn sẽ hiểu nội dung mà ko copy. Và nếu bạn copy cũng ko phải
là vấn đề của tôi....hehe, tôi ko cho điều đó là xấu hổ. Nhưng , bây giờ câu hỏi
đặt
ra là
các
hàm APIs nào mà
chúng ta cần
search, và phần
chính này sẽ
dựa trên cách
ta
đã đạt được
trước đó bởi thao tác bằng tay trên
PE. Tôi
sẽ chỉ cho bạn phiên bản hành động trực tiếp của một virus dùng kỷ thuật file
mapping (dễ hơn thao tác và nhanh hơn cách infection) , tôi sẽ trình bày cho bạn
các hàm APIs nào mà bạn cần dùng.
2.Bài
tut của By
LethalMind/29A
Û
Now let's deal with export table
Tôi ko dạy bạn ở đây cấu trúc của export
table. Điều đó có thể tìm thấy trong hầu hết mọi zine ngày nay. Nhưng tôi sẽ chỉ
cho bạn như thế nào tôi dùng nó để get hàm API bằng checksum. Một checksum là
cái quái gì vậy?. Một checksum là một số được tính tóan từ một số lớn hơn
nó, để
đại diện số lớn hơn đó, nó là một lọai
fingerprint. Ví dụ, chúng ta hảy lấy 1234. Chúng ta có thể sinh ra một checksum
bởi cộng tất cả các số của nó giữa chúng. Vì vậy checksum của 1234 là 1+2+3+4 =
10. Bây giờ, nếu chúng ta thấy 1234, và chúng ta có checksum của nó, chúng ta có
thể nói rằng của hai phù hợp với nhau. Nhưng nếu 1235, 1+2+3+5= 11 và checksum
sẽ ko phù hợp. Checksum cho chúng ta một cách để chỉ ra vài thứ mà ko biết nó
hòan tòan. Đó là cách rất thường dùng, bởi vì để
lấy một
hàm API, chúng
ta cần hàm API đó ,chúng ta cần phải chứa name rất dài của chúng trong 1 biến,
và điều đó sẽ gia tăng kích thước các viruses của chúng ta, và các string này
cũng rất đáng nghi ngờ cho các reverser. Tôi đã chọn làm
một 32-bit
checksum bởi vì nó bảo đảm hơn và ko mất nhiều space, nhưng tôi cũng
có thể
lựa
chọn lọai
checksum khác
lớn hơn hay nhỏ
hơn 32-bits,nếu lớn hơn,
bạn sẽ có xác xuất giảm thiểu chỉ định sai
lệnh một
tên
hàm API. Bây
giờ, tôi nghĩ một snippet of code sẽ là tiện dụng hơn là lời nói. Routine này đã
quá tối ưu hóa rồi, nhưng tôi nghĩ rằng nó được chú thích tốt hơn là tối ưu hóa
tốt hơn, và hey, đó là phần chính trong tut này :)
8<------------------------------ ---------------------------------="" e="" i="" n="" o:p="" p="" s="" t="">------------------------------>
mov
eax,ecx ;
EAX = KernelBase
mov
ebx,eax ;
EBX = KernelBase
add
eax,[eax.MZ_lfanew] ;
Get address of PE header
mov
esi,ebx ;
Get address of Export
;
directory
add
esi,[eax.NT_OptionalHeader
\
.OH_DirectoryEntries \
.DE_Export \
.DD_VirtualAddress]
mov
edx,ebx ;
Get address of exported
add
edx,[esi.ED_AddressOfNames] ;
API names
mov
ecx,[esi.ED_NumberOfNames] ;
Get number of exported
xor
eax,eax ;
API names
lea
edi,WORD PTR [ebp+BeginAPIList] ; Point to beginning of
list
mov
[SESI+ebp],esi ;
Save some regs
mov
[SEAX+ebp],eax
;
mov
[SECX+ebp],ecx
;
mov
[SEBX+ebp],ebx
;
mov
[SEDX+ebp],edx
;
Search_for_API_name:
mov
esi,ebx ;
Get address of next exported
add
esi,[edx+eax*4] ;
API name
Next_API_name:
pusha
xor
edx,edx
mov
edx,DWORD PTR[edi] ; Take the
checksum
add
edi,4 ; Point To
Next
test
edx,edx ; Is this
checksum 0 ?
jz
EndAPIRetrieving ; Yeah,
we're done with them
LoopChsksm:
xor
eax,eax
lodsb ; Take a char (ie :
"X")
shl
ax,8 ; move it
left (ie : "X_")
sub
edx,eax ; Substract
that from checksum
cmp
ax,0 ; Is the
char 0 ?
jz
LoopConti ; Yes,
check if checksum too
xor
ax,ax
;
lodsb ; Load another char
(ie : "Y")
sub
edx,eax ; Substract
that from checksum
cmp
ax,0 ; Is the
char 0 ?
jnz
LoopChsksm ; No,
Continue looping
LoopConti:
test
edx,edx ; Have we
zeroed our checksum ?
jz
FoundAPI ; YES, we
have found the right API
popa
;
inc
eax ; Nope,
next name....
loop
Search_for_API_name
FoundAPI:
popa
mov
esi,[SESI+ebp]
mov
edx,ebx ; Get
address of exp. API ordinal
add
edx,[esi.ED_AddressOfOrdinals]
movzx
eax,word ptr [edx+eax*2] ; Get
index into exp.API functions
Check_Index:
mov
edx,ebx ; Get
address of exported API function
add
edx,[esi.ED_AddressOfFunctions]
add
ebx,[edx+eax*4] ; Get address of requested API
function
mov
eax,ebx
;
End_GetProcAddressET:
add
edi,4
stosd ; Save API's adress
into our array
mov
eax,[SEAX+ebp] ; Restore
some regs
mov
ebx,[SEBX+ebp]
;
mov
ecx,[SECX+ebp]
;
mov
edx,[SEDX+ebp]
;
jmp
Search_for_API_name ; Next
Name
SESI dd
0
SEAX dd
0
SECX dd
0
SEBX dd
0
SEDX dd
0
BeginAPIList:
sCloseHandle dd
'Cl'+'os'+'eH'+'an'+'dl'+'e'*100h
aCloseHandle dd 0
sCreateFileA dd
'Cr'+'ea'+'te'+'Fi'+'le'+'A'*100h
aCreateFileA dd 0
sCreateFileMappingA dd 'Cr'+'ea'+'te'+'Fi'+'le'+'Ma'+'pp'+'in'+'gA'
aCreateFileMappingA dd
0
dd 0
8<---------------------------e -----------------------------="" -="" d="" e="" i="" n="" o:p="" p="" s="" t="">---------------------------e>
3.Ví
dụ coding trong MASM32
Tôi
code lại 1 chương trình ví dụ hiển thị 1 hộp thông báo “All are Oki!”,
chỉ đơn giản vậy thôi. Nhưng đặc điểm của đọan code này là ko sử dụng
các file .inc để hổ trợ biên dịch. Đồng thời nó hòan tòan độc lập,
ko ảnh hưởng đến offset của đọan code. Như đã nói trong tut 2 trong lọat
tut này, tôi sử dụng 2 hàm LoadLibraryA và GetProcAddress để lấy base
của user32.dll và offset của hàm MessageBoxA với mục đích hiển thị hộp
thông báo. Và cuối cùng tôi muốn nhắc các bạn, trong thủ tục GetAPIs,
các bạn chú ý, nếu một trong các tên hàm cần tìm ko chính xác thì
chương trình sẽ bị crash. Bạn hảy thử thay đổi chuổi “LoadLibraryA”
thành “LoadLibrary” thì các bạn sẽ thấy chương trình crash ngay.
.586
.model flat, stdcall
option
casemap:none
.code
start:
jmp
indep_start
incode
segment
;==============================================================================
indep_start:
call
Delta
Delta:
pop
ebp
sub ebp,offset
Delta
;---------------------------------------------------------------
;Get base
Kernel
;---------------------------------------------------------------
assume fs:nothing
find_kernel32:
push esi
xor eax,eax
mov eax,fs:[eax+30h]
test eax,eax
js find_kernel32_9x
find_kernel32_nt:
mov eax,[eax+0ch]
mov eax,[eax+1ch]
mov eax,[eax]
mov eax,[eax+08h]
jmp
find_kernel32_finished
find_kernel32_9x:
mov eax,[eax+34h]
lea eax,[eax+7ch]
mov eax,[eax+3ch]
find_kernel32_finished:
pop esi
mov [ebp+kernel],eax ; save base kernel into kernel
var
;------------------------------------------------------------------
;Get required
APIs
;------------------------------------------------------------------
lea
edi,[ebp+@@Offsetz]
lea
esi,[ebp+@@Namez]
call
GetAPIs ;
Retrieve all APIs
;------------------------------------------------------------------
; Main Code
;------------------------------------------------------------------
lea
esi,[ebp+szUser32dll]
push
esi
call [ebp+_LoadLibrary]
lea esi,[ebp+szMessageBoxA]
push
esi
push
eax
call [ebp+_GetProcAddress]
mov [ebp+_MessageBoxA],eax
push
00h ; Sytle of
MessageBox
lea esi,[ebp+szTitle1]
push
esi ; Title of
MessageBox
lea esi,[ebp+szMessage]
push
esi ; The
message itself
push
00h ; Handle
of owner
call
[ebp+_MessageBoxA] ; The API call
itself
push
00h
call
[ebp+_ExitProcess] ; End
program
;------------------------------------------------------------
; Procedures GetAPI and
GetAPIs
;------------------------------------------------------------
GetAPI proc
mov
edx,esi ;
Save ptr to name
@_1:
cmp byte ptr [esi],0 ; Null-terminated
char?
jz
@_2 ; Yeah, we got
it.
inc
esi ;
Nopes, continue searching
jmp
@_1 ;
bloooopz...
@_2:
inc esi ; heh, don't
forget this ;)
sub
esi,edx ; ESI = API Name
size
mov
ecx,esi ;
ECX = ESI = size of name
xor
eax,eax ;
EAX = 0
mov
word ptr [ebp+Counter],ax ;
Counter set to 0
mov
esi,[ebp+kernel] ;
Get kernel's PE head. offset
add
esi,3Ch
lodsw ; in
AX
add
eax,[ebp+kernel] ;
Normalize it
mov
esi,[eax+78h] ;
Get Export Table RVA
add
esi,[ebp+kernel] ;
Ptr to Address Table RVA
add
esi,1Ch
lodsd ; EAX =
Address Table RVA
add
eax,[ebp+kernel] ; Normalize
mov
dword ptr [ebp+AddressTableVA],eax ; Store it in VA
form
lodsd ; EAX = Name
Ptrz Table RVA
add
eax,[ebp+kernel] ;
Normalize
push
eax ; mov
[ebp+NameTableVA],eax
lodsd ; EAX =
Ordinal Table RVA
add
eax,[ebp+kernel] ;
Normalize
mov
dword ptr [ebp+OrdinalTableVA],eax ; Store in VA
form
pop
esi ;
ESI = Name Ptrz Table VA
@_3:
push esi ; Save ESI for l8r
restore
lodsd ; Get value
ptr ESI in EAX
add
eax,[ebp+kernel] ;
Normalize
mov
esi,eax ;
ESI = VA of API name
mov
edi,edx ;
EDI = ptr to wanted API
push
ecx ;
ECX = API size
cld ; Clear
direction flag
repe
cmpsb ;
Compare both API names
pop
ecx ;
Restore ECX
jz
@_4 ;
Jump if APIs are 100% equal
pop
esi ;
Restore ESI
add
esi,4 ;
And get next value of array
inc
word ptr [ebp+Counter] ;
Increase counter
jmp
@_3 ; Loop
again
@_4:
pop esi ; Avoid shit in
stack
movzx
eax,word ptr [ebp+Counter] ;
Get in AX the counter
shl
eax,1 ;
EAX = AX * 2
add
eax,dword ptr [ebp+OrdinalTableVA] ; Normalize
xor
esi,esi ;
Clear ESI
xchg
eax,esi ;
EAX = 0, ESI = ptr to Ord
lodsw ; Get
Ordinal in AX
shl
eax,2 ;
EAX = AX * 4
add
eax,dword ptr [ebp+AddressTableVA] ; Normalize
mov
esi,eax ;
ESI = ptr to Address RVA
lodsd ; EAX =
Address RVA
add
eax,[ebp+kernel] ;
Normalize and all is done.
ret
GetAPI
endp
;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;
GetAPIs
proc
@@1:
push esi
push
edi
call
GetAPI
pop
edi
pop
esi
stosd
@@2:
cmp byte ptr
[esi],0
jz
@@3
inc
esi
jmp
@@2
@@3:
cmp byte ptr
[esi+1],0BBh
jz
@@4
inc
esi
jmp
@@1
@@4:
ret
GetAPIs
endp
;------------------------------------------------------------------
;Data for independent code
;------------------------------------------------------------------
kernel dd 00000000h
Counter dd 00000000h
AddressTableVA dd 00000000h
NameTableVA dd 00000000h
OrdinalTableVA dd 00000000h
@@Namez label byte
@GetProcAddress db
"GetProcAddress",0
@LoadLibrary db "LoadLibraryA",0
@ExitProcess db "ExitProcess",0
db 0BBh
@@Offsetz label byte
_GetProcAddress dd 00000000h
_LoadLibrary dd 00000000h
_ExitProcess dd 00000000h
szUser32dll db "user32.dll",0
szMessageBoxA db "MessageBoxA",0
_MessageBoxA dd 00000000h
szTitle db "A
Example Get
APIs",0
szMessage db "All are Oki!",0
;===============================================================
incode
ends
end start
|
Các
bạn hảy tự thực hành viết lại đọan code độc lập với tính năng như
trên nhưng dùng thủ tục Get APIs trong bài tut của LethalMind nhé. Coi như
là 1 bài tập.
-----------------------------------------------------------------------------------
Benina
05/04/2006
Update
14/05/2006
Mail:
benina.rea@gmail.com
(Không
đồng ý bất kỳ ai sử dụng tài liệu này cho mục đích thương mại nếu
ko được phép của người dịch)
Đăng lại 2017