Trang chủ‎ > ‎RCE‎ > ‎Assembly Language‎ > ‎

Benina's space ASM Tutorials

Source: http://rootb1ez.wordpress.com/category/asm/

[ASM] Part 2: Finding the base address of kernel32.dll library (3)

13.05.2010

rootb1ez ASM Để lại bình luận

Phân tích :

Ở đây chỉ chú trọng
đến các chỉ thị quan trọng đọan code trong code độc lập:

  1. Dùng selector đã tải vào
    thanh ghi segment FS để tìm vị trí TEB trong memory của thread
    hiện hành đã được định vị.


Struct TEB{

…..

struct _PEB*
ProcessEnvironmentBlock;


…..

};

Tìm con trỏ đến
PEB structure tại offset 0x30 trong TEB


mov
eax,fs:[eax+30h]

  1. Nếu tại thời điểm này, SF bằng 1 thì hệ
    điều hành là Window 9x. Nếu khác, chạy trên NT

test eax,eax

js
find_kernel32_9x

  1. Tìm con trỏ đến dữ
    liệu loader data trong cấu trúc PEB. PEB chưa bao giờ được
    công bố định nghĩa về nó trong SDK hay DDK nhưng ta có
    thể nhận được thông tin về nó trong cách dùng windbg kernel
    mode debugger và cả phần mở rộng của nó.

0:000>
!kdex2x86 .strct PEB

Loaded kdex2x86
extension DLL

Struct _PEB
(sizeof-422)

+000 byte InheriteAddressSpace

…..

+00c struct PEB_LDR_DATA *Ldr


Con trỏ đến PEB_LDR_DATA được chứa tại offset
0x0c trong PEB

mov eax,[eax+0ch]


  1. Định vị điểm
    đầu của thành phần InitializationOrderModuleList (offset 0x1c)

typedef struct _PEB_LDR_DATA {

ULONG
Length;


BOOLEAN Initialized;

PVOID
SsHandle;


LIST_ENTRY InLoadOrderModuleList;

LIST_ENTRY InMemoryOrderModuleList;

LIST_ENTRY InInitializationOrderModuleList;

};

Trỏ đến điểm
đầu của thành phần InitializationOrderModuleList

mov
esi,[eax+1ch]

  1. Mỗi thành phần trong list
    chứa thông tin về các thư viện động (dlls) trong khỏang không
    vùng nhớ của process ( đối với chuổi khởi tạo đầu
    của chúng). Thành phần đầu tiên chứa thông tin về thư
    viện ntdll.dll . Bằng cách duyệt qua list này đến thành
    phần thứ 2 là thư viện kernel32.dll, base address nhận được
    từ offset 0x08

Struct LIST_ENTRY{

Struct LIST_ENTRY*
Flink;


Struct LIST_ENTRY* Blink

};

Duyệt qua list đến thành phần thứ 2:

mov eax,[eax]

mov
eax,[eax+08h]

  1. Các phần code sau đây cho việc
    tìm Kernel của Windows 9x ko được công bố, vì vậy tôi ko
    thể giải thích cặn kẻ được. Chúng ta chỉ biết được
    nó chạy chính xác và source này đã được public trên
    NET rất nhiều.

mov eax,[eax+0x34]

chứa
con trỏ đến offset 0x34 trong eax (undocumented)

7. Load addr tại eax + 0x7c

lea
eax,[eax+0x7c]

  1. Trích base addr kernel32.dll


mov eax, [eax+03c]

4.Finding
the base address of kernel32.dll library from 
IAT of Target

Đây là kỹ thuật tìm base của
kernel32.ddl từ bảng IAT của chính Target. Kỹ thuật này ko mới, cũng như
ko hòan hảo bằng các kỹ thuật khác. Nhưng chúng ta tìm hiểu qua cho
biết.

Nguyên lý kỹ thuật:

-Kỹ thuật này tìm xem trong bảng IAT có
import kernel32.dll.

-Nếu có thì tìm xem có import hàm GetModuleHandleA hay không.

-Nếu tìm thấy thì dùng hàm này để
lấy base của Kernel32.dll.

Source
code sau đây tôi tham khảo từ source của 
Beta
virus Atav by Radix16

.586

.model flat,
stdcall

option casemap:none

include \masm32\include\windows.inc

include
\masm32\include\user32.inc

includelib \masm32\lib\user32.lib

include \masm32\include\kernel32.inc

includelib
\masm32\lib\kernel32.lib

.data

AppName db "Find Base Kernel ",0

dinhdang db "Base Kernel: %lx ",0

.data?

buffer db 512 dup(?)

hanle
dd ?


.code

start:

shit_size equ (offset deltaA – offset _dirty)

_dirty:

pushad
; Push all the registers


pushfd ;
Push the FLAG register

call deltaA ;
Hardest code to undestand ;)

deltaA: pop ebp

mov
eax,ebp

sub ebp,offset
deltaA

;
Kỹ thuật này sẽ tự tính tóan ra image base

sub eax,shit_size ; Obtain the Image Base on

sub eax,00001000h ;
the fly

NewEIP equ $-4


mov dword ptr [ebp+imagebase],eax


call base_kernel ;Gọi
hàm tìm Base kernel32

;————————————————————————————–

;Phần này ko phải là
đọan code độc lập mà chỉ thông báo base kernel khi tìm xong

;————————————————————————————–

invoke wsprintf,
addr buffer, addr dinhdang, eax

invoke
MessageBox,0, addr buffer, addr AppName,MB_OK+MB_ICONINFORMATION

invoke
GetModuleHandleA,addr hanle ;Hàm này
add vào để kiểm tra proc base_kernel

invoke
ExitProcess, 0

;—————————————————————————————–

;Hàm tìm base kernel

;Hàm này các regs sau
ko thay đổi giá trị: ebp

;Giá trị trả về trong eax, nếu eax=0 thì target
ko import hàm GetModuleHandleA và ko

; tìm ra
base kernel

;—————————————————————————————–

base_kernel:

call Kernel?
;Gọi hàm tìm xem target có import kernel32.dll hay
ko?

cmp ebx,0

jz
Not_Found_Kernel32 ;Điểm thóat tìm
base Kernel32

mov esi, ebx ;ebx= image_import_descriptor of
kernel32

mov ebx,[esi+10h]
;Get addr of ID_FirstThunk

add
ebx,[ebp + imagebase] ;chuẩn hóa
ID_FirstThunk

mov [ebp + offset f_RVA],ebx ;Save f_RVA= addr of ID_FirstThunk

mov
eax,[esi] ;eax=
image_import_descriptor of kernel32

cmp
eax,0

jz Not_Found_Kernel32

 
;Đến đây chúng ta bắt đầu tìm hàm GetModuleHandleA
;Trước khi tìm hàm GetModuleHandleA thì chuẩn bị các regs để làm tham số cho thủ tục Get_Module_Handle 
;edx= OriginalFirstThunk of kernel32 được chuẩn hóa
;ecx= importsize
;eax=0

mov
esi,[esi] ;esi=
image_import_descriptor of kernel32

add
esi,[ebp + offset imagebase]

mov
edx,esi ;Chuẩn hóa esi ->edx

mov
ecx,[ebp+offset importsize] ;ecx=importsize

mov
eax,0 ;eax=0

Jmp Get_Module_Handle

;---------------------------------------------------------------------------------------------------
;Kỹ thuật tìm Kernel32 này là search trong import đễ tìm file có import kernel
;---------------------------------------------------------------------------------------------------
;Thủ tục Kernel? :
;Chú ý: -trong thủ tục này các thanh ghi sau đây ko thay đổi giá trị ebp,ecx
;       -các thanh ghi sau đây bị thay đổi giá trị esi,eax,ebx,edx
;Hàm này có tên là Kernel? 
;Nó tìm xem trong target có import dll Kernel32 hay ko. Nêu ko thì
;ebx=0, còn nếu tìm thấy có thì ebx là addr virtual of image_import_descriptor của Kernel32.dll
;Thủ tục này save : OH_AddressOfEntryPoint= entrypoint; DD_VirtualAddress= importvirtual; DD_Size= importsize
;-----------------------------------------------------------------------------------------------------      

Kernel?: ;Đây là hàm tìm xem
target có import kernel32 hay ko


mov esi,[ebp + offset imagebase]
 ;Giá trị imagebase lúc đầu là 00400000h đây cũng là imagebase của
program này tức là pMapping


;khi
program được mapping vào memory


;Tại đây dường như ta có cảm giác có bug. Nhưng
thực ra khi biên dịch chương trình trong tasm


;thì imagebase được đề xuất là 00400000h, còn
khi inject thì imagebase được virus patch right với


;imagebase của program bị inject.
Ở đây hòan tòan ko có bug.

cmp
word ptr[esi],’ZM’ ;check file PE?

jne
GetEnd_kernel 
;ko = thì jump to điểm cuối -> set ebx=0

add
esi,3ch ;esi ptr to
image_nt_headers

mov esi,[esi]

add
esi,[ebp + offset imagebase] ;Chuẩn hóa addr
image_nt_headers

push esi

cmp
word ptr [esi], ‘EP’ ;Check
Win App PE

jne
GetEnd_kernel ;ko = thì jump to
điểm cuối -> set ebx=0

add
esi, 28h ;Get
OH_AddressOfEntryPoint

mov
eax, [esi]

mov [ebp+entrypoint], eax ;Save entrypoint of
target

pop esi

add
esi,80h ;Get DE_Import

;Save info of DE_Import

mov
eax,[esi]

mov
[ebp+importvirtual],eax 
;Save DE_Import = DD_VirtualAddress of
Import

mov eax,[esi+4]

mov [ebp+importsize],eax
;Save DD_Size of Import

mov
esi,[ebp+importvirtual] 
;esi= importvirtual

add esi,[ebp + offset imagebase] ;Chuẩn hóa importvirtual cho code
độc lập

mov ebx,esi

mov
edx,esi

add edx,[ebp + importsize]

;***********************************************************************
;Đây là vòng lặp tìm xem target có import Kernel32.dll hay ko
;ebx=addr of image_import_descriptor của 1 dll 
;edx là chặn dưới của import dùng để làm điều kiện thóat khi search

;************************************************************************

Search_Kernel:

mov esi,[esi + 0ch] ;Get ID_name in
image_import_descriptor

 
;Ở đây tôi nghĩ nên thêm thủ tục uppercase string ID_name và lowercase

add
esi,[ebp + offset imagebase]

push
eax

mov eax,swKernel32l

cmp [esi],eax
;so sánh với dword có giá trị là
‘nrek’ là nghịch của ‘kern’

pop eax

Je
K32Found

push eax

mov
eax,swKernel32u

cmp
[esi],eax 
;so sánh với dword có giá trị là ‘nrek’ là nghịch của ‘kern’

pop
eax

Je K32Found

add
ebx, 14h ;size
of image_import_descriptor

mov
esi, ebx

cmp
esi, edx

jg
GetEnd_kernel

jmp
Search_Kernel

;Kết thúc vòng lặp 
;************************************************************************** 

GetEnd_kernel:


xor ebx,ebx

K32Found:

Ret ;Sau khi tìm ra có
import kernel32 thì trả về nơi gọi,

;ebx = addr image_import_descriptor of kernel32

;------------------------------------------------------------------------------------ 
; proc Get_Module_Handle
; Tham số đầu vào:
; edx= OriginalFirstThunk of kernel32 được chuẩn hóa
; eax=0 biến đếm
; ecx=importsize
; Return: eax=0 khi ko tìm thấy hàm GetModuleHandle; ngược lại là eax=base of Kernel32
;----------------------------------------------------------------------------------- 
;Bắt đầu vòng lặp    

Get_Module_Handle:


cmp dword ptr [edx],0

je Not_Found_Kernel32


cmp byte ptr [edx+3],80h 
;check import theo
name hay ordinal?

je
Not_Here

mov
esi,[edx] 
;addr image_import_by_name của hàm

push
ecx ;bảo lưu ecx


add esi,[ebp + offset imagebase] ;chuẩn hóa esi

add esi,2 ;Get
esi = IBN_Name của image_import_by_name


mov edi,offset gmhGetModuleHandleA ;chú
ý chưa chuẩn hóa offset

add edi,ebp ;chuẩn
hóa


mov ecx,gmhsize ;gmhsize là hằng số size của chuổi
‘GetModuleHandleA’


repz cmpsb ;so sánh hai chuổi


pop ecx ;phục hồi lại ecx


je f_GetModuleHandelA

Not_Here:
        inc eax                                       ;eax=eax+1                        
        add edx,4                                     ;edx point to next OriginalFirstThunk                      
        loop Get_Module_Handle
;Kết thúc vòng lặp
        jmp Not_Found_Kernel32                        ;Nếu duyệt qua bảng import mà ko tìm ra GetModuleHandleA thì jmp
f_GetModuleHandelA:
        shl eax,2                                     ;eax=eax*2 để định vị FirstThunk trong array FirstThunk                      
        mov ebx,[ebp+offset f_RVA]                    ;Get start array FisrtThunk
        add eax,ebx                                   ;Get localtion of require FirstThunk                  
        mov eax,[eax]                                 ;Get addr GetModuleHanldeA func          
 
        mov edx,offset se_Kernel32                    ;Get string ‘Kernel32.dll”                 
        add edx,ebp                                   ;chuẩn hóa                   
        push edx                                      ;push tham số vào stack cho hàm GetModuleHanleA             
        call eax                                      ;Gọi hàm GetModuleHandleA                        
        cmp eax,0                                     ;Check giá trị trả về  eax là base của kernel32
        jne Found_Adress_base                         ;Nếu duyệt qua bảng import và tìm ra GetModuleHandleA thì jmp Found_Adress, 
                                                     ;lúc đó eax là base của kernel32
        Jmp Not_Found_Kernel32 

Not_Found_Kernel32:

xor
eax,eax

ret

Found_Adress_base:

ret

;—————————————————————————————-

;Data for func
base_kernel

;—————————————————————————————-

nop

imagebase
dd 00400000h

entrypoint dd ?

Kernel32
dd 00000000h

swKernel32l equ 06e72656bh ;’nrek’

swKernel32u equ
04e52454bh ;’NREK’

se_Kernel32 db ‘KERNEL32.dll’,0

importvirtual
dd ?

importsize
dd ?

f_RVA
dd ?

gmhGetModuleHandleA db
‘GetModuleHandleA’,0

gmhsize =
$-gmhGetModuleHandleA ; hằng số

;——————————————————————————-

base_kernel_end:

_enddirty:

end start

Hy vọng sự sưu
tập này giúp ích được mọi người phần nào. Tôi viết các tuts
này ko phải vì tiền, hay vì cái gì. Nó chỉ vì tôi , do tôi
rất mau quên .Và nó cũng cho bạn : )


Benina (25/03/2006)
fixed 2008

[ASM] Part 2: Finding the base address of kernel32.dll library (2)

13.05.2010

rootb1ez ASM Để lại bình luận

;ÄÄÄ[ CUT HERE
]ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


Đây
là đọan code được viết lại trong MASM :

.586

.model flat, stdcall

option casemap:none

include \masm32\include\windows.inc

include
\masm32\include\user32.inc

includelib \masm32\lib\user32.lib

include
\masm32\include\kernel32.inc

includelib \masm32\lib\kernel32.lib

limit equ
5

.data

AppName db "Find Base Kernel
",0

dinhdang
db "Base Kernel: %lx ",0

.data?

buffer db 512 dup(?)


.code

start:

jmp indep_start

continous_host:

invoke wsprintf, addr buffer, addr dinhdang,
eax

invoke MessageBox,0, addr buffer, addr
AppName,MB_OK+MB_ICONINFORMATION

invoke
ExitProcess, 0

incode segment

;================================================================================

indep_start:

call Delta

Delta:

pop ebp

sub ebp,offset Delta

mov esi,[esp]

and esi,0FFFF0000h

call GetK32

jmp
continous_host

;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-;

;Ehrm, tôi xem như bạn
ít nhất phải là một coder ASM bình thường, vậy tôi ;

;coi như bạn đã biết bock đầu tiên của các
chỉ trị là lấy delta offset (well,nó ;

;ko cần thiết và có
liên quan gì trong ví dụ này , dù sao tôi cũng thích làm
điều ;

;này
cho nó giống một virus code). Well, block thứ hai
là những gì chúng ta đang 
;

;quan tâm. Chúng ta put trong
ESI addr mà ứng dụng của chúng ta được gọi
;

;đó là
addr được chỉ ra bởi ESP ( nếu chúng ta ko chạm đến stack
sau khi 
;

;chương trình loading, tất nhiên là vậy rồ!i). Chỉ
thị thứ hai, đó là AND, lấy 
;

;điểm bắt đầu của một page từ code của chúng ta
đã được called. Chúng ta 
;

;call routine của chúng ta, và sau đó chúng ta
terminate process ;) 
;

;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-;

GetK32:

__1:

cmp byte
ptr [ebp+K32_Limit],00h

jz WeFailed

cmp word ptr [esi],"ZM"

jz
CheckPE

__2:

sub esi,10000h

dec byte ptr [ebp+K32_Limit]

jmp __1

;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;

; Trước tiên, chúng
ta check 
xem chúng ta tới
giới hạn limit của chúng ta chưa
;

;(of 50 pages). Sau đó chúng
ta check xem trong điểm bắt đầu page có phải 
;

;là MZ sign ko, và nếu tìm
thấy chúng ta đi đến check PE header. Nếu ko, chúng ;

;ta trừ
cho 10 page (10000h bytes), chúng ta giảm giá trị limit, và
search một 
;

;lần nữa

;

;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú
ú-ú;

CheckPE:

mov edi,[esi+3Ch]

add edi,esi

cmp dword ptr [edi],"EP"

jz
WeGotK32

jmp __2

WeFailed:

mov esi,0BFF70000h

WeGotK32:

xchg eax,esi

ret

K32_Limit
dw limit

;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;

; Chúng
ta lấy giá trị từ offset 3Ch tính từ MZ header (handles của
 address ;

;RVA của nơi bắt đầu PE header),
chúng ta chuẩn hóa (normalize ) giá trị này ;

;với addr
của page, và nếu memory address được đánh dấu bởi offset
này là ;

;PE mark, chúng ta
giả định rằng chúng ta đã tìm thấy …và quả thật chúng
ta ;

;đã tìm xong ;) ;

;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;

KernelAdress
dd ?

;=================================================================================

incode ends

end start

2-Phương pháp của LethalMind :

Cách này scanning từ
return addr của hàm CreateProcess cài đặt process của app. Đây là
phần dịch nguyên bản gốc bài tut của LethalMind :

Trước hết,
ta cần một chút về lý thuyết :

Khi một chương
trình bị nhiễm độc bắt đầu chạy, nó bị gọi bởi hàm
CreateProcess. Điều đó có ý nghĩa gì ?? Đó là stack có một
return address trong đó. Vì vậy chỉ với điều này bạn đã có
thể thực hiện scan lại memory từ address đó (mà
chúng ta biết chúng ở bên trong Kernel) cho đến khi bạn ở tại
nơi bắt đầu của nó. Nó thật dễ dàng và là một phương pháp
thật hiệu quả, vì nó ngắn gọn, tương thích (Tôi ko thấy nó ko
tương thích bao giờ :) và lại nhanh chóng. Và tôi cũng có thể
nói nó thật ngăn nắp:)

Bây giờ code để minh
họa cho trick này:

8<—————————— S N I P E T
———————————


KernelAdress dd ?


StartOfYourVirus:

mov ecx,[esp] ; Return address of call from

;
CreateProcess

GetKrnlBaseLoop: ; Get Kernel32 module base
adress

xor edx,edx ;

dec ecx ; Scan backward

mov dx,[ecx+03ch] ; Take beginning of PE header

test dx,0f800h ; Is it a PE header ?

jnz GetKrnlBaseLoop ; No, forget about it

cmp ecx,[ecx+edx+34h] ; Compare current adress with the

;
address that PE should be loaded at

jnz GetKrnlBaseLoop ; Different ? Search again

mov [KernelAdress+ebp],ecx ; ecx hold KernelBase… Store it

8<—————————E
N D – S N I P E T —————————–


Ví dụ code sau đây viết lại cho MASM.

.586

.model flat,
stdcall

option casemap:none

include
\masm32\include\windows.inc

include \masm32\include\user32.inc

includelib
\masm32\lib\user32.lib

include
\masm32\include\kernel32.inc

includelib \masm32\lib\kernel32.lib

.data

AppName db "Find Base Kernel
",0

dinhdang
db "Base Kernel: %lx ",0

.data?

buffer db 512 dup(?)


.code

start:

jmp indep_start

continous_host:

invoke
wsprintf, addr buffer, addr dinhdang, ecx

invoke
MessageBox,0, addr buffer, addr AppName,MB_OK+MB_ICONINFORMATION

invoke ExitProcess, 0

incode segment

;==============================================================================

indep_start:

call Delta

Delta:

pop ebp

sub ebp,offset Delta

mov
ecx,[esp] ; Return
adress of call from

;
CreateProcess

GetKrnlBaseLoop: ; Get Kernel32 module base
adress

xor edx,edx ;

dec
ecx ; Scan
backward

mov dx,[ecx+03ch] ; Take beginning of PE header

test dx,0f800h ;
Is it a PE header ?

jnz GetKrnlBaseLoop ; No, forget about it

cmp ecx,[ecx+edx+34h] ;
Compare current adress with the


; address that PE should be loaded at

jnz GetKrnlBaseLoop ;
Different ? Search again

mov [KernelAdress+ebp],ecx ; ecx hold KernelBase… Store it

jmp continous_host

KernelAdress dd ?

;==============================================================================

incode ends

end start

3. Finding the base address of kernel32.dll
library from PEB

Dựa vào kiến thức về PEB
và TEB, ta có thể tìm ra base addr của Kernel . Theo tôi nghĩ,
đây là cách hòan hảo nhất so với các cách tôi đã trình bày.
Để trình bày cách này dễ hiểu nhất, tôi sẽ đưa source code ra
trước và chúng ta sẽ phân tích từng dòng lệnh .

.586

.model flat,
stdcall

option casemap:none

include
\masm32\include\windows.inc

include \masm32\include\user32.inc

includelib \masm32\lib\user32.lib

include \masm32\include\kernel32.inc

includelib
\masm32\lib\kernel32.lib

.data

AppName db "Find Base Kernel ",0

dinhdang db "Base Kernel: %lx ",0

.data?

buffer
db 512 dup(?)


.code

start:

jmp indep_start

continous_host:

invoke
wsprintf, addr buffer, addr dinhdang, e
ax

invoke
MessageBox,0, addr buffer, addr AppName,MB_OK+MB_ICONINFORMATION

invoke ExitProcess, 0

incode segment

;==============================================================================

indep_start:

call
Delta

Delta:

pop ebp

sub ebp,offset Delta

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

jmp continous_host

;==============================================================================

incode ends

end start


[ASM] Part 2: Finding the base address of kernel32.dll library (1)

13.05.2010

rootb1ez ASM Để lại bình luận

Part 2: Finding the base address of
kernel32.dll library

Author: Benina

Spinx đã từng viết một
bài hướng dẫn writting code virus trên 4rum HVA rất nổi tiếng, tui
xin trích một đọan trong bài tut đó như sau:

Khi gọi ngắt trên dos, địa chỉ ngắt luôn được HĐH tự động xác
định qua bảng vector ngắt. Cứ việc gọi int xxx (0CD/xxx) việc còn lại là
của CPU. Với API lại khác. API thực chất chỉ là các hàm thư viện viết
sẵn của HĐH và load lên bộ nhớ như một chương trình. Để gọi được nó cần
có địa chỉ của nó trong tay. Đương nhiên câu hỏi được đặt ra là vậy các
chương trình “hợp pháp” gọi API như thế nào? Ta quay trở lại với khái
niệm cơ bản trên windows. Các chương trình sau khi biên dịch đều có một
bảng import table. Các hàm API chương trình sẽ sử dụng đều được ghi
trong import table. Khi chương trình chạy windows loader sẽ làm nhiệm vụ
xây dụng một bảng địa chỉ IAT (import address table) cho các hàm này.
Thật đáng tiếc ta không thể thay đổi bảng này vì windows đã để mắt đến
nó. Vì vậy để goi API từ VR ta phải làm lại thao tác của loader và xây
dựng bản địa chỉ riêng.

Khi đó lệnh gọi tương đối hàm API sẽ có dạng

call [ebp+_]

ebp+_ ==> chứa địa
chỉ hàm API

APIs nó nằm ở đâu bạn không hề biết, làm sao bây giờ. Đâu có
đó, thịt chó còn có rau thơm. Windows cung cấp cho ta một hàm để lấy địa
chỉ các làm API theo tên là hàm là GetProcAddress. Vấn đề
ở đây là GetProcAddress cũng là 1 hàm API. Vậy yêu cầu tối
thiểu là phải có địa chỉ 1 hàm GetProcAddress trong Windows. Hàm này
thuộc kernel của win nên bây giờ câu hỏi chỉ còn kernel của windows nằm ở
đâu. Trả lời điều này dễ hơn nhiều. Ta có ngay một danh sách:

0BFF70000h = Win95 Kernel
Addr

077F00000h = WinNT Kernel
Addr

077e00000h = Win2k Kernel
Addr

0BFF60000h= WinME Kernael
Addr (in Memory)

077e600000h=WinXP,Win2K3
Kernel Addr (cái này tôi thêm vào)


Ta có thể yên tâm sử dụng cho đến khi
Microsoft thay đi mất ;) Thực ra địa chỉ này có thể không hoàn toàn
chính xác như vậy. Ta nên kiểm tra ký tự “MZ” (header của file
kernel32.dll) để xác định chính xác vị trí kernel. Có kernel header đối
chiếu export/import table của kernel để đọc địa chỉ hàm API theo tên.
Oải quá phải không? Đáng tiếc là no way. Thực ra cũng không quá khó đâu
vì tất cả đều có trong PE/NE HEADER. Các bạn đọc và nghiền ngẫm kỹ lại
đi.

Để hiểu được và thực hành được những gì spinx nói ở
trên chúng ta cần học rất nhiều vấn đề. Bài tut này sẽ giúp
bạn khám phá từng chút một những gì bác spinx đã đề cập.

Tại sao tôi lại nói
về coding virus?. Như tut Part 1 tôi đã nói, code độc lập chẳng
khác gì đọan code virus, vì vậy nghiên cứu chúng chẳng khác
nào nghiên cứu về virus!. Nhưng như tôi đã nói, các bài tuts mà
tôi viết chỉ để học tập và nghiên cứu, tôi hòan tòan ko chịu
trách nhiệm về những gì các bạn ứng dụng những kiến thức
này để phá phách. Mong các bạn đừng làm những việc ngu xuẩn
để rồi mang họa vào thân trước tiên.

