Trang 1 Xây dựngthưviệnliênkết động trongMASM Translated and Written by NhatPhuongLe www.reaonline.net Tutorial 17 Building a DLL in MASM32 Reverse Engineering Association Iczelions Win32 Assembly Tutorials For more updated info, please check http://nhatphuongle.spaces.live.com XâydựngthưviệnliênkếtđộngtrongMASM Translated and Written by NhatPhuongLe www.reaonline.net 1. Lý thuyết Nếu bạn lập trình đủ lâu, thì bạn nhận thấy rằng các chương trình mà trước đây bạn viết thường có những đoạn code giống nhau, nó thường là các thủ hàm (routines). Nó giúp bạn tiết kiệm thời gian tránh phải viết lại mỗi khi bắt đầu viết một chương trình mới. Trở lại thời lập trình trên nền DOS, những hàm thường xuyên được gọi sử dụng, sẽ được lưu trữ trong tập tin đối tượng (.OBJ, .LIB, . RES). Khi bạn muốn sử dụng các hàm này, bạn chỉ liênkết tới tập tin đối tượng này, sau đó trình liênkết sẽ extract các hàm này và nhúng nội dung của hàm vào tập tin EXE. Tiến trình này được gọi là liênkết tĩnh. Các thưviện lúc biên dịch trong ngôn ngữ lập trình C là ví dụ dễ thấy nhất. Liênkết tĩnh không hiệu quả bởi vì đoạn mã để thực thi một hàm thường phải nhúng nhiều lần vào các tập tin .EX E khác nhau. Không gian đĩa cứng của bạn sẽ tốn nhiều bộ nhớ để lưu trữ các bản sao của hàm. Tuy nhiên, phương pháp này có thể chấp nhận được bởi vì thường chỉ có một chương trình được gọi thực thi trên bộ nhớ. Tuy nhiên, dưới nền tảng Windows thì việc tiết kiệm bộ nhớ được quan tâm hơn bởi vì có thể có nhiều chương trình chạy cùng lúc. Lúc này, bộ nhớ được chia nhau sử dụng bởi các chương trình, và nếu như chương trình bạn càng lớn thì nó sẽ “ngốn” bộ nhớ càng nhiều. Windows đã đưa ra hướng giải quyết cho vấn đề này là thưviệnliênkết động. Windows sẽ không nạp các bản sao của một DLL lên bộ nhớ vì thế thậm chí có nhiều thể hiện của chương trình đang chạy cùng 1 lúc, cũng chỉ có duy nhất một bản sao của DLL mà chương trình đang sử dụng được nạp lên bộ nhớ. Tôi giải thích rõ hơn cho bạn hiểu. Thực ra, tất cả các tiến trình đều sử dụng chung một DLL, mà DLL này chính là bản sao của chính nó. Điều này thì tương tự như là có nhiều bản sao của DLL trong bộ nhớ. Nhưng trong thực tế, Windows sẽ “thực hiện” điều này bằng cách phân trang bộ nhớ và tất cả các tiến trình sẽ chia sẻ m ã nguồn của cùng 1 DLL. Vì thế trong bộ nhớ vật lý, chỉ có duy nhất một bản sao của mã nguồn DLL. Tuy nhiên, mỗi tiến trình sẽ có một section dữ liệu của DLL cho riêng nó. Chương trình liênkết với DLL lúc thực thi không giống như kiểu liênkết tĩnh cũ. Đó là lý do tại sao nó được gọi là thưviệnliênkết động. Bạn cũng có thể giải phóng DLL tại thời điểm biên dịch khi bạn không sử dụng nó nữa. Nếu như chỉ có một chương trình của bạn sử dụng DLL, nó sẽ được giải phóng ra khỏi bộ nhớ ngay lập tức. Nhưng nếu như DLL này vẫn được sử dụng bởi các chương trình khác, thì DLL vẫn còn nằm trên bộ nhớ cho đến khi chương trình cuối cùng sử dụng nó xong và yêu cầu giải phóng nó ra khỏi bộ nhớ. Tuy nhiên, trình linker sẽ có một nhiệm vụ khó hơn phải làm đó là fi x lại địa chỉ cho tập tin EXE. Bởi vì khi nó không thể “extract” các hàm và nhúng nội dung của hàm vào trong tập tin EXE, bằng cách này hay cách khác nó phải lưu trữ đầy đủ thông tin về DLL và các hàm trong tập tin EXE để nó có thể định vị và nạp lại một chính xác DLL tại thời điểm biên dịch. Quá trình này được gọi là base relocation. Đây là nơi mà thưviện nhập được import. Một thưviện nhập chứa thông tin về DLL. Trình linker có thể extract thông tin khi nó cần từ thưviện nhập vào tập t in EXE. Khi Windows Loader nạp chương trình vào bộ nhớ, nó sẽ tìm xem chương trình có liênkết với DLL hay không, nó sẽ tìm DLL đó và ánh xạ nó vào trong không gian địa chỉ của tiến trình và tiến hành fix địa chỉ của các hàm call gọi tới các hàm trong DLL. Bạn cũng có thể lựa chọn cách nạp DLL vào bộ nhớ bằng cách riêng của bạn không cần nhờ vào Windows Loader. Phương pháp này có những thuận lợi và khó khăn sau: Trang 2 Reverse Engineering Association XâydựngthưviệnliênkếtđộngtrongMASM Translated and Written by NhatPhuongLe www.reaonline.net Nó không cần phải import bất kỳ thưviện nhập nào vì thế bạn có thể nạp và sử dụng DLL thậm chí nó đi kèm với thưviện nhập. Tuy nhiên, bạn vẫn phải biết thông tin về các hàm bên trong DLL, có báo nhiêu tham số được truyền vào hàm hay đại loại thế. Khi bạn “nhờ” loader nạp DLL cho chương trình của bạn, nếu như loader không tìm thấy DLL nó sẽ thông báo “A required .DLL file, xxx.dll is missing”, và chương trình của bạn không thể run thậm chí nếu như DLL không thật sự cần t hiết cho xử lý trong chương trình chính đang gọi. Nhưng khi bạn nạp DLL theo cách của bạn thì khi DLL không được tìm thấy và nó không cần thiết cho xử lýu của chương trình, thì chương trình chỉ thông báo cho bạn biết và chương trình vẫn run tốt. Nếu bạn sử dụng LoadLibrary, bạn phải gọi hàm GetProcAddress cho mỗi hàm mà bạn muốn gọi. GetProAddress sẽ cho bạn địa chỉ entrypoint của một hàm trong DLL có liên quan. Vì thế mã nguồn của bạn có thể dài hơn một tí và chậm hơn một chút khi thực thi. Tuy nhiên, nó không đáng kể so với thuận lợi, bạn có thể nạp DLL không cần nhờ loader. Thấy được ưu và khuyết điểm của việc gọi hàm LoadLibrary, chúng ta sẽ đi vào chi tiết làm thế nào để tạo một DLL. Đây là cấu trúc của một DLL chuẩn: DLLSkeleton.asm .386 .model flat,stdcall option casemap:none include \masm32\include\windows.inc include \masm32\include\user32.inc include \masm32\include\kernel32.inc includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib DllEntry proc hInstance:HINSTANCE,reason:DWORD, reserved1:DWORD . mov eax,TRUE ret DllEntry Endp Function proc . ret Function endp End DllEntry DLLSkeleton.DEF LIBRARY DLLSkelton EXPORTS Function Trang 3 Reverse Engineering Association XâydựngthưviệnliênkếtđộngtrongMASM Translated and Written by NhatPhuongLe www.reaonline.net Trên đây là cấu trúc chung của một chương trình DLL. Mỗi DLL phải có điểm vào (entrypoint) của hàm. Hàm DllEntry trong DLL sẽ được gọi khi thưviện được Windows nạp vào bộ nhớ và khi Windows yêu cầu chấm dứt. DllEntry proc hInstance:HINSTANCE,reason:DWORD, reserved1:DWORD . mov eax,TRUE ret DllEntry Endp Bạn có thể đặt tên entrypoint của DLL tùy ý, miễn sao là tên này giống với tên sau nhãn END End DllEntry Hàm này nhận vào 3 tham số, trong đó có 2 tham số quan trọng là hInstance và reversed1: Tham số đầu tiên mà hàm này sử dụng là mã thể hiện của thưviện hay còn gọi là instance handle (tìm hiểu sâu vào kiến trúc Windows 32-bits, bạn sẽ thấy instacne handle chính là địa chỉ vùng nhớ đầu tiên nơi thưviện DLL được nạp vào). Nếu thưviện của bạn sử dụng đến những tài nguyên yêu cầu cung cấp mã thể hiện của chương trình gọi (như Dialogbox chẳng hạn) thì bạn cần phải lưu biến hInstan ce này như một biến toàn cục. Tham số thứ 2 là reason, tham số này có thể mang 4 giá trị cho biết nguyên do và tại sao Dll được Windows nạp vào bộ nhớ. Trong phần tiếp theo đây, bạn hãy hình dung ngữ cảnh trong đó một chương trình ứng dụng được cho kích hoạt và chạy nhiều lần song song với nhau. Mỗi lần chương trình được gọi, Windows xem đó như là một tiến trình (process). Mỗi tiến trình nằm trong một vùng không gian địa chỉ ảo độc lập với nhau. Giá trị tham số reason Ý nghĩa DLL_PROCESS_ATTACH Hàm DllEntry với tham số DLL_PROCESS_ATTACH được gọi khi process tiến hành load DLL Thưviện DLL đang được Windows ánh xạ vào vùng nhớ của tiến trình (thực hiện lời gọi DLL) Đây là thời điểm để DLL khởi tạo các biến, cấp phát vùng nhớ hay những thao tác cần thiết khác trước khi cho phép tiến trình gọi đến các hàm của thưviện DLL_PROCESS_DETACH Thưviện DLL được giải phóng khỏi vùng nhớ của tiến trình do 1 trong 3 nguyên nhân: nạp DLL không thành công, tiến trình kết thúc, hay tiến trình gọi hàm FreeLibrary. Đây là thời điểm để giải phóng các biến hay tài nguyên mà DLL đã cấp phát Trang 4 Reverse Engineering Association Xây dựngthưviệnliênkết động trongMASM Translated and Written by NhatPhuongLe www.reaonline.net DLL_THREAD_ATTACH Khi tiến trình tạo mới một tiểu trình (Thread), Windows gọi hàm DllEntry của tất cả các thưviện DLL đang được sử dụng với tiến trình đó. Đây là thời điểm để khởi tạo các biến dùng cho tiểu trình Lưu ý rằng tình huống này chỉ xảy ra khi tiểu trình được tạo sau khi thưviện DLL đã load vào tiến trình, nghĩa là nếu DLL được load bằng hàn LoadLibrary thì tất cả các t iểu trình hiện có (trong tiến trình) sẽ không gọi hàm DllEntry với tham số này. DLL_THREAD_DETACH Khi 1 tiểu trình kết thúc, Windows gọi hàm DllEntry của tất cả các thưviện DLL đang được sử dụng với tiểu trình này. Đây là thời điểm để giải phóng các biến dùng cho tiểu trình Tham số cuối cùng truyền cho hàm DllEntry là reversed1 được hệ thống dành cho mục đích riêng. Bạn trả về giá trị TRUE cho thanh ghi EAX, nếu như bạn muốn DLL chạy. Ngược lại, nếu EAX chứa giá trị FALSE thì DLL sẽ không được nạp. Ví dụ, nếu code khởi tạo chỉ định một số vùng nhớ và nó không thể làm điều đó thành công, thì hàm DllEntry sẽ trả về giá trị FALSE để chỉ ra rằng DLL không thể chạy được. Bạn có thể đặt các hàm của bạn trong DLL trước hoặc sau entrypoint đều được. Nhưng nếu bạn muốn nó có thể được gọi từ chương trình khác bạn phải đặt các hàm trong export list chính là danh sách các hàm được export ra bên ngoài cho các DLL hoặc chương trình khác sử dụngtrong file định nghĩa (.DEF) LIBRARY DLLSkelton EXPORTS Function Thông thường, bạn phải có dòng đầu tiên. Phát biểu LIBRARY sẽ định nghĩa tên module bên trong của DLL. Bạn nên để nó phù hợp với tên file của DLL (trong ví dụ này là DLLSkeleton). Phát biểu EXPORTS sẽ nói cho trình linker biết những hàm nào trong DLL được export, đó là những hàm có thể được gọi bởi các chương trình khác. Trong ví dụ này, chúng ta muốn các modules khác có thể gọi hàm Function, vì thế ta đặt tên của hàm này (Function) phía sau phát biểu EXPORT. Nếu bạn không viết code trên một IDE dành riêng cho asm, thì bạn có thể viết một tập tin .BAT để biên dịch và liênkết các tập tin đối tượng. Tuy nhiên, có một sự thay đổi trong việc viết lệnh trong file .BAT như sau: Thông thường, bạn muốn trình linker sau khi liênkết các tập tin đối tượng thành tập tin .EXE thì bạn viết lệnh như sau: \masm32\bin\Link /SUBSYSTEM:WINDOWS /OUT:xxx.exe xxx.obj Trang 5 Reverse Engineering Association Xây dựngthưviệnliênkết động trongMASM Translated and Written by NhatPhuongLe www.reaonline.net Bây giờ, muốn xuất ra một tập tin DLL bạn phải viết như sau: \masm32\bin\Link /SUBSYSTEM:WINDOWS /DLL /DEF:xxx.def xxx.obj Còn câu lệnh để yêu cầu MASM biên dịch thì bạn viết như cũ: \masm32\bin\ml /c /coff xxx.asm Make.BAT @echo off if exist xxx.obj del xxx.obj if exist xxx.dll del xxx.dll \masm32\bin\ml /c /coff xxx.asm \masm32\bin\Link /SUBSYSTEM:WINDOWS /DLL /DEF:xxx.def xxx.obj del xxx.obj del xxx.exp dir xxx.* pause 2. Chương trình minh họa Yêu cầu: Viết chương trình hiển thị MessageBox với thông báo Hello World sử dụng DLL. Hướng dẫn : Tạo Project mới trong RadASM, chọn Assembler : masm Project Type : Dll Project Project Name : DLLHelloWorld Project Description : HelloWorld using DLL Chọn kiểu Template: None Viết code cho file DLLHelloWorld.asm như sau: .386 .model flat,stdcall option casemap:none include \masm32\include\windows.inc include \masm32\include\user32.inc include \masm32\include\kernel32.inc includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib .data AppName db "DLL HelloWorld",0 Trang 6 Reverse Engineering Association Xây dựngthưviệnliênkết động trongMASM Translated and Written by NhatPhuongLe www.reaonline.net HelloMsg db "Hello, you're calling a function in this DLL",0 LoadMsg db "The DLL is loaded",0 UnloadMsg db "The DLL is unloaded",0 ThreadCreated db "A thread is created in this process",0 ThreadDestroyed db "A thread is destroyed in this process",0 .code DllEntry proc hInstance:HINSTANCE, reason:DWORD, reserved1:DWORD .if reason==DLL_PROCESS_ATTACH invoke MessageBox,NULL,addr LoadMsg,addr AppName,MB_OK .elseif reason==DLL_PROCESS_DETACH invoke MessageBox,NULL,addr UnloadMsg,addr AppName,MB_OK .elseif reason==DLL_THREAD_ATTACH invoke MessageBox,NULL,addr ThreadCreated, addr AppName,MB_OK .else ; DLL_THREAD_DETACH invoke MessageBox,NULL,addr ThreadDestroyed, addr AppName,MB_OK .endif mov eax,TRUE ret DllEntry Endp HelloWorld proc invoke MessageBox,NULL,addr HelloMsg,addr AppName, MB_YESNO + MB_ICONASTERISK + MB_DEFBUTTON2 ret HelloWorld endp End DllEntry File DLLHelloWorld.DEF LIBRARY DLLHelloWorld EXPORTS HelloWorld Compile, Link, Build DLLHelloWorld.asm ta được file DLLHelloWorld.DLL Trang 7 Reverse Engineering Association Trang 8 Xây dựngthưviệnliênkết động trongMASM Translated and Written by NhatPhuongLe www.reaonline.net Tạo một Project mới trong RadASM Assembler : masm Project Type : Win32 App Project Name : HelloWorld Viết code cho file HelloWorld.asm như sau: .386 .model flat,stdcall option casemap:none include \masm32\include\windows.inc include \masm32\include\kernel32.inc includelib DLLHelloWorld.lib includelib \masm32\lib\kernel32.lib HelloWorld PROTO .code start: invoke HelloWorld invoke ExitProcess,NULL end start Chép 2 file DLLHelloWorld.DLL và DLLHelloWorld.Lib vào thư mục của project HelloWorld: Nhấn CTRL + F5 để chạy chương trình, bạn được như sau: Reverse Engineering Association . Association Xây dựng thư viện liên kết động trong MASM Translated and Written by NhatPhuongLe www.reaonline.net Nó không cần phải import bất kỳ thư viện nhập. 8 Xây dựng thư viện liên kết động trong MASM Translated and Written by NhatPhuongLe www.reaonline.net Tạo một Project mới trong RadASM Assembler : masm