主頁 > 企業開發 > 驅動開發:內核RIP劫持實作DLL注入

驅動開發:內核RIP劫持實作DLL注入

2023-06-17 08:04:50 企業開發

本章將探索內核級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拿到,為了能通過代碼拿到該函式的入口地址,我提取fffff804204de668fffff804204de670位置處的特征碼,由于fffff804204de668距離PsSuspendThread函式開頭只有24位元組,所以直接通過-24即可得到,

通過呼叫SearchOPcode()并傳入機器碼即可直接拿到PsSuspendThread的入口地址,根據上述方式我們需要分別得到PsSuspendThreadPsResumeThread這幾個函式的記憶體基址,這些函式的具體作用如下所示;

  • PsSuspendThread() 用于暫停或者掛起執行緒
  • PsResumeThread() 用于恢復執行緒

其次還需要通過MmGetSystemRoutineAddress函式動態的得到ZwGetNextThreadPsGetThreadTebPsGetProcessWow64Process這幾個函式的基址,這些函式的具體作用如下所示;

  • 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為6084Ntdll.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的訊息,說明注入成功了,如下圖所示;

文章作者:lyshark (王瑞)
文章出處:https://www.cnblogs.com/LyShark/p/17159809.html
本博客所有文章除特別宣告外,均采用 BY-NC-SA 許可協議,轉載請注明出處!

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/555390.html

標籤:訊息安全

上一篇:記錄--前端如何優雅匯出多表頭xlsx

下一篇:返回列表

標籤雲
其他(161155) Python(38236) JavaScript(25498) Java(18244) C(15237) 區塊鏈(8271) C#(7972) AI(7469) 爪哇(7425) MySQL(7254) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5875) 数组(5741) R(5409) Linux(5347) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4599) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2436) ASP.NET(2404) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) .NET技术(1984) 功能(1967) HtmlCss(1967) Web開發(1951) C++(1941) python-3.x(1918) 弹簧靴(1913) xml(1889) PostgreSQL(1881) .NETCore(1863) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • IEEE1588PTP在數字化變電站時鐘同步方面的應用

    IEEE1588ptp在數字化變電站時鐘同步方面的應用 京準電子科技官微——ahjzsz 一、電力系統時間同步基本概況 隨著對IEC 61850標準研究的不斷深入,國內外學者提出基于IEC61850通信標準體系建設數字化變電站的發展思路。數字化變電站與常規變電站的顯著區別在于程序層傳統的電流/電壓互 ......

    uj5u.com 2020-09-10 03:51:52 more
  • HTTP request smuggling CL.TE

    CL.TE 簡介 前端通過Content-Length處理請求,通過反向代理或者負載均衡將請求轉發到后端,后端Transfer-Encoding優先級較高,以TE處理請求造成安全問題。 檢測 發送如下資料包 POST / HTTP/1.1 Host: ac391f7e1e9af821806e890 ......

    uj5u.com 2020-09-10 03:52:11 more
  • 網路滲透資料大全單——漏洞庫篇

    網路滲透資料大全單——漏洞庫篇漏洞庫 NVD ——美國國家漏洞庫 →http://nvd.nist.gov/。 CERT ——美國國家應急回應中心 →https://www.us-cert.gov/ OSVDB ——開源漏洞庫 →http://osvdb.org Bugtraq ——賽門鐵克 →ht ......

    uj5u.com 2020-09-10 03:52:15 more
  • 京準講述NTP時鐘服務器應用及原理

    京準講述NTP時鐘服務器應用及原理京準講述NTP時鐘服務器應用及原理 安徽京準電子科技官微——ahjzsz 北斗授時原理 授時是指接識訓通過某種方式獲得本地時間與北斗標準時間的鐘差,然后調整本地時鐘使時差控制在一定的精度范圍內。 衛星導航系統通常由三部分組成:導航授時衛星、地面檢測校正維護系統和用戶 ......

    uj5u.com 2020-09-10 03:52:25 more
  • 利用北斗衛星系統設計NTP網路時間服務器

    利用北斗衛星系統設計NTP網路時間服務器 利用北斗衛星系統設計NTP網路時間服務器 安徽京準電子科技官微——ahjzsz 概述 NTP網路時間服務器是一款支持NTP和SNTP網路時間同步協議,高精度、大容量、高品質的高科技時鐘產品。 NTP網路時間服務器設備采用冗余架構設計,高精度時鐘直接來源于北斗 ......

    uj5u.com 2020-09-10 03:52:35 more
  • 詳細解讀電力系統各種對時方式

    詳細解讀電力系統各種對時方式 詳細解讀電力系統各種對時方式 安徽京準電子科技官微——ahjzsz,更多資料請添加VX 衛星同步時鐘是我京準公司開發研制的應用衛星授時時技術的標準時間顯示和發送的裝置,該裝置以M國全球定位系統(GLOBAL POSITIONING SYSTEM,縮寫為GPS)或者我國北 ......

    uj5u.com 2020-09-10 03:52:45 more
  • 如何保證外包團隊接入企業內網安全

    不管企業規模的大小,只要企業想省錢,那么企業的某些服務就一定會采用外包的形式,然而看似美好又經濟的策略,其實也有不好的一面。下面我通過安全的角度來聊聊使用外包團的安全隱患問題。 先看看什么服務會使用外包的,最常見的就是話務/客服這種需要大量重復性、無技術性的服務,或者是一些銷售外包、特殊的職能外包等 ......

    uj5u.com 2020-09-10 03:52:57 more
  • PHP漏洞之【整型數字型SQL注入】

    0x01 什么是SQL注入 SQL是一種注入攻擊,通過前端帶入后端資料庫進行惡意的SQL陳述句查詢。 0x02 SQL整型注入原理 SQL注入一般發生在動態網站URL地址里,當然也會發生在其它地發,如登錄框等等也會存在注入,只要是和資料庫打交道的地方都有可能存在。 如這里http://192.168. ......

    uj5u.com 2020-09-10 03:55:40 more
  • [GXYCTF2019]禁止套娃

    git泄露獲取原始碼 使用GET傳參,引數為exp 經過三層過濾執行 第一層過濾偽協議,第二層過濾帶引數的函式,第三層過濾一些函式 preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'] (?R)參考當前正則運算式,相當于匹配函式里的引數 因此傳遞 ......

    uj5u.com 2020-09-10 03:56:07 more
  • 等保2.0實施流程

    流程 結論 ......

    uj5u.com 2020-09-10 03:56:16 more