I. VÀO ĐỀ:

Windows cung cấp các
hàm APIs cho người dùng sử dụng trong các ứng dụng trong môi
trường Windows. Và hầu như 100% các ứng dụng chạy trên nền
Windows đều phải sử dụng các hàm APIs. Đọan code độc lập để
thực hiện nhiều tác vụ mà coder mong muốn cũng phải sử dụng
qua các hàm APIs. Nếu chỉ dùng các chỉ thị assember mà ko dùng
các hàm APIs, thì đọan code độc lập cũng chẳng làm được gì
nhiều. Điều này cũng giống như bạn ở trong một thành phố lớn
có đầy đủ các phương tiện giao thông , mà bạn chẳng có nổi
tiền để đi xe buýt, như thế thì bạn sẽ cuốc bộ đến tất cả
mọi nơi , mà nhiều khi có những nơi bạn ko thể đi đến được!.
Vì vậy đọan code độc lập dù muốn dù ko cũng phải sử dụng
các hàm APIs.

Trước khi đi tiếp vấn đề các bạn cũng cần biết qua
vài điều. Như bạn biết , một file .exe khi được người dùng kích
họat cho run thì Windows sẽ tạo ra một process để thực thi, công
việc này Windows sẽ giao cho tiến trình Windows Loader đảm trách
thực hiện. Windows Loader đầu tiên sẽ mapping image của module
file .exe vào memory , sau đó nó sẽ load các thư viện động .dll
liên quan đến process bằng hàm LoadModule, tiếp tục nó dùng hàm
GetProcAddress để get các addr của các hàm APIs cần Import của
process (dựa vào PE Header). Và cuối cùng dùng
hàm CreateProcess để cài đặt process và bắt đầu thực hiện chỉ
thị đầu tiên của ứng dụng. Các bạn nên nhớ chuổi tiến trình
thực hiện này của Windows Loader.

Một vấn đề đặt ra ở đây,
như trong tut phần 1 tôi đã nói, một đọan code độc lập khi cư
trú trong memory phải thuộc một proces nào đó. Do đó chắc các
bạn cho rằng chúng ta dễ dàng sử dụng các hàm APIs trong đọan
code độc lập. Điều này là sai lầm vì nhiều lý do:

Thứ nhất, các hàm
APIs import có hạn chế khi được import vào process do chương trình
chính sử dụng hàm nào thì mới import hàm đó. Vì vậy, nếu
trong đọan code độc lập ta muốn dùng một hàm API mà ko được
import thì ko thể sử dụng được.

Thứ hai , như bác spinx đã
nói : Các chương trình sau
khi biên dịch đều có một bảng import table. Các hàm API chương trình sẽ
sử dụng đều được ghi trong import table. Khi chương trình chạy windows
loader sẽ làm nhiệm vụ xây dụng một bảng địa chỉ IAT (import address
table) cho các hàm này. Thật đáng tiếc ta không thể thay đổi bảng này vì
windows đã để mắt đến nó
 . Vì vậy, ý tưởng thay đổi
bảng import table 
(nới rộng thêm bảng này để add hàm API cần
dùng) để có được hàm API cần dùng trong
đọan code độc lập là 
rất khó thực hiện (thực hiện được nhưng
phải dùng hàm API., giống như mèo lại
hoàn mèo)

Thứ ba, là vì đặc tính
của nó là đọan code “độc lập” nên nó sẽ ko phụ thuộc
vào bảng IAT của bất kỳ một tiến trình nào.

Do đó chỉ còn một
cách là trong đọan code độc lập chúng ta sẽ lập trình lại
tiến trình mà Windows loader đã làm. Nhưng cái khó ở chổ các
hàm LoadModule và GetProAddress mà Windows Loader sử dụng cũng là
hàm APIs thuộc kernel.dll. Cho nên, để giải quyết vấn đề, chúng
ta phải xem xét kernel.dll định trú trong memory tại đâu tức là
tìm base addr của kernel. Rồi từ đó, chúng ta sẽ tìm ra addr
của 2 hàm nói trên. Như tôi đã từng nói, 100% các app trong
Windows đều sử dụng các hàm APIs trong kernel.dll. Vì vậy chắc
chắn kernel sẽ được load vào trong vùng memory mà Windows cấp
phát cho process ứng dụng. Và thêm 1 điều nữa, các đọan code
độc lập lại là 1 phần của một process nào đó. Vời mối liên
hệ này, trong code độc lập có thể tìm ra base addr của kernel.

Chúng ta lại biết
thêm rằng (như spinx đã nói), kernel lại được Windows fix cố định
tại 1 addr. Nhưng từng ver thì addr này lại được fix khác nhau.
Ví dụ:

0BFF70000h = Win95 Kernel Addr

077F00000h
= WinNT Kernel Addr

077e00000h = Win2k Kernel
Addr

0BFF60000h= WinME Kernael
Addr (in Memory)

077e600000h=WinXP,Win2K3
Kernel Addr (cái này tôi thêm vào)

Vì vậy, để đọan code độc
lập phù hợp với từng ver, chúng ta phải có thuật tóan tìm
base addr của kernel cho hầu hết các ver Windows. Nội dung chính
của bài tut này chính là tìm base addr của kernel.dll mà đọan
code độc lập cần phải làm.

II. THUẬT TÓAN TÌM
BASE ADDR KERNEL :

Nói thuật tóan cho nó ghê gớm , chứ thực ra ta
có nhiều cách để tìm base addr kernel. Trong tut này tôi sẽ đề
cập đến 3 cách thông thường mà tôi sẽ tổng hợp cho bạn như sau:

1.Hardcoding Addr:

Phương pháp này là
chuối nhất và đơn giản nhất. Nhưng trước khi tìm hiểu pp này
bạn phải đọc qua tut “PE tutorial 3” của Iczelion’z để
biết cách check 1 file có phải PE file hay ko. Tôi xin tóm tắt pp
này như sau:

Như chúng ta biết các base addr của kernel được
fixed cố định tại một addr tùy theo ver của Windows.

Vì vậy dựa vào bài :
“Detecting operating systems without Microsoft Advanced Programming
Interface” mà tôi đã public ta tìm được ver của Windows, từ đó ta
hardcode addr Kernel base theo ver Windows tìm được.

Hoặc để nhanh gọn hơn,
chúng ta sẽ sẽ thử từng addr mà ta biết như list dưới đây có
phải là một PE file ko :

0BFF70000h = Win95 Kernel
Addr

077F00000h = WinNT Kernel
Addr

077e00000h = Win2k Kernel
Addr

0BFF60000h= WinME Kernael
Addr (in Memory)

077e600000h=WinXP,Win2K3
Kernel Addr (cái này tôi thêm vào)

Khi thỏa thì đó chính là
base addr của kernel. Nhưng cách này ta phải dùng thủ thuật lập
trình SEH. Đây lại là một câu chuyện khác. Xin phép tôi sẽ hầu
bạn đề tài này sau.

Sau đây là một ví dụ về ứng dụng bài
“Detecting operating systems without Microsoft Advanced Programming
Interface”. Tức là , một app hoàn chỉnh thì luôn phải phù hợp
với các ver, do đó khi chúng ta muốn đọan code ứng dụng cũng
hòan hảo như một ứng dụng thì chúng ta phải biết được Current
OS, sau khi tìm được OS hiện hành, chúng ta sẽ hardcoding addr
của Kernel Base. Source này chuối nhất đấy ;)) ko nên dùng

.586

.model flat,
stdcall

option casemap:none

include
\masm32\include\windows.inc

include \masm32\include\user32.inc

includelib
\masm32\lib\user32.lib

include
\masm32\include\kernel32.inc

includelib \masm32\lib\kernel32.lib

.data

AppName db "Find Base Kernel
",0

dinhdang
db "Base Kernel: %lx ",0

.data?

buffer db 512 dup(?)


.code

start:

jmp
indep_start

continuos_host:

invoke
wsprintf, addr buffer, addr dinhdang, eax

invoke MessageBox,0,
addr buffer, addr AppName,MB_OK+MB_ICONINFORMATION

invoke
ExitProcess, 0

;================================================================================

incode segment

indep_start:

call Delta

Delta:

pop ebp

sub ebp,offset Delta

pushad
; store all registers

mov esi,dword ptr
[ebp+OS_UNKNOWN]

assume
fs:nothing

mov
ebx,fs:[18h] ; get self
pointer from TEB

mov
eax,fs:[30h] ; get pointer
to PEB / database

.if eax==7FFDF000h && ebx==7FFDE000h
; WinNT based

mov ebx,[eax+0A8h] ;
get OSMinorVersion

mov eax,[eax+0A4h] ;
get OSMajorVersion

.if eax==5 && ebx==0
; is it Windows 2000?

mov esi,dword ptr [ebp+OS_WIN2K]

.elseif eax==5 &&
ebx==1 ; is it Windows XP?

mov esi,dword ptr
[ebp+OS_WINXP]

.elseif
eax==5 && ebx==2 ; is it
Windows 2003?

mov
esi,dword ptr [ebp+OS_WIN2K3]

.elseif eax<=4
; is it Windows NT?

mov esi,dword ptr
[ebp+OS_WINNT]

.endif

.else
; Win9X based

mov edx,00530000h
; the magic value to search

mov eax,fs:[18h]
; get the TEB base address

mov ebx,[eax+58h]
; TEB-base + 58h (W95)

mov ecx,[eax+7Ch]
; TEB-base + 7Ch (WME)

mov eax,[eax+54h]
; TEB-base + 54h (W98)

.if ebx==edx
; is it Windows 95?

mov esi,dword ptr [ebp+OS_WIN95]

.elseif eax==edx
; is it Windows 98?

mov esi,dword ptr
[ebp+OS_WIN98]

.elseif
ecx==edx ; is it Windows
ME?

mov
esi,dword ptr [ebp+OS_WINME]

.endif

.endif ; of base check NT/9X

mov dword ptr
[ebp+_CurrentOS],esi

popad ;
restore all registers

;—————————————————————————

mov eax,dword ptr
[ebp+_CurrentOS]

.if eax==-1

mov dword ptr
[ebp+_BaseKernel],
 00000000h

.elseif eax==1

mov dword ptr
[ebp+_BaseKernel], 0BFF70000h

.elseif eax==2

mov dword ptr
[ebp+_BaseKernel], 0BFF70000h

.elseif eax==3

mov dword ptr [ebp+_BaseKernel], 0BFF60000h

.elseif eax==4

mov dword ptr [ebp+_BaseKernel],
077F00000h

.elseif
eax==5

mov dword
ptr [ebp+_BaseKernel], 077e00000h

.elseif eax==6

mov dword ptr [ebp+_BaseKernel], 077e60000h

.elseif eax==7

mov dword ptr
[ebp+_BaseKernel], 077e60000h

.endif

mov eax,dword ptr [ebp+_BaseKernel]

jmp continuos_host

OS_UNKNOWN
dword -1

OS_WIN95
dword 1

OS_WIN98
dword 2

OS_WINME
dword 3

OS_WINNT
dword 4

OS_WIN2K
dword 5

OS_WINXP
dword 6

OS_WIN2K3
dword 7

_CurrentOS
dword 0

_BaseKernel
Dword 0

incode ends

;==============================================================================

end start


2.Scanning from
Return value of CreateProcess in stack

Lợi dụng tiến trình
Windows Loader khi cài đặt process bằng hàm CreateProcess và bắt
đầu chuẩn bị thực thi chỉ thị đầu tiên đọan
code độc lập cũng là chỉ thị đầu tiên của app (ứng dụng),
lúc đó sau khi CreateProcess cài đặt process xong, trong stack sẽ
chứa giá trị return trở lại cho tiến trình Windows Loader mà
Windows Loader là một tiến trình trong Kernel , do đó giá trị
trong stack là một addr nào đó của module Kernel. Từ addr này,
chúng ta dùng thuật tóan scan ngược lên và thử từng addr để xem
addr nào là base của một PE file bằng thuật tóan “check PE file”
vì Kernel cũng là 1 PE file. Khi đó ta tìm được base addr Kernel.

Ở đây tôi sẽ phân
tích qua 2 cách thông dụng đã public trên NET. Một là cách coding
của Billy Belceb£ và một là cách của LethalMind
trong nhóm Vxer 29A.

1-Phương pháp của Billy Belceb£

Cách của Billy scan
tại các vị trí đầu các page viì các module được mapping bắt
đầu từ đầu 1 page. Do đó các này có vẽ nhanh hơn cách của
LethalMind, nhưng lại có 1 khuyết điểm là tồn tại 1 hardcoding

mov esi,0BFF70000h

Theo tôi thấy, cách
của LethalMind có vẽ chậm hơn nhưng công nhận nó gắn gọn hơn
nhiều. Sau đây là tòan bộ bài dịch cách coding của Billy. Source
là TASM nhưng tôi có code lại 1 ví dụ trong MASM theo sau đây.

%
A simple way for get KERNEL32 base address %

ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

Như
bạn biết, khi chúng ta thực thi một ứng dụng, code được gọi
từ một phần code of KERNEL32 (nghĩa là, giống như KERNEL tạo ra
một CALL đến code của chúng ta) và, nếu bạn nhớ lại, khi một
call được gọi ra, return address ở trong stack
( đó là trong memory address của ESP). Chúng ta hảy xem một ví dụ thực hành như
sau:

;ÄÄÄ[
CUT HERE ]ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


.586p ; Bah… simply for phun.

.model flat ; Hehehe i love 32 bit stuph ;)

.data ; Some data (needed by TASM32/TLINK32)

db ?


.code

start:

mov eax,[esp] ; Now EAX would be BFF8XXXXh (if w9X)


; ie,
somewhere inside the API


; CreateProcess :)

ret ;
Return to it ;)

end start

;ÄÄÄ[
CUT HERE ]ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

Well,
đơn giản!. Chúng ta có trong EAX một giá
trị có dạng như BFF8XXXX (XXXX là một giá trị ko quan trọng, nó
được đặt như vầy vì ta ko cần thiết để biết nó một cách
chính xác, đừng làm phiền tôi với những thứ ngớ ngẩn đó;).
Như Win32 platforms thường “xoay vòng” một page all (các bạn đọc
tut VMM để biết thêm ), chúng ta cần tìm điểm bắt đầu của bất
cứ page nào, và khi KERNEL32 header chỉ ở tại nơi bắt đầu của
một page, chúng ta có thể check dễ dàng nó . Và khi chúng ta
tìm được PE header này (tôi đang nói về nó) thì
chúng ta đã tìm KERNEL32 base address. Hrmm,
chúng ta có thể cho giới hạn scanning 50h
pages thôi là đủ.

Hehe,
Đừng quá lo lắng. Đọan code theo sau ngay đây ;)

;ÄÄÄ[ CUT HERE
]ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

.586p


.model flat

extrn ExitProcess:PROC


.data

limit equ 5


db 0


;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;

; Ko sử dụng và ko có data
cơ bản :)
; ;

;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;


.code

test:

call delta

delta:

pop ebp

sub ebp,offset delta

mov esi,[esp]

and esi,0FFFF0000h

call GetK32


push 00000000h

call ExitProcess


;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;

; Ehrm, tôi xem như bạn ít
nhất phải là một coder ASM bình thường, vậy tôi 
;

;
coi như bạn đã biết bock đầu tiên của các chỉ trị là lấy
delta offset (well,nó ko ;

;cần thiết và có liên quan
gì trong ví dụ này , dù sao tôi cũng thích làm điều này 
;

;cho nó giống một virus
code). Well, block thứ hai là những gì chúng ta đang
;

;quan
tâm. Chúng ta put trong ESI addr mà ứng dụng của chúng ta được
gọi 
;

;đó là addr được chỉ ra bởi
ESP ( nếu chúng ta ko chạm đến stack sau khi ;

;chương
trình loading, tất nhiên là vậy rồ!i). Chỉ thị thứ hai, đó là
AND, lấy ;

;điểm bắt đầu của một page từ code của chúng
ta đã được called. Chúng ta 
;

;call
routine của chúng ta, và sau đó chúng ta terminate process ;);

;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;


GetK32:

__1:

cmp byte ptr [ebp+K32_Limit],00h

jz WeFailed


cmp word
ptr [esi],"ZM"

jz CheckPE


__2:


sub esi,10000h

dec byte ptr
[ebp+K32_Limit]

jmp __1

;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú
ú-ú;

; Trước tiên,
chúng ta check chúng ta tới giới hạn limit của chúng ta chưa ;

;(of 50 pages).
Sau đó chúng ta check xem trong điểm bắt đầu page có phải ;

;là
MZ sign ko, và nếu tìm thấy chúng ta đi đến check PE header. Nếu
ko, chúng ;

;ta
trừ cho 10 page (10000h bytes), chúng ta giảm giá trị limit, và
search một ;

;lần
nữa .
;
;

;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú
ú-ú;

CheckPE:

mov edi,[esi+3Ch]

add edi,esi

cmp dword ptr
[edi],"EP"

jz WeGotK32


jmp __2

WeFailed:

mov esi,0BFF70000h

WeGotK32:

xchg eax,esi

ret


K32_Limit dw limit


;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;

; Chúng ta lấy giá trị từ
offset 3Ch tính từ MZ header (handles của address ;

;RVA của nơi bắt đầu PE
header), chúng ta chuẩn hóa (normalize ) giá trị này
;

;với
addr của page, và nếu memory address được đánh dấu bởi offset
này là ;

;PE mark, chúng ta giả định rằng chúng ta đã
tìm thấy …và quả thật chúng ta ;

;đã tìm thấy rồi đó ;) ;

;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;


end test

[ASM] Writing a DLL in tasm32

14.03.2010

rootb1ez ASM Để lại bình luận

Author: Benina
My blog: rootbiez.tk
Download :
 
Bài viết tóm tắt cách tạo 1 dll trong tasm32. Ở đây ko nói về lý thuyết dll. Bài này chỉ là một bài thực hành. Tôi viết chỉ để lưu trữ cho riêng tôi.
Một dll cơ bản như sau:

 

   1:                          .586p
   2:                          .model  flat, stdcall
   3:                          locals
   4:                          jumps
   5:                          
   6:  include                 c:\tasm32\include\shitheap.inc
   7:  include                 dll.inc
   8:  include                 struct.inc
   9:   
  10:                          .data
  11:  String1              db      "kernel32.dll", 0
  12:  hmodule              dd      ?
  13:   
  14:                          .code
  15:  ;dllmain, entrypoint dll
  16:  start                   proc
  17:                          arg     imagebase
  18:                          arg     reason
  19:                          arg     reserved
  20:                          
  21:                          pushad                       
  22:                          cmp     reason, 1
  23:                          jne     __e_dllinit
  24:   
  25:               ;(code here)
  26:   
  27:  __e_dllinit:            popad
  28:                          mov     eax, 1
  29:                          leave
  30:                          retn    0ch
  31:                          endp
  32:                          
  33:  public my_Function
  34:  my_Function:
  35:                          ;code my func to export
  36:                          Retn
  37:   
  38:  end     start
  39:   
.Phân tích:

0/Header file:

   1:                          .586p
   2:                          .model  flat, stdcall
   3:                          locals
   4:                          jumps
 
.586p :Dùng các chỉ thị của bộ vi xử lý 586p trở lên
.model flat, stdcall : Bộ nhớ dùng flat memory model. Và quy ước gọi hàm là stdcall
jumps : Làm cho TASM tìm ra vị trí của một lệnh nhảy có điều kiện.
locals : Cho phép block-scoped symbols

 

1/Giá trị trả về của dllmain retn 0ch:

Phần khởi trị đầu tại entry point của chương trình của bạn phải quite với một chỉ lệnh ret 0Ch và ko exit với ExitProcess như file exe. Lý do đơn giản là loader gọi entry point gống như sau:

   1:  BOOL WINAPI DllEntryPoint(
   2:                  HINSTANCE  hinstDLL,        // handle of DLL module
   3:                  DWORD  fdwReason,           // reason for calling function
   4:                  LPVOID  lpvReserved         // reserved
   5:                 );
 
Nói một chút về lệnh RETN: Lệnh “RETN 4” có nghĩa là chuyển điều khiển EIP đến [esp+4] tức là sẽ bỏ qua 4 bytes addr trên stack để lấy addr cho EIP tại esp+4.
Chúng ta chú ý:
Ta có 4 tham số cho hàm proc của dll:

 

                        arg     imagebase
                        arg     reason
                        arg     reserved
 
Vì vậy theo như lệnh retn nói trên chúng ta sẽ trả về : retn 0ch
 
2/Tham số reason:
Có các giá trị sau:

 

Setting                 Value   Description
DLL_PROCESS_ATTACH      1       New process has started
DLL_THREAD_ATTACH       2       New thread has been created
DLL_THREAD_DETACH       3       Thread is about to be terminated
DLL_PROCESS_DETACH      0       Process is about to terminate

 

DLL đụoc gọi khi nó bị gắn vào process hay thread hay khi nó tách ra từ một process hay thread. Do đó tham số reason sẽ có các giá trị trên

3/Exported func được viết như sau:

 public myFunction
 
 myFunction PROC
     ; your code goes here ...
     ret
 myFunction ENDP

 

Hay

public my_Function
my_Function:
                        ;code my func to export
                        Retn

 

Nếu bạn ko hai báo func của bạn là publuc thì linker sẽ cho bạn cảnh báo.

4/Bảo quản thanh ghi:

Vài thanh ghi phải được bảo quản trong DLL entrypoint của chúng ta. Điều này rất quan trọng, nếu bạn ko bảo quản (preserve) chúng thì process loaded DLL sẽ kết thúc mà ko đưa ra thông báo lỗi gì sau khi DLL entrypoint đã run. Tôi ko biết những thanh ghi gì được bảo quản, nhưng ESI là chắc chắn. Một cách tốt nhất là dùng PUSHAD và POPAD để bảo quản tất cả các thanh ghi. Giá trị trả về chỉ quan trọng khi entrypoint được gọi với giá trị DLL_PROCESS_ATTACH cho tham số reason. Nó phải khác zero vì mục đích là đề xuất với hàm LoadLibrary API rằng khởi trị đầu thành công. Nó trả về zero thì DLL sẽ remove khỏi process. Vì vậy sau cùng của dll ta cần set eax:

                        mov     eax, 1
                        leave

 

5/Viết file .def:

Để export hàm của bạn, bạn cần write một file ‘.def’
Các file definition files (.def) này dường như rất giống C++. Tôi ko biết nhiều về chúng nhưng tôi biết bạn có thể viết như sau cho việc export hàm của bạn:
EXPORTS 
myFunction 

 

File .def nên đặt cùng tên với file asm của dll

 

6/Các file inc được kèm vào như dll.inc:

Dùng để chứa các hàm import, ví dụ

extrn ActivateActCtx:proc
extrn AddAtomA:proc
extrn AddAtomW:proc

 

còn struct.inc, để chứa các cấu trúc phục vụ cho coding dll

7/Biên dịch:

Không có resource, ta tạo file bat để biên dịch:

;------------------------------------------------------------------------------
@echo off
 
if exist %1.obj del %1.obj
if exist %1.exe del %1.exe
 
: -----------------------------------------
: assemble *.asm into an OBJ file
: -----------------------------------------
 
C:\tasm32\bin\TASM32.EXE  /ml /z /m9 /q  %1.asm
 
if errorlevel 1 goto errasm
 
: -----------------------
: link the main OBJ file
: -----------------------
 
c:\tasm32\bin\tlink32  /Tpd /aa /c /x  %1.obj,,,c:\tasm32\importlib\import32.lib,%1.def
 
if errorlevel 1 goto errlink
 
;----------------------------
;pewrite
;---------------------------
 
c:\tasm32\tasm32\pewrite %1.exe
 
if errorlevel 1 goto errpewrite
 
goto TheEnd
 
:errlink
 
: ----------------------------------------------------
: display message if there is an error during linking
: ----------------------------------------------------
 
echo.
echo There has been an error while linking this project.
echo.
goto TheEnd
 
:errasm
 
: -----------------------------------------------------
: display message if there is an error during assembly
: -----------------------------------------------------
 
echo.
echo There has been an error while assembling this project.
echo.
 
goto TheEnd
 
: -----------------------------------------------------
: display message if there is an error during pewrite
: -----------------------------------------------------
 
:errpewrite
 
echo.
echo There has been an error while pewrite this project.
echo.
 
goto TheEnd
 
:TheEnd
 
:if exist %1.obj del %1.obj
 
pause
 
;----------------------------------------------------------

 

Ví dụ cụ thể:

a/dll.asm như sau:

   1:                  .586p
   2:                          .model  flat, stdcall
   3:                          locals
   4:                          jumps
   5:                       
   6:  include                 dll.inc
   7:   
   8:                          .data
   9:   
  10:   msg_title   DB "Dll title",0
  11:   msg_message DB "Hello World! dll loader",0
  12:   msg_message_func DB "dll func loader",0
  13:   
  14:   
  15:   
  16:                          .code
  17:  ;dllmain, entrypoint dll
  18:  start                   proc
  19:                          arg     imagebase
  20:                          arg     reason
  21:                          arg     reserved
  22:                          
  23:                          pushad                       
  24:                          cmp     reason, 1
  25:                          jne     __e_dllinit
  26:   
  27:                  ;(code here)
  28:          push 0
  29:          push offset msg_title
  30:          push offset msg_message
  31:          push 0
  32:          call MessageBoxA
  33:   
  34:  __e_dllinit:            popad
  35:                          mov     eax, 1
  36:                          leave
  37:                          retn    0ch
  38:                          endp
  39:  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;                        
  40:  public myFunction
  41:   
  42:  myFunction PROC
  43:          push 0
  44:          push offset msg_title
  45:          push offset msg_message_func
  46:          push 0
  47:          call MessageBoxA
  48:       ret
  49:  myFunction ENDP
  50:   
  51:  end     start

 

b/dll.inc như sau:

extrn ExitProcess:proc
extrn MessageBoxA:proc

 

c/dll.def như sau:

EXPORTS
     myFunction

 

d/Loader gọi hàm trong dll như sau:

