Callback

等了键盘等了好久!终于拿到了喜欢的HHKB键盘!然后又可以继续学习!写笔记了!嗨嗨嗨!等待键盘的日子度日如年!嗨嗨嗨!

模块 线程 进程回调

  1. 模块监视 : LoadLibraryA NtLoadDriver
  2. 进程监视 : 进程创建 进程销毁
  3. 线程监视 : 线程创建 线程销毁

线程 进程 模块注册回调还有销毁回调都是这些的代码

PsSetCreateProcessNotifyRoutine;
PsSetCreateProcessNotifyRoutineEx;
PsSetCreateThreadNotifyRoutine;
PsSetCreateThreadNotifyRoutineEx;
PsSetLoadImageNotifyRoutine;
PsRemoveLoadImageNotifyRoutine;
PsRemoveCreateThreadNotifyRoutine;

PsSetCreateProcessNotifyRoutine:

Untitled

//PsSetCreateProcessNotifyRoutine(createProcessCallback, FALSE);
VOID CreateThreadCallback(
	_In_ HANDLE ProcessId,
	_In_ HANDLE ThreadId,
	_In_ BOOLEAN Create
)
{
	if (Create)
	{
		DbgPrintEx(77, 0, "[db]:ProcessId = %d,ThreadId = %d 线程创建了\r\n", ProcessId, ThreadId);
	}
	else
	{
		DbgPrintEx(77, 0, "[db]:ProcessId = %d,ThreadId = %d 线程销毁\r\n", ProcessId, ThreadId);
	}
}

PsSetCreateProcessNotifyRoutineEx:

这个函数有些不一样,我们可以看一下ida

a1 是我们的回调函数,a2 是开关 创建回调还是销毁回调的开关

Untitled

因为a2 是开关,当等于TURE的时候是销毁回调,所以我们看创建回调的时候只需要看这部分:

Untitled

由于a3 = 1 所以判断是否回调成功的逻辑在 MmVerifyCallbackFunction 如果不看这里,正常自己写代码不处理的话,会返回st = 0xC0000022 错误,#define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L) 拒绝加载 这部分和签名是有关系的,可以看到是这里让v5 = 1

Untitled

追溯一下v4,可以看到a1传入的是我们的回调函数,这里判断了回调函数在我们的哪个文件中,所以v4是我们的文件

Untitled

逆向可以知道类型是KLDR_DATA_TABLE_ENTRY,判断当前文件的flags的位是不是0x20

Untitled

PLDR_DATA_TABLE_ENTRY ex = (PLDR_DATA_TABLE_ENTRY)pDriver->DriverSection;
ex->Flags |= 0x20;
st = PsSetCreateProcessNotifyRoutineEx(createProcessCallbackEx,FALSE);

VOID createProcessCallbackEx(
	_Inout_ PEPROCESS Process,
	_In_ HANDLE ProcessId,
	_Inout_opt_ PPS_CREATE_NOTIFY_INFO CreateInfo
)
{
	DbgBreakPoint();
	if (CreateInfo)
	{
		DbgPrintEx(77, 0, "[db]:ProcessId = %d ,%wZ,CreationStatus = %x 创建了\r\n", ProcessId, CreateInfo->ImageFileName, CreateInfo->CreationStatus);
	}
	else
	{
		DbgPrintEx(77, 0, "[db]:ProcessId = %d 销毁了\r\n", ProcessId);
	}
}

PsSetCreateThreadNotifyRoutine:

PsSetCreateThreadNotifyRoutine(CreateThreadCallback);

VOID CreateThreadCallback(
	_In_ HANDLE ProcessId,
	_In_ HANDLE ThreadId,
	_In_ BOOLEAN Create
)
{
	if (Create)
	{
		DbgPrintEx(77, 0, "[db]:ProcessId = %d,ThreadId = %d 线程创建了\r\n", ProcessId, ThreadId);
	}
	else
	{
		DbgPrintEx(77, 0, "[db]:ProcessId = %d,ThreadId = %d 线程销毁\r\n", ProcessId, ThreadId);
	}
}

PsSetLoadImageNotifyRoutine:

PsSetLoadImageNotifyRoutine(LoadImageCallback);

VOID LoadImageCallback(
	_In_opt_ PUNICODE_STRING FullImageName,
	_In_ HANDLE ProcessId,                // pid into which image is being mapped
	_In_ PIMAGE_INFO ImageInfo
)