最新发布
  • 驅動開發:內核RIP劫持實作DLL注入

    本章將探索內核級DLL模塊注入實作原理,DLL模塊注入在應用層中通常會使用`CreateRemoteThread`直接開啟遠程執行緒執行即可,驅動級別的注入有多種實作原理,而其中最簡單的一種實作方式則是通過劫持EIP的方式實作,其實作原理可總結為,掛起目標行程,停止目標行程EIP的變換,在目標行程開啟... ......

    uj5u.com 2023-06-17 08:04:50 more
  • 記錄--前端如何優雅匯出多表頭xlsx

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 前言 xlsx匯出是比較前后端開發程序中都比較常見的一個功能。但傳統的二維表格可能很難能滿足我們對業務的需求,因為當資料的維度和層次比較多時,二維表格很難以清晰和壓縮的方式展現所有的資訊,所以我們也就經常能碰到多級表頭開發了。 demo ......

    uj5u.com 2023-06-16 08:23:51 more
  • 利用jQuery制作簡易的table上下無縫輪播

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="wi ......

    uj5u.com 2023-06-16 08:23:36 more
  • 使用vue ui創建vue專案

    vue ui是什么? 簡單來說,vue ui是一個可視化圖形界面,方便你去創建、更新和管理vue專案,包括下載router,vuex,axios,elementui等插件,配置好一些屬性以及依賴關系,方便我們使用,我個人第一次接觸它就感覺非常非常非常智能和強大 配置步驟 1、安裝Vue CLI,因為 ......

    uj5u.com 2023-06-16 08:23:14 more
  • 解決:js 根據圖片鏈接(image url)下載,有的打開預覽,有的下載

    1、問題描述 https://*****/drugTestReport/20230515/202305151106111386737.png https://*****/drugTestReport/20230605/202306051540314553141.jpg 同樣結構的兩個圖片鏈接,使用w ......

    uj5u.com 2023-06-16 08:22:59 more
  • 基于Spark的大規模日志分析

    摘要:本篇文章將從一個實際專案出發,分享如何使用 Spark 進行大規模日志分析,并通過代碼演示加深讀者的理解。 本文分享自華為云社區《【實戰經驗分享】基于Spark的大規模日志分析【上進小菜豬大資料系列】》,作者:上進小菜豬。 隨著互聯網的普及和應用范圍的擴大,越來越多的應用場景需要對海量資料進行 ......

    uj5u.com 2023-06-16 08:22:55 more
  • 什么是微格式

    微格式是一種用于在HTML檔案中嵌入語意化資訊的簡單而輕量級的標記語言。它們通過使用已有的HTML標簽和類名來表示結構化資料,以便機器能夠更容易地理解和處理這些資料。 微格式的目標是為了讓資訊更易于被自動化工具(如搜索引擎、資料聚合器、日歷應用程式等)提取和決議。通過添加特定的類名和屬性值,可以標記 ......

    uj5u.com 2023-06-16 08:22:50 more
  • 前端vue仿京東天貓簡單好用的瀑布流瀑布流式布局串列組件waterfa

    前端vue仿京東天貓簡單好用的瀑布流瀑布流式布局串列組件waterfall, 下載完整代碼請訪問uni-app插件市場址:https://ext.dcloud.net.cn/plugin?id=13046 效果圖如下: ![image](https://img2023.cnblogs.com/blo ......

    uj5u.com 2023-06-16 08:22:41 more
  • 解決:js 根據圖片鏈接(image url)下載,有的打開預覽,有的下載

    1、問題描述 https://*****/drugTestReport/20230515/202305151106111386737.png https://*****/drugTestReport/20230605/202306051540314553141.jpg 同樣結構的兩個圖片鏈接,使用w ......

    uj5u.com 2023-06-16 08:22:26 more
  • 基于Spark的大規模日志分析

    摘要:本篇文章將從一個實際專案出發,分享如何使用 Spark 進行大規模日志分析,并通過代碼演示加深讀者的理解。 本文分享自華為云社區《【實戰經驗分享】基于Spark的大規模日志分析【上進小菜豬大資料系列】》,作者:上進小菜豬。 隨著互聯網的普及和應用范圍的擴大,越來越多的應用場景需要對海量資料進行 ......

    uj5u.com 2023-06-16 08:22:22 more