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

实现监控

 

 

这个地方的内存存储方式知识点挺好,但时间关系先略过

(映射相关的)

Image

 

下面这个地方有个进程回调通知:当你进程一启动,你的驱动就被调度了,进程被拦截

就走进程回调: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部分,这样我们能知道这个进程想要去干啥,根据他的行为去做破坏

 

 

 

回调函数一般都是静态,担心你放在类里(但我们没有设置静态,设置静态的话,别人用不成)

Image

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时间

好方法是:在字符串操作下面下断点,直接跃过去

Image

 

在Ring0,句柄是8开头(驱动级打开一个文件的特点)

Image

 

注意进程回调的一个机关:!!!!!!!!!!!!!!!!!!!!!!!!

 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

 

演示:

Image

这个Flags为1代表进程启动

如果为0  代表进程消亡

面试官可能会问:你怎么知道进程启没启动?

 

里面ParentId 690 是桌面  (calc.exe靠桌面启动的)

Image

 

(内核级的上下背景文切换)

驱动不是进程,只是一个模块

一般获得所处的环境  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

然后把子系统这个地方修改一下

Image

而且Windows Service程序 ,不允许调试。

 

static CDriverAssist* m_Instance;

 

bool CDriverAssist::Initialize()

{

    m_Instance = new CDriverAssist();  //单例模式

Image

面试官喜欢问的:单例模式

单例模式:构造函数私有。确保对象只能有一个,不能有多个对象

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

9

配Dll路径

Image

 

 

masm不要打上

Image

然后给Entry.asm

Image

选自定义工具

Image

 

32位

Image

 

 

然后再弄个无入口点

Image

 

最后是为了生成dll

Image

 

 

 

 

 

 

 

 

 

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

10

 

 

 

$意思是当前

$+5是call指令是5个字节(e8)

Image

 

 

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想增加牛逼的简历,就去学习

Image

 

 

 

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;

posted @ 2026-03-27 14:49  L1dk  阅读(8)  评论(0)    收藏  举报