Phần loader này ko kiểm tra giá trị trả về của các APIs:

   1:                          .586p
   2:                          .model  flat
   3:                          locals
   4:                          jumps
   5:                          
   6:  extrn ExitProcess:proc
   7:  extrn MessageBoxA:proc
   8:  extrn LoadLibraryA:proc
   9:  extrn GetProcAddress:proc
  10:   
  11:   
  12:  o    equ     offset
  13:   
  14:  .data
  15:   
  16:      msg_title   DB "MessageBox title",0
  17:      msg_message DB "Hello World!",0
  18:   
  19:      dllname db "dll.DLL",0
  20:      dllfunc db "myFunction",0
  21:   
  22:      hlib dd ?
  23:      fn_myFunction dd ?
  24:   
  25:  .code
  26:  start:                 
  27:   
  28:          push 0
  29:          push offset msg_title
  30:          push offset msg_message
  31:          push 0
  32:          call MessageBoxA
  33:   
  34:          push o dllname
  35:          call LoadLibraryA
  36:          mov hlib,eax
  37:   
  38:   
  39:          push o dllfunc
  40:          push hlib
  41:          call GetProcAddress
  42:          mov fn_myFunction,eax
  43:   
  44:          call fn_myFunction
  45:   
  46:          push 0
  47:          call ExitProcess
  48:   
  49:   end     start

[ASM] Enum Processes (Part 1)

23.01.2010

rootb1ez ASM Để lại bình luận

 

Author: Benina

Download: [Source]

Đọc tut tại: http://docs.google.com/View?id=ddstr8xk_29gqtfvxcv

Tuts này tôi chỉ tổng kết một số cách liệt kê processes đang thực thi hiện hành trong Windows. Mục đích chính cũng là để tôi lưu trữ vì vậy các đoạn code trong tuts này không chuẩn mực (ko có kiểm tra lỗi xảy ra bằng cách dùng hàm GetLastError), có nhiều điểm cần phải bổ sung. Nhưng dù sao đi nữa nó cũng có ích phần nào cho các bạn nghiên cứu lập trình masm.

Tôi thực hành trên Windows XP SP2. Trên các Windows khác code có thể ko thực thi.

1.Phương pháp 1: Using Process Status Helper Library

Đầu tiên ta dùng hàm EnumProcesses để liệt kê các PIDs (chỉ mục process) các processes đang running trong system. Giá trị trả về của hàm là một con trỏ đến mảng array dword chứa các PIDs (tham số pProcessIDs) và tổng số bytes chứa mảng array trả về (tham số pBytesReturned).

Sau đó ta dùng một vòng lặp duyệt qua từng thành phần trong mảng array dword, tức là duyệt qua từng PID trong array để đọc tên processes.

Muốn đọc được name process ta phải dùng một loạt các hàm APIs như sau:

-Mở process bằng hàm OpenProcess với flag là PROCESS_QUERY_INFORMATION or PROCESS_VM_READ. Với flag như vậy chúng ta mới truy vấn được thông tin của processes.

-Liệt kê Modules của process vừa mở bằng hàm EnumProcessModules. Hàm này sẽ trả về một mảng array chứa tên các modules (ascii) và tổng số bytes chứa mảng array đó.

-Sau đó ta dùng hàm GetModuleBaseName để đọc tên base name module. Tức là tên module thực thi process.

-Cuối cùng ta nhớ CloseHandle process mở trước đó

Sau đây là một số bước thực hiện tóm tắt:

Bước 1: Liệt kê các PIDs bằng cách dùng hàm EnumProcesses

Hàm EnumProcesses nhận các PIDs của các processes trong system.

Syntax

C++

BOOL WINAPI EnumProcesses(

  __out  DWORD *pProcessIds,

  __in   DWORD cb,

  __out  DWORD *pBytesReturned

);

Parameters

pProcessIds [out]

Con trỏ trỏ đến một array nhận danh sách các chỉ mục process (list of process identifiers).

cb [in]

Kích thước (bytes) của mãng pProcessIds array.

pBytesReturned [out]

Số bytes trả về trong pProcessIds array.

Return Value

Nếu hàm thành công thì giá trị trả về là nonzero.

Nếu hàm sai, giá trị trả về là zero. Để lấy thông tin lỗi, call GetLastError.

Bước 2a: Dùng hàm OpenProcess mở process để ta truy vấn thông tin từ nó

Mở process bằng hàm OpenProcess với flag là PROCESS_QUERY_INFORMATION or PROCESS_VM_READ. Với flag như vậy chúng ta mới truy vấn được thông tin của processes.

Bước 2b:Dùng hàm EnumProcessModules để liệt kê các modules trong process được mở trước đó

Gọi hàm EnumProcessModules để nhận một handle cho mỗi module trong process được chỉ định.

Syntax

C++

BOOL WINAPI EnumProcessModules(

  __in   HANDLE hProcess,

  __out  HMODULE *lphModule,

  __in   DWORD cb,

  __out  LPDWORD lpcbNeeded

);

Parameters

hProcess [in]

handle của process cần liệt kê các modules.

lphModule [out]

Một array nhận danh sách các module handles.

cb [in]

size of lphModule array, đơn vị bytes.

lpcbNeeded [out]

số bytes cần thiết để chứa tất cả các module handles trong lphModule array.

Return Value

Nếu hàm thành công trả về giá trị là nonzero.

Nếu hàm fails, return value là zero. To get extended error information, call GetLastError.

Bước 3:Dùng hàm GetModuleBaseName để lấy addr chứa name của module

GetModuleBaseName Function

Nhận địa chỉ đầu tiên chứa name of module được chỉ định.

Syntax

C++

DWORD WINAPI GetModuleBaseName(

  __in      HANDLE hProcess,

  __in_opt  HMODULE hModule,

  __out     LPTSTR lpBaseName,

  __in      DWORD nSize

);

Parameters

hProcess [in]

handle của process chứa module.

handle phải có quyền truy xuất PROCESS_QUERY_INFORMATION và PROCESS_VM_READ. Để có nhiều thông tin hơn, hảy xem chủ đề Process Security and Access Rights.

hModule [in, optional]

handle của module. Nếu tham số này là NULL, hàm này trả về name of file được sử dụng để cài đặt process đang calling.

lpBaseName [out]

Con trỏ đến buffer nhận base name of module. Nếu base name lớn hơn maximum number of characters được chỉ định bởi tham số nSize parameter, base name sẽ bị cắt bỏ.

nSize [in]

size of lpBaseName buffer, đơn vị characters.

Return Value

Nếu hàm thành công, giá trị trả về chỉ định chiều dài length of string được copy đến buffer, in characters.

Nếu hàm sai, return value là zero. Để biết thông tin lỗi xảy ra, call GetLastError.

Thực hành:

-Tạo project Dialog

-Trong rc tạo Listview, với Type là Report

push esi

push edi

lea eax,buffer

mov pbuf,eax

invoke EnumProcesses,pbuf,4096,ADDR breq 

shr breq, 2     ; get process count

mov esi, pbuf

mov edi, breq

.while edi>0

push esi

push edi

mov eax,dword ptr [esi]

mov pid,eax

invoke OpenProcess,PROCESS_QUERY_INFORMATION \

or PROCESS_VM_READ,FALSE,eax

mov hProcess,eax

.if hProcess != 0

invoke EnumProcessModules,hProcess,ADDR hMod,\

4,ADDR cbNeeded

.if eax!=0

invoke GetModuleBaseName,hProcess,hMod,\

addr buf2,260

.if eax==0

invoke lstrcpy,addr buf2,chr$("System0")

.endif

.elseif

invoke lstrcpy,addr buf2,chr$("System1")

.endif

.elseif

invoke lstrcpy,addr buf2,chr$("System2")

.endif

;add to Item of listview

invoke wsprintf,addr buf3, addr template,pid

lea eax,buf2

mov    lvi.imask, LVIF_TEXT

mov    lvi.iItem,0

mov    lvi.iSubItem,0

mov    lvi.pszText, eax

invoke SendMessage, hList, LVM_INSERTITEM,\

0, ADDR lvi

;add to SubItem of Item

lea eax,buf3

mov    lvi.imask, LVIF_TEXT inc    lvi.iSubItem

mov    lvi.pszText,eax

invoke SendMessage, hList, LVM_SETITEM, \

0, ADDR lvi

invoke RtlZeroMemory,addr buf2,260

invoke RtlZeroMemory,addr buf3,260

invoke CloseHandle,hProcess

pop edi

pop esi

add esi, 4

sub edi, 1

.endw

pop edi

pop esi

invoke MessageBox,0,chr$("Method 1"),chr$("Method"),MB_OK

2.Phương pháp 2: Using the ZwQuerySystemInformation function

Dùng hàm ZwQuerySystemInformation để liệt kê processes:

Hàm này nhận các thông tin đặc biệt của hệ thống.

Hàm này là hàm được export bởi thư viện ntdll.dll, vì vậy muốn sử dụng nó chúng ta phải GetProcAddress địa chỉ hàm này.

