在 R3 下防护动态加载的模块不被意外卸载需要很多的策略,比如:LDR 断链、VAD 记录擦除、PE 头擦除、修改入口函数、内存注入等。文本我们将浅析模块静态化技术这一项技术。模块静态化是一个很常见的模块保护技术,它通过修改模块的引用计数为 -1,来使得模块不可被标准的 API 成功卸载。
系列文章:
DLL 的引用计数(或加载计数)是 DLL 被请求加载到进程中的次数记录。每次加载 DLL 时(通过 LoadLibrary) 添加到进程中,其引用计数递增 1 ,每次从进程中释放 DLL(通过 FreeLibrary)时, 引用计数递减 1。进程中初次加载模块时会将模块映射到地址空间中,当之后多次加载同一个模块时,并不会重新加载这个模块,而是只返回相同的地址。当引用计数达到 0 时, DLL 完全从进程中取消映射。并且规定,静态加载的模块,其引用计数为 -1,并且不能增加计数或通过 FreeLibrary / LdrUnLoadDll 等卸载模块。
Windows API 没有提供有关加载的 DLL 的大量信息。 Windows 提供 ToolHelp 库来检索加载到进程中的 DLL 的信息,但它只提供了非常基本的信息:例如模块名称、模块句柄等。为了获得更多信息,需要更深入地挖掘。
另一种类似的技术是通过 uFlags 位域覆盖修改系统用于区别静态模块和动态模块的标志位,修改后系统会认为模块是静态的进而阻止模块的卸载。
修改前,动态加载的模块可以被 RemoteDll 等工具卸载:
修改后,按钮灰显,表明这个模块不可以被卸载:
这种修改不需要针对 R3 下哪种工具,只需要在加载了模块的进程内修改即可。
本文参考文献:
(1)The covert way to find the Reference Count of DLL - www.SecurityXploded.com
(2)Make Your Dynamic Module Unfreeable (Anti-FreeLibrary) | secrary[dot]com
DLL 的引用计数存储在 PEB (Process Environment Block)中。PEB 包含 DLL 的链表,而链表中包含有关该特定 DLL 的结构化信息。
任何进程的 PEB 块通常位于地址 0x7ffdf000。 但是,有标准方法可以获取此地址。有 NTDLL.DLL 的无文档函数 NtQueryInformationProcess 可用于检索PROCESS_BASIC_INFORMATION 结构。PBI 结构如下:
struct _PROCESS_BASIC_INFORMATION
{
PVOID Reserved1;
PPEB PebBaseAddress;
PVOID Reserved2[2];
ULONG_PTR UniqueProcessId;
PVOID Reserved3;
} PROCESS_BASIC_INFORMATION;
第二个成员 PebBaseAddress 是指向 PEB 结构体的指针,该结构体可用于获取已加载模块的信息列表。
也可以使用 fs/gs 寄存器偏移来获取,这是 MSVC 编译器支持的接口:
#ifdef _WIN64
PPEB_LDR_DATA64 pPebLdrData = NULL;
ULONGLONG ModuleSum = NULL;
PPEB64 peb = (PPEB64)__readgsqword(0x60);
#else
PPEB_LDR_DATA32 pPebLdrData = NULL;
ULONG ModuleSum = NULL;
PPEB32 peb = (PPEB32)__readfsdword(0x30);
#endif
而对于 PEB 结构体,微软是没有公开文档的,需要自己进行重定义(原始结构体定义中缺少我们需要的部分),经查阅逆向文献,得到如下的结构体定义(部分不需要用到的成员已经被截断):
typedef struct _PEB_LDR_DATA32
{
ULONG Length; // +0x00
BOOLEAN Initialized; // +0x04
PVOID SsHandle; // +0x08
LIST_ENTRY InLoadOrderModuleList; // +0x0c
LIST_ENTRY InMemoryOrderModuleList; // +0x14
LIST_ENTRY InInitializationOrderModuleList;// +0x1c
} PEB_LDR_DATA32, * PPEB_LDR_DATA32; // +0x24
typedef struct _PEB32
{
UCHAR InheritedAddressSpace; //0x0
UCHAR ReadImageFileExecOptions; //0x1
UCHAR BeingDebugged; //0x2
union
{
UCHAR BitField; //0x3
struct
{
UCHAR ImageUsesLargePages : 1; //0x3
UCHAR IsProtectedProcess : 1; //0x3
UCHAR IsImageDynamicallyRelocated : 1; //0x3
UCHAR SkipPatchingUser32Forwarders : 1; //0x3
UCHAR IsPackagedProcess : 1; //0x3
UCHAR IsAppContainer : 1; //0x3
UCHAR IsProtectedProcessLight : 1; //0x3
UCHAR IsLongPathAwareProcess : 1; //0x3
};
};
PVOID Mutant; //0x4
PVOID ImageBaseAddress; //0x8
PEB_LDR_DATA32* Ldr; //0xc
RTL_USER_PROCESS_PARAMETERS* ProcessParameters; //0x10
PVOID SubSystemData; //0x14
PVOID ProcessHeap; //0x18
RTL_CRITICAL_SECTION* FastPebLock; //0x1c
SLIST_HEADER* volatile AtlThunkSListPtr; //0x20
PVOID IFEOKey; //0x24
} PEB32, * PPEB32;
typedef struct _STRING64
{
USHORT Length; //0x0
USHORT MaximumLength; //0x2
ULONGLONG Buffer; //0x8
}STRING64, * LPSTRING64;
typedef struct _PEB_LDR_DATA64
{
ULONG Length; //0x0
UCHAR Initialized; //0x4
PVOID SsHandle; //0x8
LIST_ENTRY InLoadOrderModuleList; //0x10
LIST_ENTRY InMemoryOrderModuleList; //0x20
LIST_ENTRY InInitializationOrderModuleList; //0x30
PVOID EntryInProgress; //0x40
UCHAR ShutdownInProgress; //0x48
PVOID ShutdownThreadId; //0x50
}PEB_LDR_DATA64, *PPEB_LDR_DATA64;
typedef struct _PEB64
{
UCHAR InheritedAddressSpace; //0x0
UCHAR ReadImageFileExecOptions; //0x1
UCHAR BeingDebugged; //0x2
union
{
UCHAR BitField; //0x3
struct
{
UCHAR ImageUsesLargePages : 1; //0x3
UCHAR IsProtectedProcess : 1; //0x3
UCHAR IsImageDynamicallyRelocated : 1; //0x3
UCHAR SkipPatchingUser32Forwarders : 1; //0x3
UCHAR IsPackagedProcess : 1; //0x3
UCHAR IsAppContainer : 1; //0x3
UCHAR IsProtectedProcessLight : 1; //0x3
UCHAR IsLongPathAwareProcess : 1; //0x3
};
};
UCHAR Padding0[4]; //0x4
ULONGLONG Mutant; //0x8
ULONGLONG ImageBaseAddress; //0x10
PEB_LDR_DATA64* Ldr; //0x18
ULONGLONG ProcessParameters; //0x20
ULONGLONG SubSystemData; //0x28
ULONGLONG ProcessHeap; //0x30
ULONGLONG FastPebLock; //0x38
ULONGLONG AtlThunkSListPtr; //0x40
ULONGLONG IFEOKey; //0x48
}PEB64, *PPEB64;
在 PEB 结构中,我们需要获取 PEB_LDR_DATA 结构,也就是这里的 Ldr 成员:
PPEB_LDR_DATA Ldr
它是指向 PEB_LDR_DATA 结构的指针。
PEB_LDR_DATA 包含我们感兴趣的加载器数据结构,它包含已加载模块的链表:
typedef struct _PEB_LDR_DATA64
{
ULONG Length; //0x0
UCHAR Initialized; //0x4
PVOID SsHandle; //0x8
LIST_ENTRY InLoadOrderModuleList; //0x10
LIST_ENTRY InMemoryOrderModuleList; //0x20
LIST_ENTRY InInitializationOrderModuleList; //0x30
PVOID EntryInProgress; //0x40
UCHAR ShutdownInProgress; //0x48
PVOID ShutdownThreadId; //0x50
}PEB_LDR_DATA64, *PPEB_LDR_DATA64;
数据结构为 LIST_ENTRY 的三个链表分别为:InLoadOrderModuleList、 InMemoryOrderModuleList 和 InInitializationOrderModuleList。他们存储按照不同排序方式对模块排序的结果。
那么问题就转化为了如何解析链表来遍历每一个模块的信息。
具体遍历原理可以参考我的这篇文章:利用 PEB_LDR_DATA 结构枚举进程模块信息。就不再重复解释了。
这两个技术都通过 LDR_DATA_TABLE_ENTRY 中的成员来完成:
前者通过修改 DdagNode 结构中的 LoadCount 以及 LDR 中废弃的(旧系统用到) ObsoleteLoadCount 成员,将他们赋值为 -1,即可修改模块属性为静态:
pLdrDataEntry->DdagNode->LoadCount = 0xffffffff;
pLdrDataEntry->ObsoleteLoadCount = 0xffff;
后者通过修改位域 ProcessStaticImport 为 TRUE(1) 来实现的,该位域结构如下:
union
{
UCHAR FlagGroup[4]; //0x68
ULONG Flags; //0x68
struct
{
ULONG PackagedBinary : 1; //0x68
ULONG MarkedForRemoval : 1; //0x68
ULONG ImageDll : 1; //0x68
ULONG LoadNotificationsSent : 1; //0x68
ULONG TelemetryEntryProcessed : 1; //0x68
ULONG ProcessStaticImport : 1; //0x68
ULONG InLegacyLists : 1; //0x68
ULONG InIndexes : 1; //0x68
ULONG ShimDll : 1; //0x68
ULONG InExceptionTable : 1; //0x68
ULONG ReservedFlags1 : 2; //0x68
ULONG LoadInProgress : 1; //0x68
ULONG LoadConfigProcessed : 1; //0x68
ULONG EntryProcessed : 1; //0x68
ULONG ProtectDelayLoad : 1; //0x68
ULONG ReservedFlags3 : 2; //0x68
ULONG DontCallForThreads : 1; //0x68
ULONG ProcessAttachCalled : 1; //0x68
ULONG ProcessAttachFailed : 1; //0x68
ULONG CorDeferredValidate : 1; //0x68
ULONG CorImage : 1; //0x68
ULONG DontRelocate : 1; //0x68
ULONG CorILOnly : 1; //0x68
ULONG ChpeImage : 1; //0x68
ULONG ReservedFlags5 : 2; //0x68
ULONG Redirected : 1; //0x68
ULONG ReservedFlags6 : 2; //0x68
ULONG CompatDatabaseProcessed : 1; //0x68
}uFlags;
};
其实,开启 ProcessStaticImport 标志位保护模块的官方的方法是调用一次 GetModuleHandleEx 并指定 GET_MODULE_HANDLE_EX_FLAG_PIN 标志,但似乎通过官方方法并没法取消掉,我们通过编程方式枚举 LDR 信息的可以取消掉该机制。
HMODULE hTestModule = 0;
GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN, L"模块名称", &hTestModule);
开启该标志位后,无论使用多少次 FreeLibrary 都不会真正卸载模块。
下面的代码实现上述所有功能,需要注意是,有三个链表都需要枚举并修改,关于这个结构枚举的原理可以看我的另一篇文章“利用 LDR_DATA_TABLE 枚举进程模块信息”,此外需要注意如果包含 winternl 头文件,它里面有 ldrp.h 部分结构的重复声明(微软给的结构体不完整)。
ldrp.h:
#pragma once
#include
#include
//#include
// Kernels | x64 | Windows 10 | 2016 | 2210 22H2(May 2023 Update)
//0x18 bytes (sizeof)
typedef struct _RTL_BALANCED_NODE
{
union
{
_RTL_BALANCED_NODE* Children[2]; //0x0
struct
{
_RTL_BALANCED_NODE* Left; //0x0
_RTL_BALANCED_NODE* Right; //0x8
};
};
union
{
struct
{
UCHAR Red : 1; //0x10
UCHAR Balance : 2; //0x10
};
ULONGLONG ParentValue; //0x10
};
}RTL_BALANCED_NODE, * PRTL_BALANCED_NODE, * LPRTL_BALANCED_NODE;
//0x4 bytes (sizeof)
enum _LDR_DLL_LOAD_REASON
{
LoadReasonStaticDependency = 0,
LoadReasonStaticForwarderDependency = 1,
LoadReasonDynamicForwarderDependency = 2,
LoadReasonDelayloadDependency = 3,
LoadReasonDynamicLoad = 4,
LoadReasonAsImageLoad = 5,
LoadReasonAsDataLoad = 6,
LoadReasonEnclavePrimary = 7,
LoadReasonEnclaveDependency = 8,
LoadReasonUnknown = -1
};
typedef _LDR_DLL_LOAD_REASON LDR_DLL_LOAD_REASON;
//0x10 bytes (sizeof)
typedef struct _LDR_SERVICE_TAG_RECORD
{
_LDR_SERVICE_TAG_RECORD* Next; //0x0
ULONG ServiceTag; //0x8
}LDR_SERVICE_TAG_RECORD, * PLDR_SERVICE_TAG_RECORD;
//0x8 bytes (sizeof)
typedef struct _LDRP_CSLIST
{
SINGLE_LIST_ENTRY* Tail; //0x0
}LDRP_CSLIST;
//0x4 bytes (sizeof)
enum _LDR_DDAG_STATE
{
LdrModulesMerged = -5,
LdrModulesInitError = -4,
LdrModulesSnapError = -3,
LdrModulesUnloaded = -2,
LdrModulesUnloading = -1,
LdrModulesPlaceHolder = 0,
LdrModulesMapping = 1,
LdrModulesMapped = 2,
LdrModulesWaitingForDependencies = 3,
LdrModulesSnapping = 4,
LdrModulesSnapped = 5,
LdrModulesCondensed = 6,
LdrModulesReadyToInit = 7,
LdrModulesInitializing = 8,
LdrModulesReadyToRun = 9
};
typedef _LDR_DDAG_STATE LDR_DDAG_STATE;
//0x50 bytes (sizeof)
typedef struct _LDR_DDAG_NODE
{
LIST_ENTRY Modules; //0x0
PLDR_SERVICE_TAG_RECORD ServiceTagList; //0x10
ULONG LoadCount; //0x18
ULONG LoadWhileUnloadingCount; //0x1c
ULONG LowestLink; //0x20
LDRP_CSLIST Dependencies; //0x28
LDRP_CSLIST IncomingDependencies; //0x30
LDR_DDAG_STATE State; //0x38
SINGLE_LIST_ENTRY CondenseLink; //0x40
ULONG PreorderNumber; //0x48
}LDR_DDAG_NODE, * PLDR_DDAG_NODE;
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING;
typedef UNICODE_STRING* PUNICODE_STRING;
typedef const UNICODE_STRING* PCUNICODE_STRING;
//0x120 bytes (sizeof)
typedef struct _LDR_DATA_TABLE_ENTRY
{
LIST_ENTRY InLoadOrderLinks; //0x0
LIST_ENTRY InMemoryOrderLinks; //0x10
LIST_ENTRY InInitializationOrderLinks; //0x20
VOID* DllBase; //0x30
VOID* EntryPoint; //0x38
ULONG SizeOfImage; //0x40
UNICODE_STRING FullDllName; //0x48
UNICODE_STRING BaseDllName; //0x58
union
{
UCHAR FlagGroup[4]; //0x68
ULONG Flags; //0x68
struct
{
ULONG PackagedBinary : 1; //0x68
ULONG MarkedForRemoval : 1; //0x68
ULONG ImageDll : 1; //0x68
ULONG LoadNotificationsSent : 1; //0x68
ULONG TelemetryEntryProcessed : 1; //0x68
ULONG ProcessStaticImport : 1; //0x68
ULONG InLegacyLists : 1; //0x68
ULONG InIndexes : 1; //0x68
ULONG ShimDll : 1; //0x68
ULONG InExceptionTable : 1; //0x68
ULONG ReservedFlags1 : 2; //0x68
ULONG LoadInProgress : 1; //0x68
ULONG LoadConfigProcessed : 1; //0x68
ULONG EntryProcessed : 1; //0x68
ULONG ProtectDelayLoad : 1; //0x68
ULONG ReservedFlags3 : 2; //0x68
ULONG DontCallForThreads : 1; //0x68
ULONG ProcessAttachCalled : 1; //0x68
ULONG ProcessAttachFailed : 1; //0x68
ULONG CorDeferredValidate : 1; //0x68
ULONG CorImage : 1; //0x68
ULONG DontRelocate : 1; //0x68
ULONG CorILOnly : 1; //0x68
ULONG ChpeImage : 1; //0x68
ULONG ReservedFlags5 : 2; //0x68
ULONG Redirected : 1; //0x68
ULONG ReservedFlags6 : 2; //0x68
ULONG CompatDatabaseProcessed : 1; //0x68
}uFlags;
};
USHORT ObsoleteLoadCount; //0x6c
USHORT TlsIndex; //0x6e
LIST_ENTRY HashLinks; //0x70
ULONG TimeDateStamp; //0x80
struct ACTIVATION_CONTEXT* EntryPointActivationContext; //0x88
VOID* Lock; //0x90
LDR_DDAG_NODE* DdagNode; //0x98
LIST_ENTRY NodeModuleLink; //0xa0
struct LDRP_LOAD_CONTEXT* LoadContext; //0xb0
VOID* ParentDllBase; //0xb8
VOID* SwitchBackContext; //0xc0
RTL_BALANCED_NODE BaseAddressIndexNode; //0xc8
RTL_BALANCED_NODE MappingInfoIndexNode; //0xe0
ULONGLONG OriginalBase; //0xf8
LARGE_INTEGER LoadTime; //0x100
ULONG BaseNameHashValue; //0x108
LDR_DLL_LOAD_REASON LoadReason; //0x10c
ULONG ImplicitPathOptions; //0x110
ULONG ReferenceCount; //0x114
ULONG DependentLoadFlags; //0x118
UCHAR SigningLevel; //0x11c
}LDR_DATA_TABLE_ENTRY, * PLDR_DATA_TABLE_ENTRY;
//0x58 bytes (sizeof)
typedef struct _PEB_LDR_DATA32
{
ULONG Length; // +0x00
BOOLEAN Initialized; // +0x04
PVOID SsHandle; // +0x08
LIST_ENTRY InLoadOrderModuleList; // +0x0c
LIST_ENTRY InMemoryOrderModuleList; // +0x14
LIST_ENTRY InInitializationOrderModuleList;// +0x1c
} PEB_LDR_DATA32, * PPEB_LDR_DATA32; // +0x24
typedef struct _PEB32
{
UCHAR InheritedAddressSpace; //0x0
UCHAR ReadImageFileExecOptions; //0x1
UCHAR BeingDebugged; //0x2
union
{
UCHAR BitField; //0x3
struct
{
UCHAR ImageUsesLargePages : 1; //0x3
UCHAR IsProtectedProcess : 1; //0x3
UCHAR IsImageDynamicallyRelocated : 1; //0x3
UCHAR SkipPatchingUser32Forwarders : 1; //0x3
UCHAR IsPackagedProcess : 1; //0x3
UCHAR IsAppContainer : 1; //0x3
UCHAR IsProtectedProcessLight : 1; //0x3
UCHAR IsLongPathAwareProcess : 1; //0x3
};
};
PVOID Mutant; //0x4
PVOID ImageBaseAddress; //0x8
PEB_LDR_DATA32* Ldr; //0xc
struct RTL_USER_PROCESS_PARAMETERS* ProcessParameters; //0x10
PVOID SubSystemData; //0x14
PVOID ProcessHeap; //0x18
RTL_CRITICAL_SECTION* FastPebLock; //0x1c
SLIST_HEADER* volatile AtlThunkSListPtr; //0x20
PVOID IFEOKey; //0x24
} PEB32, * PPEB32;
typedef struct _STRING64
{
USHORT Length; //0x0
USHORT MaximumLength; //0x2
ULONGLONG Buffer; //0x8
}STRING64, * LPSTRING64;
typedef struct _PEB_LDR_DATA64
{
ULONG Length; //0x0
UCHAR Initialized; //0x4
PVOID SsHandle; //0x8
LIST_ENTRY InLoadOrderModuleList; //0x10
LIST_ENTRY InMemoryOrderModuleList; //0x20
LIST_ENTRY InInitializationOrderModuleList; //0x30
PVOID EntryInProgress; //0x40
UCHAR ShutdownInProgress; //0x48
PVOID ShutdownThreadId; //0x50
}PEB_LDR_DATA64, * PPEB_LDR_DATA64;
typedef struct _PEB64
{
UCHAR InheritedAddressSpace; //0x0
UCHAR ReadImageFileExecOptions; //0x1
UCHAR BeingDebugged; //0x2
union
{
UCHAR BitField; //0x3
struct
{
UCHAR ImageUsesLargePages : 1; //0x3
UCHAR IsProtectedProcess : 1; //0x3
UCHAR IsImageDynamicallyRelocated : 1; //0x3
UCHAR SkipPatchingUser32Forwarders : 1; //0x3
UCHAR IsPackagedProcess : 1; //0x3
UCHAR IsAppContainer : 1; //0x3
UCHAR IsProtectedProcessLight : 1; //0x3
UCHAR IsLongPathAwareProcess : 1; //0x3
};
};
UCHAR Padding0[4]; //0x4
ULONGLONG Mutant; //0x8
ULONGLONG ImageBaseAddress; //0x10
PEB_LDR_DATA64* Ldr; //0x18
ULONGLONG ProcessParameters; //0x20
ULONGLONG SubSystemData; //0x28
ULONGLONG ProcessHeap; //0x30
ULONGLONG FastPebLock; //0x38
ULONGLONG AtlThunkSListPtr; //0x40
ULONGLONG IFEOKey; //0x48
}PEB64, * PPEB64;
#ifdef _WIN64
typedef PEB64 PEB;
typedef PPEB64 PPEB;
typedef PEB_LDR_DATA64 PEB_LDR_DATA;
typedef PPEB_LDR_DATA64 PPEB_LDR_DATA;
#else
typedef PEB32 PEB;
typedef PPEB32 PPEB;
typedef PEB_LDR_DATA32 PEB_LDR_DATA;
typedef PPEB_LDR_DATA32 PPEB_LDR_DATA;
#endif
main.cpp:
DWORD WINAPI EasyProtectLibrary(LPVOID lpThreadParameter)
{
auto ldrpt = (LPLDR_PROTECT_STRUCT)lpThreadParameter;
if (ldrpt == nullptr) return 0;
BOOL bNewValue = ldrpt->bEnableProtect;
BOOL bOldProtect = 0;
DWORD index = 0;
DWORD bResponse = 0;
const WCHAR lpFileName[] = HOOK_MODULE_NAME;
PPEB_LDR_DATA pPebLdrData = nullptr;
PLDR_DATA_TABLE_ENTRY pLdrDataEntry = nullptr;
PLIST_ENTRY pListEntryStart = nullptr;
PLIST_ENTRY pListEntryEnd = nullptr;
SIZE_T ulTestSize = 0;
SIZE_T ulRealSize = wcsnlen_s(lpFileName, MAX_PATH);
#ifdef _WIN64
ULONGLONG ModuleSum = NULL;
PPEB peb = (PPEB)__readgsqword(0x60);
#else
ULONG ModuleSum = NULL;
PPEB32 peb = (PPEB32)__readfsdword(0x30);
#endif
__try {
pPebLdrData = peb->Ldr;
// 以模块加载顺序排列的链表
pListEntryStart = pPebLdrData->InLoadOrderModuleList.Flink;
pListEntryEnd = pPebLdrData->InLoadOrderModuleList.Blink;
for (index = 0; pListEntryStart != pListEntryEnd; index++)
{
pLdrDataEntry = CONTAINING_RECORD(pListEntryStart,
LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
ulTestSize = wcsnlen_s(pLdrDataEntry->BaseDllName.Buffer, MAX_PATH);
if (ulTestSize != ulRealSize || ulTestSize == MAX_PATH)
{
pListEntryStart = pListEntryStart->Flink;
continue;
}
if (!_wcsicmp(pLdrDataEntry->BaseDllName.Buffer, lpFileName))
{
if (bNewValue == TRUE)
{
// 引用计数主要有两个成员,引用计数为 -1 表示静态加载的模块,
// 并且不允许卸载
pLdrDataEntry->DdagNode->LoadCount = 0xffffffff;
pLdrDataEntry->ObsoleteLoadCount = 0xffff;
}
else {
// 引用计数主要有两个成员,引用计数为 -1 表示静态加载的模块,
// 并且不允许卸载
pLdrDataEntry->DdagNode->LoadCount = 1;
pLdrDataEntry->ObsoleteLoadCount = 1;
}
// ProcessStaticImport 位域如果为 1, 则任何卸载调用都将直接返回 TRUE
// 而不做任何资源释放操作
pLdrDataEntry->uFlags.ProcessStaticImport = bNewValue;
bResponse |= 0x1;
break;
}
pListEntryStart = pListEntryStart->Flink;
}
// 以内存位置排列的模块链表
pListEntryStart = pPebLdrData->InMemoryOrderModuleList.Flink;
pListEntryEnd = pPebLdrData->InMemoryOrderModuleList.Blink;
for (index = 0; pListEntryStart != pListEntryEnd; index++)
{
pLdrDataEntry = CONTAINING_RECORD(pListEntryStart,
LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
ulTestSize = wcsnlen_s(pLdrDataEntry->BaseDllName.Buffer, MAX_PATH);
if (ulTestSize != ulRealSize || ulTestSize == MAX_PATH)
{
pListEntryStart = pListEntryStart->Flink;
continue;
}
if (!_wcsicmp(pLdrDataEntry->BaseDllName.Buffer, lpFileName))
{
if (bNewValue == TRUE)
{
pLdrDataEntry->DdagNode->LoadCount = 0xffffffff;
pLdrDataEntry->ObsoleteLoadCount = 0xffff;
}
else {
pLdrDataEntry->DdagNode->LoadCount = 1;
pLdrDataEntry->ObsoleteLoadCount = 1;
}
pLdrDataEntry->uFlags.ProcessStaticImport = bNewValue;
bResponse |= 0x2;
break;
}
pListEntryStart = pListEntryStart->Flink;
}
// 以初始化顺序加载的模块列表
pListEntryStart = pPebLdrData->InInitializationOrderModuleList.Flink;
pListEntryEnd = pPebLdrData->InInitializationOrderModuleList.Blink;
for (index = 0; pListEntryStart != pListEntryEnd; index++)
{
pLdrDataEntry = CONTAINING_RECORD(pListEntryStart,
LDR_DATA_TABLE_ENTRY, InInitializationOrderLinks);
ulTestSize = wcsnlen_s(pLdrDataEntry->BaseDllName.Buffer, MAX_PATH);
if (ulTestSize != ulRealSize || ulTestSize == MAX_PATH)
{
pListEntryStart = pListEntryStart->Flink;
continue;
}
if (!_wcsicmp(pLdrDataEntry->BaseDllName.Buffer, lpFileName))
{
if (bNewValue == TRUE)
{
pLdrDataEntry->DdagNode->LoadCount = 0xffffffff;
pLdrDataEntry->ObsoleteLoadCount = 0xffff;
}
else {
pLdrDataEntry->DdagNode->LoadCount = 1;
pLdrDataEntry->ObsoleteLoadCount = 1;
}
pLdrDataEntry->uFlags.ProcessStaticImport = bNewValue;
bResponse |= 0x4;
break;
}
pListEntryStart = pListEntryStart->Flink;
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {
OutputDebugStringW(L"Er:Exception occurred while accessing memory.\n");
return FALSE;
}
return bResponse;
}
这样,我们只需要在模块初始化和脱钩时,调用该函数就可以在进程中简单地保护/脱保护钩子模块。
文本浅析模块静态化技术这一项技术。是一种有效的实践。
发布于:2024.02.03;更新于:2024.02.03