{
	if (ImageInfo->SystemModeImage)
	{
		DbgPrintEx(77, 0, "[db]:加载驱动 %wZ,ProcessId = %d\r\n", FullImageName, ProcessId);
	}
	else
	{
		DbgPrintEx(77, 0, "[db]:dll路径 %wZ,pid = %d\r\n", FullImageName, ProcessId);
	}
}

注册后需要销毁,直接在unload的时候,写入销毁即可

Untitled

逆向分析 相关回调函数,寻找回调控制变量,然后修改变量的某个位,来控制回调开关:

通过PspNotifyEnableMask这个全局变量来控制回调的开关

Untitled

对象回调

对象回调就是如下这种,就是pchunter里面的这个object钩子

Untitled

如下的CallbackList是Windows支持的回调

Untitled

Windows 提供了一些回调的接口是优先于这个CallbackList的

Untitled

首先回到用 SecurityProcedure 首先要先检查安全性,然后调用OpenProcedure 打开进程产生回调

这个DumpProcedure回调是用了DuplicateHandle相当于 假设有个进程A openprocess 私有句柄表 插入了一个 这个DuplicateHandle 就是私有句柄表拷贝到我这个进程B中

SupportsObjectCallbacks 这个位是控制CallbackList的,这个位置不能随便改,原本要支持就是支持了,不支持就是不支持了,如果进行改动会产生pg

ValidAccessMask 这个就是相当于openprocess中的all的那个权限与这个位置进行与操作,如果他们和传过来的一样的就OK,如果不相等就返回,所以这里改成0,就是别人没有办法openprocess打开其他所有的东西,这个是一个全局的类型,所以会影响全局

测试一下: 用户态调试系统初始化函数→ DbgInitialize

Untitled

把ValidAccessMask = 0

Untitled

结果:

Untitled

现在玩一下这个回调钩子,这个回调钩子是一套API

#include <ntifs.h>

PVOID RegistrationHandle = NULL;

EXTERN_C PUCHAR NTAPI PsGetProcessImageFileName(PEPROCESS eprocess);

typedef struct _LDR_DATA_TABLE_ENTRY
{
	LIST_ENTRY InLoadOrderLinks;                                    //0x0
	LIST_ENTRY InMemoryOrderLinks;                                  //0x8
	LIST_ENTRY InInitializationOrderLinks;                          //0x10
	VOID* DllBase;                                                          //0x18
	VOID* EntryPoint;                                                       //0x1c
	ULONG SizeOfImage;                                                      //0x20
	UNICODE_STRING FullDllName;                                     //0x24
	UNICODE_STRING BaseDllName;                                     //0x2c
	ULONG Flags;                                                            //0x34

}LDR_DATA_TABLE_ENTRY, * PLDR_DATA_TABLE_ENTRY;

OB_PREOP_CALLBACK_STATUS
ProcessPreCallBack(
	_In_ PVOID RegistrationContext,
	_Inout_ POB_PRE_OPERATION_INFORMATION OperationInformation
)
{
	PEPROCESS CurProcess = IoGetCurrentProcess();
	PEPROCESS TargetProcess = (PEPROCESS)OperationInformation->Object;

	PUCHAR curName = PsGetProcessImageFileName(CurProcess);
	PUCHAR targetName = PsGetProcessImageFileName(TargetProcess);

	if (OperationInformation->Operation == OB_OPERATION_HANDLE_CREATE)
	{
		if (strstr(targetName, "1.exe"))
		{
			OperationInformation->Parameters->CreateHandleInformation.DesiredAccess = 0;
		}
	}
	else
	{
		if (strstr(targetName, "1.exe"))
		{
			OperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess = 0;
		}
	}
	return OB_PREOP_SUCCESS;
}

VOID
DriverUnload(
	_In_ struct _DRIVER_OBJECT* DriverObject
)
{
	if (RegistrationHandle)
	ObUnRegisterCallbacks(RegistrationHandle);
}

NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pSeg)
{
	OB_CALLBACK_REGISTRATION ObRegInfo = { 0 };
	ObRegInfo.Version = ObGetFilterVersion();
	ObRegInfo.OperationRegistrationCount = 1;
	ObRegInfo.RegistrationContext = NULL;
	RtlInitUnicodeString(&ObRegInfo.Altitude, L"12345");

	OB_OPERATION_REGISTRATION ObOpRegInfo = { 0 };
	ObOpRegInfo.ObjectType = *PsProcessType;
	ObOpRegInfo.Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
	ObOpRegInfo.PreOperation = ProcessPreCallBack;
	ObOpRegInfo.PostOperation = NULL;

	ObRegInfo.OperationRegistration = &ObOpRegInfo;

	PLDR_DATA_TABLE_ENTRY ChangePd = (PLDR_DATA_TABLE_ENTRY)pDriver->DriverSection;
	ChangePd->Flags |= 0x20;

	NTSTATUS st = ObRegisterCallbacks(&ObRegInfo, &RegistrationHandle);
	

	pDriver->DriverUnload = DriverUnload;
	return STATUS_SUCCESS;
}