mov ZwQuerySystemInformation, FUNC(GetProcAddress,FUNC(GetModuleHandle,chr$("ntdll.dll")),CTXT("ZwQuerySystemInformation

Mô tả hàm ZwQuerySystemInformation:

NTSTATUS WINAPI ZwQuerySystemInformation(

  __in       SYSTEM_INFORMATION_CLASS SystemInformationClass,

  __inout    PVOID SystemInformation,

  __in       ULONG SystemInformationLength,

  __out_opt  PULONG ReturnLength

);

SystemInformationClass [in] : tham số này chỉ định loại thông tin cần nhận được khi gọi hàm này. Vì ở đây chúng ta cần biết thông tin về các processes và threads của chúng nên chúng ta dùng tham số có giá trị sau để chỉ định loại thông tin cần lấy:

SystemProcessesAndThreadsInformation equ 5

SystemInformation [in, out] : là con trỏ trỏ đến buffer nhận thông tin theo yêu cầu. Kích thước và cấu trúc của thông tin này thay đổi dựa trên giá trị của tham sốSystemInformationClassVì thông tin cần lấy là process nên cấu trúc nhận được sẽ như sau:

SYSTEM_PROCESS_INFORMATION struct

NextEntryDelta dd ?

ThreadCount dd ?

Reserved1 dd 6 dup (?)

CreateTime dq ?

UserTime dq ?

KernelTime dq ?

ProcessName UNICODE_STRING <?>

BasePriority dd ?

ProcessId dd ?

InheritedFromProcessId dd ?

HandleCount dd ?

Reserved2 dd 2 dup (?)

VmCounters VM_COUNTERS <?>

IoCounters IO_COUNTERS <?>

Threads SYSTEM_THREAD_INFORMATION <?>

SYSTEM_PROCESS_INFORMATION ends

Chú thích các thành phần trong cấu trúc này:

# NextEntryOffset

    Offset from begining of output buffer to next process entry. On last entry contains zero.

# NumberOfThreads

    Number of process’es threads. Also number of members in Threads array descripted below.

# Reserved[3]

    Reserved.

# CreateTime

    Process creation time, in 100-ns units.

# UserTime

    Effective time in User Mode.

# KernelTime

    Effective time in Kernel Mode.

# ImageName

    Process name, based on executable file name.

# BasePriority

    Process base priority.

# ProcessId

    Unique identifier of process.

# InheritedFromProcessId

    Creator’s identifier.

# HandleCount

    Nr of open HANDLEs.

# Reserved2[2]

    Reserved.

# PrivatePageCount

    Number of memory pages assigned to process.

# VirtualMemoryCounters

    Memory performance counters.

# IoCounters

    IO performance counters.

# Threads[0]

    Array of SYSTEM_THREAD structures descripting process’s threads.

Chú ý:

-Chúng ta phải khai báo con trỏ này là một biến trên Heap thì hàm mới thực hiện được. Nếu dùng biến toàn cục thì hàm sẽ trả về sai.

LOCAL SystemInformation:DWORD

-Con trỏ này cũng có thể dùng con trỏ được cấp phát bằng lệnh VirtualAlloc

SystemInformationLength [in] : Kích thước bytes của buffer được trỏ đến bởi tham sốSystemInformation.

ReturnLength [out, optional] : Là con trỏ đến vị trí mà ở đó hàm viết ra kích thước thật sự của thông tin yệu cầu. Nếu kích thước lớn hơn hoặc bằng tham sốSystemInformationLength thì hàm sẽ copy thông tin vào trong SystemInformation buffer. Nếu khác, thì hàm sẽ trả về NTSTATUS error code và trả về kích thước của buffer cần thiết trong ReturnLength mà thông tin yêu cầu cần.

Chú ý: Con trỏ tham số này cũng phải khai báo là một biến trong Heap:

LOCAL cb_x : DWORD

-Vậy đầu tiên chúng ta không thể biết thông tin chúng ta cần bao nhiêu bytes đế chứa, do đó chúng ta sẽ gọi hàm ZwQuerySystemInformation với kích thước buffer chỉ 4byte để hàm bị sai và trả về kích thước thật thụ thông tin muốn lấy trong tham số ReturnLength. Sau đó chúng ta sẽ cấp phát mới (dùng hàm VirtualAlloc ) mộtvùng nhớ có kích thước bằng với ReturnLengthCuối cùng chúng ta sẽ gọi lại hàmZwQuerySystemInformation với các tham số phù hợp.

lea eax,cb_x

push eax

push 4

lea eax,SystemInformation

push eax

push SystemProcessesAndThreadsInformation

call [ZwQuerySystemInformation]

mov SystemInformation, FUNC(VirtualAlloc,NULL,\

[cb_x],MEM_COMMIT,PAGE_READWRITE)

push NULL

push [cb_x]

push eax

push SystemProcessesAndThreadsInformation

call [ZwQuerySystemInformation]

Sau đó chúng ta sẽ dùng một vòng lặp để lấy thông tin trong buffer củaSystemInformationClass ra. Thông tin này có cấu trúcSYSTEM_PROCESS_INFORMATION. Chúng ta sẽ trích xuất nó và cho hiển thị:

invoke RtlZeroMemory,addr lvi,sizeof lvi

mov lvi.imask,LVIF_TEXT

lea eax,lpBuffer

mov lvi.pszText,eax

invoke RtlZeroMemory,addr lpBuffer,260

mov esi,[SystemInformation]

assume esi:ptr SYSTEM_PROCESS_INFORMATION

.while ([esi].NextEntryDelta)

add esi,[esi].NextEntryDelta

mov lvi.iSubItem,0

mov eax,[esi].ProcessName.Buffer

invoke wsprintf,addr lpBuffer,CTXT("%ls"),eax

invoke SendMessage,hList,LVM_INSERTITEM,NULL,addr lvi inc lvi.iSubItem

invoke wsprintf,addr lpBuffer,CTXT("%ld"),\

[esi].ProcessId

invoke SendMessage,hList,LVM_SETITEM,NULL,addr lvi

inc n

.endw

Cuối cùng chúng ta sẽ free vùng nhớ đã cấp phát trước đó

assume esi:nothing

invoke VirtualFree,[SystemInformation],10000h,MEM_DECOMMIT

invoke MessageBox,0,chr$("Method 2"),chr$("Method"),MB_OK

Sau đây là các macros cần tham khảo thêm khi coding trong project này

1/-FUNC: tương đương macro rv

Macro này sẽ gọi một hàm API và giá trị trả về trong eax, được dùng như sau:

FUNC

mov rval, FUNC(AnyProcedureCall,args ……)

Description

Call bất kỳ procedure nào với một giá trị trả về return value.

Parameters

Any procedure call complete with arguments that returns a value.

Return Value

The return value from a procedure call which is normally the EAX register.

Comments

This macro utilises (sử dụng) the "invoke" syntax and the EXITM <eax> macro operator to return a value which can be either assigned (chỉ định) to a variable (biến) with MOV or directly used in the same manner as the EAX register.

mov retval, FUNC(APIcall,arguments …)
test FUNC(YourProc,arguments …), eax
cmp FUNC(AnyProc,arguments …), 1234
add edx, FUNC(APIcall,arguments …)

Ví dụ 1:

mov ZwQuerySystemInformation, FUNC(GetProcAddress,FUNC(GetModuleHandle,CTXT("ntdll.dll")),CTXT("ZwQuerySystemInformation

Tương đương:

invoke GetModuleHandle,CTXT("ntdll.dll")

invoke GetProcAddress,eax, CTXT("ZwQuerySystemInformation")

mov ZwQuerySystemInformation,eax

Ví dụ 2:

mov hProcess, rv(OpenProcess,PROCESS_QUERY_INFORMATION or PROCESS_VM_READ,FALSE,pid)

2/-CTXT : tương đương macro chr$ hoặc SADD

chr$

mov pstr, chr$("quoted text",13,10)

Description

Write mixed text and byte data to the .DATA section and return the starting address of that data.

Parameters

A variable number of items of quoted text and byte data.

Return Value

The return value is the starting address of the data written to the .DATA section.

Comments

This macro writes directly to the .DATA section and it can use either quoted text or byte data in the normal format for the .DATA section. It can be used in places where the internal MASM reserve characters interfere with quoted text or byte data being used.
fn MessageBox,hWnd,chr$("one",13,10,"two",13,10"three",13,10),lpTitle,MB_OK

Ví dụ:

invoke GetModuleHandle,CTXT("ntdll.dll")

Tương đương:

.data

szntdll db "ntdll.dll",0

.code

invoke GetModuleHandle,addr szntdll

3/- ptr$ : tính địa chỉ một biến local

ptr$

mov lpstring, ptr$(buffer)

Description

Tính address of một biến local variable đến một biến con trỏ (pointer variable).

Parameters

1. buffer Một buffer được chỉ định trên stack.

Return Value

address of buffer.

Comments

Macro này loads address of một biến stack vào trong một register and sets the first two (2) bytes to zero. Assigning the return value to a variable makes the value into a pointer which is generally easier to use that repeatedly using LEA to obtain the address.

Ví dụ:

LOCAL pbuf  :DWORD

LOCAL buffer[4096]:BYTE

mov pbuf, ptr$(buffer)  

Tương đương:

lea eax, buffer

mov pbuf,eax

4/- cat$: nối nhiều string

cat$

mov lpbuffer, cat$(lpBuffer,[variable number of strings])

Mô tả

Nối nhiều string vào trong một buffer.

Tham số

1. lpBuffer The address of the source buffer to append other strings to.
2. A variable number of string addresses được gắn vào source buffer.

Return Value

Giá trị trả về là addr của source buffer mà nó có thể được quy trở lại chính nó hay đến con trỏ khác (string handle).

Comments

This is a very flexible and useful macro that can handle multiple strings in different formats. If the source buffer does not already have string data in it, the first byte MUST be set to a zero byte so it is seen as a zero length string. The source string buffer must be large enough to receive all of the string data appended to it.

Example

mov lpbuffer, cat$(lpbuffer,"The result is ",ustr$(count)," bytes in length")

Ví dụ:

mov ptxt, cat$(ptxt,"pid ",str$(pid)," ",pbuf)

5/- ustr$: chuyển một số 32bit int thành một str kết thúc zero

ustr$

mov lpResult, ustr$(uInteger)

Description

Convert an unsigned 32 bit integer to a zero terminated string.

Parameters

1. uInteger An unsigned 32 bit integer.

Return Value

The return value is the address of the zero terminated string that holds the result as an OFFSET.

Comments

Each macro call in the assembler source code provides a 20 byte long buffer in the data section to hold the results of the conversion.

3.Phương pháp 3: Using ToolHelp32 API

Một process đang running trong system sẽ được Windows lưu trữ các thông tin liên quan đến process đó như heaps, modules, threads. Tại một thời điềm nào đó, Windows sẽ lưu trữ toàn bộ các thông tin của các processes đang chạy hiện hành. Toàn bộ dữ liệu lưu trữ của các processes hiện hành tại một thời điểm gọi là một snapshot. Chúng ta sẽ dùng hàm API CreateToolhelp32Snapshot để bắt lấy snapshot của các processes hiện hành. Sau đó dùng hàm Process32First đề lấy thông tin process đầu tiên trong snapshot. Để lấy thông tin process kế tiếp trong snapshot ta dùng hàm Process32Next. Như vậy là ta có thể lấy tất cả thông tin của toàn bộ processes đang running. Thông tin lấy được chứa trong mọt cấu trúc có tên là PROCESSENTRY32. Phương pháp này là rõ ràng nhất, và có thể thấy nó là phương pháp chính quy mà Windows khuyến cáo sử dụng.

Sau đây là một số thông tin về các hàm API liên quan sử dụng.

CreateToolhelp32Snapshot Function

Bắt lấy một snapshot of các processes được chỉ định, giống như heaps, modules, và threads được sử dụng bởi các processes này.

Syntax

HANDLE WINAPI CreateToolhelp32Snapshot(

  __in  DWORD dwFlags,

  __in  DWORD th32ProcessID

);

Parameters

dwFlags [in]

Các phần of system được included trong snapshot. Tham số này có thể là một hay nhiều giá trị sau đây.

Value

Meaning

TH32CS_INHERIT
0x80000000

Cho biết rằng snapshot handle là thừa kế.

TH32CS_SNAPALL

Bao gồm tất cả các processes và các threads trong system, cộng heaps và modules of process được chỉ định trong th32ProcessID. Qui đổi chỉ rõ các giá trị TH32CS_SNAPHEAPLIST, TH32CS_SNAPMODULE, TH32CS_SNAPPROCESS, và TH32CS_SNAPTHREAD được kết hợp với nhau sử dụng OR operation (‘|’).

TH32CS_SNAPHEAPLIST
0x00000001

Bao gồm tất cả các heaps of process được chỉ định trong th32ProcessID trong snapshot. Để liệt kê các heaps, xem Heap32ListFirst.

TH32CS_SNAPMODULE
0x00000008

Bao gồm tất cả các modules of  process được chỉ 9ịnh trong th32ProcessID trong snapshot. Để liệt kê các modules, xem Module32First.

64-bit Windows: Sử dụng flag này trong một 32-bit process bao gồm 32-bit modules of  process được chỉ định trong th32ProcessID, khi sử dụng nó trong một 64-bit process bao gồm 64-bit modules. Để bao gồm 32-bit modules of process được chỉ định trongth32ProcessID từ một 64-bit process, sử dụng TH32CS_SNAPMODULE32 flag.

TH32CS_SNAPMODULE32
0x00000010

Bao gồm tất cả các 32-bit modules of process được chỉ định trong th32ProcessID trong snapshot khi  called từ một 64-bit process. Flag này có thể được kết hợp  với TH32CS_SNAPMODULE hay TH32CS_SNAPALL.

TH32CS_SNAPPROCESS
0x00000002

Bao gồm tất cả các processes trong system trong snapshot. Để liệt kê các processes, xem Process32First.

TH32CS_SNAPTHREAD
0x00000004

Bao hàm tất cả các threads trong system vào trong snapshot. Để liệt kê các threads, xemThread32First.

Để nhận ra các threads thuộc về một process cụ thể, so sánh các process identifier của nó với th32OwnerProcessID member of THREADENTRY32 structure khi liệt kê các threads.

th32ProcessID [in]

process identifier of  process được bao hàm trong snapshot. Tham số này có thể là zero để cho biết current process. Tham số này được sử dụng khi TH32CS_SNAPHEAPLIST, TH32CS_SNAPMODULE, TH32CS_SNAPMODULE32, hay TH32CS_SNAPALL value được chỉ định. Nói cách khác, nó được bỏ qua và tất cả các processes được included vào trong snapshot.

Nếu process được chỉ định là một 64-bit process và caller là một 32-bit process, thì hàm này fails và last error code là ERROR_PARTIAL_COPY (299).

Return Value

Nếu function thành công, nó returns một open handle của snapshot được chỉ định.

Nếu function fails, nó returns INVALID_HANDLE_VALUE. Để có được thông tin phần báo error, call GetLastError.

Remarks

Snapshot được bắt lấy bởi hàm này được kiểm tra bởi  các tool  help functions khác để cung cấp các results của chúng. Truy xuất đến snapshot chỉ được read . Snapshot handle hành động như một object handle và lệ thuộc vào các quy tắc tương tự liên quan đến các processes nào và  threads nào trong đó mà nó hợp lệ.

Để liệt kê các trạng thái heap hay module cho tất cả processes, chỉ định  TH32CS_SNAPALL và set th32ProcessID là zero. Rồi, đối với process thêm vào trong snapshot, call CreateToolhelp32Snapshot một lần nữa, chỉ định process identifier của nó và  TH32CS_SNAPHEAPLIST or TH32_SNAPMODULE value.

Hủy snapshot, sử dụng hàm CloseHandle function.

Chú ý rằng bạn có thể sử dụng QueryFullProcessImageName function để nhận full name of một executable image cho cả hai 32- và 64-bit processes từ một 32-bit process.

Example Code

Cho ví dụ, xem Taking a Snapshot and Viewing Processes.

Requirements

Client

Requires Windows Vista, Windows XP, or Windows 2000 Professional.

Server

Requires Windows Server 2008, Windows Server 2003, or Windows 2000 Server.

Header

Declared in Tlhelp32.h.

Library

Use Kernel32.lib.

DLL

Requires Kernel32.dll.

See Also

CloseHandle
Heap32ListFirst
Module32First
Process32First
Snapshots of the System
Thread32First
Tool Help Functions

Send comments about this topic to Microsoft

Process32First Function

Nhận thông tin về first process bắt gặp trong một system snapshot.

Syntax

BOOL WINAPI Process32First(

__in HANDLE hSnapshot,

__inout LPPROCESSENTRY32 lppe

);

Parameters

hSnapshot [in]

Một handle của snapshot trả về từ hàm gọi trước đó là hàm CreateToolhelp32Snapshotfunction.

lppe [in, out]

Một pointer trỏ đến một PROCESSENTRY32 structure. Nó chứa thông tin process như là name of executable file, process identifier, và process identifier of parent process.

Return Value

Returns TRUE nếu thành phần đầu tiên của process list được copy đến buffer hay khác là FALSE. ERROR_NO_MORE_FILES error value được trả về bởi GetLastError function nếu ko có processes tồn tại hay snapshot ko chứa thông tin process .

Remarks

Ứng dụng gọi hàm phải set dwSize member of PROCESSENTRY32 là size, đơn vị bytes, of structure.

Để nhận thông tin về các processes khác đã được ghi trong snapshot tương tự, ta sử dụng hàm Process32Next function.

Example Code

Chi ví dụ, xem Taking a Snapshot and Viewing Processes.

Requirements

Client

Requires Windows Vista, Windows XP, or Windows 2000 Professional.

Server

Requires Windows Server 2008, Windows Server 2003, or Windows 2000 Server.

Header

Declared in Tlhelp32.h.

Library

Use Kernel32.lib.

DLL

Requires Kernel32.dll.

Unicode/ANSI

Implemented as Process32FirstW (Unicode) and Process32First (ANSI).

See Also

CreateToolhelp32Snapshot
Process Walking
Process32Next
PROCESSENTRY32
Tool Help Functions

Send comments about this topic to Microsoft

Process32Next Function

Nhận thông tin về next process được ghi trong một system snapshot.

Syntax

BOOL WINAPI Process32Next(

__in HANDLE hSnapshot,

__out LPPROCESSENTRY32 lppe

);

Parameters

hSnapshot [in]

Một handle của snapshot returned từ một hàm trước đó là CreateToolhelp32Snapshotfunction.

lppe [out]

Một pointer trỏ đến một PROCESSENTRY32 structure.

Return Value

Returns TRUE nếu phần tử kế tiếp of process list được copy đến buffer hay khác thì FALSE . Gía trị ERROR_NO_MORE_FILES error value được returned bởi GetLastErrorfunction nếu ko có processes tồn tại hay snapshot ko chứa thông tin process information.

Remarks

Nhận thông tin về first process thu được trong một snapshot, sử dụng Process32Firstfunction.

Example Code

Cho một ví dụ, xem Taking a Snapshot and Viewing Processes.

Requirements

Client

Requires Windows Vista, Windows XP, or Windows 2000 Professional.

Server

Requires Windows Server 2008, Windows Server 2003, or Windows 2000 Server.

Header

Declared in Tlhelp32.h.

Library

Use Kernel32.lib.

DLL

Requires Kernel32.dll.

Unicode/ANSI

Implemented as Process32NextW (Unicode) and Process32Next (ANSI).

See Also

CreateToolhelp32Snapshot
Process Walking
Process32First
PROCESSENTRY32
Tool Help Functions

Send comments about this topic to Microsoft

PROCESSENTRY32 Structure

Mô tả một entry từ list of các processes cư trú trong system address space khi một snapshot đước bắt lấy.

Syntax

typedef struct tagPROCESSENTRY32 {
DWORD dwSize;
DWORD cntUsage;
DWORD th32ProcessID;
ULONG_PTR th32DefaultHeapID;
DWORD th32ModuleID;
DWORD cntThreads;
DWORD th32ParentProcessID;
LONG pcPriClassBase;
DWORD dwFlags;
TCHAR szExeFile[MAX_PATH];

} PROCESSENTRY32, 
*PPROCESSENTRY32;

Members

dwSize

Size of structure, đơn vị bytes. Trước khi gọi hàm Process32First function, set thành phần này là sizeof(PROCESSENTRY32). Nếu bạn ko khởi trị đầu dwSize, thìProcess32First fails.

cntUsage

Thành phần này ko được sử dụng và luôn set là zero.

th32ProcessID

Process identifier.

th32DefaultHeapID

Thành phần này ko được sử dụng và luôn set là zero.

th32ModuleID

Thành phần này ko được sử dụng và luôn set là zero.

cntThreads

Số execution threads được bắt đầu bởi process.

th32ParentProcessID

Identifier of process mà nó được cài đặt process này (its parent process).

pcPriClassBase

The base priority (quyền ưu tiên cơ bản) of các threads nào đó được cài đặt bởi process này.

dwFlags

Thành phần này ko được sử dụng, và luôn set thành zero.

szExeFile

Name of executable file cho process. Để nhận full path đến executable file, callModule32First function và check szExePath member of MODULEENTRY32 structure mà nó được trả về. Tuy nhiên, nếu calling process là một 32-bit process, bạn phải callQueryFullProcessImageName function để nhận full path of executable file cho một 64-bit process.

Example Code

Cho ví dụ, xem Taking a Snapshot and Viewing Processes.

Requirements

Client

Requires Windows Vista, Windows XP, or Windows 2000 Professional.

Server

Requires Windows Server 2008, Windows Server 2003, or Windows 2000 Server.

Header

Declared in Tlhelp32.h.

Unicode/ANSI

Declared as PROCESSENTRY32W (Unicode) and PROCESSENTRY32 (ANSI).

See Also

Process32First
Process32Next

Send comments about this topic to Microsoft

Thực hành:

invoke SendMessage,hList,LVM_DELETEALLITEMS,NULL,NULL

invoke CreateToolhelp32Snapshot, TH32CS_SNAPALL, 0

mov hSnapShot, eax                ; store the snapshot handle

mov myProcess.dwSize, sizeof myProcess

invoke RtlZeroMemory,addr lvi,sizeof lvi

mov lvi.imask,LVIF_TEXT

invoke Process32First,hSnapShot, ADDR myProcess

.while eax

mov lvi.iSubItem,0

mov    lvi.iSubItem,0

lea eax,myProcess.szExeFile

mov lvi.pszText,eax

invoke SendMessage,hList,LVM_INSERTITEM,NULL,addr lvi

inc lvi.iSubItem

lea eax,lpBuffer

mov lvi.pszText,eax

invoke wsprintf,addr lpBuffer,CTXT("%ld"),\

myProcess.th32ProcessID

invoke SendMessage,hList,LVM_SETITEM,NULL,addr lvi

invoke Process32Next,hSnapShot, ADDR myProcess

.endw

invoke CloseHandle,hSnapShot                    ; close snapshot handle

invoke MessageBox,0,chr$("Method 3"),chr$("Method"),MB_OK

HÉT PHẦN 1

BENINA

[ASM] MicroSoft Windows Sockets Guide

22.01.2010

rootb1ez ASM Để lại bình luận

 

by Bumblebee/29a

Tranz by Benina

Index

~~~~~

1.Overview (tổng quan)

2.What is windows sockets? (windows sockets là gì?)

3.Micro$oft extensions (các hàm mở rộng của M$)

4.Step by step: make a connection (hường dẫn từng bước tạo một kết nối)

5.Read and Write (đọc và viết)

Apendix: wsocks.inc (thư mục đính kèm)

1.Overview

~~~~~~~~~~

Đây ko phải là một bài hướng dẫn đặc biệt gì cả . Chỉ là những ghi chép lại việc tôi tìm hiểu qua quá trịnh thực hiện win32 sockets of chuẩn BSD socket standards. Bài này có thể giúp bạn làm một remote connection (liên kết từ xa) với virus của bạn vì dụ như để send một e-mail chẳng hạn.

Nói đơn giản là mô tả một đọan code từ i-worm.Anaphylaxis.

2.What is windows sockets?

~~~~~~~~~~~~~~~~~~~~~~~~~~

Đặc điểm của Windows Sockets là định nghĩa một giao diện lập trình mạng network programming cho Microsoft Windows, mà nó được đặt nền tảng trên mô hình "socket" đã được phổ biến tại Berkeley Software Distribution (BSD). Nó bao gồm cả hai lọai “Berkeley socket style routines” (các thủ tục theo kiểu mẫu socket của Berkeley) và cài đặt các phần mở rộng đặc trưng của Windows (Windows-specific extensions) rất quen thuộc đã được thíết kế cho phép lập trình viên có được tiện lợi điều khiển thông điệp một cách tự nhiên trong Windows.

Nhưng, socket là cái quái gì vậy? Ý tưởng của BSD là một file để điều khiển từ xa máy tính (machine). Sokects đươc sử dụng trong BDS giống như các files đơn giản. Bạn có thể sử dụng thông thường như ‘write’ và ‘read’ để viết và đọc từ socket. Nhưng M$ thay đổi sự trừu tượng của level (lớp) này và cho bạn các hàm đặc biệt để làm cho bạn nhớ rằng bạn đang sử dụng các hàm API chết tiệt của họ.

3.Micro$oft extensions

~~~~~~~~~~~~~~~~~~~~~~

Da6y là các hàm functions được cung cấp bởi M$ mà chúng ko có trong

Berkeley:

         WSAAsyncGetHostByAddr()
WSAAsyncGetHostByName()
WSAAsyncGetProtoByName()
WSAAsyncGetProtoByNumber()
WSAAsyncGetServByName()
WSAAsyncGetServByPort()
WSAAsyncSelect()
WSACancelAsyncRequest()
WSACancelBlockingCall()
*WSACleanup()
WSAGetLastError()
WSAIsBlocking()
WSASetBlockingHook()
WSASetLastError()
*WSAStartup()
WSAUnhookBlockingHook()

 

M$ cung cấp các hàm quỷ quái này bởi vì cách hoạt động của Windows là thực hiện đa tác vụ (multitasking). Chuyện gì xảy ra nếu một ứng dụng chờ một sự kết nối(connection)? Kết nối (connection) yêu cầu ứng dụng phải “xem xét” nó một cách bắt buộc nếu kết nối là ok. Vì vậy ứng dụng ko thể lấy các thông điệp windows đang gởi đến chúng. Các hàm chỉ ra phía trên cung cấp các công việc không bị ngăn chặn (nonblocking work) như cơ chế nói trên.

Chúng ta chỉ quan tâm đến các hàm đánh dấu sao ‘*’. Những hàm khác ko cần thiết vì tôi sẽ sử dụng socks theo cách thức BSD (BSD=UNIX=LUNUX rulez!).

4.Step by step: make a connection

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Bước đầu tiên chúng ta làm là kiểm tra sự cài đặt wsocks trong máy tính. Chúng ta sẽ sử dụng wsocks 1.1, vì vậy chúng ta tạo ra một check (kiểm tra) với hàm WSAStartup.

        push    offset wsadata                  ; WSADATA struct
        push    VERSION1_1                      ; we want version 1.1
        call    WSAStartup                          ; check wsocks installation
        cmp     eax,0                          ; on error:
        jne     qApp                          ; exit app

        mov     ax,VERSION1_1                    ; WSAStartup returns the version
        cmp     ax,word ptr [wsadata.mVersion]      ; test version
        jne     exitAppQsocks                       ; quit sockets and exit App

 

Hàm WSAStartup cần 2 đối số: một con trỏ pointer đến cấu trúc WSADATA struct và số version yêu cầu cần thiết để ứng dụng hoạt động. Hàm API này trả về (retuns) trong thành phần .mVersion của cấu trúc WSADATA số version thấp nhất có thể sử dụng mà bạn cần biết hay sẽ báo lỗi xảy ra nếu hàm thất bại. Hơn nữa hàm WSAStartup sẽ báo cho Windows biết bạn đang sử dụng wsocks. Vì vậy khi version mà bạn yêu cầu ko phù hợp thì bạn cần thiết phải gọi hàm WSACleanup để xóa bỏ trạng thái của Windows bị gây ra trước đó do gọi hàm WSAStarup:

 

        call    WSACleanup                ; end wsocks use

Giả sử bây giờ chúng ta có version mà chúng ta cần. Tại điềm này chúng ta cần open một socket.

        push    PCL_NONE            ; protocol
push SOCK_STREAM ; type of socket
push AF_INET ; family
call socket ; open a socket
cmp eax,SOCKET_ERR ; on error:
je doCleanUp ; WSACleanup

 

Hàm socket cần 3 tham số: protocol, type và family.

Tham số protocol có thể được set nhưng điều này có thể làm cho cơ chế bảo mật của windows hủy bỏ kết nối (connection) của chúng ta. Ví dụ: chúng ta muốn tạo ra một telnet connection. Nếu chúng ta set protocol thành telnet protocol và Windows thì ko cho phép sử dụng protocol này thì socket của chúng ta sẽ bị sai (fails). Vì vậy cách tốt nhất là set protocol là none.

Tham số type of socket có thể là: STREAM hay DATAGRAM. Đầu tiên là định hướng đến kết nối (connection) và thứ hai là gởi các packets mà chúng có thể đến nơi nhận (receiver) theo thứ tự khác nhau khi chúng đã được gởi đi. Hơn nữa người gởi (sender) sẽ ko thể biết được packet có đến nơi nhận hay ko khi ko cần sự trợ giúp phản hồi của người nhận (receiver).

Tham số sau cùng family có thể là: AI_UNIX, AF_INET… nhưng chỉ các AF_INET addresses được hổ trợ trong version mà tôi kiểm tra.

Chúng ta sử dụng PCL_NONE, SOCK_STREAM và AF_INET. Hàm socket trả về (returns) giá trị SOCKET_ERR (-1) nếu hàm gọi bị sai và trả về socket handle nếu hàm gọi thành công.

Bước cuối cùng là tạo ra một kết nối (connection). Theo lý thuyết chúng ta connect (kết nối) socket, điều đó được dùng để quản lý kết nối (connection), là điều khiển từ xa máy tính. Trước tiên chúng ta cần lắp giá trị vào cấu trúc SOCKADDR struct (đây là cấu trúc đã bị chỉnh sửa (modified struct): tôi đã kết hợp các cấu trúc khác lại vì vậy chúng ta sẽ chỉ sử dụng cho loại AF_INET).

 

    SOCKADDR        struct
    sin_family    dw    0        ; ever AF_INET
    sin_port    dw    0        ; the port
    sin_addr    dd    0           ; addr of server node
    sin_zero    db    8 dup(0)    ; not used 
    SOCKADDR        ends

 

sin_family thì dễ dàng được lắp trị vào, nhưng sin_port và sin_addr thì phức tạp hơn. sin_port là port (cổng) mà ở đó chúng ta muốn connect(kết nối). Nhưng số này phải là số byte thứ tự của network (network byte order)(nguyên cứu thêm về mô hình mạng). Sockets cung cấp hàm htons để làm điều này:

        push    PORT                    ; number of port
call htons ; get port in network byte mov word ptr [sockaddr.sin_port],ax ; order

 

Hàm htons lấy port và trả về returns một WORD chứa network byte order.

sin_addr thì phức tạp nhất. Chúng ta cần addr of host đề connect (kết nối). Đây là một số number dùng để nhận dạng ra nút (node) trên mạng (mạng là sự truyền thông giữa các node). Nhưng thông thường chúng ta lại có định dạng như sau ‘domain.ext’ (ví dụ:IBM.comnetscape.com,…). Vì vậy chúng ta phải lấy IP của nó (xxx.xxx.xxx….) mà chúng có định dạng dotted format và rồi lấy địa chỉ của nó.

        push    offset server            ; addr of the string ('oeee.net')
call gethostbyname ; get the hostent struct cmp eax,0 ; on error: je exitQsocksC ; close sock, cleanup and exit app ; eax contains the pointer to HOSTENT mov eax,dword ptr [eax+HOSTENT_IP] ; get pointer to IP into HOSTENT mov eax,dword ptr [eax] ; get pointer to IP mov dword ptr [sockaddr.sin_addr],eax ; that's all! push sizeOfSockaddr ; the size of sockaddr struct push offset sockaddr ; the addr of sockaddr push dword ptr [fd] ; the handle of the socket call connect ; connect now! cmp ax,SOCKET_ERR ; on error: je exitQsocksC ; close sock, cleanup and exit app

 

Ví dụ này tương đối đầy đủ rõ ràng: chúng ta lấy trong cấu trúc HOSTENT mà nó có thành phần HOSTENT_IP là addr IP of nút (node) trên mạng. Rồi lắp giá trị vào sin_addr và cấu trúc sockaddr struct bây giờ đã sẳn sàng cho việc tạo ra kết nối (connection). Hàm connect cần các tham số sau: size of SOCKADDR struct (là hằng số vì do tôi đã chỉnh sửa cấu trúc mô tả trước đó ;), pointer đến SOCKADDR struct và handle of socket.

That’s all. Chúng ta chỉ cần close socket khi công việc hoàn thành ta sử dụng hàm closesocket:

        push    dword ptr [fd]            ; handle of the socket
call closesocket

 

5.Read and Write

~~~~~~~~~~~~~~~~

Miscro$oft cung cấp các hàm APIs khác để đọc và viết nhưng chúng ta sẽ sử dụng hàm: send và recv.

 

        push    0                    ; normal (can be OOBD too)
push ecx ; size of message to send
push esi ; addr to message
push eax ; socket handle
call send

push 0 ; normal
push 4 ; size to read
push offset response ; addr to store message
push eax ; soket handle
call recv

 

Hàm send làm việc và cho ra các errors (lỗi) tương tự như hàm _lwrite và cũng xảy ra tương tự với hàm recv và hàm _lread tương ứng.

Hàm send và recv đang bị block (ngăn chặn). Điều này có nghĩa là nếu bạn đang sending hay receiving và không có dữ liệu nào được send/receive, thì socket sẽ ngăn chặn ứng dụng cho đến khi có data sẳn dàng để dùng, kết nối (connection) bị sai fails hay process kết thúc. Vì vậy đây là điều cuối cùng những gì chúng ta cần sử dụng. Chúng ta cài đặt một thread và thead này tạo ra một kết nối (connection) và sends/receives (gởi/nhận) các thông điệp messages (tạo ra sự truyền thông). Main process (tiến trình chính) mà chúng cài đặt ra thread chỉ để chờ time (thời gian) cho phép thread làm việc. Nhưng nếu thread bị blocked time (thời gian) trong main process mất hiệu lực . Thì main process kết thúc thread và tiếp tục công việc của nó.

That’s all folks!

 APENDIX: wsocks.inc

;
; WSocks.inc: include file for windows sockets .
; Designed for TASM5 and Win32.
;
; (C) 1999 Bumblebee.
;
; This file contains basic structures and stuff to work
; with windows sockets.
;

; Descriptions of the API:
; arguments in order of PUSH ;)

; only for debug
extrn WSAGetLastError:PROC

; starts the use of winsock dll
; addr WSADATA, version requested
; returns: 0 ok
extrn WSAStartup:PROC

; terminates the use of winsock dll
; returns: SOCK_ERR on error
extrn WSACleanup:PROC

; opens a new socket
; protocol (PCL_NONE), type (SOCK_??), addr format (AF_??)
; returns: socket id or SOCKET_ERR (socket is dw)
extrn socket:PROC

; closes a socket
; socket descriptor
;
extrn closesocket:PROC

; sends data (this socks are a shit... Unix uses simple write)
; flags (1 OOB data or 0 normal ) , length, addr of buffer, socket
; returns: caracters sent or SOCKET_ERR on error
extrn send:PROC

; reveives data (this socks are a shit... Unix uses simple read)
; flags (use 0), length, addr of buffer, socket
; returns: caracters sent or SOCKET_ERR on error
extrn recv:PROC

; connects to a server
; sizeof struct SOCKADDR, struct SOCKADDR, socket
; returns: SOCKET_ERR on error
extrn connect:PROC

; gets the name of the current host
; length of the buffer for name, addr of buffer for name
; return: SOCKET_ERR on error
extrn gethostname:PROC

; gets strcut hostent
; addr of name
; returns: ponter to the struct or 0 on error
extrn gethostbyname:PROC

; converts a zstring like "xxx.xxx.xx...." to netw byte order
; zstring ptr to change to dotted addr format
; returns: in_addr (dd)
extrn inet_addr:PROC

; dw to convert into netw byte order (usually the port)
; returns: the value in network byte order (dw)
extrn htons:PROC

; Structs :o

; sockaddr struct for connection
; modified (for better use)
; if you want the original look for it into a winsock.h
SOCKADDR struct sin_family dw 0 ; ex. AF_INET sin_port dw 0 ; use htons for this sin_addr dd 0 ; here goes server node (from inet_addr) sin_zero db 8 dup(0) SOCKADDR ends ; for WSAStartup diagnose
WSADATA struct mVersion dw 0 mHighVersion dw 0 szDescription db 257 dup(0) szSystemStatus db 129 dup(0) iMaxSockets dw 0 iMaxUpdDg dw 0 lpVendorInfo dd 0 WSADATA ends ; Some nice equs ; what version of winsock do you need? (usually 1.1)
VERSION1_0 equ 0100h
VERSION1_1 equ 0101h
VERSION2_0 equ 0200h

AF_UNIX equ 1 ; local host
AF_INET equ 2 ; internet (most used)
AF_IMPLINK equ 3 ; arpanet
AF_NETBIOS equ 17 ; NetBios style addresses

; types of sockets
SOCK_STREAM equ 1 ; stream (connection oriented; telnet like)
SOCK_DGRAM equ 2 ; datagram (packets, packets, packets)

; protocol
PCL_NONE equ 0 ; none (define the protocol not needed)

SOCKET_ERR equ -1 ; standard winsock error

HOSTENT_IP equ 10h ; where is the IP into the hostent struct APENDIX ENDS

[ASM] A Expl to get Image Path Name from PEB

20.01.2010

rootb1ez ASM Để lại bình luận

 

Author: Benina

Download Source : http://cid-ac1f6a5fa666a923.skydrive.live.com/self.aspx/.Public/vidupeb.rar

Một ví dụ khai thác dữ liệu từ cấu trúc PEB.

Bài viết này chỉ là một ví dụ đơn giản cho việc chúng ta lấy tên đường dẫn  của chính chương trình và hiển thị trong một hộp MessageBox.

Như ta biết PEB có cấu trúc như sau:

struct _PEB {

0x000 BYTE InheritedAddressSpace;

0x001 BYTE ReadImageFileExecOptions;

0x002 BYTE BeingDebugged;

0x003 BYTE SpareBool;

0x004 void* Mutant;

0x008 void* ImageBaseAddress;

0x00c _PEB_LDR_DATA* Ldr;

0x010 _RTL_USER_PROCESS_PARAMETERS* ProcessParameters;

0x014 void* SubSystemData;

0x018 void* ProcessHeap;

0x01c _RTL_CRITICAL_SECTION* FastPebLock;

0x020 void* FastPebLockRoutine;

0x024 void* FastPebUnlockRoutine;

0x028 DWORD EnvironmentUpdateCount;

0x02c void* KernelCallbackTable;

0x030 DWORD SystemReserved[1];

0x034 DWORD ExecuteOptions:2;

// bit offset: 34, len=2

0x034 DWORD SpareBits:30;

// bit offset: 34, len=30

0x038 _PEB_FREE_BLOCK* FreeList;

0x03c DWORD TlsExpansionCounter;

0x040 void* TlsBitmap;

0x044 DWORD TlsBitmapBits[2];

0x04c void* ReadOnlySharedMemoryBase;

0x050 void* ReadOnlySharedMemoryHeap;

0x054 void** ReadOnlyStaticServerData;

0x058 void* AnsiCodePageData;

0x05c void* OemCodePageData;

0x060 void* UnicodeCaseTableData;

0x064 DWORD NumberOfProcessors;

0x068 DWORD NtGlobalFlag;

0x070 _LARGE_INTEGER CriticalSectionTimeout;

0x078 DWORD HeapSegmentReserve;

0x07c DWORD HeapSegmentCommit;

0x080 DWORD HeapDeCommitTotalFreeThreshold;

0x084 DWORD HeapDeCommitFreeBlockThreshold;

0x088 DWORD NumberOfHeaps;

0x08c DWORD MaximumNumberOfHeaps;

0x090 void** ProcessHeaps;

0x094 void* GdiSharedHandleTable;

0x098 void* ProcessStarterHelper;

0x09c DWORD GdiDCAttributeList;

0x0a0 void* LoaderLock;

0x0a4 DWORD OSMajorVersion;

0x0a8 DWORD OSMinorVersion;

0x0ac WORD OSBuildNumber;

0x0ae WORD OSCSDVersion;

0x0b0 DWORD OSPlatformId;

0x0b4 DWORD ImageSubsystem;

0x0b8 DWORD ImageSubsystemMajorVersion;

0x0bc DWORD ImageSubsystemMinorVersion;

0x0c0 DWORD ImageProcessAffinityMask;

0x0c4 DWORD GdiHandleBuffer[34];

0x14c void (*PostProcessInitRoutine)();

0x150 void* TlsExpansionBitmap;

0x154 DWORD TlsExpansionBitmapBits[32];

0x1d4 DWORD SessionId;

0x1d8 _ULARGE_INTEGER AppCompatFlags;

0x1e0 _ULARGE_INTEGER AppCompatFlagsUser;

0x1e8 void* pShimData;

0x1ec void* AppCompatInfo;

0x1f0 _UNICODE_STRING CSDVersion;

0x1f8 void* ActivationContextData;

0x1fc void* ProcessAssemblyStorageMap;

0x200 void* SystemDefaultActivationContextData;

0x204 void* SystemAssemblyStorageMap;

0x208 DWORD MinimumStackCommit;

};

Theo bài viết “PEB & TEB STRUCTURE” mà tôi đã viết muốn tìm base addr của PEB ta có thể coding như sau:

;————————-

;Get PEB of this proc

;————————-

assume fs: nothing

mov eax,fs:[18h]

mov edx,dword ptr [eax+30h]

Trong cấu trúc PEB, chúng ta chú ý đến thành phần:

0x010 _RTL_USER_PROCESS_PARAMETERS* ProcessParameters;

Thành phần này có cấu trúc như sau:

kd> dt nt!_RTL_USER_PROCESS_PARAMETERS

   +0x000 MaximumLength    : Uint4B

   +0x004 Length           : Uint4B

   +0x008 Flags            : Uint4B

   +0x00c DebugFlags       : Uint4B

   +0x010 ConsoleHandle    : Ptr32 Void

   +0x014 ConsoleFlags     : Uint4B

   +0x018 StandardInput    : Ptr32 Void

   +0x01c StandardOutput   : Ptr32 Void

   +0x020 StandardError    : Ptr32 Void

   +0x024 CurrentDirectory : _CURDIR

   +0x030 DllPath          : _UNICODE_STRING

   +0x038 ImagePathName    : _UNICODE_STRING

   +0x040 CommandLine      : _UNICODE_STRING

   +0x048 Environment      : Ptr32 Void

   +0x04c StartingX        : Uint4B

   +0x050 StartingY        : Uint4B

   +0x054 CountX           : Uint4B

   +0x058 CountY           : Uint4B

   +0x05c CountCharsX      : Uint4B

   +0x060 CountCharsY      : Uint4B

   +0x064 FillAttribute    : Uint4B

   +0x068 WindowFlags      : Uint4B

   +0x06c ShowWindowFlags  : Uint4B

   +0x070 WindowTitle      : _UNICODE_STRING

   +0x078 DesktopInfo      : _UNICODE_STRING

   +0x080 ShellInfo        : _UNICODE_STRING

   +0x088 RuntimeData      : _UNICODE_STRING

   +0x090 CurrentDirectores : [32] _RTL_DRIVE_LETTER_CURDIR

Chúng ta thấy trong cấu trúc trên có thành phần:

   +0x038 ImagePathName    : _UNICODE_STRING

Như chúng ta biết

;+0x038 UNICODE_STRING  struct

;+0x038         _Length                   dw    ?

;               MaximumLength             dw    ?

;+0x03c         Buffer                    dd    ?

;      UNICODE_STRING   ends

Vậy Buffer tại +0x03c của cấu trúc _RTL_USER_PROCESS_PARAMETERS chứa tên đường dẫn của Image chương trình.

Bây giờ chúng ta sẽ coding như sau:


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

.386

.model flat, stdcall  ;32 bit memory model

option casemap :none  ;case sensitive

include C:\masm32\include\windows.inc

include C:\masm32\include\kernel32.inc

include C:\masm32\include\user32.inc

include \masm32\macros\macros.asm

includelib C:\masm32\lib\kernel32.lib

includelib C:\masm32\lib\user32.lib

.const

uniString dw "P","a","t","h"," ","o","f"," ","P","r","o","c",0

.data

dwPEB                   dd 0

dwImagePathName   dd 0

lpBuffer          dd 0

.data?

.code

start:

;————————-

;Get PEB of this proc

;————————-

assume fs: nothing

mov eax,fs:[18h]

mov edx,dword ptr [eax+30h]

;————————————————————–

;Go to 0x010 _RTL_USER_PROCESS_PARAMETERS* ProcessParameters;

;————————————————————–

mov ebx,dword ptr[edx+10h]

;———————————————————-

;Goto +0x038 ImagePathName    : _UNICODE_STRING

;

;+0x038 UNICODE_STRING  struct

;+0x038         _Length                         dw    ?

;               MaximumLength             dw    ?

;+0x03c           Buffer                              dd    ?

;      UNICODE_STRING   ends

;———————————————————-

mov ebx,dword ptr[ebx+3ch]

;Save PEB and addr ImagePathName

push edx

pop  dwPEB

push ebx

pop  dwImagePathName

            invoke MessageBoxW,0,ebx,addr uniString,MB_OK

            invoke wsprintf,addr lpBuffer,CTXT("Addr ImagePathName :%lx"),[dwImagePathName]

            invoke MessageBox,0,addr lpBuffer,chr$("Addr ImagePathName"),MB_OK  

            invoke wsprintf,addr lpBuffer,CTXT("Base address PEB:%lx"),[dwPEB]

            invoke MessageBox,0,addr lpBuffer,chr$("Base address PEB"),MB_OK

invoke ExitProcess,0

end start

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Ghi chú:

Ở đây tôi dùng hàm MessageBoxW để hiển thị chuổi unicode

Khai báo uniString như sau để hiển thị trong MessageBoxW:

uniString dw "P","a","t","h"," ","o","f"," ","P","r","o","c",0

Benina

[ASM] Extract the file’s resource to a file.

05.01.2010

rootb1ez ASM Để lại bình luận

Author: Benina

I-Mở đầu:

Có nhiều cách 1 file đẻ ra một file khác. File đẻ ra tôi gọi là file mẹ, còn file sinh ra tôi gọi là file con.
Cách thông dụng nhất là ta dùng resource của file mẹ để chứa file con, lúc đó loại resource là lọai binary tức là một file mã máy hay nói nôm na là file dll hay exe mà các bác muốn đính kèm vào file mẹ. Lúc này file mẹ “dính bầu”. Khi lập trình ta cho type của resource là RCDATA.

Để khi thực thi nó trích xuất ra file dll hay exe (tức là file mẹ đẻ con)như yêu cầu thì trong chương trình chính file mẹ(đã có resource rcdata là file dll hay exe mà ta đã add), đầu tiên ta dùng hàm FindResource để tìm resource đó, nó trả về là một handle mà ta dùng nó cho các hàm sau. Sau đó là dùng hàm LoadResource để load nó vào trong memory. Kế tiếp các bác dùng hàm SizeofResource để tính size của nó là bao nhiêu để dùng sau này. Kế đến bác dùng hàm LockResource sẽ chỉ định resource trong bộ nhớ. Nếu resource đã load bị lock, giá trị trả về là con trỏ đến byte đầu tiên của resource, nếu ngược lại là NULL.

Bây giờ ta đã có đủ các info cần thiết và resource binary đã load vào trong memory. Việc cuối cùng là cài đặt file dll hay exe như yêu cầu. Ta dùng hàm CreateFile để cài đặt. Sau khi cài đặt thành công ta bắt đầu copy toàn bộ resource trên đã load trong bộ nhớ vào file đã cài bằng hàm CreateFile. Để copy data vào file bạn phải dùng hàm WriteFile và một loop copy các byte lần lượt từ resource vào file với số bytes cần copy là size được tìm trước đó bằng hàm SizeofResource. Đến đây là xong và nhớ close handle file đã cài đặt.
Còn một cách củ chuối là các bác dùng 1 chương trình soạn thảo (có thể dùng Olly+Plugin) nào đó chuyển đổi và edit toàn bộ file dll (exe) thành một khối dữ liệu dạng số hex và khai báo khối data số hex đó vào chương trình như một biến có giá trị đầu và có size là tổng kích thước các bytes. Sau đó dùng hàm CreateFile và WriteFile như trên để tạo ra file dll. 
Còn nếu các bác pro một chút, tức là bác hiểu rõ cấu trúc file PE và biết asm thì bác có thể tạo ra 1 file khác từ khối cấu trúc dữ liệu định dạng như cấu trúc file PE trong chương trình chính . Cách này pro ở chổ bác vừa code chương trình mẹ mà vửa code luôn dll trong 1 file chương trình. Link sau đây là một ví dụ:http://www.phreedom.org/solar/code/tinype/
Một trong những người ứng dụng hoàn hảo pp này là tác giả patcher DUP2
Nói túm lại là làm sao bác load khối bytes là file dll vào memory và copy nó ra 1 file.
Tôi nhớ ko lầm thì có con trojan nó ko đính kèm khối data binary file dll vào trong nó, mà nó sẽ tìm khối binary trong 1 file khác, ví dụ như file ảnh jpg chẳng hạn. Nói chung nhiều mánh khóe trên giang hồ.
Chúc bác thành công.

II-Thực hành:

_Dùng RadASM làm bộ sọan thảo như thông thường.

_Trong Menu Project/Resource tôi add 1 resource có lọai type là RCDATA, file là masgbox.exe, Name là idc_exe, ID=500.

_Trong file inc nhớ khai báo

.const

idc_exe equ 500

_Sau đó tạo 1 button có Caption là “Extract”. Khi nhấn button này thì nó sẽ extract ra 1 file exe

Đây là file ExtractResource.inc



include windows.inc
include kernel32.inc
include user32.inc
include Comctl32.inc
include shell32.inc

include \masm32\macros\macros.asm

includelib kernel32.lib
includelib user32.lib
includelib Comctl32.lib
includelib shell32.lib

DlgProc            PROTO    :HWND,:UINT,:WPARAM,:LPARAM

.const

IDD_DIALOG1            equ 101
IDC_extract            equ    1001    ;ID of Button Extract
idc_exe                equ 500        ;ID of Resource file exe

;#########################################################################

.data?

hInstance            dd ?
file_handle            dd ?        ;Handle of CreateFile
numWriten            dd ?        ;Using in WriteFile


;-------------------------------
;For Load Resource into memory
;-------------------------------

hResource DD ?
hDataOld  DD ?
SizeRes      DD ?
pData     DD ?

.data


filename_ db 'virusfile.exe', 0    ;File name for extract file



;#########################################################################


Và đây là chương trình chính ExtractResource.asm

.386
.model flat, stdcall  ;32 bit memory model
option casemap :none  ;case sensitive

include ExtractResource.inc

.code

start:

    invoke GetModuleHandle,NULL
    mov        hInstance,eax

    invoke InitCommonControls
    invoke DialogBoxParam,hInstance,IDD_DIALOG1,NULL,addr DlgProc,NULL
    invoke ExitProcess,0

;########################################################################

DlgProc proc hWin:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM

    mov        eax,uMsg
    .if eax==WM_INITDIALOG

    .elseif eax==WM_COMMAND
        mov eax,wParam
        mov edx,eax
        shr edx,16
        .if eax==IDC_extract
        
            ;----------------------------------------        
            ;Your code at here
            ;----------------------------------------
            ;Loading the rc exe into memory
            ;----------------------------------------
            INVOKE FindResource,hInstance,idc_exe,RT_RCDATA 
            mov hResource,eax
            INVOKE LoadResource,hInstance,hResource
            mov hDataOld,eax
            INVOKE SizeofResource,hInstance,hResource
            mov SizeRes,eax
            INVOKE LockResource,hDataOld
            mov pData,eax

            ;----------Loop write file-------------------------

            invoke CreateFile,addr filename_,\
                            FILE_ALL_ACCESS,\
                            0,\
                            NULL,\
                            CREATE_ALWAYS,\
                            0,\
                            NULL
            mov file_handle,eax
            .if eax!=INVALID_HANDLE_VALUE
                mov ebx,SizeRes
                .while ebx>0
                    invoke WriteFile,file_handle,pData,1,addr numWriten,NULL
                    
                    inc pData
                    dec ebx
                    
                    
                .endw
                invoke CloseHandle,file_handle
                invoke MessageBox,hWin,chr$("Extract this file's resource to a file"),chr$("Tested"),MB_OK
            .elseif
                invoke MessageBox,hWin,chr$("No Extract this file's resource to a file"),chr$("Error"),MB_OK
            .endif                                                    
            
        .endif
        ;-------------------------------------------------------------------------------------
    .elseif eax==WM_CLOSE
        invoke EndDialog,hWin,0
    .else
        mov        eax,FALSE
        ret
    .endif
    mov        eax,TRUE
    ret

DlgProc endp

end start

Năm 2008
Benina

[ASM] Cavity search engine of 29A

05.01.2010

rootb1ez ASM Để lại bình luận

Cavity search engine of
29A

Author: Benina
REA

Bài
viết này chỉ đơn giản là nghiên cứu thuật tóan tìm một “hẫm code” (cavity code)
(một lõm code trống) của một file.

Mục
đích tìm hẫm code là để inject code tầm bậy tầm bạ vào file.

Bài
này tôi dựa vào thuật tóan của Benny and Darkman of 29A.

 

Đầu
tiên chúng ta tham khảo một cấu trúc định nghĩa trước hay dùng của các coder
viruz: 

Khi
đầu một thủ tục hay một hàm, chúng ta bảo lưu các thanh ghi để sau này phục hồi,
ta hay dùng lệnh pushad.

Vậy
khi pushad thì các thanh ghi push vào stack theo thứ tự như thế nào. Đây là cấu
trúc đó:

 

Pushad_struc           
struc

       
Pushad_edi      dd      ?   ;esp+00h

       
Pushad_esi      dd      ?   ;esp+04h

       
Pushad_ebp      dd      ?   ;esp+08h

       
Pushad_esp      dd      ?   ;esp+0ch

       
Pushad_ebx      dd      ?   ;esp+10h

       
Pushad_edx      dd      ?   ;esp+14h

       
Pushad_ecx      dd      ?   ;esp+18h

       
Pushad_eax      dd      ?   ;esp+1ah

Pushad_struc           
ends

 

Mục
đích chính dùng cấu trúc này là khi trả về “giá trị trả về” của một thủ tục vào
một thanh ghi nào đó mà chúng ta muốn.

 

 

Đây
là thủ tục chính tìm cavity code :

 

;—————————————————————————–

; Cavity search
engine Benny and Darkman of 29A

;

; Calling
parameters:

; ECX = size of
search area

; ESI = pointer
to search area

; EDI = required
size

;

; Return
parameters:

; ESI = pointer
to cave

;

; Changed
registers:

; EAX, EBX, ECX,
EDX, ESI, EDI

 

CSE:   
pushad

       
lodsb                          ; AL = byte within search area

reset_cavity_loop:

        xchg   
eax,ebx                ; BL =  "     "      "     "

        xor    
edx,edx                ; Zero EDX

        dec    
ecx                    ; Decrease counter

        jecxz  
no_cave_found          ; Zero ECX? Jump to no_cave_found

find_cave_loop:

       
lodsb                          ; AL = byte within search area

        cmp    
al,bl                  ; Current byte equal to previous byte?

        jne    
reset_cavity_loop      ; Not equal? Jump to reset_cavity_loop

        inc    
edx                    ; Increase number of bytes found in

                                     
; cave

        cmp    
edx,edi                ; Found a cave large enough?

        jne    
find_cave_loop         ; Not equal? Jump to find_cave_loop

        sub    
esi,edi                ; ESI = pointer to cave

        mov    
[esp+04h],esi

no_cave_found:

       
popad

       
ret

 

Chú
giải:

-Tham số truyền vào hàm:

 

ecx : là size của vùng nhớ
mà ta muốn search.

esi : Là con trỏ trỏ đến
vùng nhớ để search.

edi : là size vùng cave yêu
cầu

 

-Thuật tóan :

Lệnh lodsb là load một byte
từ vùng nhớ trỏ đến bởi esi vào eax và esi sẽ tự động tăng lên

Biến ebx chứa tạm byte trước
đó loaded để so sánh với byte load sau trong eax.

Chú ý lệnh xchg để hóan đổi
byte loaded trong eax qua ebx

Nếu bằng nhau thì có nghĩa
là có lõm code.

edx là biến đếm số bytes của
lõm code.

Nếu có lõm code tức là al=bl
thì so sánh với size yêu cầu (edi).

Nếu tìm thấy thì giá trị trả
về trong thanh gi esi.

 

-Kết quả trả về.

 

      Nếu không tìm thấy thì
esi ko đổi

      Nếu tìm thấy cave thì
addr chứa trong esi

 

 

 

Sau
đây là thủ tục trong masm :

 

peCaveSearchEngine
proc

;—————————————————————————–

; Cavity search
engine Benny and Darkman of 29A

;

; Calling
parameters:

; ECX = size of
search area

; ESI = pointer
to search area

; EDI = required
size

;

; Return
parameters:

; ESI = pointer
to cave

;

; Changed
registers:

; EAX, EBX, ECX,
EDX, ESI, EDI     

;——————————————————————————

CSE: 
pushad

      ;mov
al,byte ptr [esi]

      ;inc
esi

     
lodsb                   ; AL = byte within search area

reset_cavity_loop:

      xchg 
eax,ebx                 ; BL =  "     "      "     "

      xor  
edx,edx                 ; Zero EDX

      dec  
ecx               ; Decrease counter

      jecxz
no_cave_found           ; Zero ECX? Jump to no_cave_found

find_cave_loop:  

      ;mov
al,byte ptr [esi]

      ;inc
esi

     
lodsb                   ; AL = byte within search area

      cmp  
al,bl             ; Current byte equal to previous byte?

      jne  
reset_cavity_loop ; Not equal? Jump to reset_cavity_loop

      inc  
edx               ; Increase number of bytes found in

                             
; cave

      cmp  
edx,edi           ; Found a cave large enough?

      jne  
find_cave_loop          ; Not equal? Jump to find_cave_loop

      sub  
esi,edi                 ; ESI = pointer to cave

      ;assume
esp : ptr Pushad_struc

      mov  
[esp+04h],esi

      ;assume
esp: nothing

no_cave_found:

     
popad

     
ret

peCaveSearchEngine
endp  

[ASM] PART 1 : BASIC TECHNIQUES

31.12.2009

rootb1ez ASM Để lại bình luận

INDEPENDENCE CODE SECTION
PART 1 : BASIC TECHNIQUES
Author: Benina 2006 (fixed 2008)

Hôm nay chúng ta sẽ tìm hiểu về một chủ đề mới : đó là các đọan code có khả năng thực thi độc lập ko phụ thuộc vào “nơi cư trú” (tôi tạm định nghĩa là independence code section).

Tut này sẽ trình bày khái quát và các kỹ thuật cơ bản trong đọan code có đặc tính như chủ đề tut đã nói.

Trước khi đọc lọat tuts này tôi xem như bạn đã biết sử dụng qua debugger Olly. 

I.MỞ ĐẦU : 

Một đọan code có khả năng thực thi độc lập đó là đọan code khi di chuyển từ một nơi này sang nơi khác  (từ disk đến memory hay từ memory đến memory hoặc từ memory đến disk) thì nó vẫn có giá trị thực thi ko đổi.

Tức là khi cho nó thực thi nó sẽ cho ra kết quả như mong muốn dù nó nằm ở đâu trong vùng nhớ khi thực thi.

 

Chắc bạn cho tui là khùng khi đề cập đến đọan code như vậy, vì thông thường bạn nghĩ rằng tất cả các đọan code (nói chính xác là nhóm các binaries hay các chuổi bytes)đều có khả năng như vậy. Nhưng bạn lầm rồi!. Nếu bạn đã hiểu về PE format thì bạn sẽ ko cho là vậy.

 

Các đọan code có đặc tính trên đã được các Vxer (người viết virus) nghiên cứu , ứng dụng và phát triển vượt bực.

Như bạn thấy dù mang tên là virus, tức là đọan code virus này phải “ăn bám” vào 1 file hay một process nào đó như một  lọai virus sinh học bình thường, nhưng đó chỉ là phạm trù về mặt “cư trú”. Chứ thật ra đọan code virus thực thi hòan tòan “độc lập” ko phụ thuộc vào vị trí (địa chỉ VA) của nó trong vùng nhớ.

 

Ngòai ra, các đọan code độc lập còn được ứng dụng trong việc lập trình Hook. Rồi chúng ta sẽ thấy nó ứng dụng tuyệt vời như thế nào!.

 

Để bạn hiểu rõ đọan code độc lập, tôi xin lấy ví dụ sau (dịch từ tut “PE INFECTION TUTORIAL FOR BEGINNER” của LiTlLe VxW) để mô tả về sự sai lầm trong nhận thức mà tôi đã nói chúng ta lầm tưởng như trên:

 

Nếu bạn đã biết một file PE .EXE được tạo ra như thế nào thì bạn sẽ hiểu code section bắt đầu tại offset 00401000h (entry_point+image_base) (trong một file standard PE, được linked bình thường). Lấy ví dụ về một chương trình “Hello World” như sau trong asm:

 

   OFFSET    |  OPCODE IN HEX VALUE             |   CODE               
----------|----------------------------------|----------------------
00401000h | 6A00 | push byte 0
00401002h | 681A104000 | push dword caption
00401007h | 6834104000 | push dword text
0040100Ch | 6A00 | push byte 0
0040100Eh | E8ED0F0000 | call MessageBoxA
00401013h | 6A00 | push byte 0
00401015h | E8EC0F0000 | call ExitProcess
0040101Ah | 596F757220666972737420 | caption db "Your first
| 57494E33322070726F6772616D6D00 | WIN32 programm"
,0
00401034h | 48454C4C4F00 | test db "HELLO",0



Bây giờ nhìn vào offset 401002h,ban sẽ thây: 68 1A104000
| |
+--------------------+ |
| |
+--------------------------+ +-----------+
| push on stack the dword | | 0040101Ah |
+-------------+------------+ +-----------+
| push dword | | caption |
+------------+ +-----------+
 
 

Bạn đã thấy vấn đề của chúng ta chưa ? Chưa à ! Hảy hình dung bạn đặt phần code này tại đọan cuối của một file khác hay đính nó vào một process khác (giống như virus đã làm) thì code sẽ ko chạy vì address của "caption" label đã bị thay đổi !!!

 

Nó sẽ ko chạy với lý do thứ 2 là: IMPORT section ko giống như như thế…(các bạn nên đọc về tut PE format để hiểu vấn đề này)

 

Chắc bạn đã hiểu được phần nào rồi đó!

Tôi xin nói thêm ở đây 1 chút, Mirosoft đã phát hành hệ điều hành Windows có chế độ bảo vệ là: các đọan code khi nằm trong vùng nhớ (ring3 mode) thì nó luôn phải phụ thuộc vào một process nào đó. Đồng thời Mirosoft còn tạo ra các thư viện hàm APIs động để imports vào trong mỗi process chỉ những hàm nào mà process cần dùng. Làm như vậy sẽ hạn chế được các Vxer (người viết virus) tấn công hệ thống do thiếu các hàm APIs để code. Microsoft tưởng rằng với chế độ bảo vệ “mẹ bồng con” như trên thì khó có virus nào ăn bám theo được.

Nhưng hòan tòan bất ngờ khi các Vxer đã dùng các kỹ thuật “độc lập hóa” đọan code virus mà họ muốn “tiêm chích” vào hệ thống.

Đồng thời trong đọan code độc lập họ vẫn sử dụng được các hàm APIs của Windows. Rồi chúng ta sẽ học điều đó trong các phần kế của lọat tuts này.

Trong tut này chúng ta sẽ tìm hiểu về các kỹ thuật “độc lập hóa” đọan code cơ bản nhất, với các kỹ thuật này chúng ta sẽ ứng dụng nó vào các điều có ích như programming HOOK , reversing virus để khống chế nó, hay nói các khác là học anti-virus.

Tôi hòan tòan phản đối và ko chịu trách nhiệm nếu các bạn sử dụng các kiến thức này vào việc điên rồ như viết các virus, cracking software,..v..v..

Mặc dù các kiến thức này rất cơ bản đã có từ lâu, nhưng các tài liệu tiếng Việt thì tôi chưa thấy , vì vậy việc tìm hiểu nó rất khó khăn. Chắc chắn 1 điều là khi tìm hiểu mà ko có “thầy” hướng dẫn thì “đố mày làm nên”!. Do đó, nếu có gì sơ sót mong các bạn chỉ giáo.

 

II. “DELTA OFFSET” TECHNIQUE:
 

Như các bạn đã thấy, nếu đọan code khi thực thi tham chiếu đến các địa chỉ VA thì khi mang đi chổ khác khó mà thực thi đúng được. Vì vậy , tòan bộ đọan code độc lập sẽ phải sử dụng các chỉ thị asm ko phụ thuộc vào địa chỉ VA (địa chỉ ảomà Windows mapping đọan code vào memory) hay các chỉ thị tham chiếu đến VA thực thông qua RVA (VA=RVA+ Image Base).

RVA chính là khỏang cách từ địa chỉ tham chiếu đến 1 điểm nào đó thường được gọi là Image Base. Image Base của đọan code độc lập chính là địa chỉ của chỉ thị đầu tiên của đọan code. Hay nói một cách khác, trong đọan code chúng ta sẽ sử dụng kỷ thuật “tham chiếu qua địa chỉ tương đối”. Do đó kỷ thuật đầu tiên chúng ta cần tìm hiểu là kỷ thuật tìm và lưu giữ Image Base của đọan code. Kỷ thuật này mang tên là “DELTA OFFSET”.

 

Sau đây là tòan bộ phần dịch từ tut “PE INFECTION TUTORIAL FOR BEGINNER” của LiTlLe VxW về kỷ thuật “DELTA OFFEST”:

  

Tôi sẽ giải thích cho bạn về DELTA OFFSET nhưng trước tiên ta hảy xem một chương trình WIN32 program (hello.EXE)như thế nào cái đã

 

(Phần này nhắc lại phần mở đầu như đã trích dẫn)

 

Nếu bạn đã biết một file PE .EXE được tạo ra như thế nào thì bạn sẽ hiểu code section bắt đầu tại offset 00401000h (entry_point+image_base)(trong một file standard PE, được linked bình thường)

 
   OFFSET    |  OPCODE IN HEX VALUE             |   CODE               
----------|----------------------------------|----------------------
00401000h | 6A00 | push byte 0
00401002h | 681A104000 | push dword caption
00401007h | 6834104000 | push dword text
0040100Ch | 6A00 | push byte 0
0040100Eh | E8ED0F0000 | call MessageBoxA
00401013h | 6A00 | push byte 0
00401015h | E8EC0F0000 | call ExitProcess
0040101Ah | 596F757220666972737420 | caption db "Your first
| 57494E33322070726F6772616D6D00 | WIN32 programm"
,0
00401034h | 48454C4C4F00 | test db "HELLO",0


Bây giờnhìn vào offset 401002h,bạn se thấy: 68 1A104000
| |
+--------------------+ |
| |
+--------------------------+ +-----------+
| push on stack the dword | | 0040101Ah |
+-------------+------------+ +-----------+
| push dword | | caption |
+------------+ +-----------+

 

Bạn đã thấy vấn đề của chúng ta chưa ? NO ! hảy hình dung bạn đặt phần code này tại đọan cuối của một file khác (like a virus do) , thì code sẽ ko chạy vì address của "caption" label đã bị thay đổi ! ! !

 

Nó sẽ ko chạy với lý do thứ 2 là: IMPORT section ko giống như như thế…

 

Delta offset technique sẽ được sử dụng như sau:

 

      call delta          ; (push eip)
delta:
pop ebp ; (ebp=eip)
sub ebp,offset delta

 

Khi bạn thực thi hàm CALL , giá trị của EIP register (lúc đó EIP sẽ là offset của delta) sẽ push trên stack vì vậy bạn pop nó ra(pop ebp) và sub nó với dword ‘offset delta’ và bây giờ ebp trỏ đến delta label (ebp=offset delta)

 

vậy nếu ta muốn code như sau:         mov eax,dword label1
mov ebx,dword[label2]


thì code sẽ phải thay đổi thành: call delta
delta:
pop ebp
sub ebp,offset delta

lea eax,[label1+ebp]
mov ebx,dword[label2+ebp]

 

Sáng sủa chưa ? ? ? Có một kỹ thuật khác để làm giống như thế mà ko dùng delta technique…

  

Chú ý bạn có thể làm như vầy:

 

   call delta
delta:
pop edx
sub eax,offset delta
...
...
...
lea eax,[label1+edx]
mov ebx,dword[label2+edx]
 

nhưng thanh ghi register edx sẽ ko bao giờ thay đổi trong tất cả code của bạn ! ! !

Tôi hy vọng bạn sẽ hiểu được những gì đã trình bày của tác giả.

 

III. CÁC CHỈ THỊ CHUYỂN HƯỚNG ĐIỀU KHIỂN :

Khi lập trình code độc lập , chúng ta nên chú ý các chỉ thị chuyển hướng điều khiển, vì hiểu rỏ nó chúng ta sẽ ít phạm sai lầm khi coding.

Để tìm hiểu tôi xin mô tả một thử dụ sau:

Tôi bậc chương trình Olly lên và load 1 file exe nào đó vào Olly. Sau đó tôi modify assember (nhấn phím space tại addr cần modify) để thay đổi code lần lượt như sau:

Tại offset 00401000 ta thay đổi:

 

jmp 00401004

Sau đó lần lượt:

push eax
push ebx
push 0040100A
ret
call 0040100F
pop ebp
sub ebp,0040100F

 

Sau khi thay đổi xong chúng ta có code trên cửa sổ CPU như sau:

 

My Label:   Offset:      Opcode:        Code:

----------------------------------------------------------------------
00401000 > EB 02 JMP SHORT seh_exp.00401004

00401002 50 PUSH EAX

00401003 53 PUSH EBX

Delta1: 00401004 68 0A104000 PUSH seh_exp.0040100A

00401009 C3 RETN

Delta2: 0040100A E8 00000000 CALL seh_exp.0040100F

Delta: 0040100F 5D POP EBP

00401010 81ED 0F104000 SUB EBP,seh_exp.0040100F


 

Bắt đầu khảo sát từng lệnh nhé.

 

1.Lệnh chuyển hướng điều khiển JMP:

Lệnh chuyển hướng điều khiển đầu tiên cần tìm hiểu đó là lệnh jmp.

Như ta thấy :

 

00401000 >   EB 02          JMP SHORT seh_exp.00401004

| |

+----------+ +-----------------+

| |

+---opcode jmp (1 bytes) +--- 02 : distance (1 bytes)

 

Lệnh jmp có 2 byte opcode : EB là opcode của lệnh jmp và 02 là distance (khỏang cách) jump, ta có: offset cần jmp đến = offset của lệnh jmp + distance + 2

Khỏang cách jump có thể “là số âm” khi nhảy ngược về chỉ thị phía trên

Vậy chỉ thị này ko ảnh hưởng đến vấn đề fixed offset (offset cố định) trong opcode khi chương trình biên dịch code ra file exe.

Các lệnh jnz, jz, ….tương tự như vậy. Các bạn cần tìm hiểu thêm.

 

2.Cặp lệnh chuyển hướng điều khiển PUSH/RET:

Ngòai các chỉ thị jump chuyển hướng điều khiển. Còn có một cặp chỉ thị sau dùng để chuyển hướng điều khiển đó là cặp lệnh PUSH/RET.

Đây là cách dùng cặp lệnh này:

hook:                   push    offset delta2 
ret
delta2: ................

 

Như ta biết, khi ta push một giá trị vào stack , sau đó cho thực hiện chỉ thị ret, thì ngay lúc đó eip nhảy đến offset lấy từ stack đã lưu trước đó bằng lệnh push. Vì vậy, sẽ chuyển điều khiển chương trình đến offset mà ta đã push vào stack đó là offset delta2.

Ta phân tích

 

00401004     68 0A104000    PUSH seh_exp.0040100A
| |
+----------+ +--------------+
| |
+---opcode push (1 byte) +--- 0040100A : offset of delta2 (4 bytes)

 

Như ta thấy, fixed offset đã tồn tại trong Opcode của chỉ thị này, vì vậy ta ko thể dùng chỉ thị này trong code độc lập.

Nhưng nó lại có một ứng dụng rất tuyệt vời, đó là lợi dụng tính năng fixed offset, cặp lệnh này đã được sử dụng trong lập trình hook.

Tức là chúng ta sẽ copy đọan code có cặp lệnh này vào offset đầu tiên của một hàm API cần hook và sau đó patch 4 bytes fixed offset thành 4 bytes offset mà chúng ta muốn chuyển hướng điều khiển của hàm API. Nhưng đó là một câu chuyện khác chúng ta sẽ tìm hiểu sau.

Còn bây giờ, chúng ta hảy dùng phím F8 trong Olly để thực hiện các lệnh trên cho đến chỉ thị RET. Sau khi thực hiện chỉ thị ret, stack sẽ gở bỏ 1 dword lưu offset đã push trước đó. Chúng ta hảy nhớ điều này.

Ngòai ta các bạn cần tìm hiểu thêm các lệnh sau “RETN 4“,”RETN 8”,…

Ví dụ:

Lệnh “RET 4” có nghĩa là chuyển điều khiển EIP đến [esp+4] tức là sẽ bỏ qua 4 bytes addr trên stack để lấy addr cho EIP.

Chú ý:

Trong lập trình code độc lập, chúng ta ít dùng lệnh push vì nó fixed offset. Vậy làm sao chúng ta có thể push các tham số cho stack?.

Để giải quyết vấn đề này chúng ta dùng cặp lệnh sau và một thanh ghi tạm:

            lea   esi,[ebp+szUser32dll]

push esi

call [ebp+_LoadLibrary]

 

3.Chuyển hướng điều khiển bằng cặp lệnh CALL/POP:

Ta có:

0040100A     E8 00000000    CALL seh_exp.0040100F
| |
+----------+ +--------------+
| |
+---opcode call (1 byte) +--- 00000000 : distance (4 bytes)

 

Lệnh call có 5 byte opcode : E8 là opcode của lệnh call và 00000000 là distance (khỏang cách) call, ta có:

offset cần call đến = offset của lệnh call + distance + 5 bytes

Như ta thấy lệnh call tương tự như lệnh jump, nhưng nó có nhiều bytes hơn và khi thực hiện, nó sẽ push một giá trị trả về là offset sau chỉ thị call vào trong stack. Vì vậy để cân bằng lại stack sau khi chuyển điều khiển đến label Delta,chúng ta cần POP một dword ra khỏi stack.

Ghi chú thêm về “Delta offset technique”:

Như bạn thấy trong chỉ thị sub có fixed opcode như sau khi biên dịch đầu tiên ra file exe:

00401010     81ED 0F104000  SUB EBP,seh_exp.0040100F

Vì vậy khi copy các bytes này sang 1 addr khác trong memory, thì các opcode này vẫn ko thay đổi. Tức là chỉ thị vẫn có giá trị là “sub ebp,0040100F”. Nhưng lúc đó ebp lại là offset tại vị trí mới của label delta. Do đó ebp sẽ khác 0 , khác với trường hợp bạn trace trong Olly ở đây. Vì vậy ebp sẽ chứa 1 độ lệch giửa offset khi biên dịch đọan code lúc đầu với offset current khi run tại vị trí nào đó.

III. ĐỊNH HƯỚNG BIÊN DỊCH TRONG ASM ĐỂ TÍNH TỔNG SỐ BYTES 1 ĐỌAN CODE:

Trong lập trình ASM, khi lập trình về “đọan code độc lập”, chúng ta thường cần tổng số byte của một đọan code để lưu vào 1 biến.

Để làm được điều này, chúng ta dùng chỉ thị định hướng biên dịch là “$”. Chỉ thị này đại diện cho offset tại label hiện nó được sử dụng.

Để tính size của một đọan code ta dùng $ như sau:

 

Label_01:

........inpendence code..........

Size_Label_01 = $ - Label_01 ; offset Size_Label_01 trừ cho offset Label_01

 

Chú ý: Size_Label_01 ko phải là một biến mà nó là 1 giá trị để chương trình biên dịch tham chiếu đến.

Chúng ta hảy xem đọan code sau:

Vidu.asm

------------------------cut here-------------------------------------

.586

.model flat, stdcall

option casemap:none

include \masm32\include\kernel32.inc

includelib \masm32\lib\kernel32.lib

.data

.code

start:

push size_label01 ; = push 3 bytes into stack

push 0

call ExitProcess

code_sec segment

label01: push eax ; 1 byte

push ebx ;1 byte push ecx ;1 byte size_label01 = $ - label01 ; size from label01 to size_label01 = 3 bytes code_sec ends end start -------------------------------cut here-------------------------------------

 

Sau khi dùng MASM biên dịch, chúng ta sẽ thấy 1 section được tạo ra có tên là code_sec do ta dùng cặp lệnh sau để tạo ra:

code_sec segment

.........code.............

code_sec ends

 

và load file exe vừa biên dịch vào Olly, chúng ta sẽ thấy :

00401000 >/$ 68 03000000 PUSH 3

00401005 |. 6A 00 PUSH 0 ; /ExitCode = 0

00401007 \. E8 00000000 CALL <JMP.&kernel32.ExitProcess> ; \ExitProcess

 

 

Vậy chỉ thị đầu tiên là lệnh push một giá trị là 3 vào stack, giá trị 3 chính là size của đọan code cần tính tổng từ label label01 đến label size_label01.

Tôi hy vọng những gì trình bày trên đã giúp ích được các bạn . Hẹn gặp lại.

——————————————————————————–

Benina 15/03/2006

Update 31/12/2009


(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)

[ASM] Benina’s TutASM # 4: Fading Effects

18.12.2009

rootb1ez ASM Để lại bình luận

 

Dowload source: Keygen4

Tranz by Benina

Bài viết này tôi trích dịch trong lọat tut Win32_Assembler_Coding_for_Crackers_by_Goppit_v11

Để keygen có hiệu ứng “fade in”(hiện dần) và “fade out” (tắt dần) khi opening và closing, chúng ta có 2 procedures được gọi là  FadeIn và FadeOut. Đối với các hàm này chúng ta sẽ cần 2 prototypesmột biến DWORD ko được khởi trị đầu "Transparency" và các hằng số sau đây mà chúng ta phải định nghĩa khi mà chúng ko có trong windows.inc file:LWA_ALPHA, LWA_COLORKEY, WS_EX_LAYERED and DELAY_VALUE.

Vậy ta cần khai báo trong file inc:

FadeIn                  proto       :DWORD

FadeOut                 proto       :DWORD

.data?

Transparency            dd          ?

.const

DELAY_VALUE       equ         10

Để sử dụng đặc tính trong suốt (transparency features) của win2k hay XP (có nghĩa là ko có tác dụng trong các version cũ của windows), chúng ta phải sử dụng cửa sổ được xếp lớp (layered window). Điều này có thể cải tiến đáng kể sự biểu diễn và hiệu ứng trực quan cho window mà chúng có hình dạng phức tạp, hình dạng động hay muốn sử dụng các hiệu ứng alpha blending effects. Hệ thống system sẽ tạo ra một cách tự động và vẽ lại các layered windows (cửa sổ xếp lớp) và  các cửa sổ windows of các ứng dụng nằm dưới. Khi cho ra kết quả , các layered windows được lát một cách êm ả, mà ko có đặc trưng chập chờn của complex window regions. Thêm vào đó, layered windows có thể mờ một cách riêng rẽ, đó là alpha-blended (được pha trộn). Để tạo ra một layered window, window phải đựoc cài đặt hay set với lọai WS_EX_LAYERED extended windows style.  

Đầu tiên chúng ta thêm các chỉ thị sau đây trong phần xử lý thông địêp WM_INITDIALOG:

      .if eax==WM_INITDIALOG

      ;—————————————

invoke GetWindowLong,hWin,GWL_EXSTYLE

      or eax,WS_EX_LAYERED

      invoke SetWindowLong,hWin,GWL_EXSTYLE,eax

      ;———————————

      ;Set Icon

      ;———————————

      invoke LoadIcon,hInstance,500

      mov hIcon,eax

      invoke SendMessage,hWin,WM_SETICON,NULL,hIcon

      INVOKE mfmPlay,pData

invoke FadeIn,hWin

      ;==========================================================

Hàm GetWindowLong API nhận thông tin về một window. Lọai thông tin cần lấy của window được chỉ định bởi tham số thứ 2 – trong case của chúng ta là GWL_EXSTYLE, yêu cầu lọai extended windows style cho cửa sổ window và hàm này trả về giá trị trong eax. Dòng kế tiếp là adds giá trị được biểu diễn bởi WS_EX_LAYERED vào eax. Dòng thứ ba set giá trị này như extended windows style cho window.

The FadeIn procedure looks like this:

FadeIn proc hWnd:HWND

      invoke ShowWindow,hWnd,SW_SHOW

      mov Transparency,75

@@:

      invoke SetLayeredWindowAttributes,hWnd,0,Transparency,LWA_ALPHA

      invoke Sleep,DELAY_VALUE

      add Transparency,5

      cmp Transparency,255

      jne @b

      ret

FadeIn endp

Hàm SetLayeredWindowAttributes API sets the opacity (mờ đục) và transparency colour key of a layered window. Trong trường hợp này tham số cuối cùng chỉ định một transparency (alpha blend) setting, và tham số thứ hai là 0 chỉ định completely transparent (trong suốt hòan tòan). Dòng đầu tiên trong thủ tục là hiển thị window trong một định dạng non-activated format và dòng kế tiếp moves một giá trị 75 vào trong biến transparency variable của chúng ta.

Trong vòng lặp kế tiếp của các chỉ thị là gia tăng transparency thêm 5 và sets window transparency với giá trị mới cho mỗi vòng lặp đến khi transparency là 255 (maximum value = opaque). Sleep cho phép một delay nhỏ giữa mỗi loop do đó fade ko thấy xuất hiện quá nhanh chóng . Khi transparency = 255 thì procedure returns.

FadeOut procedure tương tự và họat động ngược lại:

FadeOut proc hWnd:HWND

      mov Transparency,255

@@:

      invoke SetLayeredWindowAttributes,hWnd,0,Transparency,LWA_ALPHA

      invoke Sleep,DELAY_VALUE

      sub Transparency,5

      cmp Transparency,0

      jne @b

      ret

FadeOut endp

Ta thêm hàm FadeOut vào :

      .if eax==IDC_BTN2

      INVOKE mfmPlay,0

invoke FadeOut,hWin

      invoke EndDialog,hWin,0

.elseif eax==WM_CLOSE

            INVOKE mfmPlay,0

invoke FadeOut,hWin

            invoke EndDialog,hWin,0

Lời bình cho thủ tục fade procedure này:

Hàm fade-in function của chúng ta được gọi tương ứng với WM_INITDIALOG message được gởi đến dialogbox bởi  OS. Code đặt ở đây được thực thi trước khi dialog được hiển thị trên màn hình. Kết quả ở đây là một một blank (trống) dialog hiện dần trước khi bitmap background và controls hiện ra trong khi hàm fade-out function does not suffer this problem. Một cách giải quyết khác (rất đơn giản) có thể sử dụng AnimateWindow API để làm alpha-blending. Dòng sau đây sẽ theo thông điệp WM_INITDIALOG message:

invoke AnimateWindow,hWin,1000,AW_BLEND+AW_ACTIVATE

Và dòng sau đây theo WM_CLOSE message trước khi calling EndDialog:

invoke AnimateWindow,hWin,1000,AW_BLEND+AW_HIDE

Tuy nhiên trong trường họp của chúng ta, mặc dù nó cho ra improved fade-in effect, các transparent buttons ko được xử lý tốt và được vẽ  như là visible in the foreground.

Benina (5/2009)

[ASM] Benina’s TutASM # 3: PLAYING XM MUSIC FROM MASM’S EXE

18.12.2009

rootb1ez ASM Để lại bình luận

 

Author: Benina

Phần này tôi sẽ hướng dẫn các bạn cách add data XM music vào resource của file EXE và chơi nhạc khi chương trình Runing.

Tool : RadASM+MASM32

Dowload source here:

[ Keygen3.rar]

Lý thuyết:

Trước hết các bạn phải có 2 file thư viện : mfmplayer.inc và mfmplayer.lib chứa code để chơi nhạc. File thư viện mfmplayer.lib chứa một hàm được định nghĩa trong mfmplayer.inc :

mfmPlay PROTO :DWORD

Hàm này có tên là mfmPlay, lấy một tham số DWORD là con trỏ đến dữ liệu music trong memory. Hàm này yêu cầu một dword đầu tiên được trỏ đến bởi tham số hàm, nó chính là là kích thức (size) của data music.

Nếu tham số là con trỏ đến dữ liệu music thì nó bắt đầu chơi nhạc. Nếu tham số là 0 , thì music sẽ stop. Thư viện này chỉ đơn giản vậy thôi.

Nhưng trước hết, để ta sử dụng lâu dài hàm thư viện này khi lập trình , ta hảy copy file mfmplayer.inc vào thư mục C:\masm32\include chứa chương trình MASM32 và file mfmplayer.lib vào thư mục C:\masm32\lib. Sau khi làm xong, chúng ta có thể sử dụng dịch vụ của nó được rồi đó.

Tôi xin tóm tắt các bước để chơi music như sau:

  1. Lấy handle của resource music, sử dụng hàm FindResource
  2. Load resource trong memory, sử dụng hàm LoadResource
  3. Nhưng như đã nói về hàm mfmPlay, nó cần một Dword nằm trong memory đầu tiên (first Dword) chứa size của resource. Để giải quyết vấn đề này, chúng ta sẽ định ra một vùng nhớ mới (dùng hàm GlobalAlloc) và write size của resource đến first Dword của vùng nhớ mới vừa định. Do có thêm 1 dword để lưu size, nên vùng nhớ mới lớn hơn resource music 1 dword. Kế đến chúng ta copy resource từng byte từng byte một vào memory chúng ta mới định ra trước đó.
  4. Cuối cùng, chúng ta play music với hàm mfmPlay.
  5. Khi chương trình kết thúc, chúng ta cho nhạc tắt theo bằng cách gọi hàm mfmPlay với tham số là 0

Thực hành:

Bây giờ chúng ta hảy bật RadASM và Project Keygen1.

(Chúng ta tiếp tục với Project trong TUT “Benina’s tutASM #2” của tui nhé.)

Để MASM biên dịch link với thư viện mfmplare.lib , chúng ta khai báo các dòng sau trong file Keygen1.Inc:

include mfmplayer.inc

includelib mfmplayer.lib

Bước tiếp theo, ta copy một file .xm (tôi có kèm theo file keygen.xm trong source) vào trong thư mục /res của project và add nó vào resource như sau:

Vào menu Project/Resource ta add thêm resource IDM_MUSIC như hình sau:

Trong file Keygen1.Inc phần section .const ta khai báo :

IDM_MUSIC equ 200

————————————————————————–

Và khai báo thêm các biến trong section .data ? (các biến này sử dụng như thế nào tui sẽ giải thích sau):

;———————–

hResource DD ?

hDataOld DD ?

SizeRes DD ?

pData DD ?

tham khảo thêm hình:

Kế đến trong file Keygen1.Asm ta đánh vào các lệnh sau trước hàm DialogBoxParam:

;—————————————-

;Loading the music

INVOKE FindResource,hInstance,IDM_MUSIC,RT_RCDATA

mov hResource,eax

INVOKE LoadResource,hInstance,hResource

mov hDataOld,eax

INVOKE SizeofResource,hInstance,hResource

mov SizeRes,eax

INVOKE LockResource,hDataOld

mov esi,eax

mov eax,SizeRes

add eax,SIZEOF SizeRes

INVOKE GlobalAlloc,GPTR,eax

mov pData,eax

mov ecx,SizeRes

mov dword ptr[eax],ecx

mov edi,pData

add edi,SIZEOF SizeRes

rep movsb

;———————————–

Xem hình kèm theo

Cuối cùng trong phần xử lý thông điệp khởi tạo trị đầu của Dialog ta cho nó chơi nhạc bằng hàm:

INVOKE mfmPlay,pData

Hình minh họa

Chúng ta cũng đừng quên khi chương trình thóat exit (thông điệp WM_CLOSE được send) ta cho tắt nhạc bằng hàm:

INVOKE mfmPlay,0

Hình minh họa

Bây giờ bạn hit nút “GO” xem sao. OK chứ?

Phân tích:

Tôi sẽ giải thích ở đây phần code thêm vào trong section .code của file Keygen1.Asm.

Nhu ta thấy trong đọan code “Loading the music” , trước tiên là gọi hàm:

INVOKE FindResource,hInstance,IDM_MUSIC,RT_RCDATA

mov hResource,eax

Hàm FindResource xác định địa chỉ resource với lọai và tên trong module được chỉ định.

Nó cần 3 tham số. Tham số đầu tiên là handle của module, tham số thứ hai là tên resource và cuối cùng là lọai Resource. Ở đây là RT_RCDATA.

Trong MSDN LIBRARY:

The FindResource function determines the location of a resource with the specified type and name in the specified module.

HRSRC FindResource(

HMODULE hModule, // resource-module handle

LPCTSTR lpName, // pointer to resource name

LPCTSTR lpType // pointer to resource type

);

Nếu hàm này gọi thành công , giá trị trả về là một handle của block thông tin resource được chỉ định. Thu được handle của resource, ta lưu nó vào biến hResource để sử dụng sau này, và chuyển handle này cho hàm LoadResource. Và xin nói thêm, nếu hàm FindResource gọi ko thành công , nó sẽ trả về giá trị NULL.

Dòng lệnh kế tiếp là :

INVOKE LoadResource,hInstance,hResource

mov hDataOld,eax

Tham khảo Win32 API:

The LoadResource function loads the specified resource into global memory.

HGLOBAL LoadResource(

HMODULE hModule, // resource-module handle

HRSRC hResInfo // resource handle

);

Hàm LoadResource sẽ tải resource được chỉ định vào trong memory tòan cục.

Nếu hàm này gọi thành công, giá trị trả về là một handle của block memory tòan cục có dữ liệu liên đới với resource. Nếu ko thành công, giá trị trả về là NULL.

Sau đó ta lưu handle vào biến hDataOld.

Tiếp tục ta thấy:

INVOKE SizeofResource,hInstance,hResource

mov SizeRes,eax

Hàm SizeOfResource sẽ trả về kích thước (size đơn vị là byte) của resource music, lưu kích thước này vào biến SizeRes để sử dụng sau này.

Kế tiếp là lệnh:

INVOKE LockResource,hDataOld

Hàm LockResource sẽ chỉ định resource trong bộ nhớ. Nếu resource đã load bị lock, giá trị trả về là con trỏ đến byte đầu tiên của resource, nếu ngược lại là NULL.

Trước khi đi tiếp, chúng ta cần biết qua hàm này:

rep movsb

Hàm này có tác dụng copy lần lượt từng byte từng byte một từ vùng nhớ trỏ bởi thanh ghi esi đến vùng nhớ trỏ bởi edi. Số byte cần copy chứa trong ecx.

Vì vậy như lý thuyết lúc đầu, chúng ta sẽ định ra một vùng nhớ mới (dùng hàm GlobalAlloc) và write size của resource đến first Dword của vùng nhớ mới vừa định. Do có thêm 1 dword để lưu size, nên vùng nhớ mới lớn hơn resource music 1 dword. Kế đến chúng ta copy resource từng byte từng byte một vào memory chúng ta mới định ra trước đó.

Do đó để copy resource bằng cặp lệnh nói trên (rep movsb), chúng ta sẽ cho esi trỏ đến vùng memory của resource music, và edi trỏ đến vùng memory mới tạo ra, cuối cùng ecx chứa tổng số byte cần copy.

Vì vậy , sau hàm LockResource, chúng ta sẽ lưu giá trị trả về trong eax vào esi để dùng cho cặp lệnh copy sau này.

mov esi,eax

Và do vùng memory mới cần 1 first Dword để lưu size của resource nên size vùng nhớ mới phải hơn vùng nhớ resource music 1 dword. Ta dùng cặp lệnh sau để thực hiện điều này và eax sẽ lưu size vùng nhớ mới (chú ý SizeRes có kích thước là một Dword):

mov eax,SizeRes

add eax,SIZEOF SizeRes

Kế đến ta có :

INVOKE GlobalAlloc,GPTR,eax

mov pData,eax

Hàm GlobalAlloc được dùng để định ra một vùng memory. Tham số đầu tiên là thuộc tính định vị. Ở đây là GPTR có nghĩa là giá trị trả về của hàm sẽ là một con trỏ đến vùng memory mới định vị và tất cả các byte sẽ được khởi trị đầu là zero. Tham số thứ hai là size của memory để định kích thước vùng nhớ cần tạo . Ở đây Size được tăng lên một dword để chứa size của music do yêu cầu của hàm mfmPlay.

Sau khi định memory mới, ta lưu con trỏ đến vùng memory này vào biến pData để sử dụng cho hàm mfmPlay sau này.

Như đã phân tích ở trên lệnh:

mov ecx,SizeRes

lưu Size vào ecx để dùng cho việc copy.

Chỉ thị này:

mov dword ptr[eax],ecx

lưu kích thước size vào dword đầu tiên dùng cho hàm mfmPlay.

Cặp chỉ thị sau đây:

mov edi,pData

add edi,SIZEOF SizeRes

dùng định vị con trỏ vùng nhớ mới (edi) để copy các byte. (edi trỏ đến byte sau first dword)

Cuối cùng là thực hiện copy từng byte từng byte một từ resource music vào vùng nhớ mới định.

Hy vọng những dòng phân tích trên rõ ràng. Hẹn gặp lại các bạn trong các tut sau.

Benina 28/12/2005

Update 28/12/2005

Mail: benina@walla.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)

[ASM] Benina’s TutASM # 2: Changing Colours of a Dialog

18.12.2009

rootb1ez ASM Để lại bình luận

Author: Benina

Tut này tôi sẽ cùng bạn khám phá cách thay đổi màu sắc giao diện của một chương trình dùng Dialog làm cửa sổ chính.

Đây là Demo hòan khi hòan tất tut này:

Các bạn có thể download Source theo link:

Keygen2.rar

A.Lý thuyết:

Windows gởi các thông điệp rất đa dạng liên quan đến màu sắc đến thủ tục dialog của bạn, và bằng cách xử lý các thông điệp này bạn có thể thay đổi màu sắc nào mà bạn muốn hiển thị.Cho ví dụ, để thay đổi màu sắc của chính dialog box, bạn có thể xử lý thông điệp WM_CTLCOLORDLG, để thay đổi màu sắc cho điều khiển static control , thì bạn xử lý thông điệp WM_CTLCOLORSTATIC và vân vân.

Sau đây tôi xin liệt kê một số thông điệp liên quan, bạn tra cứu Win32 API Reference để biết thêm chi tiết :

WM_CTLCOLORDLG, WM_CTLCOLOREDIT, WM_CTLCOLORLISTBOX, WM_CTLCOLORMSGBOX, WM_CTLCOLORSCROLLBAR, WM_CTLCOLORSTATIC, WM_CTLCOLORBTN

Trước tiên bạn cài đặt một Brush dùng để paint nền background và chứa nó trong 1 biến để dùng sau này. Thông điệp WM_CTLCOLORDLG và các thông điệp liên quan thường được gọi trong suốt chương trình của bạn , và nếu bạn cài đặt một brush mới mỗi lần thông điệp được send thì cuối cùng bạn sẽ truy cập RAM rất nhiều với các brush die cũng nhiều vô số kể. Vì vậy với cách cài đặt 1 brush lưu nó trong 1 biến, bạn sẽ dễ dàng điều khiển hơn, và chúng ta có thể xóa nó khi dialog bị hủy và khi chúng ta đã biết chúng ta ko dùng nó nữa.

Hàm cài đặt 1 brush là hàm sau và lưu nó vào 1 biến:

(Các bạn tập tham khảo các hàm API bằng tiếng Anh cho quen, vì chắc chắn rất tiện lợi cho bạn sau này)

The CreateSolidBrush function creates a logical brush that has the specified solid color.

HBRUSH CreateSolidBrush(

COLORREF crColor // brush color value

);

Parameters

crColor

Specifies the color of the brush.

Return Values

If the function succeeds, the return value identifies a logical brush.

If the function fails, the return value is NULL.

Remarks

A solid brush is a bitmap that Windows uses to paint the interiors of filled shapes.

After an application creates a brush by calling CreateSolidBrush, it can select that brush into any device context by calling the SelectObject function.

Trong phần xử lý thông điệp WM_CTLCOLORDLG ta cho giá trị trả về là brush mà ta đã cài đặt.

Trong phần xử lý các thông điệp liên quan như WM_CTLCOLORSTATIC vân vân, ta co thể dùng SetBkMode, SetBkColor, SetTextColor để thay đổi màu sắc của control mà thông điệp chịu trách nhiệm.Các hàm này có ý nghĩa sau:

Chức năng của hàm SetBkMode là set một kiểu pha trộn bkground của DC ta chỉ định. Kiểu pha trộn Bkground (background mix mode) được sử dụng với texts, hatched brushes và các lọai pen ko phải để vẽ line “đặc” (solid). Nó tác dụng đến các lọai line vẽ- sử dụng một pen bởi hàm cài đặt là CreatePen . SetBkMode ko gây ảnh hưởng đến lines sử dụng hàm cài đặt ExtCreatePen.

Chức năng của hàm SetBkColor là set một bkground color hiện hành hay là màu tự nhiên gần nhất nếu device ko miêu tả giá trị màu được chỉ định. Hàm này lắp các kẻ hở giữa các lines vẽ được định mẫu sử dụng hàm CreatePen để cài đặt.

Hàm SetTextColor có chức năng set màu text .

Các bạn có thể tham khảo các hàm này trong phần phụ lục của tut.

-Và cuối cùng khi thóat chương trình chúng ta đừng quên free brush của chúng ta đã cài đặt ra khỏi memory bằng hàm DeleteObject

B.Thực hành:

Trong MASM ta thực hiện theo các bước sau:

Các bạn hãy thực hiện tiếp tục project mà ta đã làm ở tutorial “Benina’ Tut # 1”.

Hảy Open project Keygen1 trong Tut#1.

1. Bước 1:

Trước tiên ta include thư viện để sử dụng các hàm API đồ họa trong file Keygen1.Inc:

include gdi32.inc

includelib gdi32.lib

Kế đến khai báo các biến và hằng color trong file Keygen1.Inc:

Trong section .CONST khai báo hằng số màu color

; ——————————————————————————

; Colors

; ——————————————————————————

CR_BACKGROUND equ 00333300h

CR_TEXT equ 00CCCC99h

Trong section .DATA ?

Khai báo biến lưu Brush cài đặt

hBgColor HBRUSH ?

2. Bước 2 :

Trong file Keygen1.Asm , phần section .CODE trước lời gọi hàm DialogBoxParam ta add lệnh sau để cài đặt Brush:

INVOKE CreateSolidBrush, CR_BACKGROUND

mov hBgColor, eax

Sau đó trong vòng lặp xử lý thông điệp ta add các lệnh:

.ELSEIF uMsg == WM_CTLCOLORDLG

mov eax, hBgColor

ret

.ELSEIF uMsg == WM_CTLCOLORSTATIC

INVOKE SetBkMode, wParam, TRANSPARENT

INVOKE SetTextColor, wParam, CR_TEXT

Invoke SetBkColor,wParam,CR_BACKGROUND

mov eax, hBgColor

ret

.ELSEIF uMsg == WM_CTLCOLOREDIT

INVOKE SetBkMode, wParam, TRANSPARENT

INVOKE SetTextColor, wParam, CR_TEXT

Invoke SetBkColor,wParam,CR_BACKGROUND

mov eax, hBgColor

ret

Giải thích đôi chút:

-Ta biết thông điệp trả về giá trị trong eax, nên ta có các lệnh

mov eax, hBgColor

ret

-Chú ý dòng lệnh Set Background Mode (hàm SetBkMode) tham số thứ 2 là TRANSPARENT . Nếu ko có dòng này thì Bkground sẽ được fill với brush mà bạn chỉ định , nhưng khi control vẽ text nó sẽ vẽ đè lên với default background color. Vì vậy nếu ta setting với TRANSPARENT thì sẽ fix được vấn đề này.

-Thay đổi màu của các control khác cũng tương tự. Bạn tìm các thông điệp có dạng sau :WM_CTLCOLOR* message trong Win32 reference để tham khảo thêm. Chú ý rằng, một edit control sẽ gởi thông điệp WM_CTLCOLORSTATIC nếu nó chỉ đọc (read only) và nếu khác thì nó sẽ send thông điệp WM_CTLCOLOREDIT.

-Nếu bạn có nhiều hơn một static control hay nhiều các control khác mà bạn muốn thay đổi màu sắc của chúng khác nhau cho từng control, bạn cần check ID của control từ thông điệp ta đang xử lý và thay đổi màu dựa trên ID đó. Bạn đã chuyển Hwnd của controls trong lParam, và bạn có thể lấy ID của control bằng cách dùng hàm GetDlgCtlrID(). Chú ý rằng static control được cho bởi ID mặc định của IDC_STATIC (-1) bởi resource editor, vì vậy nếu bạn muốn có thể nói với chúng phần bạn sẽ cần chỉ định chúng Ids mới. Phần này các bạn tự tìm hiểu và viết code nhé.

3. Bước 3:

Cuối cùng chúng ta cần free Object Brush ra khỏi bộ nhớ. Trước lệnh ExitProcess và sau lệnh DialogBoxParam trong file Keygen1.Asm ta add thêm lệnh:

INVOKE DeleteObject, hBgColor

Đây là hình ảnh sau khi thực hiện trong file Keygen1.Inc

Đây là hình ảnh sau khi thực hiện trong file Keygen.Asm

Sau khi thực hiện xong ta Hit nút “GO” xem sau nhé. OK rồi chứ!

C.Phụ lục:

Tham khảo các hàm API:

1. SetBkMode :

The SetBkMode function sets the background mix mode of the specified device context. The background mix mode is used with text, hatched brushes, and pen styles that are not solid lines.

int SetBkMode(

HDC hdc, // handle of device context

int iBkMode // flag specifying background mode

);

Parameters

hdc

Identifies the device context.

iBkMode

Specifies the background mode. This parameter can be either of the following values:

Value Description

OPAQUE Background is filled with the current background color before the text, hatched brush, or pen is drawn.

TRANSPARENT Background remains untouched.

Return Values

If the function succeeds, the return value specifies the previous background mode.

If the function fails, the return value is zero.

Remarks

The SetBkMode function affects the line styles for lines drawn using a pen created by the CreatePen function. SetBkMode does not affect lines drawn using a pen created by the ExtCreatePen function.

The iBkMode parameter can also be set to driver-specific values. GDI passes such values to the device driver and otherwise ignores them.

2. SetBkColor

The SetBkColor function sets the current background color to the specified color value, or to the nearest physical color if the device cannot represent the specified color value.

COLORREF SetBkColor(

HDC hdc, // handle of device context

COLORREF crColor // background color value

);

Parameters

hdc

Identifies the device context.

crColor

Specifies the new background color.

Return Values

If the function succeeds, the return value specifies the previous background color as a COLORREF value.

If the function fails, the return value is CLR_INVALID.

Remarks

This function fills the gaps between styled lines drawn using a pen created by the CreatePen function; it does not fill the gaps between styled lines drawn using a pen created by the ExtCreatePen function.

If the background mode is OPAQUE, the background color is used to fill gaps between styled lines, gaps between hatched lines in brushes, and character cells. The background color is also used when converting bitmaps from color to monochrome and vice versa.

3. SetTextColor:

The SetTextColor function sets the text color for the specified device context to the specified color.

COLORREF SetTextColor(

HDC hdc, // handle of device context

COLORREF crColor // text color

);

Parameters

hdc

Identifies the device context.

crColor

Specifies the color of the text.

Return Values

If the function succeeds, the return value is a color reference for the previous text color.

If the function fails, the return value is CLR_INVALID. To get extended error information, call GetLastError.

Remarks

The text color is used to draw the face of each character written by the TextOut and ExtTextOut functions. The text color is also used in converting bitmaps from color to monochrome and vice versa.

Hy vọng các bạn hài lòng. Các bạn cần nghiên cứu thêm để Template của ta ngày càng đẹp.

Các bạn có thể download Source theo link:

[ Keygen2.rar]

Benina 28/12/2005

Update 28/12/2005

Mail: benina@walla.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)

[ASM] Benina’s TutASM #1: Basic template Keygen

18.12.2009

rootb1ez ASM Để lại bình luận

Author: Benina

Tòan bộ code của tut này các bạn download tại link

[ Keygen1.rar]

Hôm nay tui sẽ hướng dẫn các bạn cách làm một template Keygen cơ bản trong RadASM.

Nói sơ qua về Keygen cho ai chưa biết về nó: Keygen chính là một chương trình nhỏ có tác dụng cho người dùng nhập vào một name user, từ đó nó sẽ tính tóan ra một số serial để đăng ký sử dụng 1 soft nào đó. Các cracker trên thế giới hay sử dụng lọai chương trình này để cung cấp cho người sử dụng. Vậy giao diện của 1 Keygen về căn bản như sau :

-Thanh tiêu đề là “Keygen- NameSoft” : để xác định chức năng của chương trình là tạo key đăng ký cho soft có tên là NameSoft.

-Add một Icon cho chương trình (các nhóm cracker thường add Icon của nhóm crack của mình).

-Để chương trình đẹp mắt đồng thời các nhóm cracker thường thực hiện bản quyền của keygen bằng cách add vào Dialog một hình bimap là biểu tượng của nhóm crack.

-Một Static là “User:” để báo chức năng của hộp Edit Text theo sau đó cho phép người dùng nhập vào tên user. Một Static là “Serial :” để báo chức năng của hộp Edit Text theo sau là hiển thị Serial đã tính tóan được.

-Hai hộp Edit Text , một cho phép người dùng nhập vào một tên user, hộp thứ hai hiển thị serial đã tính tóan

-Hai nút Button là Generate khi nhấn nó sẽ thực hiện tính tóan serial và nút button còn lại là Exit để thóat chương trình.

Các bạn xem hình:

Bây giờ chúng ta sẽ thực hiện từng bước từng bước một.

Trước khi đọc tut này bạn nên xem qua tut : “Using RadASM tut #1” có trên Site Benina’s

-Trước tiên bạn tạo 1 DialogApp như tut tui vừa đề cập , tui sẽ ko nhắc lại ở đây. Project của ta có tên là Keygen1. Kế đến ta chuẩn bị 2 file bitmap và icon , copy nó vào thư mục /res của project chúng ta vừa tạo.

Trong cửa sổ Project ta Dclick vào file Keygen1.dlg để bắt đầu tạo giao diện cho Keygen. Bây giờ màn hình chương trình của ta như sau:

1.Thay đổi thanh tiêu đề của program:

Trong cửa sổ Properties ta chon Caption và đánh vào tên thanh tiêu đề : “Keygen-NameSoft”

2.Add Icon cho chương trình.

Các bạn làm theo tut “Using RadASM tut #1”.

-Add resource icon:

-Khai báo biến cho Icon trong file Keygen1.Inc tại section .data ?

hIcon dd ?

-Trong file Keygen1.Asm ta add thêm lệnh trong section .code tại DlgProc phần xử lý thông điệp WM_INITDIALOG:

invoke LoadIcon,hInstance,500

mov hIcon,eax

invoke SendMessage,hWin,WM_SETICON,NULL,hIcon

giống như hình sau:

Bây giờ chúng ta nên kiểm tra bước này xem có lỗi gì xảy ra ko bằng cách nhấn nút “GO”. Ok rồi nhé:

3.Add 1 hình biểu tượng cho Keygen:

Dclick vào file Keygen1.dlg để sọan thảo giao diện. Bây giờ ta click vào button Image trên thanh toolbar như hình :

Trong giao diện sọan thảo ta click và drap một hình chử nhật như sau:

Trong cửa sổ Properties ta thay đổi các mục:

-Mục Type ta đổi thành Bitmap

-Mục Image ta chọn file bmp đã copy vào thư mục /res lúc đầu

Chương trình của ta bây giờ sẽ có dạng sau :

Ta thay đổi chỉnh sửa để image có vị trí như sau:

Bây giờ nếu bạn hit vào nút “GO” thì hình ảnh của chúng ta sẽ ko xuất hiện trong chương trình như mong muốn. Vì trong resource của chương trình ko có hình bmp này. Do đó ta vào menu Project/Resource để add bitmap.Trước hết ta click vào hình Image trong cửa sổ sọan thảo để kick nó và nhìn vào của sổ Properties để lấy thông tin về ID và Name của Image

Vậy ID=1001 và Name=IDC_IMG1

Trên menu chọn Project/Resource ta thực hiện như hình sau:

Sau khi thực hiện xong ta vào file Keygen1.Inc để khai báo biến định nghĩa resource để ta sử dụng sau này nếu cần. Đây là một thói quen các bạn nên làm:

Trong section .CONST ta khai báo:

IDC_IMG1 equ 1001

Như hình sau:

Bây giờ ta hit GO thử xem . Nếu hình bmp chưa xuất hiện trong chương trình thì ta dời hình sang trái cho nó out ra ngòai cửa sổ chương trình của ta 1 chút như sau:

Sau đó ta hit nút “GO” lại xem sao. OK rồi nhé. Bây giờ cho dời hình lại như cũ đi bạn và cứ hit GO thử coi. Nó OK đúng ko?.

4. Cài đặt 2 Static:

Click vào button Static trên thanh toolbar để tạo 2 Static

Chọn lần lược các Static và thay đổi Caption thành “User” và “Serial”

Như tui đã nói, chúng ta nên có thói quen khai báo biến định nghĩa các item của chương trình trong file Keygen1.Inc để tiện sử dụng sau này. Thực hiện các bước tương tự như khai báo Image ở bước trên.

Khai báo thêm 2 biến như sau trong file Keygen1.Inc:

IDC_STC1 equ 1002

IDC_STC2 equ 1003

5. Cài đặt 2 EditText và 2 Button :

Làm tương tự như trên . Trên thanh Toolbar ,button cài đặt các đối tượng EditText và Button là:

Bây giờ chương trình của ta là :

Trong file Keygen1.Inc ta khai báo các biến định nghĩa các item vừa add:

IDC_EDT1 equ 1004

IDC_EDT2 equ 1005

IDC_BTN1 equ 1006

IDC_BTN2 equ 1007

Giống hình sau:

Bây giờ ta hit “GO” xem sao. OH, tốt rồi!.

6. Một số chỉnh sửa thêm:

-Bây giờ ta chọn Dialog của chương trình , vào cửa sổ Properties thay đổi :

-MiniButton thành FALSE

-MaxButton thành FALSE

để làm mất hiệu lực 2 button này trên thanh tiêu đề chương trình.

-Kế đến ta chọn Edit Text dùng để chứa Serial là IDC_EDT2 và thay đổi Properties của nó tại mục Locked thành TRUE, mục đích ko cho người dùng sọan thảo Edit Text này.

7.Viết code cho các Button:

* Code cho Button &Exit:

Trong phần xử lý thông điệp WM_COMMAND của thủ tục DlgProc ta đánh vào các lệnh sau:

;——————————–

;My code

;——————————–

mov eax,wParam

.if eax==IDC_BTN2

invoke EndDialog,hWin,0

.endif

;———————————–

Như hình sau:

Như vậy , khi ta nhấn button &Exit, code trên sẽ thực hiện hàm EndDialog để kết thúc Dialog.

* Code cho Button &Generate:

Trước tiên trong file Keygen1.Inc ta khai báo 2 biến tòan cục để chứa Name và Serial tạo ra: Đó là 2 biến sName và sSerial:

sSerial TCHAR 32h dup(?)

sName TCHAR 32h dup(?)

Các bạn xem hình sau:

Trong file Keygen1.ASM trong phần xử lý thông điệp WM_COMMAND ta thêm phần code sau đây:

.elseif eax==IDC_BTN1

INVOKE GetDlgItemText, hWin,IDC_EDT1, ADDR sName, SIZEOF sName

invoke KeyProc

INVOKE SetDlgItemText, hWin,IDC_EDT2, ADDR sSerial

.endif

Hình minh họa:

Theo code trên, đầu tiên ta lấy chuổi Name người dùng đánh vào trong IDC_EDT1 rồi lưu vào biến sName bằng hàm GetDlgItemText

Kế đến thực hiện hàm tạo key là KeyProc , hàm này tính tóan key dựa trên sName nhập vào. Giá trị trả về của hàm luôn được lưu trong biến sSerial được xử lý trong thủ tục hàm của nó.

Ta cần khai báo prototye của hàm KeyProc trong file Keygen1.Inc như sau:

KeyProc PROTO

Xem hình sau:

Bây giờ ta viết thủ tục KeyProc trong file Keygen1.ASM tại vị trí sau cùng của .code(nhưng trước lệnh end start):

KeyProc proc

invoke lstrcpy,ADDR sSerial,ADDR sName

ret

KeyProc endp

Thủ tục này chỉ có chức năng copy string trong biến sName , và gán vào biến sSerial.

Và cuối cùng ta dùng hàm SetDlgItemText để gán sSerial cho Edit Text hiển thị Serial.

Đây là tòan bộ code ta vừa sọan thảo

Bây giờ bạn hảy hit nút GO và thử lại mọi thứ xem sao.

Ổn chứ các bạn!.

Sau khi biên dịch xong chương trình của ta cực nhỏ , chỉ có 96KB mà thôi.

Tòan bộ code của tut này các bạn download tại link: [Source]

Tut đến đây xin hết. Chắc chắn sẽ ko khỏi sai sót. Mong các bạn thông cảm và chỉ điểm.

Hẹn gặp lại trong các tuts sau.

Benina 26/12/2005

Update 26/12/2005

Mail: benina@walla.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)

[ASM] Detecting operating systems without Microsoft Advanced Programming Interface

18.12.2009

rootb1ez ASM Để lại bình luận

Author: Thomas Krue – Universitas Virtualis

The Assembly-Programming-Journal, Vol. 1, No. 1 (2004)

Tranz by: Benina

Tiếp theo bài tut PEB&TEB structure, tôi xin dịch bài tut này để cho các bạn thấy được rõ hơn những kiến thức mà ta đã tìm hiểu được trong tut vừa qua. Vì là bài dịch, nên câu cú có thể ko hòan hảo, thậm chí hơi khó hiểu, mong các bạn thông cảm và nhớ đọc lại nhiều lần tôi đảm bảo các bạn sẽ hiểu thôi.

Bài viết này sẽ chỉ cho bạn cách dò tìm version của OS Mirosoft : Windows 95,98,Me – hệ điều hành non NT-based và Windows NT4,2000,XP,2003 – hệ điều hành NT-based- mà ko cần dùng hàm APIs để tránh kỷ thuật đảo mã (Reverse Engineer) set breakpoint trên hàm APIs.

I. Introduction

Khi phân tích cấu trúc của TEB (cũng được biết đến như là TIB: Thread Information Block) trên các OS khác nhau, chúng ta tìm thấy data thêm vào sau cấu trúc này sau khi startup . Data thêm vào này dường như (trên OS dựa trên nền tảng NT ‘NT-based’) có cấu trúc ko logical hay ko có chiều dài định sẳn, mà ở đó (data thêm vào)-đối OS dựa trên nền tảng NT -chứa thông tin bên trong PEB. Vậy chỉ còn cách để mô tả ý nghĩa của data này cho OS ko dựa trên nền tảng NT là debug cùng một ứng dụng trên các OS khác nhau.

II. Application start:

Có nhiều cách để dò tìm ra OS. Có thể thực hiện bằng cách dùng hàm API GetversionEx và checking giá trị trả về của version trong cấu trúc OSVERSIONINFO(EX).Hoặc bằng cách truy xuất thanh ghi CS. Cách khác là phân tích các thanh ghi registers trong lúc start up ứng dụng. Có các quy tắc đặc biệt đối với các registers khi OS “sửa chửa” các thanh ghi registers sẽ như thế nào trước khi thực thi chỉ thị đầu tiên:

Giá trị startup cho Windows 95/98/ME

EAX== Entry point ứng dụng

EBX==00530000h, một giá trị cố định

Các giá trị startup cho Windows NT/2000/XP/2003

EAX==NULL

EBX==7FFDF000h, con trỏ trỏ đến PEB

Bởi biết được các quy tắc này, chúng ta có thể check OS dựa trên suốt thời gian startup.Và rồi chúng ta chứa các giá trị thanh ghi của EAX và EBX để dành sử dụng trong tương lai- hay là dùng để giải quyết các vấn đề khác.

A.Thread Environment Block

TEB được “sửa chửa” trong suốt thời gian startup ứng dụng và chứa các pointers đến thread liên quan đến dữ liệu thêm vào cấu trúc TEB. Cấu trúc TEB structure có mặt trên tất cả các OS. Nó có kích thước được định nghĩa là 34h Bytes. TEB address có thể được tính tóan bằng cách truy xuất segment register FS theo cách sau đây:

assume fs:nothing

mov eax,fs:[18h]

Thanh ghi EAX sẽ chứa base address của block này. TEB chứa – tại address 18h trong cấu trúc – một pointer trỏ đến chính nó :

pSelf DWORD ? ; 18h pointer to TEB/TIB

Thành phần sau cùng của TEB là con trỏ trỏ đến process database. Trên OS dựa trên nền NT giá trị này sẽ trỏ đến address của Process Environment Block (xem Section II-C)

B. Additional data following TEB

Như đã nói trong phần Section I, data thêm vào này ko có cấu trúc logical và chỉ tham vấn được trên OS ko dựa trên nền NT . Trên Windows NT, 2000, XP và 2003 data thêm vào này được định nghĩa như sau:

NT_TEB_ADDON struct

LastErrorValue DWORD ? ; 00h (34h TEB)

LastStatusValue DWORD ? ; 04h (38h TEB)

CountOwnedLocks DWORD ? ; 08h (3Ch TEB)

HardErrorsMode DWORD ? ; 0Ch (40h TEB)

NT_TEB_ADDON ends

Windows 95, 98, ME ko có một cấu trúc như vầy; data thêm vào đã bị phá rối .

C. Process Environment Block

Hệ điều hành Windows dựa trên nền tảng NT chứa data liên quan đến process bên trong Process Environment Block. Address của cấu trúc này có thể lấy được bằng cách truy xuất vào segment register FS:

assume fs:nothing

mov eax,fs:[30h]

EAX sẽ chứa base address của PEB.

pProcess DWORD ? ; 30h pointer to process database

thông tin về Version được chứa bên trong cấu trúc PEB structure:

OSMajorVersion DWORD ? ; A4h <=4->NT / 5->2K/XP/2K3

OSMinorVersion DWORD ? ; A8h 0->2K / 1->XP / 2->2K3

D. NT-based definitions

Windows NT, 2000, XP và 2003 sử dụng fixed addresses (địa chỉ cố định) để chứa PEB và TEB. PEB luôn được chứa tại address 7FFDF000h và TEB bắt đầu tại 7FFDE000h. Bởi biết được 2 giái trị cố định này (two fixed values), chúng ta có thể dò tìm ra nền tảng của OS.

III. The Trick

Section II-B đã chỉ ra một cấu trúc add-on structure cho hệ điều hành NT-based . Dử liệu tồn tại trên hệ điều hành non NT-based cũng vậy. Nhưng nó được chứa theo cách khác. Để tính tóan các vị trí memory đúng đắn liên quan đến dò tìm OS – chúng ta dùng một trick phân tích data thêm vào .

Nếu chúng ta nhìn vào NT-TEB-ADD-ON structure chỉ ra trong Section II-B, chúng ta thấy thành phần LastErrorValue. Hầu như tất cả Windows API’s sẽ return một giá trị error value mà nó có thể truy xuất bằng GetLastError[1]. Thêm vào đây ,ta có thể thao tác bằng tay đối với LastErrorValue bằng SetLastError[1] API. Bằng cách sử dụng hàm API này và hiển thị vùng memory area bên trên TEB,các vị trí của LastErrorValue là:

Windows 95 – TEB-base + 60h

Windows 98 – TEB-base + 60h

Windows ME – TEB-base + 74h

Bây giờ chúng ta có thể dò tìm Windows ME or Windows 95/98. Bước đầu tiên chúng ta tính tóan , nhưng ko phải là bước sau cùng . Có thể dò tìm sự khác nhau giữa Windows 95 and Windows 98.

Section II đã chỉ ra giá trị khởi đầu và các rules (quy tắc) của nó. Giá trị khởi đầu của EBX trên OS ko dựa trên NT là 00530000h. Một cách chính xác giá trị này sẽ được tìm thấy bên trong phần dữ liện thêm vào – close LastErrorValue đã tính tóan xong ngay bây giờ. Bằng cách phân tích nó ở vị trí này, Kết quả sẽ là:

Windows 95 – TEB-base + 58h

Windows 98 – TEB-base + 54h

Windows ME – TEB-base + 7Ch

Bây giờ chúng ta có thể tham vấn đến mỗi OS ko dựa trên nền NT.

IV. Code creation (cài đặt code )

Đúng lúc này, chúng ta có thể dò tìm thông tin về version cho mỗi OS. Chúng ta muốn một mã độc lập cho OS, chúng ta cần xây dựng các thông tin cho nó . Cũng vậy có thể tìm ra thông tin về version theo luồng độc lập (workflow independent). Section VI sẽ chỉ cách giải quyết vấn đề hòan chỉnh trong Assembler. Trước hết , chúng ta lấy base addresses of PEB và TEB và tính tóan operating system base bằng cách phân tích chúng :

assume fs:nothing

mov ebx,fs:[18h] ; get self pointer from TEB

mov eax,fs:[30h] ; get pointer to PEB / database

.if eax==7FFDF000h && ebx==7FFDE000h

; WinNT based

.else

; Win9X based

.endif ; of base check NT/9X

Thông tin về version cho NT-based operation systems được chứa trong PEB. Chúng ta chỉ phải phân tích các giá trị OSMajorVersion và OSMinorVersion:

mov ebx,[eax+0A8h] ; get OSMinorVersion

mov eax,[eax+0A4h] ; get OSMajorVersion

.if eax==5 && ebx==0 ; is it Windows 2000?

.elseif eax==5 && ebx==1 ; is it Windows XP?

.elseif eax==5 && ebx==2 ; is it Windows 2003?

.elseif eax<=4 ; is it Windows NT?

.endif

Hệ điều hành non NT-based có thể được dò tìm bằng cách phân tích vùng data thêm vào trên TEB, tìm giá trị 00530000h:

mov edx,00530000h ; the value to search

mov eax,fs:[18h] ; get the TEB base address

mov ebx,[eax+58h] ; TEB-base + 58h (W95)

mov ecx,[eax+7Ch] ; TEB-base + 7Ch (WME)

mov eax,[eax+54h] ; TEB-base + 54h (W98)

.if ebx==edx ; is it Windows 95?

.elseif eax==edx ; is it Windows 98?

.elseif ecx==edx ; is it Windows ME?

.endif

V. Conclusions

Tìm operating system (hệ điều hành) bằng cách dùng kỹ thuật này chỉ để tránh dùng các hàm APIs. Các hàm khác , ví dụ GetCommandLine[1]IsDebuggerPresent[1] hay các hàm được có tên trong trong bài này có thể viết lại giống như cách đã trình bày . Lời cuối ,reverse engineer (kỷ thuật đảo mã) ko thể set breakpoints trên các hàm gọi API, bởi vì chúng ko tồn tại trong chương trình của chúng ta như bạn đã thấy. Và sự pha trộn các OS khác lại với nhau sẽ làm cho quá trình đảo nghịch mã khó khăn hơn.

References

[1] Microsoft Corporation, Microsoft Developer Networkhttp://msdm.microsoft.com

[2] Yuschuk, O., Olly Debuggerhttp://home.t-online.de/home/ollydbg

[3] Hutchenson, S., MASM V8http://www.masmforum.com

VI. Appendix

.const

;– return values from OS_GetOS

OS_UNKNOWN equ -1

OS_WIN95 equ 1

OS_WIN98 equ 2

OS_WINME equ 3

OS_WINNT equ 4

OS_WIN2K equ 5

OS_WINXP equ 6

OS_WIN2K3 equ 7

.code

OS_GetOS proc

local _theReturnValue:DWORD

pushad ; store all registers

mov _theReturnValue,OS_UNKNOWN

assume fs:nothing

mov ebx,fs:[18h] ; get self pointer from TEB

mov eax,fs:[30h] ; get pointer to PEB / database

.if eax==7FFDF000h && ebx==7FFDE000h ; WinNT based

mov ebx,[eax+0A8h] ; get OSMinorVersion

mov eax,[eax+0A4h] ; get OSMajorVersion

.if eax==5 && ebx==0 ; is it Windows 2000?

mov _theReturnValue,OS_WIN2K

.elseif eax==5 && ebx==1 ; is it Windows XP?

mov _theReturnValue,OS_WINXP

.elseif eax==5 && ebx==2 ; is it Windows 2003?

mov _theReturnValue,OS_WIN2K3

.elseif eax<=4 ; is it Windows NT?

mov _theReturnValue,OS_WINNT

.endif

.else ; Win9X based

mov edx,00530000h ; the magic value to search

mov eax,fs:[18h] ; get the TEB base address

mov ebx,[eax+58h] ; TEB-base + 58h (W95)

mov ecx,[eax+7Ch] ; TEB-base + 7Ch (WME)

mov eax,[eax+54h] ; TEB-base + 54h (W98)

.if ebx==edx ; is it Windows 95?

mov _theReturnValue,OS_WIN95

.elseif eax==edx ; is it Windows 98?

mov _theReturnValue,OS_WIN98

.elseif ecx==edx ; is it Windows ME?

mov _theReturnValue,OS_WINME

.endif

.endif ; of base check NT/9X

popad ; restore all registers

mov eax,_theReturnValue

ret ; return to caller

OS_GetOS endp

http://masm32vn.com (thanz NTA)

http://h1.ripway.com/benina/

http://benina.250free.com

Benina 07/02/2006

Update 07/02/2006

Mail: benina@walla.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)

