注入小结
平常用的最多的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; }