杂七杂八博客读后感

/Windows句柄章/
https://bbs.pediy.com/thread-90449-2.htm pspcidtable完全解读
PspCidTable与每个进程私有的句柄表异同点:
1.PspCidTable 中存放的对象是系统中所有的进线程对象指针,其索引就是 PID 和 CID 2.PspCidTable中存放是对象体(指向EPROCESS和ETHREAD),而每个进程私有的句柄表则存放的是对象头(OBJECT_HEADER)3.PspCidTable 是一个独立的句柄表,而每个进程私有的句柄表以一个双链连接起来
3.PspCidTable 是一个独立的句柄表,而每个进程私有的句柄表以一个双链连接起来

PspCidTable 访问的是句柄对象体 得减去0x18才是对象头
EPROCESS 通过 HANDLE_TABLE 访问索引直接是对象头

https://bbs.pediy.com/thread-100352.htm?source=1 [原创]Windows Xp句柄表结构 笔记
与https://bbs.pediy.com/thread-90449-2.htm类似 都是讲解句柄
但多了些实际操作
windows能保证对象头的分配地址总是8的倍数,所以这32位的对象头的指针低3位肯定都为0,windows将这低3位用作其他用途;因为内核对象分配都在0x80000000以上,所以最高位必须为1,window把这位当作锁位。因此,其实把最低3位置0,最高位置1才是真正指向对象头部。
winXP的句柄还是三层结构,每一层的大小为PAGE_SIZE。对于X86系统就是4KB。
并且每一层的最后一个元素用作统计。
因此,第2、1层可以存储4KB/4 - 1 = 1023项地址。
第0层可以存储4KB / 8 - 1 = 511项。

https://bbs.pediy.com/thread-84827.htm?source=1 Windows句柄表分配算法分析
ExCreateHandleTable先ExpAllocateHandleTable,然后把申请的handle_table放到句柄表双链
PspCidTable通过ExCreateHandleTable创建,再从双链中移除,成为一个独立的句柄表

有具体代码
//核心是下面两句
HandleTable = ExpAllocateHandleTable( Process, TRUE );//创建句柄表
InsertTailList( &HandleTableListHead, &HandleTable->HandleTableList );//放入双向链表中

PHANDLE_TABLE_ENTRY NewLowLevel = NULL, HandleEntry;
ULONG BaseHandle;
//
// Allocate the pool for lower level
//
NewLowLevel = ExpAllocateTablePagedPoolNoZero( HandleTable->QuotaProcess,
TABLE_PAGE_SIZE
);//申请内存,大小为一级表的大小TABLE_PAGE_SIZE
NewMidLevel = ExpAllocateTablePagedPool( HandleTable->QuotaProcess,
PAGE_SIZE
); //申请一块内存作为MidLevel,即二级表,大小为PAGE_SIZE,用以存放一级表的指针

if (NewMidLevel == NULL) {

return NULL;

}

//
// If we need a new mid-level, we’ll need a low-level too.
// We’ll create one and if success we’ll save it at the first position
//

NewLowLevel = ExpAllocateLowLevelTable( HandleTable, DoInit ); //申请一个一级表.
//有人问过为什么这个函数在申请二级表时同时还会申请一个一级表,看这个函数的调用时机就知道了.
//调用过程ExCreateHandle->ExpAllocateHandleTableEntry->ExpAllocateHandleTableEntrySlow->ExpAllocateMidLevelTable
//对ExCreateHandle更具体的分析,那是句柄分配的知识,稍后再说,以免偏题,现在只须知道调用ExpAllocateHandleTableEntrySlow时则表明句柄已达上限,需要再申请新的句柄表就行了
//而ExpAllocateHandleTableEntrySlow调用ExpAllocateMidLevelTable的第一个时机是TableLevel=0且句柄已达上限的时候,
//这时候需要申请这个二级表,那就说明一级表不够用了(三级表和二级表都只放指针,一级表中才是真正放内容的),需要再申请一个一级表,而两个一级表就使得句柄表的级数跃升为两级(MidLevel).所以,申请MidLevel的Table时其实就是稍带着把再申请一个一级表的工作也做好了(同样的,前面已经看到,申请HANDLE_TABLE时也是同时申请好了第一个一级表),这只是一级表跃升为二级表时的一个必做工作,仅此而已.总的说,二级表也只是框架,它有了内容(一级表)才能真正去放东西
//调用ExpAllocateMidLevelTable的另一种情况是此时TableLevel=2,但最后一个二级表已放满了.此时要申请一个一级表就需要先申请一个新的二级表,情况和前面类似了
if (NewLowLevel == NULL) {

ExpFreeTablePagedPool( HandleTable->QuotaProcess,
					   NewMidLevel,
					   PAGE_SIZE
					 );

return NULL;

}