[ASM] TASM32+HiEditor+ResEd

18.12.2009

rootb1ez ASM Để lại bình luận

Gói Editor này cho TASM đã lâu rồi. Khi nào rãnh tôi sẽ post gói mới tôi đang sử dụng.

By:benina

I had configed a tasm32 for newbies studying coding tasm with deroko.

+This RAR include:
-TASM32 ver 5.3+5.0
-ResEd 1.1.4.3
-HiEditor V2.0.1.3
-INC files of deroko ARTeam

+Using:
-Unrar to fodler c:\tasm32
-Edit and Build ALL on memu.

+Link
http://benina.rea.googlepages.com/tasm32.part01.rar
http://benina.rea.googlepages.com/tasm32.part02.rar
http://benina.rea.googlepages.com/tasm32.part03.rar
http://benina.rea.googlepages.com/tasm32.part04.rar
http://benina.rea.googlepages.com/tasm32.part05.rar
http://benina.rea.googlepages.com/tasm32.part06.rar
http://benina.rea.googlepages.com/tasm32.part07.rar
http://benina.rea.googlepages.com/tasm32.part08.rar
http://benina.rea.googlepages.com/tasm32.part09.rar

[ASM] Own extract overlay to a file

18.12.2009

rootb1ez ASM Để lại bình luận

  Author: Benina

