What is a DLL?

DLL export các hàm để có thể được sử dụng bởi một tiến trình. File DLL không thể được thực thi giống file EXE. Thay vào đó, nó cần phải được gọi bởi một tiến trình để thực thi. Ví dụ, file kernel32.dll export hàm CreateFileW nên nếu tiến trình nào sử dụng hàm này thì nó cần phải nạp file kernel32.dll vào bộ nhớ của nó.

Một vài DLL được nạp vào bộ nhớ tự động do các hàm mà các DLL đó export cần thiết cho việc thực thi của tiến trình một cách bình thường. Ví dụ: ntdll.dll, kernel32.dllkernelbase.dll.

Hình dưới minh họa danh sách các DLL mà chương trình explorer.exe sử dụng:

System-Wide DLL Base Address

Hệ điều hành Windows sử dụng cùng một địa chỉ vùng nhớ cho một vài DLL hệ thống và có thể được sử dụng bởi tất cả các tiến trình có trên máy. Điều này là để tối ưu hóa bộ nhớ và cải thiện hiệu năng. Hình bên dưới minh họa cho việc kernel32.dll được nạp lên tại cùng một địa chỉ vùng nhớ (0x7fff9fad0000) ở nhiều tiến trình khác nhau.

DLL Entry Point

Các DLL có thể chỉ định một hàm đầu vào (entry point function) khi một sự kiện nào đó xảy ra:

  • DLL_PROCESS_ATTACHED - một tiến trình đang nạp DLL.
  • DLL_THREAD_ATTACHED - một tiến trình đang tạo một thread.
  • DLL_THREAD_DETACH - một thread kết thúc một cách bình thường.
  • DLL_PROCESS_DETACH - một tiến trình unload DLL.

Đoạn code sau minh họa cho cấu trúc code của một DLL:

BOOL APIENTRY DllMain(
    HANDLE hModule,             // Handle to DLL module
    DWORD ul_reason_for_call,   // Reason for calling function
    LPVOID lpReserved           // Reserved
) {
    
    switch (ul_reason_for_call) {
        case DLL_PROCESS_ATTACHED: // A process is loading the DLL.
        // Do something here
        break;
        case DLL_THREAD_ATTACHED: // A process is creating a new thread.
        // Do something here
        break;
        case DLL_THREAD_DETACH: // A thread exits normally.
        // Do something here
        break;
        case DLL_PROCESS_DETACH: // A process unloads the DLL.
        // Do something here
        break;
    }
    return TRUE;
}

Exporting a Function

Để export một function, chúng ta cần sử dụng từ khóa extern__declspec(dllexport). Ví dụ:

////// sampleDLL.dll //////
 
extern __declspec(dllexport) void HelloWorld(){
// Function code here
}

Dynamic Linking

Việc nạp và liên kết code (các DLL) trong quá trình thực thi sử dụng linker và import address table (IAT) thay vì liên kết trong quá trình biên dịch được gọi là liên kết động (dynamic linking).

Việc gọi một hàm chẳng hạn như MessageBoxA sẽ khiến cho hệ điều hành nạp DLL mà có export hàm này vào vùng nhớ của tiến trình gọi hàm. Cụ thể hơn, user32.dll sẽ được nạp và quá trình này được thực hiện tự động bởi hệ điều hành khi tiến trình bắt đầu chạy.

Giả sử ta có file sampleDLL.dll export hàm sau:

////// sampleDLL.dll //////
 
extern __declspec(dllexport) void HelloWorld(){
// Function code here
}

Loading a DLL

Trong trường hợp sampleDLL.dll chưa có ở trong vùng nhớ của tiến trình, ta có thể nạp vào bằng hàm LoadLibrary như sau:

HMODULE hModule = LoadLibraryA("sampleDLL.dll"); // hModule now contain sampleDLL.dll's handle

Có thể thấy, giá trị trả về của LoadLibraryA là một HMODULE.

Retrieving a DLL’s Handle

Trong trường hợp sampleDLL.dll đã được nạp vào vùng nhớ thì có thể dùng hàm GetModuleHandle để lấy ra handle của DLL như sau:

HMODULE hModule = GetModuleHandleA("sampleDLL.dll");

Retrieving a Function’s Address

Để sử dụng hàm khi DLL đã ở trong vùng nhớ của tiến trình thì cần lấy ra con trỏ chứa địa chỉ của hàm bằng GetProcAddress:

PVOID pHelloWorld = GetProcAddress(hModule, "HelloWorld");

Invoking The Function

Cuối cùng, ta cần ép kiểu cho con trỏ chứa địa chỉ hàm về kiểu con trỏ hàm (Function Pointer) của hàm HelloWorld để có thể gọi hàm:

// Constructing a new data type that represents HelloWorld's function pointer 
typedef void (WINAPI* HelloWorldFunctionPointer)();  
 
void call(){
    HMODULE hModule = LoadLibraryA("sampleDLL.dll");
    
    PVOID pHelloWorld = GetProcAddress(hModule, "HelloWorld");
    
    // Type-casting the 'pHelloWorld' variable to be of type 'HelloWorldFunctionPointer' 
    HelloWorldFunctionPointer HelloWorld = (HelloWorldFunctionPointer)pHelloWorld;
    
    HelloWorld();   // Calling the 'HelloWorld' function via its function pointer 
}

Một ví dụ khác:

typedef int (WINAPI* MessageBoxAFunctionPointer)( // Constructing a new data type, that will represent MessageBoxA's function pointer 
  HWND          hWnd,
  LPCSTR        lpText,
  LPCSTR        lpCaption,
  UINT          uType
);
 
void call(){
    // Retrieving MessageBox's address, and saving it to 'pMessageBoxA' (MessageBoxA's function pointer)
    MessageBoxAFunctionPointer pMessageBoxA = (MessageBoxAFunctionPointer)GetProcAddress(LoadLibraryA("user32.dll"), "MessageBoxA");
    if (pMessageBoxA != NULL){
        // Calling MessageBox via its function pointer if not null    
        pMessageBoxA(NULL, "MessageBox's Text", "MessageBox's Caption", MB_OK); 
    }
}

Ví dụ trên giả sử user32.dll mà export hàm MessageBoxA chưa được nạp vào vùng nhớ của tiến trình.

rundll32.exe

Chúng ta cũng có thể chạy những hàm được export bởi các DLL mà không cần lập trình. Cụ thể, ta có thể dùng rundll32.exe để gọi hàm của DLL như sau:

rundll32.exe <dllname>, <function exported to run>

Ví dụ, để gọi hàm LockWorkStation của User32.dll để khóa màn hình, ta có thể dùng lệnh sau:

rundll32.exe user32.dll,LockWorkStation

Creating a DLL File With Visual Studio

Khi tạo một project có template là DLL, chúng ta sẽ được cung cấp các file là: framework.hpch.h and pch.cpp. Các file này là các precompiled header, được sử dụng để tối ưu việc biên dịch cho các project lớn. Đối với các project nhỏ, chúng ta có thể xóa chúng đi.

Sau đó, ta cần thiết lập project sao cho nó không sử dụng các precompiled header. Để thiết lập, chọn Properties > C/C++ > Precompiled Headers và thay đổi ‘Precompiled Header’ thành ‘Not Using Precompiled Headers’ rồi nhấn ‘Apply’.

Note

Toàn bộ code trong khóa học lập trình mã độc đều được viết bằng C nên ta cần phải đổi đuôi mã nguồn từ .cpp thành .c.

list
from outgoing([[MalDev - Dynamic Linking Library (DLL)]])
sort file.ctime asc

Resources