https://bbs.pediy.com/thread-73028.htm?source=1 Windows XP 与 Windows 2000 句柄表
PspCidTable(未导出)是指向HANDLE_TABLE指针,这是一个特殊的句柄表,保存着所有进程和线程对象的指针;PID(进程ID)和 ThreadID(线程ID)就是在这个句柄表中的索引。它不属于任何进程,被称作内核句柄表,但在XP、2003中也属于名为“system”进程的。

https://bbs.pediy.com/thread-82982.htm?source=1 有关句柄分配算法
分配句柄算法
https://bbs.pediy.com/thread-84827.htm?source=1更详细

https://bbs.pediy.com/thread-195322.htm?source=1 X64下的解析句柄表
自己实现ExEnumHandleTable
MmIsAddressValid
MmGetSystemRoutineAddress
EnumHandleProcedure

https://bbs.pediy.com/thread-181544.htm?source=1 关于ObReferenceObjectByHandle中对句柄的处理
NtQueryObject
获取句柄对象名和类型名的时候都会使用NtQueryObject函数
NtQueryObject函数中是通过ObReferenceObjectByHandle根据Handle获得Object
PsGetCurrentProcessByThread
PsGetCurrentThread
ObpIncrPointerCount
SeComputeDeniedAccesses
EncodeKernelHandle
DecodeKernelHandle
_ObReferenceObjectByHandleWithTag
SeComputeDeniedAccesses

/hook章/
https://bbs.pediy.com/thread-251412.htm?source=1 一篇文章带你理解HOOK技术
ImageDirectoryEntryToData
AddressHook
IAT_HOOK 程序中存储导入函数地址的数据结构,如果HOOK了导入函数地址。就可以在函数调用的时候,将函数流程HOOK到我们指定的流程。
1.构造Detour函数
2.获取Target函数地址
3.获取Target函数的IAT地址
EAT_HOOK 使用EAT_HOOK需要注意一下两点:第一:EAT存储的是函数地址的偏移,所以在HOOK EAT的时候需要加上基地址,在写入EAT的时候,Detour地址需要减去BaseAddress。第二,EAT不对隐式链接起作用,只对显示链接起作用,也就是说对于那种GetProcAddress的那种调用起作用。
步骤
1.获取Target函数在HookModule上的RVA
2.获取导出函数数组首地址
3.遍历查找Target函数RVA
4.切记在修改函数地址之前,需要保存EAT地址和原函数地址、
5.将Detour函数地址写入EAT
VirtualFunctionHook
C++虚函数存在的意义是为了方便使用多态性。在实现虚函数Hook的时候需要注意如下问题:1.在构建DetourFun函数的时候,一定要构造DetourClass,因为在调用虚函数的时候使用了Thiscall的函数调用约定,如果直接调用detourfun函数应该使用的标准调用约定,两者不统一,会出错。2.当使用Trampolinefun回调的时候,需要重新实例化一个TrampolineClass。
GetClassVirtualFnAddress
VirtualProtect
第一步:仍然是构造DetourClass类和TrampolineClass类
第二歩:将Target函数地址保存在TableTrampoline虚表中,方便回调。这时候需要获取两个值,第一个TableTrampoline虚表,第二个TargetFun地址。由于TableTrampoline虚函数表在类的起始位置。所以类的地址就是虚函数表的地址,第二,TargetFun函数地址位于虚函数中,存储在类似于数组的结构,可以用其索引指向获取虚函数地址
第三步,将Detour函数地址写入到TargetClass的原始虚表TableToHook中
InlineHook
WriteProcessMemory
InlineHook(A)
这一类InlineHook是一类较为特殊的InlineHook,他修改的不是开始的多个字节,而是修改Target函数中call指令的地址。比如说VirtualAlloc函数中调用了VirtualAllocEx函数,这次inlineHook其实就是修改了VirtualAllocEx的地址(调用处的地址),这样做的好处是可以避免被一些Hook检测工具检测。但是这样的缺点是兼容性不是很好,因为一些API的函数可能会因为系统的改变而改变。
InlineHook(B)
使用InlineHook,需要了解到三种函数:

Target函数:目标函数,我们选定的HOOK的函数
Detour函数:我们构造的函数,用于搭载HOOK完Target函数后,我们制定的操作
TrampolineFun函数:负责回调Target函数,在回调的时候,需要注意的时候重新执行HOOK修改的三条指令,并绕过HOOK的地方

     InlineHook主要的步骤就是修改Target函数的前五个字节。大概有以下几个步骤:

Step1:构造Detour函数
Step2:构造TrampolineFun函数
Step3:获取TrampolineFun和HookPoint的地址。
Step4:填充需要修改的指令
Step5:使用ReadProcessMemory保存原指令
Step5:使用WriteProcessMemory修改Target函数指令

     虽然步骤看着简单,但是里面坑还是很多的。首先是构造构造Detour函数,这里需要注意的是Detour函数声明需要和Target函数保持一致,否则函数返回会异常,而且还要在DetourFun中还要调用TrampolineFun。
	 第二歩是构造TrampolineFun函数,Trampoline函数是用于在Detour回调Target函数,在使用Trampoline首先执行Target被修改的三条指令,为了避免调用Target函数堆栈异常。然后使用jmp的方式跳转到Target函数中第四条指令,绕过被修改的指令,这是为了实现永久化
	 第三步是获取HookPoint和pfnTrampolineFun的地址,这一步的目的我也不是很清楚,但是我删除这两个指令,程序也是正常的。,接着设置回调点,这是为了在TrampolineFun中,设置跳转。回调点为了被修改指令之后
	 第四步是填充我们修改的指令
InlineHook(C)

SSDT_HOOK
得到服务索引的步骤:
Step1:将Ntdll.dll载入内存
Step2:获取导出函数地址
Step3:计算函数索引
InitializeObjectAttributes
ZwOpenFile
ZwCreateSection
ZwMapViewOfSection

MmCreateMdl
MmMapLockedPages
RtlCopyMemory
MmUnmapLockedPages
IoFreeMdl
GetSSDTFunctionIndex
MmBuildMdlForNonPagedPool

IRP_Hook
Object Hook
NT_SUCCESS
ObReferenceObjectByHandle
OBJECT_TO_OBJECT_HEADER
sysenterHook

面经
首先是二次HOOK,就是被别人HOOK了之后自己再次HOOK,这里可以提供4种方法,第一可以换个位置HOOK。第二就是替换原HOOK,也就是说将别人HOOK的指令修改为自己HOOK的指令。这样应该是比较有效的,但是需要注意的是修改指令数量一定要和对方的一致,或者修改之前将原来的HOOK还原,不然容易产生错误。第三,在Detour函数中HOOK,第四,在Target函数中的原来HOOK的地址后面HOOK。
第二是X64下HOOK应该注意什么?首先X64和X86本质区别就是地址总线上的差别,一个是264次,一次传输64位数据,一个是232次,一次传输32位数据。由此造成的差异就是内存地址大小问题,在32位机器上主要是4个字节,64位机器上就变成了8个字节。这样的话对于指针的使用就需要考虑到两个架构上的兼容性和差异性。例如在32下可以使用ULONG,但是在64位下使用ULONG_PTR。这样就可以有效避免由于编码问题产生的异常(或者统一使用ULONG_PTR)。第二就是PE格式上,由于x86和x64PE结构上存在微小差异,所以在进行AddressHook的时候需要注意。第三可能涉及到跳转的问题。

检测HOOK:

1.HOOK修改的是内存中的数据,本地文件却没有修改。可以将本地文件加载到内存中,然后进行对比
2.对内存模块进行CRC校验
3.设置回调函数,检测某个IAT或者函数的前几个指令是否被修改
4.对VirtualProtect函数和WriteProcess函数进行HOOK,检测修改内容的合法性
5.利用PsSetCreateProcessNotifyRoutineEx注册回调函数,监控进程创建,对比特定的进程,如果创建,设置创建标志为假,创建失败
6.利用PsSetCreateThreadNotifyRoutine注册回调函数,监控线程创建,通过进程路径.找到对应进程名.判断是否符合,如果是的话.找到回调函数地址( pWin32Address = (UCHAR**)((UCHAR)Thread + 0x410);)并改为C3
7.利用PsSetLoadImageNotifyRoutine拦截模块,首先需要获取模块基地址(让其载入),PE寻找基地址,解析到OEP,修改oep为ret即可