Download tut+source:

http://benina.rea.googlepages.com/OwnExtratOverlay.rar

Có nhiều người biết Overlay là gì, nhưng cũng có nhiều anh, cụ thể là các newbiez, thì lại ko biết rõ ràng nó là cái gì. Nếu các bạn học reverse dùng Peid để khảo sát một file thì hay thấy xuất hiện từ overlay này khi Peid thông báo kết quả. Và cũng có một số người thì lại đóan mò nó là kỹ thuật nén này nén nọ. Ko biết đâu mà mò. Đã ko biết còn làm rối thêm. Riêng tôi, lúc trước có hỏi overlay là gì trên 4rum REA thì ko ai trả lời. Đành tìm tòi vậy.

Lý thuyết:

Thật mai mắn, tôi lang thang trên mạng và đã tìm ra câu trả lời Overlay là cái gì. Đây là bài viết hết sức ngắn gọn nhưng thật đầy đủ của Teddy Rogers. Mời các bạn tham khảo trước.

Overlays-Extra Data

Tranz by: Benina (REA) 2007

Seek n Destroy _ Hints & Tips _ Overlays / Extra Data

Posted by: Teddy Rogers Jan 5 2007, 10:21 AM

Một overlay (aka extra-data) là một data đơn giản đựoc gắn vào physical image of một file thực thi Portable Executable. Giả sử ta lấy một file PE, gắn thêm một đọan các bytes (stuff) vào cuối bên trong file, và nó có một overlay. Vùng data này ko được định nghĩa như một phần của image bởi PE header và do vậy nó ko phải là một phần của  virtual image of loaded PE.
Chú ý điều này:
DOS MZ header và stub (trong cấu trúc PE) ko được sử dụng nếu PE thực thi trong Windows, nhưng vùng e_lfanew field thì được sử dụng để định vị  PE header. (Các bạn học thêm về cấu trúc file PE).
Một overlay ko phải là một phần của virtual image, vì vậy nó ko có address. Để tham chiếu đến nó bạn phải read nó từ physical image of file thực thi executable. Vì vậy để thực thi code bên trong một overlay bạn phải cấp phát động memory cho nó, đọc nó từ disk, rồi tạo một hàm gọi call đến offset thích hợp. Offset sẽ là tĩnh static, nhưng address sẽ thay đổi dựa trên base address of memory block chứa overlay, tương tự như  RVAs và VAs.
Đối với PE overlays, ko có tài liệu liên quan hay bất cứ dữ liệu liên quan gì về nó, nó là một lọai dữ liệu đơn độc (proprietary nature) đính kèm theo file. Nó chỉ là extra data (dữ liệu đính kèm), ko có định dạng hay bất cứ gì được công bố. 
Một vài năm trước phần lớn các ứng dụng mà nó sử dụng overlays để tham chiếu đến chúng bởi một static physical offset trong file. Điều này gây ra vấn đề với các executable packers khi static offset sẽ bị thay đổi. Cách thích hợp để tìm ra overlay, và bây giờ phần lớn các ứng dụng làm là dùng sum of last section’s physical offset và  last section’s physical size. Chúng được tìm thấy trong object/section table.
Chú ý rằng một khi bạn (include) đính kèm extra-data/overlay trong PE section thì thực tế nó sẽ ko dài hơn overlay định trước, tức là chiều dài đọan extra-data sẽ cố định khi đứng đơn độc hay dính vào file.
Nếu bạn cần một tool để gắn extra data tại cuối of file, bạn có thể dùng bất kỳ Hex editor nào hay mỗi lệnh ‘copy’ command!
Chỉ cài đặt file được đặt tên là ‘CopyOverlay.bat’ với nội dụng như sau:

