DLL injection

Trong lập trình máy tính, DLL injection (dịch: Tiêm chích DLL) là một kỹ thuật được sử dụng để chạy mã code trong không gian địa chỉ (en) của một 'tiến trình khác' thông qua cách ép nó tải một thư viện liên kết động. DLL injection hay được các 'chương trình bên ngoài' sử dụng để gây ảnh hưởng đến hành vi của một 'chương trình khác' theo cách mà tác giả của nó không hề tiên liệu hoặc dự định. Ví dụ, 'mã được tiêm' có thể móc (en) (hook) vào các lời gọi hàm hệ thống, hoặc đọc nội dung của các hộp văn bản mật khẩu, những điều vậy không thể được thực hiện theo cách thông thường. Một chương trình mà được sử dụng để tiêm 'mã tùy ý' vào các 'tiến trình tùy ý' thì được gọi là một DLL injector. DLL injection là một phương pháp gian lận phổ biến trong các trò chơi bởi việc chạy một DLL bên trong một chương trình cũng như việc có thể thực thi code vào trong game đều là những việc rất dễ dàng. Tuy nhiên, hầu hết các trò chơi đều tự bảo vệ chính mình khỏi DLL injection. Trên Microsoft Windows, có nhiều cách để ép một tiến trình phải nạp và thực thi code của một DLL, nằm ngoài ý định của tác giả phần mềm đó: Các DLL được liệt kê trong mục registry HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs đều được tải vào mọi tiến trình có tải User32.dll (en) trong lời gọi ban đầu tới DLL đó. Bắt đầu với Windows Vista, AppInit_DLL bị tắt theo mặc định. Bắt đầu với Windows 7, cơ sở hạ tầng của AppInit_DLL hỗ trợ code signing (en). Bắt đầu với Windows 8, toàn bộ tính chức năng của AppInit_DLL đều bị tắt nếu có bật Secure Boot, bất chấp code signing hoặc thiết lập registry. Các tệp DLL được liệt kê trong registry key HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\AppCertDLLs đều được nạp vào mọi tiến trình có gọi các hàm Win32 API là CreateProcess, CreateProcessAsUser, CreateProcessWithLogonW, CreateProcessWithTokenW và WinExec. Đấy là cách đúng để sử dụng DLL injection một cách hợp lẽ trên phiên bản Windows hiện tại – Windows 10. DLL phải được ký bằng một chứng chỉ hợp lệ. Các hàm để thao túng tiến trình như CreatRemoteThread hoặc các kỹ thuật code injection như AtomBombing, có thể được sử dụng để tiêm DLL vào một chương trình sau khi nó đã bắt đầu. Mở một handle (en) tới tiến trình mục tiêu. Điều này có thể được thực hiện bằng cách spawn (en) ra tiến trình đấy hoặc bằng cách lần nó dựa trên một đầu mối mà chắc chắn có tồn tại khi tiến trình đó chạy – ví dụ, một cửa sổ (en) có tiêu đề đễ dự đoán, hoặc bằng cách lấy danh sách các 'tiến trình đang chạy' và quét tìm 'tên tệp' của 'tệp thực thi mục tiêu'. Cấp phát bộ nhớ trong 'tiến trình mục tiêu', rồi ghi tên của 'DLL cần tiêm' vào bộ nhớ đó. Bước này có thể được bỏ qua nếu một 'tên DLL phù hợp' đã có sẵn trong tiến trình mục tiêu. Ví dụ: nếu một tiến trình mà có liên kết đến 'User32.dll (en)', 'GDI32.dll (en)', 'Kernel32.dll (en)' hoặc bất kỳ thư viện nào khác có tên kết thúc bằng ‘32.dll’, thì có thể tải thư viện có tên '32.dll'. Kỹ thuật này trong quá khứ đã được chứng minh là có hiệu quả để vượt qua hàng rào ngăn ngừa DLL injection của một tiến trình. Tạo một thread mới trong 'tiến trình mục tiêu' với 'địa chỉ bắt đầu' của thread được đặt thành địa chỉ của LoadLibrary và đối số được đặt thành địa chỉ của chuỗi văn bản vừa được ghi vào mục tiêu ở bước 2. Thay vì ghi tên của một 'DLL cần tải' vào mục tiêu và bắt đầu thread mới tại LoadLibrary, người ta có thể viết hẳn một 'mã-để-thực-thi' vào mục tiêu và bắt đầu thread tại mã đó. Hệ điều hành sau đó gọi vào thường trình sơ khởi (initialization (en) routine) của 'DLL được tiêm'. Lưu ý rằng nếu không có sự đề phòng, lối tiếp cận này có thể bị 'tiến trình mục tiêu' phát hiện do các tin báo DLL_THREAD_ATTACH được gửi đến mọi 'mô-đun đang chạy' ngay khi một thread bắt đầu. Các lời gọi hook (en) của Windows như SetWindowsHookEx. Sử dụng hàm SuspendThread hoặc NtSuspendThread để tạm đình chỉ tất cả các thread, sau đó sử dụng hàm SetThreadContext hoặc NtSetContextThread để sửa đổi ngữ cảnh của một thread đang tồn tại trong ứng dụng đó để thực thi 'mã được tiêm', từ đó có thể nạp DLL. Khai thác các 'hạn chế về thiết kế' trong Windows và các ứng dụng có gọi hàm LoadLibrary hoặc LoadLibraryEx mà không chỉ định 'đường dẫn hợp cách đầy đủ' (full-qualified path (en)) đến DLL được tải. Shim (en) ở mức hệ điều hành. Thay thế một 'DLL đặc thù ứng dụng' bằng một 'bản thay thế giả mạo' có bản thực hiện của các hàm được xuất (export) tương tự như bản gốc. Trên các hệ điều hành kiểu Unix với 'trình liên kết động' dựa trên ld.so (trên BSD) và ld-linux.so (trên Linux), các 'thư viện tùy ý' có thể được liên kết với một 'tiến trình mới' bằng cách đặt tên đường dẫn của thư viện đấy vào biến môi trường LD_PRELOAD (en), biến đó có thể được đặt trên toàn cục hoặc riêng lẻ cho một tiến trình. Ví dụ, trong bash, lệnh này khởi chạy lệnh "prog" với 'thư viện dùng chung' từ tệp "test.so", tệp này được liên kết với nó tại thời điểm khởi chạy: LD_PRELOAD="./test.so" prog Một thư viện như vậy có thể được tạo ra bởi GCC bằng cách biên dịch 'tập tin nguồn' (chứa các toàn cục mới để được liên kết) bằng các tùy chọn -fpic hoặc -fPIC, và liên kết bằng tùy chọn -shared. Thư viện đấy có quyền truy cập vào các 'symbol ngoại tại' được khai báo trong chương trình như mọi thư viện khác. Trên OS X, 'lệnh sau' khởi chạy lệnh "prog" với 'thư viện dùng chung' từ tệp "test.dylib" được liên kết với nó tại thời điểm khởi chạy: DYLD_INSERT_LIBRARIES="./test.dylib" DYLD_FORCE_FLAT_NAMESPACE=1 prog Cũng có thể sử dụng các kỹ thuật dựa trên debugger trên các hệ thống kiểu Unix. Trong hệ điều hành Windows, có một cách đơn giản để tiêm DLL vào tiến trình khác là sử dụng hàm API CreateRemoteThread. Phương pháp là tạo một thread vào tiến trình đấy từ xa, thread đó sẽ nạp 'DLL mong muốn' vào. Hàm CreateRemoteThread yêu cầu một 'con trỏ hàm (en)' trỏ tới một hàm nằm ở trong 'không gian địa chỉ' của tiến trình mục tiêu – tức là "phải có sẵn" một hàm có chức năng nạp 'DLL mong muốn' ở ngay tiến trình mục tiêu. Có một sự thật có thể được khai thác, đó là kernel32.dll được ánh xạ tới cùng một địa chỉ trong hầu hết tất cả các tiến trình. Do đó hàm LoadLibrary của kernel32.dll (có chức năng nạp DLL bất kỳ) cũng sẽ có cùng địa chỉ trong hầu hết tiến trình. Cùng với việc hầu hết các ứng dụng đều liên kết với kernel32.dll nên hàm LoadLibrary luôn có sẵn ở hầu hết các tiến trình. Vì vậy có thể dùng CreateRemoteThread tạo một thread bắt đầu tại hàm LoadLibrary với đối số là đường dẫn tới DLL cần nạp. Xem ví dụ bên dưới cho cụ thể: #include <windows.h> HANDLE inject_DLL(const char* file_name, int PID) { // lấy một handle tới tiến trình mục tiêu HANDLE h_process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID); // lấy đường dẫn đầy đủ tới file dll char fullDLLPath[_MAX_PATH]; GetFullPathName(file_name, _MAX_PATH, fullDLLPath, NULL); // cấp phát bộ nhớ tại tiến trình mục tiêu LPVOID DLLPath_addr = VirtualAllocEx(h_process, NULL, _MAX_PATH, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); // viết đường dẫn dll vào bộ nhớ đó WriteProcessMemory(h_process, DLLPath_addr, fullDLLPath, strlen(fullDLLPath), NULL); // lấy địa chỉ của LoadLibraryA (đều giống nhau ở mọi tiến trình) // để bật thread thực thi tại nó LPVOID LoadLib_addr = GetProcAddress(GetModuleHandle("Kernel32"), "LoadLibraryA"); // bắt đầu một thread thực thi từ xa tại LoadLibraryA // rồi truyền đường dẫn dll làm đối số HANDLE h_rThread = CreateRemoteThread(h_process, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLib_addr, DLLPath_addr, 0, NULL); // đợi cho đến khi thread đó hoàn thành WaitForSingleObject(h_rThread, INFINITE); // lấy 'giá trị trả về' của hàm LoadLibraryA nếu cần DWORD exit_code; GetExitCodeThread(h_rThread, &exit_code); // giải phóng handle của thread vừa mới được inject... CloseHandle(h_rThread); // ...và bộ nhớ được cấp phát cho đường dẫn DLL đấy VirtualFreeEx(h_process, DLLPath_addr, 0, MEM_RELEASE); // ...và handle cho tiến trình mục tiêu đấy CloseHandle(h_process); return (HANDLE)exit_code; } ^ James Shewmaker (2006). “Analyzing DLL Injection” (PDF). GSM Presentation. Bluenotch. Bản gốc (PDF) lưu trữ ngày 3 tháng 12 năm 2008. Truy cập ngày 31 tháng 8 năm 2008. ^ a b Iczelion (tháng 8 năm 2002). “Tutorial 24: Windows Hooks”. Iczelion's Win32 Assembly Homepage. Bản gốc lưu trữ ngày 1 tháng 8 năm 2008. Truy cập ngày 31 tháng 8 năm 2008. ^ Rocky, Pulley (ngày 19 tháng 5 năm 2005). “Extending Task Manager with DLL Injection”. CodeProject. CodeProject. Bản gốc lưu trữ ngày 6 tháng 2 năm 2009. Truy cập ngày 1 tháng 9 năm 2008. ^ a b Nasser R. Rowhani (ngày 23 tháng 10 năm 2003). “DLL Injection and function interception tutorial”. CodeProject. CodeProject. Bản gốc lưu trữ ngày 15 tháng 6 năm 2008. Truy cập ngày 31 tháng 8 năm 2008. ^ a b Ivo Ivanov (ngày 2 tháng 12 năm 2002). “API hooking revealed”. CodeProject. CodeProject. Bản gốc lưu trữ ngày 14 tháng 10 năm 2008. Truy cập ngày 31 tháng 8 năm 2008. ^ a b Robert Kuster (ngày 20 tháng 8 năm 2003). “Three Ways to Inject Your Code into Another Process”. CodeProject. CodeProject. Bản gốc lưu trữ ngày 20 tháng 7 năm 2008. Truy cập ngày 31 tháng 8 năm 2008. ^ “Working with the AppInit_DLLs registry value”. Microsoft Help and Support. Microsoft. ngày 21 tháng 11 năm 2006. Truy cập ngày 31 tháng 8 năm 2008. ^ Raymond Chen (ngày 13 tháng 12 năm 2007). “AppInit_DLLs should be renamed Deadlock_Or_Crash_Randomly_DLLs”. The Old New Thing. Microsoft. Truy cập ngày 31 tháng 8 năm 2008. ^ “dllmain.c”. ReactOS. ReactOS Foundation. ngày 8 tháng 7 năm 2008. Truy cập ngày 31 tháng 8 năm 2008.[liên kết hỏng] ^ AppInit_DLLs in Windows 7 and Windows Server 2008 R2 ^ “AppInit DLLs and Secure Boot”. MSDN. Truy cập ngày 29 tháng 3 năm 2016. ^ “'AtomBombing' Microsoft Windows Via Code Injection”. Dark Reading (bằng tiếng Anh). Truy cập ngày 20 tháng 4 năm 2017. ^ Trent Waddington. “InjectDLL”. Bản gốc lưu trữ ngày 30 tháng 12 năm 2019. Truy cập ngày 31 tháng 8 năm 2008. ^ “Dll Injection”. DreamInCode.net. MediaGroup1. ngày 4 tháng 5 năm 2006. Bản gốc lưu trữ ngày 2 tháng 9 năm 2008. Truy cập ngày 31 tháng 8 năm 2008. ^ Greg Jenkins (tháng 11 năm 2007). “DLL Injection Framework”. Ring3 Circus. WordPress. Truy cập ngày 31 tháng 8 năm 2008. ^ Drew Benton (ngày 17 tháng 8 năm 2007). “A More Complete DLL Injection Solution Using CreateRemoteThread”. CodeProject. CodeProject. Truy cập ngày 1 tháng 9 năm 2008. ^ “CreateProcess”. Platform SDK for Windows XP SP2. Microsoft. Truy cập ngày 31 tháng 8 năm 2008. ^ “PROCESS_INFORMATION”. Platform SDK for Windows XP SP2. Microsoft. Truy cập ngày 31 tháng 8 năm 2008. ^ “FindWindow WinAPI”. MSDN. ^ “GetWindowThreadProcessId Function”. Platform SDK for Windows XP SP2. Microsoft. Truy cập ngày 31 tháng 8 năm 2008. ^ “EnumProcesses”. Platform SDK for Windows XP SP2. Microsoft. Truy cập ngày 31 tháng 8 năm 2008. ^ “GetModuleBaseName”. Platform SDK for Windows XP SP2. Microsoft. Truy cập ngày 31 tháng 8 năm 2008. ^ “VirtualAllocEx”. Platform SDK for Windows XP SP2. Microsoft. Truy cập ngày 31 tháng 8 năm 2008. ^ “WriteProcessMemory”. Platform SDK for Windows XP SP2. Microsoft. Truy cập ngày 31 tháng 8 năm 2008. ^ “Outpost Bypassing Self-Protection via Advanced DLL injection with handle stealing Vulnerability”. Matousec. ngày 1 tháng 12 năm 2006. Truy cập ngày 31 tháng 8 năm 2008. ^ “CreateRemoteThread”. Platform SDK for Windows XP SP2. Microsoft. Truy cập ngày 31 tháng 8 năm 2008. ^ “LoadLibrary”. Platform SDK for Windows XP SP2. Microsoft. Truy cập ngày 31 tháng 8 năm 2008. ^ “DllMain”. Platform SDK for Windows XP SP2. Microsoft. Truy cập ngày 31 tháng 8 năm 2008. ^ “SetWindowsHookEx Function”. Platform SDK for Windows XP SP2. Microsoft. Truy cập ngày 31 tháng 8 năm 2008. ^ “AppInit_DLLs Registry Value and Windows 95”. Microsoft Help and Support. Microsoft. ngày 1 tháng 3 năm 2005. Truy cập ngày 31 tháng 8 năm 2008. ^ “Dll Injection using SetWindowsHookEx() Method”. Game Reversal. ngày 3 tháng 4 năm 2008. Truy cập ngày 1 tháng 9 năm 2008. ^ “SetThreadContext DLL Injection”. ngày 16 tháng 1 năm 2007. Truy cập ngày 1 tháng 9 năm 2008. ^ Ben Botto (ngày 6 tháng 9 năm 2008). “DLL Injector”. Bản gốc lưu trữ ngày 7 tháng 2 năm 2009. Truy cập ngày 1 tháng 9 năm 2008. ^ “Insecure Library Loading Could Allow Remote Code Execution”. Microsoft. ngày 10 tháng 6 năm 2011. Truy cập ngày 20 tháng 4 năm 2016. ^ “Secure loading of libraries to prevent DLL preloading attacks”. Microsoft. ngày 10 tháng 6 năm 2011. Truy cập ngày 8 tháng 8 năm 2012. ^ “Microsoft Security Advisory: Insecure library loading could allow remote code execution”. Microsoft. ngày 10 tháng 6 năm 2011. Truy cập ngày 20 tháng 4 năm 2016. ^ Nicolas Falliere (ngày 26 tháng 9 năm 2010). “Stuxnet Infection of Step 7 Projects”. Symantec. ^ Linus Torvalds; David Engel; Eric Youngdale; Peter MacDonald; Hongjiu Lu; Lars Wirzenius; Mitch D'Souza (ngày 14 tháng 3 năm 1998). “ld.so/ld-linux.so – dynamic linker/loader”. UNIX man pages. Bản gốc lưu trữ ngày 6 tháng 2 năm 2009. Truy cập ngày 31 tháng 8 năm 2008. ^ “Code Gen Options”. Using the GNU Compiler Collection (GCC). Free Software Foundation. Truy cập ngày 31 tháng 8 năm 2008. -fpic Generate position-independent code (PIC) suitable for use in a shared library, if supported for the target machine. sqq. ^ “Link Options”. Using the GNU Compiler Collection (GCC). Free Software Foundation. Truy cập ngày 31 tháng 8 năm 2008. -shared Produce a shared object which can then be linked with other objects to form an executable. sqq. ^ “The LD_PRELOAD trick”. Peter Goldsborough. Truy cập ngày 17 tháng 5 năm 2017. ^ Gregory Shpitalnik (ngày 12 tháng 2 năm 2009). “Code Injection into Running Linux Application”. Code Project. Bản gốc lưu trữ ngày 12 tháng 6 năm 2010. Truy cập ngày 18 tháng 11 năm 2010.