https://blog.csdn.net/qq_37232329/article/details/94836379 导出表钩子之EAT HOOK解析
直接修改PE实现hook 价值较高
EAT HOOK与IAT HOOK原理是一样的,都是通过修改PE来达到HOOK的目的。只是EAT HOOK是从来源入手而IAT HOOK则是从自身入手。

  1. 通过IMAGE_OPTIONAL_HEADER.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress找到导出表的地址(注意这里是RVA要加上ImageBase才能得到VA,下面都是一样)。与导入表不同,导出表只有一张!

  2. 遍历IMAGE_EXPORT_DESCRIPTOR.AddressOfNames(一共IMAGE_EXPORT_DESCRIPTOR.NumberOfNames个)找到被HOOK的API名称。记得与此同时要保存IMAGE_EXPORT_DESCRIPTOR.AddressOfNameOrdinals的值

  3. 如果找到了API名称则把IMAGE_EXPORT_DESCRIPOTR.AddressOfFunctions[IMAGE_EXPORT_DESCRIPTOR.AddressOfNameOrdinals]即原函数的RVA地址替换成新函数地址。
    #include <windows.h>
    #include
    #include <tchar.h>

BOOL EATHook(LPCTSTR szDllName, LPCTSTR szFunName, LPVOID NewFun) {
DWORD addr = 0;
DWORD index = 0;
DWORD dwProtect;
HMODULE hMod = LoadLibrary(szDllName);
if (NULL == hMod)
return(FALSE);
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hMod;
PIMAGE_OPTIONAL_HEADER pOptHeader = (PIMAGE_OPTIONAL_HEADER)((PBYTE)hMod + pDosHeader->e_lfanew + 24);
PIMAGE_EXPORT_DIRECTORY pExpDes = (PIMAGE_EXPORT_DIRECTORY)((PBYTE)hMod + pOptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
PULONG pAddressOfFunctions = (PULONG)((PBYTE)hMod + pExpDes->AddressOfFunctions);
PULONG pAddressOfNames = (PULONG)((PBYTE)hMod + pExpDes->AddressOfNames);
PUSHORT pAddressOfNameOrdinals = (PUSHORT)((PBYTE)hMod + pExpDes->AddressOfNameOrdinals);

for (int i = 0; i < pExpDes->NumberOfNames; ++i) {
	index = pAddressOfNameOrdinals[i];
	LPCTSTR pFuncName = (LPTSTR)((PBYTE)hMod + pAddressOfNames[i]);
	if (!_tcscmp((LPCTSTR)pFuncName, szFunName)) {
		addr = pAddressOfFunctions[index];
		break;
	}
}
VirtualProtect(&pAddressOfFunctions[index], 0x1000, PAGE_READWRITE, &dwProtect);
pAddressOfFunctions[index] = (DWORD)NewFun - (DWORD)hMod;
WriteProcessMemory(GetCurrentProcess(), &pAddressOfFunctions[index], (LPCVOID)((DWORD)NewFun - (DWORD)hMod), sizeof(NewFun), &dwProtect);

return(TRUE);

}

int __stdcall MyMessageBox(
HWND hWnd,
LPCTSTR lpText,
LPCTSTR lpCaption,
UINT uType
) {
_tprintf(“Hello, world!\n”);

return(0);

}

typedef int (WINAPI* LPFNMESSAGEBOX)(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType);

int _tmain() {
EATHook(“USER32.dll”, “MessageBoxA”, MyMessageBox);
HMODULE hDll = GetModuleHandle(“USER32.dll”);
LPFNMESSAGEBOX lpMessageBox = (LPFNMESSAGEBOX)GetProcAddress(hDll, “MessageBoxA”);
if (NULL == lpMessageBox)
return(-1);
lpMessageBox(NULL, “Hello, EAT Hook”, “Info”, MB_OK);

return(0);

}

https://bbs.pediy.com/thread-228669.htm?source=1 Hook原理
与https://bbs.pediy.com/thread-251412.htm?source=1类似 但https://bbs.pediy.com/thread-251412.htm?source=1更全

https://bbs.pediy.com/thread-38759.htm?source=1 微软研究院Detour开发包之API拦截技术

https://bbs.pediy.com/thread-224514.htm x64内核中的HOOK技术. 拦截进程,拦截线程,拦截模块(思路)
  PsSetCreateProcessNotifyRoutineEx
CreateProcessNotifyEx
NT_SUCCESS
线程监控API

监控:    PsSetCreateThreadNotifyRoutine

取消监控:  PsRemoveCreateThreadNotifyRoutine
PsLookupProcessByProcessId
PsLookupThreadByThreadId

获取获取EPROCESS与获取ETHREAD方法
status = PsLookupProcessByProcessId(ProcessId, &Process);  //1.通过进程ID,获取EPROCESS
if (!NT_SUCCESS(status))
return;

status = PsLookupThreadByThreadId(ThreadId, &Thread);    //2.通过线程ID,获取ETHREAD

但莫模块是给的ImageBae,也就是模块基址. 所以我们只需要解析PE找到OEP,把OEP代码改成ret即可.

API:

监控:

PsSetLoadImageNotifyRoutine

取消监控:

PsRemoveLoadImageNotifyRoutine

https://bbs.pediy.com/thread-62574.htm 导出表钩子------EAT HOOK
ZwQuerySystemInformation
ExAllocatePool
ZwQuerySystemInformation
ExFreePool

https://bbs.pediy.com/thread-82066.htm?source=1 Anti SSDT Hook
在xp中ntdll.dll的调用都是通过sysenter切换到内核的,sysenter调用了KiFastCallEntry,而KiFastCallEntry函数则是在ssdt表找到相应的
函数地址,然后call,所以只要伪造一张原始的ssdt表,并且欺骗过KiFastCallEntry函数,这样ssdt hook就无效了。。。

https://bbs.pediy.com/thread-63611.htm?source=1 SSDT Hook For Delphi
SSDT Hook例子
ZwOpenProcessNextHook

https://bbs.pediy.com/thread-99970.htm?source=1 FSD HOOK与SSDT HOOK恢复简单思路
思路 建议https://bbs.pediy.com/thread-251412.htm?source=1更全 fsd HOOK同IRP Hook

https://bbs.pediy.com/thread-77500.htm?source=1 HOOK SSDT AND HOOK Shadow SSDT FOR DELPHI
DELPHI

https://bbs.pediy.com/thread-78218.htm?source=1 扫盲贴,HOOK SSDT 短文一篇。
适合基础

https://www.kanxue.com/chm.htm?id=12118&pid=node1000838 HOOK学习笔记与心得
入门贴

https://bbs.pediy.com/thread-223317.htm?source=1 理解/检测 Inline Hooks/ WinAPI Hooks (Ring3)
web浏览器中hook 获取密码指令

https://bbs.pediy.com/thread-97649.htm?source=1 内核HOOK的安全问题(全程现场回放)
https://bbs.pediy.com/thread-28895.htm?source=1 汇编ring3下实现HOOK API
亮点在于用汇编实现

https://bbs.pediy.com/thread-159355.htm?source=1 函数hook注意事项

  1.    自己定义的detour函数和目标target函数的参数很返回值完全一致
    

因为没有代码,所以对于目标函数的参数和返回值的确定是需要逆向分析的,在有pdb的情况下很嗨,直接IDA等工具能帮你准确确定。没有pdb的情况下需要通过参数所占内存大小、特征甚至动态调试去确定,这里想强调一点是指针类型、数值类型、和引用类型的区分很关键且容易出错。另外有一种可怕的经验是返回值是一个结构
崩溃原因:
堆栈平衡
函数类型 参数类型
函数里面非法内存访问也会导致挂掉
hook的时机不对
多线程得挂起,再hook

https://bbs.pediy.com/thread-42362.htm?source=1 [原创]必备绝技–Hook大法( 上 )
https://bbs.pediy.com/thread-251412.htm?source=1更全
https://bbs.pediy.com/thread-42422.htm?source=1
含有内核hook代码完整

https://bbs.pediy.com/thread-128221.htm?source=1 SSDT hook & inline hook
是创建一个通用的hook框架,包括object hook, inline hook, idt hook, SSDT hook, IAT hook , EAT hook, IRP hook
/SSDT/
https://bbs.pediy.com/thread-101165.htm?source=1 R3读文件获取原始SSDT Shadow里地址

https://bbs.pediy.com/thread-58157.htm?source=1 SDTrestore Version 0.2 学习注释存档
RING3下恢复SSDT
/PE章/
https://bbs.pediy.com/thread-250924.htm 一篇文章带你理解PE三表
从dll注入那看你的文章,看到了这里。我觉的导入表关键是双桥结构,映像加载时桥2断裂,重新填充为函数的真实地址;导出表关键是,导出索引的初始值,还有就是从导出名称索引找到导出函数地址索引,比如病毒用到的根据导出表找GetProcAddress的地址;重定位表关键是高4位和低12位。另外,想写地址无关的代码,汇编很简单,一个call,紧跟着一个pop,再计算一个sub,就得到了相对偏移,放到一个寄存器里当重定位的偏移就可以了。个人拙见。

猜你喜欢

转载自blog.csdn.net/sinat_36391009/article/details/108311150