Sandbox学习(1)
Sandbox(1)
[转]Sandboxie 的工作原理 - Lance~ -
博客园
SecBoxie使用Ring3用户态拦截与Ring0内核态强制重定向的分层隔离架构,Ring3层主动监控、拦截目标进程并将其挂起,然后通过Shell
Code注入将Detours
Hook引擎以及与Ipc相关的自定义Hook处理函数写入目标进程当中并执行Hook,完成Hook之后恢复进程运行,IpcHook函数会接管IPC的请求如函数NtCreateEvent,调用GetObjectName函数解析IPC对象原始路径(TruePath),剥离系统默认命名空间前缀,拼接专属根路径(\Sandbox\SecBoxie\)生成CopyPath,并通过IOCTL发送到驱动,驱动将原始路径(TruePath)强制替换为沙箱重定向路径(CopyPath),并为
CopyPath
创建独立的内核对象命名空间,使目标进程只能访问该命名空间下的IPC对象,无法看到主机目录下的任何对象,主机也无法访问CopyPath命名空间内的对象,实现双向隔离。驱动再把创建好的事件句柄返回给Ring3的IpcHook函数进而再给到目标进程,从而实现路径重定向隔离目标进程,整个过程对目标进程完全透明,在目标进程中无需修改代码,实现无感知拦截。
6 进程回调
程序起来后,sandbox对启动的进程注入+hook
实现监控
这个地方的内存存储方式知识点挺好,但时间关系先略过
(映射相关的)
下面这个地方有个进程回调通知:当你进程一启动,你的驱动就被调度了,进程被拦截
就走进程回调:Process_NotifyProcessEx
如何理解进程回调?
双击一个exe在用户层启动的时候
我们的驱动在操作系统,所以会跑到操作系统来问,我们这个exe能不能启动
下面有一个回调函数Callback,已准备启动exe,就会来这调用CallBack,来判断到底能不能启动
(图中是在注册进程回调,一个win7版本,一个是xp,visata)
当驱动卸载时,回调也卸载,注意要卸载回调,不然会内存泄漏
if (__OsVersion >= WINDOWS_7) {
Status =
PsSetCreateProcessNotifyRoutineEx(ProcessNotifyProcedure, FALSE);
}
#ifdef XP_SUPPORT
else { // XP, Vista
Status =
PsSetCreateProcessNotifyRoutine(ProcessNotifyProcedure, FALSE);
}
然后下面还有一个模块回调,进程启动之后,会加载模块(exe,ntdll),可以允许你加载或者拒绝)
Status = PsSetLoadImageNotifyRoutine(ImageNotifyProcedure);
if (NT_SUCCESS(Status))
__IsImageNotifyProcedure = TRUE;
else {
return FALSE;
}
沙盒就相当于使用进程回调去判断这个进程是否该被启动,然后在进程启动的时候就把我们的Dll注入进去,然后hook掉他的系统函数,当他想要去执行一些系统操作的时候,就会跳转到我们的hook部分,这样我们能知道这个进程想要去干啥,根据他的行为去做破坏
回调函数一般都是静态,担心你放在类里(但我们没有设置静态,设置静态的话,别人用不成)
void ProcessNotifyProcedure(
PEPROCESS Process, HANDLE ProcessIdentity,
PPS_CREATE_NOTIFY_INFO CreateInfo)
{
HANDLE v1 = 0;
*/
//
// don't do anything before the main driver init says it's ok
//
if (!__ReadyToSandbox)
return;
//
// handle process creation and deletion. note that we are
running
// in an arbitrary thread context
//
if (ProcessIdentity) {
if (CreateInfo != NULL) {
v1 = PsGetCurrentProcessId();
if
(!ProcessNotifyProcedureCreate(ProcessIdentity,
CreateInfo->ParentProcessId, PsGetCurrentProcessId(), NULL))
//当一个进程启动
{
CreateInfo->CreationStatus = STATUS_ACCESS_DENIED;
//进程被拦截
}
}
else {
ProcessNotifyProcedureDelete(ProcessIdentity); //当一个进程销毁
}
}
}
在用WinDbg调试的时候,调到字符串,断点可能突然消失了
其实是在执行字符串相关的一些操作时,需要一定CPU时间
好方法是:在字符串操作下面下断点,直接跃过去
在Ring0,句柄是8开头(驱动级打开一个文件的特点)
注意进程回调的一个机关:!!!!!!!!!!!!!!!!!!!!!!!!
PLDR_DATA_TABLE_ENTRY v1 =
(PLDR_DATA_TABLE_ENTRY)(DriverObject->DriverSection);
v1->Flags |= 0x20;
记住这个结构
typedef struct _LDR_DATA_TABLE_ENTRY {
LIST_ENTRY LoadOrder;
LIST_ENTRY MemoryOrder;
LIST_ENTRY InitializationOrder;
PVOID ModuleBaseAddress;
PVOID EntryPoint;
ULONG ModuleSize;
UNICODE_STRING FullModuleName;
UNICODE_STRING ModuleName;
ULONG Flags;
USHORT LoadCount;
USHORT TlsIndex;
union {
LIST_ENTRY Hash;
struct {
PVOID SectionPointer;
ULONG CheckSum;
};
};
ULONG TimeStamp;
} LDR_DATA_TABLE_ENTRY, * PLDR_DATA_TABLE_ENTRY;
WinDbg调试
查看当前计算机所有进程:
! process 0 0
第一步:找到地址 使用 dq
kd> dq ParentProcess
fffff880`0308edc0 fffffa80`615c1b30
第二步:查看结构
kd> dt _eprocess fffffa80`615c1b30
+0x2e0 ImageFileName : [15] "calc.exe"
(这个2e0 面试官可能会问,64位中
在32位是174,这两个偏移要记住)
kd> !process 0 0
PROCESS fffffa80615c1b30
SessionId: 1 Cid: 0bc8 Peb: 7fffffdf000
ParentCid: 0690
DirBase: 117cb0000 ObjectTable: fffff8a0044435c0
HandleCount: 0.
Image: calc.exe
kd> dt _PS_CREATE_NOTIFY_INFO fffff880`0308ee40
KSandBox!_PS_CREATE_NOTIFY_INFO
+0x000 Size : 0x48
+0x008 Flags : 1
+0x008 FileOpenNameAvailable : 0y1
+0x008 IsSubsystemProcess : 0y0
+0x008 Reserved :
0y000000000000000000000000000000 (0)
+0x010 ParentProcessId : 0x00000000`00000690 Void
+0x018 CreatingThreadId : _CLIENT_ID
+0x028 FileObject : 0xfffffa80`61419ce0
_FILE_OBJECT
+0x030 ImageFileName : 0xfffff880`0308f680
_UNICODE_STRING "\??\C:\Windows\system32\calc.exe"
+0x038 CommandLine : 0xfffffa80`615d9070
_UNICODE_STRING ""C:\Windows\system32\calc.exe" "
+0x040 CreationStatus : 0n0
演示:
这个Flags为1代表进程启动
如果为0 代表进程消亡
面试官可能会问:你怎么知道进程启没启动?
里面ParentId 690 是桌面 (calc.exe靠桌面启动的)
(内核级的上下背景文切换)
驱动不是进程,只是一个模块
一般获得所处的环境 System.exe 4号进程
但你进程回调函数拦截,被加载过去,这时候再获得所处的环境就是对方了
有父进程启动了进程回调,传参传的是子进程的id
父进程的id放在createInfo
此时运行驱动的环境已经从System到父进程上了
进程Id用HADNLE定义
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7
BOOLEAN ProcessNotifyProcedureCreate(
HANDLE ProcessIdentity, HANDLE ParentProcessIdentity, HANDLE
CallerIdentity, VOID* Box)
{
void* v1, *v2;
ULONG v10, v20;
WCHAR* ImageName, * nptr2;
const WCHAR* ImagePath;
BOOLEAN parent_was_start_exe = FALSE;
BOOLEAN parent_had_rights_dropped = FALSE;
BOOLEAN parent_was_image_from_box = FALSE;
BOOLEAN process_is_forced = FALSE;
BOOLEAN add_process_to_job = FALSE;
BOOLEAN IsCreateTerminated = FALSE;
BOOLEAN IsHostInject = FALSE;
KIRQL Irql;
BOOLEAN added_to_dfp_list = FALSE;
BOOLEAN IsCheckForcedProgram = FALSE;
GetProcessName(
__Pool, (ULONG_PTR)ProcessIdentity, &v1, &v10,
&ImageName);
if (!v1) {
// Process_CreateTerminated(ProcessIdentity, -1);
return FALSE;
}
ImagePath = ((UNICODE_STRING*)v1)->Buffer;
if (!_wcsicmp(ImageName,
L"notepad.exe")||!_wcsicmp(ImageName, L"Test.exe"))
{
if (0)
{
}
else if (!Box)
{
//创建一个Box
//测试
//查看父进程信息
PROCESS* v1 =
FindProcess(CallerIdentity, &Irql);
if (!(v1 && !v1->IsHostInject)
&& CallerIdentity != ParentProcessIdentity)
{
ExReleaseResourceLite(__ProcessListLock);
KeLowerIrql(Irql);
}
if (v1 && !v1->IsHostInject)
{
}
else
{
IsCheckForcedProgram =
TRUE;
}
ExReleaseResourceLite(__ProcessListLock);
KeLowerIrql(Irql);
}
if (IsCheckForcedProgram)
{
const WCHAR* SidString = NULL;
//WCHAR ImagePath110[0x1000] = {
L"C:\\windows\\notepad.exe" }; 测试
Box =
GetForcedStartBox(ProcessIdentity, ParentProcessIdentity, ImagePath,
&IsHostInject, SidString);
/*
kd> db fffff8a0`05810b90
fffff8a0`05810b90 6e 00 6f 00 74 00 65 00-70 00 61 00 64 00 2e 00
n.o.t.e.p.a.d...
fffff8a0`05810ba0 65 00 78 00 65 00 00 00-00 00 00 00 00 00 00 00
e.x.e........
*/
if (Box == (BOX*)-1) {
IsCreateTerminated =
TRUE;
Box = NULL;
}
else if (Box) {
if (IsHostInject) {
}
else {
}
}
}
if (Box)
{
//通过Box和拦截住的信息创建Process
//获取进程启动时间
{
PROCESS* Process =
CreateProcess(ProcessIdentity, Box, ImagePath, &Irql);
ExReleaseResourceLite(__ProcessListLock);
KeLowerIrql(Irql);
ULONG64 CreateTime =
Process->CreateTime;
if (Process)
{
if
(!InjectProcessRequest(
ProcessIdentity, 0, CreateTime, ImageName, FALSE, FALSE))
{
}
}
}
}
FreeMemoryEx(v1, v10);
}
return TRUE;
}
分析:
GetProcessName(
__Pool, (ULONG_PTR)ProcessIdentity, &v1, &v10,
&ImageName);
if (!v1) {
v1指向Unicode,v10存的是长度
GetProcessName
void GetProcessName(
POOL* Pool, ULONG_PTR ProcessIdentity,
void** VirtualAddress, ULONG* ViewSize, WCHAR** BufferData)
{
NTSTATUS Status;
OBJECT_ATTRIBUTES ObjectAttributes;
CLIENT_ID ClientIdentity;
HANDLE ProcessHandle;
ULONG v7;
*VirtualAddress = NULL;
*ViewSize = 0;
*BufferData = NULL;
if (!ProcessIdentity)
return;
InitializeObjectAttributes(&ObjectAttributes,
NULL, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL, NULL);
ClientIdentity.UniqueProcess = (HANDLE)ProcessIdentity;
ClientIdentity.UniqueThread = 0;
Status = ZwOpenProcess(
&ProcessHandle, 0x400, &ObjectAttributes,
&ClientIdentity); //PROCESS_QUERY_INFORMATION
if (!NT_SUCCESS(Status))
return;
Status = ZwQueryInformationProcess(
ProcessHandle, ProcessImageFileName, NULL, 0, &v7);
if (Status == STATUS_INFO_LENGTH_MISMATCH)
{
ULONG v8 = v7 + 8 + 8; //申请的内存大小
//ushort length
//ushort maxLength
//wchar* Buffer NULL
//C:\windows\system32\calc.exe
//
//
UNICODE_STRING* v1 =
AllocateMemoryEx(Pool,v8,FALSE);
if (v1) {
v1->Buffer = NULL;
Status = ZwQueryInformationProcess(
ProcessHandle,
ProcessImageFileName, v1, v7 + 8, &v7);
if (NT_SUCCESS(Status) &&
v1->Buffer)
{
WCHAR* v2;
v1->Buffer[v1->Length
/ sizeof(WCHAR)] = L'\0';
if
(!v1->Buffer[0])
{
v1->Buffer[0] = L'?';
v1->Buffer[1] = L'\0';
}
v2 =
wcsrchr(v1->Buffer, L'\\');
if (v2) {
++v2;
if
(!*v2)
v2 = v1->Buffer;
}
else
v2 =
v1->Buffer;
*VirtualAddress = v1;
*ViewSize = v8;
*BufferData = v2;
}
else
FreeMemory(v1, v8);
}
}
ZwClose(ProcessHandle);
}
OA格式化
InitializeObjectAttributes(&ObjectAttributes,
NULL, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL, NULL);
然后给Clientid赋值
然后在驱动层里面打开子进程(虽然被拦截了,但还是能够打开它)
Status = ZwOpenProcess(
&ProcessHandle, 0x400, &ObjectAttributes,
&ClientIdentity); //PROCESS_QUERY_INFORMATION
打开之后OA里面就有值了
ZwOpenProcess第二个参数是打开的目的 0x400 是 PROCESS_QUERY_INFORMATION
然后查询进程
Status = ZwQueryInformationProcess(
ProcessHandle, ProcessImageFileName, NULL, 0, &v7);
第三个参数NULL,代表是否传递内存(NULL,代表不传递)
v7是查询的Length,然后用v7去申请内存
ULONG v8 = v7 + 8 + 8; //申请的内存大小
UNICODE_STRING有对齐粒度
(前两个是 2+2 = 4,但要按8,64位对齐,8+8
上面两个凑8,最后一个8)
然后我们需要让Wchar*指针指向路径
//ushort length
//ushort maxLength
//wchar* Buffer NULL
//C:\windows\system32\calc.exe
//
//
UNICODE_STRING* v1 =
AllocateMemoryEx(Pool,v8,FALSE);
FALSE是不要标志
这时候我们继续查询信息
这时候v1代表内存,存放位置
v7+8代表,越过UNICODE_STRING前两个成员
想要把路径放到第三个成员的位置
v1->Buffer = NULL;
Status = ZwQueryInformationProcess(
ProcessHandle,
ProcessImageFileName, v1, v7 + 8, &v7);
BOOLEAN InjectProcessRequest(
HANDLE ProcessIdentity, ULONG SessionIdentity, ULONG64 CreateTime,
const WCHAR* ImageName, BOOLEAN add_process_to_job, BOOLEAN
IsHostInject)
分析:ProcessId往这个注
SessionId是用户Id,切换上下背景文的(eg.我们可以用管理员身份或用户名身份运行,这个管理员和用户名就是SessionId)
CreateTime是进程创建时间,ImageName是进程名
然后首先初始化OA,初始化属性
InitializeObjectAttributes(&ObjectAttributes,
NULL, OBJ_CASE_INSENSITIVE |
OBJ_KERNEL_HANDLE, NULL, NULL);
然后以ProcessQuery身份打开这个进程
ClientIdentity.UniqueThread = NULL;
ClientIdentity.UniqueProcess = ProcessIdentity;
Status = ZwOpenProcess(&ProcessHandle, 0x400,
//PROCESS_QUERY_INFORMATION,
&ObjectAttributes, &ClientIdentity);
然后再打开,查询这个进程是32还是64
因为在64位系统上,启动的进程有32,64
在32,就是32
if (NT_SUCCESS(Status)) {
//查询启动进程的位数
Status = ZwQueryInformationProcess(
ProcessHandle,
ProcessWow64Information,
&IsWow64,
sizeof(IsWow64), &ReturnLength);
ZwClose(ProcessHandle);
}
进程消息结构
typedef struct _PROCESS_MESSAGE_
{
ULONG ProcessIdentity;
ULONG SessionIdentity;
ULONG64 CreateTime;
BOOLEAN IsWow64;
BOOLEAN add_to_job;
BOOLEAN IsHostInject;
ULONG reason;
WCHAR ImageName[64];
}PROCESS_MESSAGE,*PPROCESS_MESSAGE;
然后将信息传给RIng3
//将该信息通过PortObject返回Ring3
if (!SendLpcMessage(INJECT_PROCESS,
sizeof(ProcessContext), &ProcessContext))
Status = STATUS_SERVER_DISABLED;
这里是Lpc端口,做通信,有点像套接字,Lpc也能做进程间通信
因为它这个注入想要实现从驱动层向Ring3层实现注入,通过Lpc向Ring3传数据
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
8
梳理一下流程:
目前是当有进程启动,我们会创建进程回调
然后使用Lpc与Ring3层进行通信
InitializeCommonData
资源锁?
Irp ,FastIo都是属于ring3和Ring0的通信方法(一般情况 FastIo不用)
//创建设备对象
RtlInitUnicodeString(&v1, DEVICE_NAME);
Status = IoCreateDevice(
__DriverObject, 0, &v1,
FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN,
FALSE,
&_DeviceObject);
if (!NT_SUCCESS(Status)) {
_DeviceObject = NULL;
return FALSE;
}
面试官经常问:问什么设备对象标志要进行&运算?
_DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
本来有这个东西,现在给他去掉
DO_DEVICE_INITIALIZING 在DEVICE_OBJECT的Flags字段中
本来这个Flags大多的情况下都是在设置IO方式,如DO_BUFFERED_IO,
但特殊的位也可能需要在这里设置。
用处是防止当自己的设备对象初始化完成之前,别的模块来发送信息给自己的模块的。
如果程序仅在DriverEntry中创建DeviceObject的话,那么当前位将由IO管理器清除,
如果当前DeviceObject不是在DriverEntry中创建的,那么就要由程序员自己来清除。
主要用于PNP设备,以及过滤设备一类设备的安全创建中。
设备对象,双字
#define DEVICE_NAME L"\\Device\\KSandBox"
二维指针数组,里面存放函数的地址
LPFN_COMMONSERVICE* __CommonServices = NULL; //是一个二维指针动态存储一个连续内存的函数指针的数组
而且里面存放的类型都是LPFN_COMMONSERVICE
typedef NTSTATUS(*LPFN_COMMONSERVICE)(PPROCESS Process, ULONG64*
ParameterData);
SetServicePort
NTSTATUS SetServicePort(PPROCESS Process, ULONG64* ParameterData)
我们在这个函数里面获取RIng3创建的端口句柄,ParameterData是从RIng3传过来的参数
ParameterData是个指针,指针的1号存的就是RIng3创建的端口句柄
Ring0层现在拿到它,往端口里面写数据Ring3就能接收到
HANDLE PortHandle = (HANDLE)(ULONG_PTR)ParameterData[1];
//Ring3创建的端口句柄
然后我们通过句柄得到对象PortObject
if (PortHandle) {
//通过句柄获取对象
Status = ObReferenceObjectByHandle(
PortHandle, 0,
*LpcPortObjectType, KernelMode,
&PortObject, NULL);
}
然后把对象的值保存到全局__PortObject中,用资源锁防止干扰。也获取PsGetCurrentProcessId,当前进程Id
KIRQL Irql;
EnterResourceLock(__CommonResourceLock, &Irql);
//保存对象到全局变量中
OldObject =
InterlockedExchangePointer(
&__PortObject,
PortObject);
InterlockedExchangePointer(
&__ProcessIdentity,
PsGetCurrentProcessId());
LeaveResourceLock(__CommonResourceLock, Irql);
if (OldObject)
ObDereferenceObject(OldObject);
RIng3 是SDK,不是黑窗口
程序入口时WinMain函数,需要进行修改
INT WINAPI _tWinMain(
_In_ HINSTANCE Instance,
_In_opt_ HINSTANCE PrevInstance,
_In_ PTSTR CmdLine,
_In_ INT CmdShow
一般情况下调用约定是WinApi
然后把子系统这个地方修改一下
而且Windows Service程序 ,不允许调试。
static CDriverAssist* m_Instance;
bool CDriverAssist::Initialize()
{
m_Instance = new CDriverAssist(); //单例模式
面试官喜欢问的:单例模式
单例模式:构造函数私有。确保对象只能有一个,不能有多个对象
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
9
配Dll路径
masm不要打上
然后给Entry.asm
选自定义工具
32位
然后再弄个无入口点
最后是为了生成dll
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
10
$意思是当前
$+5是call指令是5个字节(e8)
InitializeInjection(类中) ----> InjectionPrepare(类外) ----->
InjectionPrepareInternal(类外)
typedef struct _MY_TARGETS
{
unsigned long long Start;
unsigned long long DataInfo;
unsigned long long DetourCode;
} MY_TARGETS;
Ring3的注入核心
ULONG InjecitonPrepareInternal(BOOLEAN IsWow64, void** TextAddress, ULONG*
TextLength,
ULONG* StartOffset, ULONG* DataInfoOffset, ULONG*
DetourCodeOffset)
ImageSectionHeader = IMAGE_FIRST_SECTION(ImageNtHeaders);
if (ImageNtHeaders->FileHeader.NumberOfSections < 2) return
Error;
if (strncmp((char*)ImageSectionHeader[0].Name,
INJECTION_SECTION, strlen(INJECTION_SECTION)) ||
strncmp((char*)ImageSectionHeader[v100].Name,
SYMBOL_SECTION, strlen(SYMBOL_SECTION))) {
return Error;
}
MyTargets =
(MY_TARGETS*)&BufferData[ImageSectionHeader[v100].PointerToRawData];
//文件粒度
if (StartOffset) *StartOffset = (ULONG)(MyTargets->Start -
ImageBase - ImageSectionHeader[0].VirtualAddress);
if (DataInfoOffset) *DataInfoOffset =
(ULONG)(MyTargets->DataInfo - ImageBase -
ImageSectionHeader[0].VirtualAddress);
if (DetourCodeOffset) *DetourCodeOffset =
(ULONG)(MyTargets->DetourCode - ImageBase -
ImageSectionHeader[0].VirtualAddress);
*TextAddress = BufferData +
ImageSectionHeader[0].PointerToRawData; //Old version: head;
*TextLength = ImageSectionHeader[0].SizeOfRawData; //Old
version: (ULONG)(ULONG_PTR)(tail - head);
xor指令:2个字节
xor rdx, rdx
nop -----90
在InjectionPrepare里
__LdrInitializeThunk = (ULONG_PTR)GetProcAddress(__Ntdll,
"LdrInitializeThunk");
if (!__LdrInitializeThunk)
return Error;
面试题:LdrInitializeThunk这个函数很重要,面试官会问具体作用
LdrInitializeThunk
LdrInitializeThunk()
Windows的Dll装入(除ntdll.dll外)和连接是通过ntdll.dll中的一个函数LdrInitializeThunk()实现的
在进入这个函数之前,目标 EXE 映像已经被映射到当前进程的用户空间,系统 DLL ntdll.dll 的映像也已经被映射, 但是并没有在 EXE 映像与
ntdll.dll 映像之间建立连接(实际上EXE 映像未必就直接调用 ntdll.dll 中的函数)。
LdrInitializeThunk()是 ntdll.dll 中不经连接就可进入的函数,实质上就是 ntdll.dll 的入口。除 ntdll.dll
以外,别的 DLL 都还没有被装入(映射)。此外,当前进程(除内核中的“进程控制块”EPROCESS
等数据结构外)在用户空间已经有了一个“进程环境块”PEB,以及该进程的第一个“线程环境块”TEB。这就是进入__true_LdrInitializeThunk()前的“当前形势”。
功能描述
1. 加载和初始化 DLL
在进入 LdrInitializeThunk 函数之前,目标 EXE 映像已经被映射到当前进程的用户空间,系统
DLL ntdll.dll 的映像也已经被映射,但尚未建立连接1。
2. 初始化进程环境块(PEB)
该函数首先检查当前进程的 PEB 是否已初始化,如果未初始化,则进行初始化,包括创建进程堆和加载器信息2。
3. 创建和管理模块队列
LdrInitializeThunk 还负责创建和管理三个模块队列:InLoadOrderModuleList、InMemoryOrderModuleList 和 InInitializationOrderModuleList,用于维护已加载模块的信息。
通过这些步骤,LdrInitializeThunk 确保了 DLL 的正确加载和初始化,为进程的正常运行提供了基础。
mov edi,edi
微软的函数开头都是这个热补丁指令
面试官喜欢问
作用
热补丁:在运行时修改函数行为。通过将 MOV EDI, EDI 修改为短跳转指令,再将其上方的 NOP 指令修改为长跳转指令,实现函数行为的动态修改。
效率优化:相比于两条 NOP 指令,执行一条 MOV 指令所需的 CPU 时钟周期更少,从而提高了效率。
DACL想增加牛逼的简历,就去学习
bool CDriverAssist::InitializePortAndThreads()
端口名用滴答随机数,防止冲突
端口名是双字
static const WCHAR* _PortName = L"\\RPC Control\\" NAME L"Port";
wsprintf(PortName, L"%s-internal-%d", _PortName, GetTickCount());
RtlInitUnicodeString(&v1, PortName);
初始化属性OA
并创建端口 获得句柄值
InitializeObjectAttributes(
&ObjectAttributes, &v1, OBJ_CASE_INSENSITIVE,
NULL, NULL);
Status = __NtCreatePort(
(HANDLE*)&m_PortHandle, &ObjectAttributes, 0,
MAX_MESSAGE_LENGTH, NULL);
在Ring3层
//创建端口
typedef
NTSTATUS(NTAPI* LPFN_NTCREATEPORT)(OUT PHANDLE PortHandle,
IN POBJECT_ATTRIBUTES ObjectAttributes,
IN ULONG MaxConnectInfoLength,
IN ULONG MaxDataLength,
IN ULONG MaxPoolUsage);
extern LPFN_NTCREATEPORT __NtCreatePort;
LPFN_NTCREATEPORT __NtCreatePort = NULL;
浙公网安备 33010602011771号