CODE

[FILE START]
@echo off
Set SourceFile=Main.exe
Set Ovl=Extended.ovl
Set Target=Main_Extended.exe
copy /B %SourceFile% + %Ovl% %Target% /B
[FILE END]
where:
SourceFile – your main executable
Ovl – overlay data
Target – main file with appended overlay

Như thế nào để code một tool tự động tìm và extract overlay? Nếu bạn  nghiên cứuImageDosHeader và ImageNtHeaders structures sẽ có tất cả thông tin bạn cần. Overlay chỉ là data sau Raw Offset + Raw Size of last section trong exe. Vì vậy, open  exe file, tìm last section, add Raw Offset + Raw Size với file pointer và nếu  filesize lớn hơn, thì sẽ có một  overlay. Nếu  size = filesize, thì ko có.  
Vậy để save overlay bạn  get offset như chi tiết ở trên và write như một  file. Dễ dàng khi bạn sử dụng làm việc với các cấu trúc windows structures.
Ted.

Thực hành :

Bài ví dụ sau đây tôi viết bằng masm, nếu các bạn lập trình bằng ngôn ngữ khác thì tự chuyển đổi nhé.

Chương trình này có chức năng tự kết xuất ra phần extra-data mà nó đang mang theo.

Như trên ta thấy, các bạn có thể tự đính kèm một extra-data vào bất cứ một file exe nào bằng cách chạy file CopyOvarlay.bat. Vì vậy chúng ta sẽ lập trình một chương trình bình thường có chức năng tự kết xuất dữ liệu đính kèm. Sau đó biên dịch thành file overlay.exe. Dùng tip trên để gắn dữ liệu ăn theo vào file exe vừa biên dịch. Sau đó chạy file có overlay thì nó sẽ kết xuất ra extra-data thành một file độc lập.

