平常用的最多的dll注入技術就是遠程線程,剛剛逛看雪,看到有人寫的面試的時候被問到的問題,其中就有dll注入的方法,我突然想到我開始面試的時候也被問了dll注入的方法,當時也是就只知道一個遠程線程,答的也不好,然后就想把一些注入技術寫個總結。接下來講的注入技術,有ring3層的lld的遠程線程和apc注入,還有ring0的apc注入,此外還有更為隱蔽的代碼注入。
先寫最廣泛的,也是相對簡單的注入方式,遠程線程。
一 ring3 dll的遠程線程
我寫的涉及到x86和x64的注入,因為x64的系統本身增加了較多權限的校驗,需要進行提權處理。所以先進行系統版本的校驗:
typedef enum _WIN_VERSION { WindowsNT, Windows2000, WindowsXP, Windows2003, WindowsVista, Windows7, Windows8, WinUnknown }WIN_VERSION; WIN_VERSION GetWindowsVersion() { OSVERSIONINFOEX OsVerInfoEx; OsVerInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); GetVersionEx((OSVERSIONINFO *)&OsVerInfoEx); // 注意轉換類型 switch (OsVerInfoEx.dwPlatformId) { case VER_PLATFORM_WIN32_NT: { if (OsVerInfoEx.dwMajorVersion <= 4 ) { return WindowsNT; } if (OsVerInfoEx.dwMajorVersion == 5 && OsVerInfoEx.dwMinorVersion == 0) { return Windows2000; } if (OsVerInfoEx.dwMajorVersion == 5 && OsVerInfoEx.dwMinorVersion == 1) { return WindowsXP; } if (OsVerInfoEx.dwMajorVersion == 5 && OsVerInfoEx.dwMinorVersion == 2) { return Windows2003; } if (OsVerInfoEx.dwMajorVersion == 6 && OsVerInfoEx.dwMinorVersion == 0) { return WindowsVista; } if (OsVerInfoEx.dwMajorVersion == 6 && OsVerInfoEx.dwMinorVersion == 1) { return Windows7; } if (OsVerInfoEx.dwMajorVersion == 6 && OsVerInfoEx.dwMinorVersion == 2 ) { return Windows8; } break; } default: { return WinUnknown; } } }
x86和x64的注入最主要的不同點就是對於x64的提權處理,接下來先講x64的提權處理。
x64的提權主要就是用到了ntdll.dll中未導出的一個函數,RtlAdjustPrivilege(),這個函數的作用是很大的,之前的瞬間關機的代碼就是用了這個函數進行提權。
/* .常量 SE_BACKUP_PRIVILEGE, "17", 公開 .常量 SE_RESTORE_PRIVILEGE, "18", 公開 .常量 SE_SHUTDOWN_PRIVILEGE, "19", 公開 .常量 SE_DEBUG_PRIVILEGE, "20", 公開 */
我們提升的權限就是SE_DEBUG_PRIVILEGE 20
//程序編譯成64位可以注入64位 編譯成32位可以注入32位 CWinApp theApp; typedef enum _WIN_VERSION { WindowsNT, Windows2000, WindowsXP, Windows2003, WindowsVista, Windows7, Windows8, WinUnknown }WIN_VERSION; VOID InjectDll(ULONG_PTR ProcessID); WIN_VERSION GetWindowsVersion(); BOOL InjectDllByRemoteThread32(const TCHAR* wzDllFile, ULONG_PTR ProcessId); WIN_VERSION WinVersion = WinUnknown; BOOL InjectDllByRemoteThread64(const TCHAR* wzDllFile, ULONG_PTR ProcessId); typedef long (__fastcall *pfnRtlAdjustPrivilege64)(ULONG,ULONG,ULONG,PVOID); pfnRtlAdjustPrivilege64 RtlAdjustPrivilege; int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) { cout<<"查看要注入進程的ID"<<endl; ULONG_PTR ProcessID = 0; WinVersion = GetWindowsVersion(); printf("Input ProcessID\r\n"); cin>>ProcessID; InjectDll(ProcessID); return 0; } VOID InjectDll(ULONG_PTR ProcessID) { CString strPath32 = L"MessageBox32.dll"; //32位dll注入32位系統 CString strPath64 = L"MessageBox64.dll"; if (ProcessID == 0) { return; } if (PathFileExists(strPath32)&&PathFileExists(strPath64)) { switch(WinVersion) { case Windows7: //這里用的是Win7 x64 sp1 { WCHAR wzPath[MAX_PATH] = {0}; GetCurrentDirectory(260,wzPath); wcsncat_s(wzPath, L"\\", 2); wcsncat_s(wzPath, strPath64.GetBuffer(), strPath64.GetLength());//dll完整路徑 strPath32.ReleaseBuffer(); if (!InjectDllByRemoteThread64(wzPath,ProcessID)) printf("Inject Fail\r\n"); else printf ("Inject Success\r\n"); break; } case WindowsXP: //WinXp x86 sp3 { WCHAR wzPath[MAX_PATH] = {0}; GetCurrentDirectory(260,wzPath); wcsncat_s(wzPath, L"\\", 2); wcsncat_s(wzPath, strPath32.GetBuffer(), strPath32.GetLength()); strPath32.ReleaseBuffer(); if (!InjectDllByRemoteThread32(wzPath,ProcessID)) printf("Inject Fail\r\n"); else printf("Inject Success\r\n"); break; } } } } BOOL InjectDllByRemoteThread64(const TCHAR* wzDllFile, ULONG_PTR ProcessId) { if (NULL == wzDllFile || 0 == ::_tcslen(wzDllFile) || ProcessId == 0 || -1 == _taccess(wzDllFile, 0)) { return FALSE; } HANDLE hProcess = NULL; HANDLE hThread = NULL; DWORD dwRetVal = 0; LPTHREAD_START_ROUTINE FuncAddress = NULL; DWORD dwSize = 0; TCHAR* VirtualAddress = NULL; //預編譯,支持Unicode #ifdef _UNICODE FuncAddress = (PTHREAD_START_ROUTINE)::GetProcAddress(::GetModuleHandle(_T("Kernel32")), "LoadLibraryW"); #else FuncAddress = (PTHREAD_START_ROUTINE)::GetProcAddress(::GetModuleHandle(_T("Kernel32")), "LoadLibraryA"); #endif if (FuncAddress==NULL) { return FALSE; } RtlAdjustPrivilege=(pfnRtlAdjustPrivilege64)GetProcAddress((HMODULE)(FuncAddress(L"ntdll.dll")),"RtlAdjustPrivilege"); if (RtlAdjustPrivilege==NULL) { return FALSE; } /* .常量 SE_BACKUP_PRIVILEGE, "17", 公開 .常量 SE_RESTORE_PRIVILEGE, "18", 公開 .常量 SE_SHUTDOWN_PRIVILEGE, "19", 公開 .常量 SE_DEBUG_PRIVILEGE, "20", 公開 */ RtlAdjustPrivilege(20,1,0,&dwRetVal); //19 hProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE, ProcessId); if (NULL == hProcess) { printf("Open Process Fail\r\n"); return FALSE; } // 在目標進程中分配內存空間 dwSize = (DWORD)::_tcslen(wzDllFile) + 1; VirtualAddress = (TCHAR*)::VirtualAllocEx(hProcess, NULL, dwSize * sizeof(TCHAR), MEM_COMMIT, PAGE_READWRITE); if (NULL == VirtualAddress) { printf("Virtual Process Memory Fail\r\n"); CloseHandle(hProcess); return FALSE; } // 在目標進程的內存空間中寫入所需參數(模塊名) if (FALSE == ::WriteProcessMemory(hProcess, VirtualAddress, (LPVOID)wzDllFile, dwSize * sizeof(TCHAR), NULL)) { printf("Write Data Fail\r\n"); VirtualFreeEx(hProcess, VirtualAddress, dwSize, MEM_DECOMMIT); CloseHandle(hProcess); return FALSE; } hThread = ::CreateRemoteThread(hProcess, NULL, 0, FuncAddress, VirtualAddress, 0, NULL); if (NULL == hThread) { printf("CreateRemoteThread Fail\r\n"); VirtualFreeEx(hProcess, VirtualAddress, dwSize, MEM_DECOMMIT); CloseHandle(hProcess); return FALSE; } // 等待遠程線程結束 WaitForSingleObject(hThread, INFINITE); // 清理資源 VirtualFreeEx(hProcess, VirtualAddress, dwSize, MEM_DECOMMIT); CloseHandle(hThread); CloseHandle(hProcess); return TRUE; } BOOL InjectDllByRemoteThread32(const TCHAR* wzDllFile, ULONG_PTR ProcessId) { // 參數無效 if (NULL == wzDllFile || 0 == ::_tcslen(wzDllFile) || ProcessId == 0 || -1 == _taccess(wzDllFile, 0)) { return FALSE; } HANDLE hProcess = NULL; HANDLE hThread = NULL; DWORD dwSize = 0; TCHAR* VirtualAddress = NULL; LPTHREAD_START_ROUTINE FuncAddress = NULL; // 獲取目標進程句柄 hProcess = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE, FALSE, ProcessId); if (NULL == hProcess) { printf("Open Process Fail\r\n"); return FALSE; } // 在目標進程中分配內存空間 dwSize = (DWORD)::_tcslen(wzDllFile) + 1; VirtualAddress = (TCHAR*)::VirtualAllocEx(hProcess, NULL, dwSize * sizeof(TCHAR), MEM_COMMIT, PAGE_READWRITE); if (NULL == VirtualAddress) { printf("Virtual Process Memory Fail\r\n"); CloseHandle(hProcess); return FALSE; } // 在目標進程的內存空間中寫入所需參數(模塊名) if (FALSE == ::WriteProcessMemory(hProcess, VirtualAddress, (LPVOID)wzDllFile, dwSize * sizeof(TCHAR), NULL)) { printf("Write Data Fail\r\n"); VirtualFreeEx(hProcess, VirtualAddress, dwSize, MEM_DECOMMIT); CloseHandle(hProcess); return FALSE; } // 從 Kernel32.dll 中獲取 LoadLibrary 函數地址 #ifdef _UNICODE FuncAddress = (PTHREAD_START_ROUTINE)::GetProcAddress(::GetModuleHandle(_T("Kernel32")), "LoadLibraryW"); #else FuncAddress = (PTHREAD_START_ROUTINE)::GetProcAddress(::GetModuleHandle(_T("Kernel32")), "LoadLibraryA"); #endif if (NULL == FuncAddress) { printf("Get LoadLibrary Fail\r\n"); VirtualFreeEx(hProcess, VirtualAddress, dwSize, MEM_DECOMMIT); CloseHandle(hProcess); return false; } // 創建遠程線程調用 LoadLibrary hThread = ::CreateRemoteThread(hProcess, NULL, 0, FuncAddress, VirtualAddress, 0, NULL); if (NULL == hThread) { printf("CreateRemoteThread Fail\r\n"); VirtualFreeEx(hProcess, VirtualAddress, dwSize, MEM_DECOMMIT); CloseHandle(hProcess); return FALSE; } // 等待遠程線程結束 WaitForSingleObject(hThread, INFINITE); // 清理 VirtualFreeEx(hProcess, VirtualAddress, dwSize, MEM_DECOMMIT); CloseHandle(hThread); CloseHandle(hProcess); return TRUE; } WIN_VERSION GetWindowsVersion() { OSVERSIONINFOEX OsVerInfoEx; OsVerInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); GetVersionEx((OSVERSIONINFO *)&OsVerInfoEx); // 注意轉換類型 switch (OsVerInfoEx.dwPlatformId) { case VER_PLATFORM_WIN32_NT: { if (OsVerInfoEx.dwMajorVersion <= 4 ) { return WindowsNT; } if (OsVerInfoEx.dwMajorVersion == 5 && OsVerInfoEx.dwMinorVersion == 0) { return Windows2000; } if (OsVerInfoEx.dwMajorVersion == 5 && OsVerInfoEx.dwMinorVersion == 1) { return WindowsXP; } if (OsVerInfoEx.dwMajorVersion == 5 && OsVerInfoEx.dwMinorVersion == 2) { return Windows2003; } if (OsVerInfoEx.dwMajorVersion == 6 && OsVerInfoEx.dwMinorVersion == 0) { return WindowsVista; } if (OsVerInfoEx.dwMajorVersion == 6 && OsVerInfoEx.dwMinorVersion == 1) { return Windows7; } if (OsVerInfoEx.dwMajorVersion == 6 && OsVerInfoEx.dwMinorVersion == 2 ) { return Windows8; } break; } default: { return WinUnknown; } } }
二 ring3 apc注入
APC注入的原理是利用當線程被喚醒時APC中的注冊函數會被執行的機制,並以此去執行我們的DLL加載代碼,進而完成DLL注入的目的,其具體流程如下:
1)當EXE里某個線程執行到SleepEx()或者WaitForSingleObjectEx()時,系統就會產生一個軟中斷。
2)當線程再次被喚醒時,此線程會首先執行APC隊列中的被注冊的函數。
3)利用QueueUserAPC()這個API可以在軟中斷時向線程的APC隊列插入一個函數指針,如果我們插入的是Loadlibrary()執行函數的話,就能達到注入DLL的目的。
#define _WIN32_WINNT 0x0400 #include <windows.h> #include <TlHelp32.h> #include <iostream> #include <string> using namespace std; #define DEF_BUF_SIZE 1024 typedef long (__fastcall *pfnRtlAdjustPrivilege64)(ULONG,ULONG,ULONG,PVOID); pfnRtlAdjustPrivilege64 RtlAdjustPrivilege; // 用於存儲注入模塊DLL的路徑全名 char szDllPath[DEF_BUF_SIZE] = {0} ; // 使用APC機制向指定ID的進程注入模塊 BOOL InjectModuleToProcessById ( DWORD dwProcessId ) { DWORD dwRet = 0 ; BOOL bStatus = FALSE ; LPVOID lpData = NULL ; UINT uLen = strlen(szDllPath) + 1; #ifdef _WIN64 // x64 OpenProcess提權操作 RtlAdjustPrivilege=(pfnRtlAdjustPrivilege64)GetProcAddress((HMODULE)(FuncAddress(L"ntdll.dll")),"RtlAdjustPrivilege"); if (RtlAdjustPrivilege==NULL) { return FALSE; } /* .常量 SE_BACKUP_PRIVILEGE, "17", 公開 .常量 SE_RESTORE_PRIVILEGE, "18", 公開 .常量 SE_SHUTDOWN_PRIVILEGE, "19", 公開 .常量 SE_DEBUG_PRIVILEGE, "20", 公開 */ RtlAdjustPrivilege(20,1,0,&dwRetVal); //19 #endif // 打開目標進程 HANDLE hProcess = OpenProcess ( PROCESS_ALL_ACCESS, FALSE, dwProcessId ) ; if ( hProcess ) { // 分配空間 lpData = VirtualAllocEx ( hProcess, NULL, uLen, MEM_COMMIT, PAGE_EXECUTE_READWRITE ) ; if ( lpData ) { // 寫入需要注入的模塊路徑全名 bStatus = WriteProcessMemory ( hProcess, lpData, szDllPath, uLen, &dwRet ) ; } CloseHandle ( hProcess ) ; } if ( bStatus == FALSE ) return FALSE ; // 創建線程快照 THREADENTRY32 te32 = { sizeof(THREADENTRY32) } ; HANDLE hThreadSnap = CreateToolhelp32Snapshot ( TH32CS_SNAPTHREAD, 0 ) ; if ( hThreadSnap == INVALID_HANDLE_VALUE ) return FALSE ; bStatus = FALSE ; // 枚舉所有線程 if ( Thread32First ( hThreadSnap, &te32 ) ) { do{ // 判斷是否目標進程中的線程 if ( te32.th32OwnerProcessID == dwProcessId ) { // 打開線程 HANDLE hThread = OpenThread ( THREAD_ALL_ACCESS, FALSE, te32.th32ThreadID ) ; if ( hThread ) { // 向指定線程添加APC DWORD dwRet = QueueUserAPC ( (PAPCFUNC)LoadLibraryA, hThread, (ULONG_PTR)lpData ) ; if ( dwRet > 0 ) bStatus = TRUE ; CloseHandle ( hThread ) ; } } }while ( Thread32Next ( hThreadSnap, &te32 ) ) ; } CloseHandle ( hThreadSnap ) ; return bStatus; } int _tmain(int argc, _TCHAR* argv[]) { // 取得當前工作目錄路徑 GetCurrentDirectoryA ( DEF_BUF_SIZE, szDllPath ) ; // 生成注入模塊DLL的路徑全名 strcat ( szDllPath, "\\DLLSample.dll" ) ; DWORD dwProcessId = 0 ; // 接收用戶輸入的目標進程ID while ( cout << "請輸入目標進程ID:" && cin >> dwProcessId && dwProcessId > 0 ) { BOOL bRet = InjectModuleToProcessById ( dwProcessId ) ; cout << (bRet ? "注入成功!":"注入失敗!") << endl ; } return 0; }
三ring3層的遠程線程的代碼注入
代碼是成功,但是每次運行就explorer直接崩潰。我開始還以為是我程序寫的有問題,后來用WinDbg Attach到explorer進程調試,才發現問題,就是函數調用的問題,在匯編中call調用函數時不是絕對地址,機器碼是相對偏移,這就涉及到函數的便宜重新重定位的問題,得不償失,代價太大。僅僅以學習為目的的話,可以考慮用0day中非常經典的類似於GetProcAddress()函數實現的191個字節的shellcode來獲取函數地址。先放上代碼吧,在Windbg中對explorer中的RemoteCodeAddr下斷點,是可以執行到那的,然后執行到call MessageBox就崩潰,就是函數的偏移的問題。
#include "stdafx.h" #include <iostream> #include <Windows.h> using namespace std; typedef long (__fastcall *pfnRtlAdjustPrivilege64)(ULONG,ULONG,ULONG,PVOID); pfnRtlAdjustPrivilege64 RtlAdjustPrivilege; static DWORD WINAPI MyFunc (LPVOID pData) { //do something //... //pData輸入項可以是任何類型值 // ::MessageBoxA(NULL,(char*)pData,"INJECT",0); //不能直接調用 //調用函數時要考慮call RVA 都是相對的偏移, return *(DWORD*)pData; } static void AfterMyFunc (void) { } int _tmain(int argc, _TCHAR* argv[]) { DWORD cbCodeSize = (ULONG_PTR)AfterMyFunc-(ULONG_PTR)MyFunc +0x100; DWORD ProcessId = 0; cin>>ProcessId; #ifdef _WIN64 // x64 OpenProcess提權操作 RtlAdjustPrivilege=(pfnRtlAdjustPrivilege64)GetProcAddress((HMODULE)(GetModuleHandle(L"ntdll.dll")),"RtlAdjustPrivilege"); if (RtlAdjustPrivilege==NULL) { return FALSE; } /* .常量 SE_BACKUP_PRIVILEGE, "17", 公開 .常量 SE_RESTORE_PRIVILEGE, "18", 公開 .常量 SE_SHUTDOWN_PRIVILEGE, "19", 公開 .常量 SE_DEBUG_PRIVILEGE, "20", 公開 */ DWORD dwReturnVal; RtlAdjustPrivilege(20,1,0,&dwReturnVal); //19 #endif HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE, ProcessId); //申請放置代碼的內存 PVOID RemoteCodeAddr = (PVOID)VirtualAllocEx( hProcess, 0, cbCodeSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); WriteProcessMemory( hProcess, RemoteCodeAddr, &MyFunc, cbCodeSize, NULL); char szBuffer[] = "HelloWorld"; PVOID RemoteDataAddr = (PVOID)VirtualAllocEx( hProcess, 0, sizeof(szBuffer), MEM_COMMIT, PAGE_READWRITE ); WriteProcessMemory( hProcess, RemoteDataAddr, szBuffer, sizeof(szBuffer), NULL); HANDLE hThread = ::CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE) RemoteCodeAddr, RemoteDataAddr, 0 , NULL); DWORD h; if (hThread) { ::WaitForSingleObject( hThread, INFINITE ); ::CloseHandle( hThread ); } //cout<<dwSizeOfMyFunc<<endl; return 0; }
四ring0的apc注入
#include "stdafx.h" #include <iostream> #include <Windows.h> #include <WinIoCtl.h> using namespace std; #define CTL_KEINJECTAPC \ CTL_CODE(FILE_DEVICE_UNKNOWN,0x830,METHOD_BUFFERED,FILE_ANY_ACCESS) typedef struct _INJECT_INFO { ULONG ProcessId; wchar_t DllName[1024]; }INJECT_INFO,*PINJECT_INFO; int _tmain(int argc, _TCHAR* argv[]) { HANDLE hFile; INJECT_INFO InjectInfo; hFile=CreateFile(L"\\\\.\\DriverLink", GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,OPEN_EXISTING,0,NULL); if(hFile==INVALID_HANDLE_VALUE) { printf("\nError: Unable to connect to the driver (%d)\n",GetLastError()); return -1; } memset(&InjectInfo,0,sizeof(INJECT_INFO)); scanf("%d",&(InjectInfo.ProcessId)); wscanf(L"%s",InjectInfo.DllName); DWORD dwReturnSize = 0; DWORD dwRet = 0; dwRet = DeviceIoControl(hFile,CTL_KEINJECTAPC, // &InjectInfo, sizeof(INJECT_INFO), NULL, NULL, &dwReturnSize, NULL); CloseHandle(hFile); return 0; } #include <ntifs.h> #include <devioctl.h> #include <ntimage.h> #endif #define DEVICE_NAME L"\\Device\\DriverDevice" #define LINK_NAME L"\\DosDevices\\DriverLink" #define CTL_KEINJECTAPC \ CTL_CODE(FILE_DEVICE_UNKNOWN,0x830,METHOD_BUFFERED,FILE_ANY_ACCESS) typedef struct _SYSTEM_THREAD_INFORMATION { LARGE_INTEGER KernelTime; LARGE_INTEGER UserTime; LARGE_INTEGER CreateTime; ULONG WaitTime; PVOID StartAddress; CLIENT_ID ClientId; KPRIORITY Priority; LONG BasePriority; ULONG ContextSwitches; ULONG ThreadState; KWAIT_REASON WaitReason; }SYSTEM_THREAD_INFORMATION,*PSYSTEM_THREAD_INFORMATION; typedef struct _SYSTEM_PROCESS_INFO { ULONG NextEntryOffset; ULONG NumberOfThreads; LARGE_INTEGER WorkingSetPrivateSize; ULONG HardFaultCount; ULONG NumberOfThreadsHighWatermark; ULONGLONG CycleTime; LARGE_INTEGER CreateTime; LARGE_INTEGER UserTime; LARGE_INTEGER KernelTime; UNICODE_STRING ImageName; KPRIORITY BasePriority; HANDLE UniqueProcessId; HANDLE InheritedFromUniqueProcessId; ULONG HandleCount; ULONG SessionId; ULONG_PTR UniqueProcessKey; SIZE_T PeakVirtualSize; SIZE_T VirtualSize; ULONG PageFaultCount; SIZE_T PeakWorkingSetSize; SIZE_T WorkingSetSize; SIZE_T QuotaPeakPagedPoolUsage; SIZE_T QuotaPagedPoolUsage; SIZE_T QuotaPeakNonPagedPoolUsage; SIZE_T QuotaNonPagedPoolUsage; SIZE_T PagefileUsage; SIZE_T PeakPagefileUsage; SIZE_T PrivatePageCount; LARGE_INTEGER ReadOperationCount; LARGE_INTEGER WriteOperationCount; LARGE_INTEGER OtherOperationCount; LARGE_INTEGER ReadTransferCount; LARGE_INTEGER WriteTransferCount; LARGE_INTEGER OtherTransferCount; SYSTEM_THREAD_INFORMATION Threads[1]; }SYSTEM_PROCESS_INFO,*PSYSTEM_PROCESS_INFO; typedef struct _LDR_DATA_TABLE_ENTRY { LIST_ENTRY InLoadOrderLinks; LIST_ENTRY InMemoryOrderLinks; LIST_ENTRY InInitializationOrderLinks; PVOID DllBase; PVOID EntryPoint; ULONG SizeOfImage; UNICODE_STRING FullDllName; UNICODE_STRING BaseDllName; ULONG Flags; USHORT LoadCount; USHORT TlsIndex; union { LIST_ENTRY HashLinks; struct { PVOID SectionPointer; ULONG CheckSum; }; }; union { ULONG TimeDateStamp; PVOID LoadedImports; }; struct _ACTIVATION_CONTEXT * EntryPointActivationContext; PVOID PatchInformation; LIST_ENTRY ForwarderLinks; LIST_ENTRY ServiceTagLinks; LIST_ENTRY StaticLinks; }LDR_DATA_TABLE_ENTRY,*PLDR_DATA_TABLE_ENTRY; typedef struct _INJECT_INFO { ULONG ProcessId; wchar_t DllName[1024]; }INJECT_INFO,*PINJECT_INFO; typedef NTSTATUS (*PLDR_LOAD_DLL)(PWSTR,PULONG,PUNICODE_STRING,PVOID*); typedef struct _KINJECT { UNICODE_STRING DllName; wchar_t Buffer[1024]; PLDR_LOAD_DLL LdrLoadDll; PVOID DllBase; ULONG Executed; }KINJECT,*PKINJECT; typedef enum _KAPC_ENVIRONMENT { OriginalApcEnvironment, AttachedApcEnvironment, CurrentApcEnvironment, InsertApcEnvironment }KAPC_ENVIRONMENT,*PKAPC_ENVIRONMENT; typedef VOID (NTAPI *PKNORMAL_ROUTINE)( PVOID NormalContext, PVOID SystemArgument1, PVOID SystemArgument2 ); typedef VOID KKERNEL_ROUTINE( PRKAPC Apc, PKNORMAL_ROUTINE *NormalRoutine, PVOID *NormalContext, PVOID *SystemArgument1, PVOID *SystemArgument2 ); typedef KKERNEL_ROUTINE (NTAPI *PKKERNEL_ROUTINE); typedef VOID (NTAPI *PKRUNDOWN_ROUTINE)( PRKAPC Apc ); void KeInitializeApc( PRKAPC Apc, PRKTHREAD Thread, KAPC_ENVIRONMENT Environment, PKKERNEL_ROUTINE KernelRoutine, PKRUNDOWN_ROUTINE RundownRoutine, PKNORMAL_ROUTINE NormalRoutine, KPROCESSOR_MODE ProcessorMode, PVOID NormalContext ); BOOLEAN KeInsertQueueApc( PRKAPC Apc, PVOID SystemArgument1, PVOID SystemArgument2, KPRIORITY Increment ); NTSTATUS ZwQuerySystemInformation(ULONG InfoClass,PVOID Buffer,ULONG Length,PULONG ReturnLength); LPSTR PsGetProcessImageFileName(PEPROCESS Process); NTSTATUS DefaultPassThrough(PDEVICE_OBJECT DeviceObject,PIRP Irp); void UnloadDriver(PDRIVER_OBJECT DriverObject); NTSTATUS DriverDispatch(PDEVICE_OBJECT DeviceObject,PIRP Irp); ULONG ApcStateOffset; PLDR_LOAD_DLL LdrLoadDll; NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject,PUNICODE_STRING pRegistryPath) { NTSTATUS Status; PDEVICE_OBJECT DeviceObject; PEPROCESS Process; PETHREAD Thread; PKAPC_STATE ApcState; PVOID KdVersionBlock,NtdllBase; PULONG ptr,Functions,Names; PUSHORT Ordinals; PLDR_DATA_TABLE_ENTRY MmLoadedUserImageList,ModuleEntry; ULONG i; PIMAGE_DOS_HEADER pIDH; PIMAGE_NT_HEADERS pINH; PIMAGE_EXPORT_DIRECTORY pIED; UNICODE_STRING uniDeviceName; UNICODE_STRING uniLinkName; RtlInitUnicodeString(&uniDeviceName,DEVICE_NAME); RtlInitUnicodeString(&uniLinkName,LINK_NAME); for (i=0;i<IRP_MJ_MAXIMUM_FUNCTION;i++) { DriverObject->MajorFunction[i] = DefaultPassThrough; } DriverObject->DriverUnload = UnloadDriver; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DriverDispatch; //創建設備對象 Status = IoCreateDevice(DriverObject,0,&uniDeviceName,FILE_DEVICE_UNKNOWN,0,FALSE,&DeviceObject); if (!NT_SUCCESS(Status)) { return Status; } Status = IoCreateSymbolicLink(&uniLinkName,&uniDeviceName); if (!NT_SUCCESS(Status)) { IoDeleteDevice(DeviceObject); return Status; } //使當前線程運行在第一個處理器上 KeSetSystemAffinityThread(1); KdVersionBlock=(PVOID)__readfsdword(0x34); //得到KdVersionBlock KeRevertToUserAffinityThread();//恢復線程運行的處理器 MmLoadedUserImageList=*(PLDR_DATA_TABLE_ENTRY*)((PUCHAR)KdVersionBlock+0x228); // Get the MmLoadUserImageList /* kd> !pcr KPCR for Processor 0 at 83f3ec00: kd> dt _kpcr 83f3ec00 +0x034 KdVersionBlock : 0x83f3dc00 Void kd> dd 0x83f3dc00+0x228 83f3de28 83f5de38 00000000 83e5dfa8 00000000 83f3de38 00000000 00000000 83f7d8c0 00000000 83f3de48 83f7d560 00000000 83f5d84c 00000000 kd> dd 83f5de38 83f5de38 8706b1e8 877cb660 00000000 00000000 83f5de48 00000000 00000000 00040107 00000000 83f5de58 865d0690 865d0690 c0403188 0007ff7e kd> dt _LDR_DATA_TABLE_ENTRY 8706b1e8 nt!_LDR_DATA_TABLE_ENTRY +0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x8713b4e0 - 0x83f5de38 ] +0x008 InMemoryOrderLinks : _LIST_ENTRY [ 0x0 - 0x0 ] +0x010 InInitializationOrderLinks : _LIST_ENTRY [ 0x0 - 0x0 ] +0x018 DllBase : 0x77ce0000 Void +0x01c EntryPoint : (null) +0x020 SizeOfImage : 0x13c000 +0x024 FullDllName : _UNICODE_STRING "\Windows\System32\ntdll.dll" +0x02c BaseDllName : _UNICODE_STRING "" +0x034 Flags : 0 +0x038 LoadCount : 1 +0x03a TlsIndex : 0 +0x03c HashLinks : _LIST_ENTRY [ 0x0 - 0x1490d9 ] +0x03c SectionPointer : (null) +0x040 CheckSum : 0x1490d9 +0x044 TimeDateStamp : 0 +0x044 LoadedImports : (null) +0x048 EntryPointActivationContext : (null) +0x04c PatchInformation : (null) +0x050 ForwarderLinks : _LIST_ENTRY [ 0x0 - 0x0 ] +0x058 ServiceTagLinks : _LIST_ENTRY [ 0x0 - 0x57005c ] +0x060 StaticLinks : _LIST_ENTRY [ 0x6e0069 - 0x6f0064 ] +0x068 ContextInformation : 0x00730077 Void +0x06c OriginalBase : 0x53005c +0x070 LoadTime : _LARGE_INTEGER 0x650074`00730079 */ DbgPrint("KdVersionBlock address: %#x",KdVersionBlock); DbgPrint("MmLoadedUserImageList address: %#x",MmLoadedUserImageList); ModuleEntry=(PLDR_DATA_TABLE_ENTRY)MmLoadedUserImageList->InLoadOrderLinks.Flink; //第一模塊 NtdllBase=ModuleEntry->DllBase; //ntdll基地址 DbgPrint("ntdll base address: %#x",NtdllBase); pIDH=(PIMAGE_DOS_HEADER)NtdllBase; pINH=(PIMAGE_NT_HEADERS)((PUCHAR)NtdllBase+pIDH->e_lfanew); pIED=(PIMAGE_EXPORT_DIRECTORY)((PUCHAR)NtdllBase+pINH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); Functions=(PULONG)((PUCHAR)NtdllBase+pIED->AddressOfFunctions); Names=(PULONG)((PUCHAR)NtdllBase+pIED->AddressOfNames); Ordinals=(PUSHORT)((PUCHAR)NtdllBase+pIED->AddressOfNameOrdinals); //搜索LdrLoadDll for(i=0;i<pIED->NumberOfNames;i++) { if(!strcmp((char*)NtdllBase+Names[i],"LdrLoadDll")) { LdrLoadDll=(PLDR_LOAD_DLL)((PUCHAR)NtdllBase+Functions[Ordinals[i]]); break; } } DbgPrint("LdrLoadDll address: %#x",LdrLoadDll); Process=PsGetCurrentProcess(); Thread=PsGetCurrentThread(); ptr=(PULONG)Thread; //確定ApcState在EThread中的偏移 for(i=0;i<512;i++) { if(ptr[i]==(ULONG)Process) { ApcState=CONTAINING_RECORD(&ptr[i],KAPC_STATE,Process); ApcStateOffset=(ULONG)ApcState-(ULONG)Thread; break; } } DbgPrint("ApcState offset: %#x",ApcStateOffset); DbgPrint("DLL injection driver loaded."); return STATUS_SUCCESS; } NTSTATUS DefaultPassThrough(PDEVICE_OBJECT DeviceObject,PIRP Irp) { Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(Irp,IO_NO_INCREMENT); return STATUS_SUCCESS; } void UnloadDriver(PDRIVER_OBJECT DriverObject) { UNICODE_STRING uniLinkName; PDEVICE_OBJECT CurrentDeviceObject; PDEVICE_OBJECT NextDeviceObject; RtlInitUnicodeString(&uniLinkName,LINK_NAME); IoDeleteSymbolicLink(&uniLinkName); if (DriverObject->DeviceObject!=NULL) { CurrentDeviceObject = DriverObject->DeviceObject; while(CurrentDeviceObject!=NULL) { NextDeviceObject = CurrentDeviceObject->NextDevice; IoDeleteDevice(CurrentDeviceObject); CurrentDeviceObject = NextDeviceObject; } } DbgPrint("UnloadDriver\r\n"); } void NTAPI InjectDllApc(PVOID NormalContext,PVOID SystemArgument1,PVOID SystemArgument2) { PKINJECT inject=(PKINJECT)NormalContext; inject->LdrLoadDll(NULL,NULL,&inject->DllName,&inject->DllBase); inject->Executed=TRUE; } void NTAPI KernelRoutine(PKAPC apc,PKNORMAL_ROUTINE* NormalRoutine,PVOID* NormalContext,\ PVOID* SystemArgument1,PVOID* SystemArgument2) { ExFreePool(apc); } BOOLEAN InjectDll(PINJECT_INFO InjectInfo) { PEPROCESS Process; PETHREAD Thread; PKINJECT mem; ULONG size; PKAPC_STATE ApcState; PKAPC apc; PVOID buffer; PSYSTEM_PROCESS_INFO pSpi; LARGE_INTEGER delay; buffer=ExAllocatePool(NonPagedPool,1024*1024); if(!buffer) { DbgPrint("Error: Unable to allocate memory for the process thread list."); return FALSE; } //5 SystemProcessInformation, if(!NT_SUCCESS(ZwQuerySystemInformation(5,buffer,1024*1024,NULL))) { DbgPrint("Error: Unable to query process thread list."); ExFreePool(buffer); return FALSE; } pSpi=(PSYSTEM_PROCESS_INFO)buffer; //找到目標進程 while(pSpi->NextEntryOffset) { if(pSpi->UniqueProcessId==InjectInfo->ProcessId) { DbgPrint("Target thread found. TID: %d",pSpi->Threads[0].ClientId.UniqueThread); break; } pSpi=(PSYSTEM_PROCESS_INFO)((PUCHAR)pSpi+pSpi->NextEntryOffset); } // 引用目標進程EProcess, if(!NT_SUCCESS(PsLookupProcessByProcessId(InjectInfo->ProcessId,&Process))) { DbgPrint("Error: Unable to reference the target process."); ExFreePool(buffer); return FALSE; } DbgPrint("Process name: %s",PsGetProcessImageFileName(Process)); DbgPrint("EPROCESS address: %#x",Process); //目標進程主線程 if(!NT_SUCCESS(PsLookupThreadByThreadId(pSpi->Threads[0].ClientId.UniqueThread,&Thread))) { DbgPrint("Error: Unable to reference the target thread."); ObDereferenceObject(Process); ExFreePool(buffer); return FALSE; } DbgPrint("ETHREAD address: %#x",Thread); ExFreePool(buffer); //切入到目標進程 KeAttachProcess(Process); mem=NULL; size=4096; //在目標進程申請內存 if(!NT_SUCCESS(ZwAllocateVirtualMemory(NtCurrentProcess(),(PVOID*)&mem,0,&size,MEM_COMMIT|MEM_RESERVE,PAGE_EXECUTE_READWRITE))) { DbgPrint("Error: Unable to allocate memory in the target process."); KeDetachProcess(); ObDereferenceObject(Process); ObDereferenceObject(Thread); return FALSE; } DbgPrint("Memory allocated at %#x",mem); mem->LdrLoadDll=LdrLoadDll; wcscpy(mem->Buffer,InjectInfo->DllName); RtlInitUnicodeString(&mem->DllName,mem->Buffer); ApcState=(PKAPC_STATE)((PUCHAR)Thread+ApcStateOffset); ApcState->UserApcPending=TRUE; memcpy((PKINJECT)(mem+1),InjectDllApc,(ULONG)KernelRoutine-(ULONG)InjectDllApc); DbgPrint("APC code address: %#x",(PKINJECT)(mem+1)); //申請apc對象 apc=(PKAPC)ExAllocatePool(NonPagedPool,sizeof(KAPC)); if(!apc) { DbgPrint("Error: Unable to allocate the APC object."); size=0; ZwFreeVirtualMemory(NtCurrentProcess(),(PVOID*)&mem,&size,MEM_RELEASE); KeDetachProcess(); ObDereferenceObject(Process); ObDereferenceObject(Thread); return FALSE; } KeInitializeApc(apc, Thread, //目標進程主線程 OriginalApcEnvironment, //目標apcz狀態 KernelRoutine, //內核apc總入口 NULL, //Rundown Rounine=NULL (PKNORMAL_ROUTINE)((PKINJECT)mem+1), //用戶空間的總apc UserMode, //插入到用戶apc隊列 mem); // 自己的apc隊列 DbgPrint("Inserting APC to target thread"); // 插入apc隊列 if(!KeInsertQueueApc(apc,NULL,NULL,IO_NO_INCREMENT)) { DbgPrint("Error: Unable to insert APC to target thread."); size=0; ZwFreeVirtualMemory(NtCurrentProcess(),(PVOID*)&mem,&size,MEM_RELEASE); KeDetachProcess(); ObDereferenceObject(Process); ObDereferenceObject(Thread); ExFreePool(apc); return FALSE; } delay.QuadPart=-100*10000; while(!mem->Executed) { KeDelayExecutionThread(KernelMode,FALSE,&delay); //等待apc執行 } if(!mem->DllBase) { DbgPrint("Error: Unable to inject DLL into target process."); size=0; ZwFreeVirtualMemory(NtCurrentProcess(),(PVOID*)&mem,&size,MEM_RELEASE); KeDetachProcess(); ObDereferenceObject(Process); ObDereferenceObject(Thread); return FALSE; } DbgPrint("DLL injected at %#x",mem->DllBase); size=0; ZwFreeVirtualMemory(NtCurrentProcess(),(PVOID*)&mem,&size,MEM_RELEASE); ObDereferenceObject(Process); ObDereferenceObject(Thread); return TRUE; } NTSTATUS DriverDispatch(PDEVICE_OBJECT DeviceObject,PIRP Irp) { PIO_STACK_LOCATION io; PINJECT_INFO InjectInfo; NTSTATUS Status = STATUS_SUCCESS; PIO_STACK_LOCATION IrpSp; PVOID InputBuffer = NULL; PVOID OutputBuffer = NULL; ULONG_PTR InputSize = 0; ULONG_PTR OutputSize = 0; ULONG_PTR IoControlCode = 0; IrpSp = IoGetCurrentIrpStackLocation(Irp); InputBuffer = OutputBuffer = Irp->AssociatedIrp.SystemBuffer; InputSize = IrpSp->Parameters.DeviceIoControl.InputBufferLength; OutputSize = IrpSp->Parameters.DeviceIoControl.OutputBufferLength; IoControlCode = IrpSp->Parameters.DeviceIoControl.IoControlCode; switch(IoControlCode) { case CTL_KEINJECTAPC: InjectInfo=(PINJECT_INFO)InputBuffer; if(!InjectInfo) { Status=STATUS_INSUFFICIENT_RESOURCES; break; } if(!InjectDll(InjectInfo)) { Status=STATUS_UNSUCCESSFUL; break; } Status=STATUS_SUCCESS; Irp->IoStatus.Information=0; break; default: Status=STATUS_INVALID_DEVICE_REQUEST; break; } Irp->IoStatus.Status=Status; IoCompleteRequest(Irp,IO_NO_INCREMENT); return Status; }
