一丶简介
纯属兴趣爱好.特来逆向玩玩.
PsSetLoadImageNotifyRoutine 是内核中用来监控模块加载.操作系统给我们提供的回调. 我们只需要填写对应的回调函数原型即可进行加监控.
既然可以进行监控.那么我们的回调函数存储在哪.这是个问题.所以特来逆向玩玩.
二丶逆向过程
首先我的思路是直接windbg 挂载win7. 然后找到对应的函数进行
uf 反汇编. 来静态查看. 如果那里有疑问则自己进行动态查看.
过程很快.因为逆向过 进程回调监控.所以这个也就很快了.慢慢学习经验.
1.逆向PsSetLoadImageNotifyRoutine
3f809b3 8bff mov edi,edi
83f809b5 55 push ebp
83f809b6 8bec mov ebp,esp
83f809b8 53 push ebx
83f809b9 56 push esi
83f809ba 57 push edi
83f809bb 33ff xor edi,edi
83f809bd 57 push edi
83f809be ff7508 push dword ptr [ebp+8] 参数1压栈: 参数1 = 我们设置的回调函数地址.
83f809c1 e8113d0200 call nt!ExAllocateCallBack (83fa46d7) 逆向一下ExAllocateCallBack
83f809c6 8bd8 mov ebx,eax
83f809c8 3bdf cmp ebx,edi
83f809ca 7425 je nt!PsSetLoadImageNotifyRoutine+0x3e (83f809f1) //内部会申请一个结构体内存.保存我们的函数指针 以及 参数 结果给eax.
nt!PsSetLoadImageNotifyRoutine+0x19:
83f809cc be802bf583 mov esi,offset nt!PspLoadImageNotifyRoutine (83f52b80)//得出回调函数数组.
nt!PsSetLoadImageNotifyRoutine+0x1e:
83f809d1 6a00 push 0 0
83f809d3 8bcb mov ecx,ebx ecx = 结构体指针
83f809d5 8bc6 mov eax,esi eax = 回调函数数组.
ExCompareExchangeCallBack(函数数组,结构体指针,0); 根据函数名可以得知 比较交换回调函数指针. 逆向志之 在下面观看.
83f809d7 e82a3d0200 call nt!ExCompareExchangeCallBack (83fa4706)
83f809dc 84c0 test al,al
83f809de 751d jne nt!PsSetLoadImageNotifyRoutine+0x4a (83f809fd)
nt!PsSetLoadImageNotifyRoutine+0x2d:
83f809e0 83c704 add edi,4
83f809e3 83c604 add esi,4
83f809e6 83ff20 cmp edi,20h
83f809e9 72e6 jb nt!PsSetLoadImageNotifyRoutine+0x1e (83f809d1)
nt!PsSetLoadImageNotifyRoutine+0x38:
83f809eb 53 push ebx
83f809ec e824b71000 call nt!SepFreeCapturedString (8408c115)
nt!PsSetLoadImageNotifyRoutine+0x3e:
83f809f1 b89a0000c0 mov eax,0C000009Ah
nt!PsSetLoadImageNotifyRoutine+0x43:
83f809f6 5f pop edi
83f809f7 5e pop esi
83f809f8 5b pop ebx
83f809f9 5d pop ebp
83f809fa c20400 ret 4
nt!PsSetLoadImageNotifyRoutine+0x4a:
83f809fd 33c9 xor ecx,ecx
83f809ff b8a02bf583 mov eax,offset nt!PspLoadImageNotifyRoutineCount (83f52ba0)
83f80a04 41 inc ecx
83f80a05 f00fc108 lock xadd dword ptr [eax],ecx
83f80a09 a1782bf583 mov eax,dword ptr [nt!PspNotifyEnableMask (83f52b78)]
83f80a0e a801 test al,1
83f80a10 750a jne nt!PsSetLoadImageNotifyRoutine+0x69 (83f80a1c)
nt!PsSetLoadImageNotifyRoutine+0x5f:
83f80a12 b8782bf583 mov eax,offset nt!PspNotifyEnableMask (83f52b78)
83f80a17 f00fba2800 lock bts dword ptr [eax],0
nt!PsSetLoadImageNotifyRoutine+0x69:
83f80a1c 33c0 xor eax,eax
83f80a1e ebd6 jmp nt!PsSetLoadImageNotifyRoutine+0x43 (83f809f6)
通过逆向次函数.可以得出我们的回调函数数组. 以及我们申请的回调函数指针.
伪代码
{
结构体指针 = ExAllocateCallBack(回调函数地址,0);
ExCompareExchangeCallBack(函数数组,结构体指针,0); 内部进行解密
}
关于上面的函数逆向.如下:
2.逆向 ExAllocateCallBack
3fa46d7 8bff mov edi,edi
83fa46d9 55 push ebp
83fa46da 8bec mov ebp,esp
83fa46dc 6843627262 push 62726243h
83fa46e1 6a0c push 0Ch
83fa46e3 6a01 push 1
83fa46e5 e81bc9f8ff call nt!ExAllocatePoolWithTag (83f31005) 申请内存 eax = ExAllocatePoolWithTag(1,0xC,62726243)
83fa46ea 85c0 test eax,eax
83fa46ec 740f je nt!ExAllocateCallBack+0x26 (83fa46fd)
83fa46ee 8b4d08 mov ecx,dword ptr [ebp+8] ecx = 参数1
83fa46f1 832000 and dword ptr [eax],0 struct->one = 0
83fa46f4 894804 mov dword ptr [eax+4],ecx struct->two = ecx[ebp + 8] ==> 参数二
83fa46f7 8b4d0c mov ecx,dword ptr [ebp+0Ch]
83fa46fa 894808 mov dword ptr [eax+8],ecx struct->Three = [eb[ + c] ==> 参数三
83fa46fd 5d pop ebp
83fa46fe c20800 ret 8
汇编代码很简单.通过下面进行赋值可以看做是一个结构体
逆向得知,ExAllocateCallBack其实是申请一块内存.
并且转化为一个结构进行赋值
例如下:
struct
{
ULONG UnKnow1;
ULONG UnKnow2;
ULONG UnKnow3;
}
但是根据传参得知. UnKnow2 == 我们的回调地址 Unknow3 ==> 我们的参数
然后查看Wrk源码.得出以下结构
typedef struct _EX_CALLBACK_ROUTINE_BLOCK {
EX_RUNDOWN_REF RundownProtect;
PEX_CALLBACK_FUNCTION Function;
PVOID Context;
} EX_CALLBACK_ROUTINE_BLOCK, *PEX_CALLBACK_ROUTINE_BLOCK;
3.逆向交换 解密函数 ExCompareExchangeCallBack
nt!ExCompareExchangeCallBack:
83fa4706 8bff mov edi,edi
83fa4708 55 push ebp
83fa4709 8bec mov ebp,esp
83fa470b 51 push ecx
83fa470c 51 push ecx
83fa470d 53 push ebx
83fa470e 56 push esi
83fa470f 57 push edi
83fa4710 8bf0 mov esi,eax eax ============> 回调函数数组
83fa4712 85c9 test ecx,ecx ecx = 结构体指针
83fa4714 7410 je nt!ExCompareExchangeCallBack+0x20 (83fa4726)
nt!ExCompareExchangeCallBack+0x10:
83fa4716 6a08 push 8
83fa4718 5a pop edx
83fa4719 e87670efff call nt!ExAcquireRundownProtectionEx (83e9b794)
83fa471e 84c0 test al,al
83fa4720 0f84aa000000 je nt!ExCompareExchangeCallBack+0xca (83fa47d0)
nt!ExCompareExchangeCallBack+0x20:
83fa4726 8b1e mov ebx,dword ptr [esi] ebx = 数组[0]
83fa4728 8bc3 mov eax,ebx eax = ebx
83fa472a eb1d jmp nt!ExCompareExchangeCallBack+0x43 (83fa4749)
nt!ExCompareExchangeCallBack+0x26:
83fa472c 85c9 test ecx,ecx
83fa472e 7407 je nt!ExCompareExchangeCallBack+0x31 (83fa4737)
nt!ExCompareExchangeCallBack+0x2a:
83fa4730 8bc1 mov eax,ecx
83fa4732 83c807 or eax,7
83fa4735 eb02 jmp nt!ExCompareExchangeCallBack+0x33 (83fa4739)
nt!ExCompareExchangeCallBack+0x31:
83fa4737 33c0 xor eax,eax
nt!ExCompareExchangeCallBack+0x33:
83fa4739 8bd0 mov edx,eax
83fa473b 8bfe mov edi,esi
83fa473d 8bc3 mov eax,ebx
83fa473f f00fb117 lock cmpxchg dword ptr [edi],edx
83fa4743 3bc3 cmp eax,ebx
83fa4745 740a je nt!ExCompareExchangeCallBack+0x4b (83fa4751)
nt!ExCompareExchangeCallBack+0x41:
83fa4747 8bd8 mov ebx,eax
=================================>
nt!ExCompareExchangeCallBack+0x43:
83fa4749 334508 xor eax,dword ptr [ebp+8] 数组[0] ^ 0 = 自己本身
83fa474c 83f807 cmp eax,7
83fa474f 76db jbe nt!ExCompareExchangeCallBack+0x26 (83fa472c) //eax = 数组[0] 所以不走这个循环.
nt!ExCompareExchangeCallBack+0x4b:
83fa4751 8bfb mov edi,ebx 开始解密 edi = ebx = 数组[0]
83fa4753 83e7f8 and edi,0FFFFFFF8h edi = 数组[0] & 0xFFFFFFF8 = 结构体指针. 至此已经解析完毕了.
83fa4756 3b7d08 cmp edi,dword ptr [ebp+8]
83fa4759 7569 jne nt!ExCompareExchangeCallBack+0xbe (83fa47c4)
nt!ExCompareExchangeCallBack+0x55:
83fa475b 85ff test edi,edi
83fa475d 7461 je nt!ExCompareExchangeCallBack+0xba (83fa47c0)
nt!ExCompareExchangeCallBack+0x59:
83fa475f 648b3524010000 mov esi,dword ptr fs:[124h]
83fa4766 66ff8e86000000 dec word ptr [esi+86h]
83fa476d 8745fc xchg eax,dword ptr [ebp-4]
83fa4770 8b0dd8c7f483 mov ecx,dword ptr [nt!ExpCallBackFlush (83f4c7d8)]
83fa4776 83e101 and ecx,1
83fa4779 8745f8 xchg eax,dword ptr [ebp-8]
83fa477c 85c9 test ecx,ecx
83fa477e 7414 je nt!ExCompareExchangeCallBack+0x8e (83fa4794)
nt!ExCompareExchangeCallBack+0x7a:
83fa4780 b9d8c7f483 mov ecx,offset nt!ExpCallBackFlush (83f4c7d8)
83fa4785 e803e4efff call nt!ExfAcquirePushLockExclusive (83ea2b8d)
83fa478a b9d8c7f483 mov ecx,offset nt!ExpCallBackFlush (83f4c7d8)
83fa478f e81d10f8ff call nt!ExfReleasePushLockExclusive (83f257b1)
nt!ExCompareExchangeCallBack+0x8e:
83fa4794 66ff8686000000 inc word ptr [esi+86h]
83fa479b 0fb78686000000 movzx eax,word ptr [esi+86h]
83fa47a2 6685c0 test ax,ax
83fa47a5 750c jne nt!ExCompareExchangeCallBack+0xad (83fa47b3)
nt!ExCompareExchangeCallBack+0xa1:
83fa47a7 8d4640 lea eax,[esi+40h]
83fa47aa 3900 cmp dword ptr [eax],eax
83fa47ac 7405 je nt!ExCompareExchangeCallBack+0xad (83fa47b3)
nt!ExCompareExchangeCallBack+0xa8:
83fa47ae e8e63ce9ff call nt!KiCheckForKernelApcDelivery (83e38499)
nt!ExCompareExchangeCallBack+0xad:
83fa47b3 8bd3 mov edx,ebx
83fa47b5 83e207 and edx,7
83fa47b8 42 inc edx
83fa47b9 8bcf mov ecx,edi
83fa47bb e81718eaff call nt!ExReleaseRundownProtectionEx (83e45fd7)
nt!ExCompareExchangeCallBack+0xba:
83fa47c0 b001 mov al,1
83fa47c2 eb0e jmp nt!ExCompareExchangeCallBack+0xcc (83fa47d2)
nt!ExCompareExchangeCallBack+0xbe:
83fa47c4 85c9 test ecx,ecx
83fa47c6 7408 je nt!ExCompareExchangeCallBack+0xca (83fa47d0)
nt!ExCompareExchangeCallBack+0xc2:
83fa47c8 6a08 push 8
83fa47ca 5a pop edx
83fa47cb e80718eaff call nt!ExReleaseRundownProtectionEx (83e45fd7)
nt!ExCompareExchangeCallBack+0xca:
83fa47d0 32c0 xor al,al
nt!ExCompareExchangeCallBack+0xcc:
83fa47d2 5f pop edi
83fa47d3 5e pop esi
83fa47d4 5b pop ebx
83fa47d5 c9 leave
83fa47d6 c20400 ret 4
逆向这个函数可以得知.最后我们的数组会与 0xFFFFFFF8 进行and操作.操作之后的结果就是我们上面 逆向ExAllocateCallBack
得出的结构体指针原型.
结构体->two 成员就是记录我们想要的回调函数地址.
三丶总结
1.通过逆向 ExAllocateCallBack 我们得出了结构体原型.以及我们的回调函数如何存储
typedef struct _EX_CALLBACK_ROUTINE_BLOCK {
EX_RUNDOWN_REF RundownProtect;
PEX_CALLBACK_FUNCTION Function;
PVOID Context;
} EX_CALLBACK_ROUTINE_BLOCK, *PEX_CALLBACK_ROUTINE_BLOCK;
2.通过逆向 ExCompareExchangeCallBack我们得知数组是如何解密的
PEX_CALLBACK_ROUTINE_BLOCK pRoutine = 数组[0] & 0xFFFFFFF8;
pRoutine->Function == 我们要寻找的回调函数
3.简介
从以前的逆向PsSetCreateProcessRoutine得知.他也是通过 & 0xFFFFFFFF8进行解密的.
win64的也有逆向出来. 界面的数值为 & 0xFFFFFFFFFFFFFFF0 进行解密的. 关于64的.后面继续发.
在64位下感觉数组解密应该用的都是 0xFFFFFFFFFFFFFFF0 进行解密的(win7 64)