Trao đổi với tôi

http://www.buidao.com

3/2/17

[INDEPENDENT CODE], PART 3: GET ADDRESSES OF APIs

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    ;
 ; đượ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 racá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="">
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            d    "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