Ghi chú : Phần code dưới đây chỉ có tính chất minh họa để học tập. Ko chuẩn mực về mặt lập trình cũng như ko kiểm tra các trường hợp erro bị lỗi có thể xảy ra (đừng có mà chê bai nhuể nhải nhe, nghe chán phèo, hehe).

Bây giờ chúng ta xem code:

File overlay.inc

include windows.inc

include kernel32.inc

include user32.inc

include \masm32\macros\macros.asm

includelib kernel32.lib

includelib user32.lib

include \masm32\include\masm32.inc

includelib \masm32\lib\masm32.lib

.data

hinstance dd 0 ; for own main program

hfile dd 0 ; for temp file

file_handle dd 0 ; for extract file

ddcommandline dd 0

size_commandline dd 0

size_currdir dd 0

szFileName db 512 dup (0)

szfilecopy db "111xxx.exe",0

szvirus db "virus1.exe",0

szFmt db ‘File size : %4X’,0

ddfilesize dd 0

ddfilesizeheader dd 0

sizeoverlay dd 0

buffertemp db MAX_PATH dup (0)

buffer db 512 dup (0)

buf db 00h,00,00,00

OffsetPos OVERLAPPED <NULL,NULL,5A0h,NULL,NULL>

.data?

numWriten dd ? ;Using in WriteFile

numReaded dd ?

;===================================================

File ovarlay.asm

.386

.model flat, stdcall ;32 bit memory model

option casemap :none ;case sensitive

include overlay.inc

.code

start:

invoke GetModuleHandle, NULL

mov hinstance,eax

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; get own file name

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

invoke GetCommandLine

mov ddcommandline,eax

invoke lstrlen,ddcommandline

mov size_commandline,eax

invoke GetCurrentDirectory,MAX_PATH,offset buffertemp

invoke lstrlen, addr buffertemp

mov size_currdir,eax

mov eax,size_commandline

sub eax,size_currdir

sub eax,4

mov ecx,size_currdir

add ecx,2

invoke szMid, ddcommandline,addr szFileName,ecx,eax

invoke RtlZeroMemory, addr buffertemp, MAX_PATH

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; copy own file to a temp file named "111xxx.exe"

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

invoke CopyFile,offset szFileName,offset szfilecopy,1

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; get own file size from physical

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

invoke CreateFile,offset szfilecopy,GENERIC_READ+GENERIC_WRITE,\

FILE_SHARE_READ OR FILE_SHARE_WRITE,0,\

OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0

mov hfile,eax

invoke GetFileSize,eax,0

mov ddfilesize,eax

mov eax,ddfilesize

invoke wsprintf,addr buffer,offset szFmt, eax

invoke MessageBox,NULL,offset buffer,chr$("From physical disk"),MB_OK

invoke RtlZeroMemory, addr buffer, 512

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; get own file size from header

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

mov eax,hinstance

mov esi,[eax+3Ch]

add esi,eax

mov edi,esi ; EDI = ESI = Ptr

; to PE header

movzx eax,word ptr [edi+06h] ; AX = n§ of sections

dec eax ; AX– sub 1 section to goto beginning

; last section

imul eax,eax,28h ; EAX = AX*28

add esi,eax ; Normalize

add esi,78h ; Ptr to dir table

mov edx,[edi+74h] ; EDX = n§ of dir entries

; (NumberOfRvaAndSizes)

shl edx,3 ; EDX = EDX*8

add esi,edx ;esi ptr to beginning last setion

mov eax,[esi+10h]

add eax, [esi+14h]

mov ddfilesizeheader,eax

invoke wsprintf,addr buffer,offset szFmt, eax

invoke MessageBox,NULL,offset buffer,\

chr$("From file’s header"),MB_OK

invoke RtlZeroMemory, addr buffer, 512

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Check overlay

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

mov eax,ddfilesize

sub eax,ddfilesizeheader

mov sizeoverlay,eax

.if (eax <= 1000) ;check everlay

invoke CloseHandle,hfile

invoke DeleteFile,offset szfilecopy

invoke MessageBox,NULL, chr$("Overlay not exist"),chr$("Notice"),MB_OK

.elseif

invoke CreateFile,addr szvirus,\

FILE_ALL_ACCESS,\

0,\

NULL,\

CREATE_ALWAYS,\

0,\

NULL

mov file_handle,eax

;Extract overlay to a file

.if eax!=INVALID_HANDLE_VALUE

mov ebx,sizeoverlay

mov ecx,ddfilesizeheader

.while ebx>0

mov OffsetPos.loffset,ecx

push ecx

invoke ReadFile, hfile, ADDR buf, 1,\

ADDR numReaded, ADDR OffsetPos

invoke WriteFile,file_handle,\

offset buf,1,addr numWriten,NULL

pop ecx

inc ecx

dec ebx

.endw

invoke CloseHandle,file_handle

invoke MessageBox,NULL,

chr$("Extract overlay to a file"),chr$("Tested"),\

MB_OK

.elseif

invoke MessageBox,NULL,\

chr$("Cannot extract ovarlay to a file"),\

chr$("Error"),MB_OK

.endif

invoke CloseHandle,hfile

invoke DeleteFile,offset szfilecopy

.endif

invoke ExitProcess,NULL

end start

Phân tích:

+Đầu tiên chúng ta lấy tên file đang chạy thông qua hai hàm API chính là GetCommandLine và GetCurrentDirectory. Cách này không chính qui lắm nhưng được cái là có tính chất anti debug. Các bạn tự tình hiểu tại sao nó lại anti được nhe. Vấn đề này là một đề tài riêng tôi sẽ đề cập trong một bài viết khác (nếu còn sung sức).

invoke GetModuleHandle, NULL

mov hinstance,eax

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; get own file name

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

invoke GetCommandLine

mov ddcommandline,eax

invoke lstrlen,ddcommandline

mov size_commandline,eax

invoke GetCurrentDirectory,MAX_PATH,offset buffertemp

invoke lstrlen, addr buffertemp

mov size_currdir,eax

mov eax,size_commandline

sub eax,size_currdir

sub eax,4

mov ecx,size_currdir

add ecx,2

invoke szMid, ddcommandline,addr szFileName,ecx,eax

invoke RtlZeroMemory, addr buffertemp, MAX_PATH

+Bước tiếp theo, sau khi có tên file, chúng ta sẽ copy chính file đang chạy thành một bản sao copy có tên mới là “111xxx.exe”. Ta dùng bản sao file này để tính kích thước vật lý của file và để copy dữ liệu đính kèm extra-data thành một file trích xuất .

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; copy own file to a temp file named "111xxx.exe"

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

invoke CopyFile,offset szFileName,offset szfilecopy,1

+Với bản sao file exe này, chúng ta sẽ get size của nó thông qua hàm GetFileSize. Size này là size vật lý của file trên disk. Nếu file chưa gắn extra-data thì size này chính là size của file chương trình . Nếu file exe đã đuợc gắn extra-data thì size này bao gồm cả size file exe và cả extra-data.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; get own file size from physical

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

invoke CreateFile,offset szfilecopy,GENERIC_READ+GENERIC_WRITE,\

FILE_SHARE_READ OR FILE_SHARE_WRITE,0,\

OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0

mov hfile,eax

invoke GetFileSize,eax,0

mov ddfilesize,eax

mov eax,ddfilesize

invoke wsprintf,addr buffer,offset szFmt, eax

invoke MessageBox,NULL,offset buffer,chr$("From physical disk"),MB_OK

invoke RtlZeroMemory, addr buffer, 512

+Bây giờ chúng ta sẽ tính size của file chương trình, size này chính là size file exe (không có phần extra-data). Dựa vào trick trên trong phần lý thuyết chúng ta biết kích thước của một file PE có thể tính dựa vào phần header file như sau:

FileSize = PointerToRawData của section cuối cùng + SizeOfRawData của section cuối cùng.

Chú ý rằng, dù cho file exe có bị overlay hay không thì Header của file exe vẫn ko thay đổi. Và luôn tính được Filesize như trên.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; get own file size from header

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

mov eax,hinstance

mov esi,[eax+3Ch]

add esi,eax

mov edi,esi ; EDI = ESI = Ptr to PE header

movzx eax,word ptr [edi+06h] ; AX = n§ of sections

dec eax ; AX– sub 1 section to goto beginning

; last section

imul eax,eax,28h ; EAX = AX*28

add esi,eax ; Normalize

add esi,78h ; Ptr to dir table

mov edx,[edi+74h] ; EDX = n§ of dir entries (NumberOfRvaAndSizes)

shl edx,3 ; EDX = EDX*8

add esi,edx ;esi ptr to beginning last setion

mov eax,[esi+10h]

add eax, [esi+14h]

mov ddfilesizeheader,eax

invoke wsprintf,addr buffer,offset szFmt, eax

invoke MessageBox,NULL,offset buffer,chr$("From file’s header"),MB_OK

invoke RtlZeroMemory, addr buffer, 512

+Sau khi ta tính ra các size file. Ta sẽ so sánh xem size file vật lý có bằng size file tính trên header không. Nếu bằng hay nhỏ hơn 1000 (1kb, vì một số trình biên dịch có độ lệnh nhỏ hơn 1kb) thì file này chưa bị gắn overlay. Nếu lớn hơn 1000 (1kb) thì file có phần overlay. Lúc này chúng ta sẽ tiến hành trích xuất extra-data. Trong phần code này chúng ta tính luôn size dữ liệu overlay để dùng sau này.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Check overlay

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

mov eax,ddfilesize

sub eax,ddfilesizeheader

mov sizeoverlay,eax

.if (eax <= 1000) ;check everlay

invoke CloseHandle,hfile

invoke DeleteFile,offset szfilecopy

invoke MessageBox,NULL, chr$("Overlay not exist"),chr$("Notice"),MB_OK

.elseif

+Đây là code trích xuất dữ liệu extra-data thành 1 file. Trước tiên ta cài đặt một file mới có tên là “virus1.exe” bằng hàm CreateFile. File này chính là file lưu extra-data. Sau đó ta dùng hàm ReadFile để đọc 1 byte từ file temp “111xxx.exe” cài đặt trước đó tại vị trí đầu tiên chứa dữ liệu extra-data vào biến buf. Và dùng hàm WriteFile để viết byte chứa trong biến buf vừa read vào file mới “virus1.exe”. Lần lược chúng ta sẽ chép các bytes từ phần dữ liệu overlay vào trong file virus1.exe thông qua vòng lặp while. Số lượng các bytes cần chép là sizeoverlay.

invoke CreateFile,addr szvirus,\

FILE_ALL_ACCESS,\

0,\

NULL,\

CREATE_ALWAYS,\

0,\

NULL

mov file_handle,eax

;Extract overlay to a file

.if eax!=INVALID_HANDLE_VALUE

mov ebx,sizeoverlay

mov ecx,ddfilesizeheader

.while ebx>0

mov OffsetPos.loffset,ecx

push ecx

invoke ReadFile, hfile, ADDR buf, 1,\

ADDR numReaded, ADDR OffsetPos

invoke WriteFile,file_handle,\

offset buf,1,addr numWriten,NULL

pop ecx

inc ecx

dec ebx

.endw

invoke CloseHandle,file_handle

invoke MessageBox,NULL,

chr$("Extract overlay to a file"),chr$("Tested"),\

MB_OK

.elseif

invoke MessageBox,NULL,\

chr$("Cannot extract ovarlay to a file"),\

chr$("Error"),MB_OK

.endif

invoke CloseHandle,hfile

invoke DeleteFile,offset szfilecopy

.endif

invoke ExitProcess,NULL

+Cuối cùng ta xóa file tạm temp “111xxx.exe” và close các handles. Thế là xong.

Sau khi code xong, chúng ta biên dịch file trên, tiếp theo gắn dữ liệu extra-data vào file vừa biên dịch như tip trên đề cập. Rồi chạy file có overlay đó xem sao. Sau khi chạy xong sẽ sinh ra một file virus1.exe đúng file dùng làm extra-data mà ta gắn kết vào file exe trước đó.

Chúc các bạn thành công.

Kết luận:

Code trên quả thật rất rườm rà. Thuộc hàng dỡm đời. Một số bước không cần thiết. Đồng thời cũng không check các lỗi hàm API có thể xảy ra (vì quá lười suy nghĩ tiếp). Nhưng dù sao, nó cũng có tính chất dùng để học tập và quan trọng là RUNNING. Cuối cùng, tôi muốn nhắc lại, trong code trên có một trick anti-debug các bạn tự tìm hiểu tạo sao nhe. Trick này hình như chưa ai sử dụng. Một lần nữa tôi mong muốn các bạn nên hòan chỉnh code trên và cùng chia sẽ cho cộng đồng.

Thanz u

Benina 2009

[ASM] Self Modifying Code

15.12.2009

rootb1ez ASM Để lại bình luận

 

Benina (tranz in 2008) modified 2009

[Code+Tut]

http://cid-ac1f6a5fa666a923.skydrive.live.com/self.aspx/.Public/SelfModifyingCode.rar

Tut này chỉ để thực hành nếu bạn quan tâm đến việc thay đổi chính code của chương trình (self modifying code). Tôi sẽ chỉ giải thích một cách cơ bản và phần còn lại là do bạn tửơng tượng.

Tools cần thiết:

- MASM32 compiller

- Có lẻ vài chất gây nghiên cho hướng thú (xanax, bromazepam, diazepam or

  any other good benzodiazepine)

Ok, Ý tưởng cơ bản là gì?. Chúng ta cần một vài đọan trong section .code mà nó “trống” (free, không có code thực thi) để dùng cho việc viết đè data (dữ liệu) lên đó , đọan code trống đó giống như các dòng lệnh chỉ thị NOP. Những thứ khác bạn cần là code mà chúng ta muốn thay đổi. Trong ví dụ của tôi, tôi sẽ chỉ dùng hàm gọi API MessageBox. Quan trọng là có bao nhiêu bytes trống data code mà chúng ta cần.

Chúng ta bắt đầu với phần trao đổi sau.

MessageBox calling code là:

push MB_OK ;push MB_OK as type of our msgbox

push offset szCap ;push the offset of our caption

push offset szText ;push the offset of our text

push NULL ;push NULL as parent handle (no parent)

lea eax, MessageBox ;loads the effective address of MessageBox API function in EAX

;this is important ’cause when we copy this code to

;some other place the offset will change if we

;call directly the MessageBox API because of the

;segmenting

call eax ;and call eax

Hex of code này là như sau (chú ý các addresses sẽ khác):

6A00         = PUSH    0                          

6823304000   = PUSH    403023                     

6800304000   = PUSH    403000                     

6A00         = PUSH    0                          

8D05EC104000 = LEA     EAX, DWORD PTR DS:[4010EC]                              

FFD0         = CALL    NEAR EAX

Nói cách khác chúng ta có:

6A 00 68 23 30 40 00 68 00 30 40 00 6A 00 8D 05 EC 10 40 00 FF D0 = 22 bytes of code

Ok, bây giờ chúng ta có vài thứ để bắt đầu rồi. Chúng ta cần add 22 dòng chỉ thị NOPs nơi nào đó trong code của chúng ta mà ở đó chúng ta muốn gọi hàm MessageBox API. Và sau đây sẽ là ví dụ code:

jmp _decrypt ;jumps to the decrypt part

EncryptedOffset: ;this we use to name an offset

nop     ;our free code starts here

            nop

            nop

            nop

            nop

            nop

            nop

            nop

            nop

            nop

            nop

            nop

            nop

            nop

            nop

            nop

            nop

            nop

            nop

            nop

            nop

            nop              

            nop              

            nop

nop ;and ends here

jmp _continue ;jump to the end

_decrypt: ;this is the decryption part

mov edx,offset EncryptedOffset      ;edx = our free data [it’s not encrypted but this doesn’t metter :)]

invoke VirtualProtect,edx,22,PAGE_EXECUTE_READWRITE,addr oldProt ;we invoke VirtualProtect to get

;write access to the free .code segment

mov esi, offset EncryptedOffset ;esi = our free code offset

mov edi, offset DecryptionData ;edi = our executable code

mov ecx, 22 ;ecx hods how many times to loop the loop

xor eax, eax ;we clear eax (eax = 0)

lp1: ;our loop label

mov al, byte ptr [edi] ;al = current byte pointed by edi

mov byte ptr [esi], al ;copy this byte to the offset pointed by esi

inc esi ;esi + 1

inc edi ;edi + 1

loop lp1 ;jumps to lp1 label while ecx > 0

jmp EncryptedOffset ;when the loop ends we jump to the code we just copyed

DecryptionData: ;our offset with important data :)

push MB_OK ;the code of the data :)

push offset szCap ;…

push offset szText ;…

push NULL ;…

lea eax, MessageBox ;…

call eax ;…

_continue: ;we reach here when everything is done

Đây là tất cả những gì cần làm. Nhưng cho phép tôi nói vài lời, đây là một phương án rất tệ về tự thay đổi chính code của chương trình 9self modifying code). Nếu tôi cần làm “self mod code” thì tôi không bao giờ làm nó như cách này. Bạn có thể khai báo mã thực thi (executable code) của bạn trong vài biến , encrypt (mã hóa) nó ..vân vân…. Đây chỉ là một ví dụ đơn giản, Tôi đã làm nó trong vài phút. Tôi đã từng nói gì cho những người mới bắt đầu với tut này, là hảy dùng trí tưởng tượng của bạn. Bạn có thể sử dụng proc khác để đặt free code, tránh việc labelling, sau đó chỉ thay thế lea <register>, <label> thành lea <register>, <procname> và bạn run nó . :)

Sorry about bad english, good luck.

MiB!2k+

Comments