Untitled

大概意思就是:

Untitled

回调结构分析

回调结构分析 主要看ObRegisterCallbacks,通过逆向主要确定这两个结构体

Untitled

写一个openprocess的回调 然后测试一下

Untitled

解析可以很好看出来

Version = 0x100

OperationRegistrationCount = 0x1

RegistrationContext = NULL

Altitude = “12345” db 一下地址就行 我没有操作 → 自己操作一下就行

大概都在这个表中,可以很好的解析出来!

Untitled

ObpCallPreOperationCallbacks 调用你注册的回调的 要是把这个函数hook了 就完全可以让你自己的openprocess操作跳过所有的回调

还有函数也需要处理一下 就是ExEnumHandleTable,之前上一章的笔记最后通过这个遍历句柄,来实现操作,可以进行降权操作,所以也需要处理一下

龟哥的代码:

BOOLEAN NTAPI My_ExEnumHandleTablePtr(IN PVOID HandleTable,IN PVOID EnumHandleProcedure,IN PVOID EnumParameter,OUT PHANDLE Handle OPTIONAL)
{
	PEPROCESS Process = NULL;

	if (HandleTable)
	{
		if (Version > 7601)
		{
			Process = (PEPROCESS)(((PULONG64)HandleTable)[2]);
		}
		else
		{
			Process = (PEPROCESS)(((PULONG64)HandleTable)[1]);
		}
	}

	if (Process && PsGetCurrentProcess() != Process)
	{
		if (!strcmp(PsGetProcessImageFileName(Process), "MyDebug.exe"))
		{
			return FALSE;
		}
	}
	MyExEnumHandleTable OriginalExEnumHandleTable = ExEnumHandleTableDetach->Entry;

	return OriginalExEnumHandleTable(HandleTable,EnumHandleProcedure,EnumParameter,Handle);
}

VOID HookExEnumHandleTable(BOOL lj)
{
	Version = CheckSystemVersion();

	PVOID ul_ExEnumHandleTable = GetProcAddress(L"ExEnumHandleTable");
	if (!ul_ExEnumHandleTable)
	{
		return;
	}
	if (lj)
	{
		ExEnumHandleTableDetach = GuardAttach(&ul_ExEnumHandleTable, My_ExEnumHandleTablePtr);
	}
	else
	{
		GuardDetach(&ul_ExEnumHandleTable, ExEnumHandleTableDetach, My_ExEnumHandleTablePtr);
	}

}
NTSTATUS My_ObpCallPreOperationCallbacksPtr(IN ULONG64 a1,IN ULONG64 a2,IN ULONG64 a3)
{
	MyObpCallPreOperationCallbacks OriginalObpCallPreOperationCallbacks = ObpCallPreOperationCallbacksDetach->Entry;
	if (!strcmp(PsGetProcessImageFileName(PsGetCurrentProcess()), "MyDebug.exe"))
	{
		//__debugbreak();
		return STATUS_SUCCESS;
	}
	return OriginalObpCallPreOperationCallbacks(a1, a2, a3);
}

VOID HookObpCallPreOperationCallbacks(BOOL lj)
{

	PVOID ul_ObpCallPreOperationCallbacks = address[ObpCallPreOperationCallbacks];
	if (!ul_ObpCallPreOperationCallbacks)
	{
		return;
	}
	if (lj)
	{ 
		ObpCallPreOperationCallbacksDetach = GuardAttach(&ul_ObpCallPreOperationCallbacks, My_ObpCallPreOperationCallbacksPtr);
	}
	else
	{
		GuardDetach(&ul_ObpCallPreOperationCallbacks, ObpCallPreOperationCallbacksDetach, My_ObpCallPreOperationCallbacksPtr);
	}

}

CE搜数据手法

1.找到CE进程。把CE句柄表断链

2.CE进程断链

3.把CE文件名字改成csrss.exe

4.注册句柄回调。优先级小于保护优先级。也就是海拔数字大于保护的回调海拔数字

不过这样会被pg,应该没关系!有两三个小时就够用啦,上去搜搜数据啥的!