INIT:00010D85 public start
INIT:00010D85 start proc near
INIT:00010D85 mov edi, edi
INIT:00010D87 push ebp
INIT:00010D88 mov ebp, esp
……………………略去
INIT:00010DC1 mov dword_10C90, eax
INIT:00010DC6 pop ebp
INIT:00010DC7 jmp DriverEntry ; 一般最后一个Jmp就是跳到DriverEntry
INIT:00010DC7 start endp
IDA可以帮我们做很多事,很多东西不用我们自己去猜,一看就明白的东西我就略过了
.text:0001088E ; int __stdcall DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING pRegistryPath)
.text:0001088E DriverEntry proc near ; CODE XREF: start+42 j
.text:0001088E
.text:0001088E DeviceObject = dword ptr -4
.text:0001088E DriverObject = dword ptr 8
.text:0001088E pRegistryPath = dword ptr 0Ch
.text:0001088E
.text:0001088E mov edi, edi
.text:00010890 push ebp
.text:00010891 mov ebp, esp
………………………………………………………………
;略掉部分创建设备的代码
.text:000108E2 ; ---------------------------------------------------------------------------
.text:000108E2
.text:000108E2 loc_108E2: ; CODE XREF: DriverEntry+45 j
.text:000108E2 mov eax, offset DispatchCreateClose ;接下来要自己照着DriverObject的结构算一下,算出来各个派遣函数指向哪,当然在本程序中没什么用
.text:000108E7 mov [esi+40h], eax ; IRP_MJ_CLOSE
.text:000108EA mov [esi+38h], eax ; IRP_MJ_CREATE
.text:000108ED mov dword ptr [esi+70h], offset DriverIOCtrl ; IRP_MJ_DEVICE_CONTROL
.text:000108F4 mov dword ptr [esi+34h], offset DriverUnload
.text:000108FB call GetProcessNameOffset ;从eprocess中得到文件名偏移
.text:00010900 mov uNameOffset, eax
.text:00010905 call FindPspTerminateThreadByPointer ;从PsTerminateSystemThread搜索PspTerminateThreadByPointer
.text:0001090A cmp eax, ebx
.text:0001090C mov PspTerminateThreadByPointer, eax
.text:00010911 jz short loc_10933
.text:00010913 call FindPspExitThread ; 跟上面那个差不多,懒得逆向了PspTerminateThreadByPointer中搜索PspExitThread
.text:00010918 cmp eax, ebx
.text:0001091A mov PspExitThread, eax
.text:0001091F jz short loc_10933
.text:00010921 call InlineHookPspExitThread ;这是关键,一会儿会跟进
.text:00010926 push ebx ; State
.text:00010927 push ebx ; Type
.text:00010928 push offset Event ; Event
.text:0001092D call ds:KeInitializeEvent ;创建事件,刚逆的时候还看不出来这是用来干什么的,后面会知道
………………略去
.text:00010939 DriverEntry endp
InlineHookPspExitThread 如果你自己跟进去是非常郁闷的,因为它会显示成
mov ax, word ptr unk_10CB0
mov [edi+4], ax
mov edi, dword_10CA4
mov eax, dword_10C98
mov [edi], eax
mov ax, word ptr unk_10C9C
十几行诸如此类的代码,搞得我郁闷了很久,也不太确定这就是Inline, F5也毫无结果,只是知道push offset ff 8b,
查了查code table 发现c3是retn,于是想起retn返回栈顶的地址,Push xxxxxxxx再retn就是相当于jmp xxxxxxxx
再后来想到程序还没过滤NotePad呢 参考了以下字符串发现NotePad交叉参考为空,上下文又有一堆无意义的数字未被引用
于是恍然大悟这根本就是代码,不是数据,于是从.text:000105B4 到.text:00010677全部转换成代码,哈 豁然开朗
.text:000105B4 db 6 dup(0CCh)
.text:000105BA ; ---------------------------------------------------------------------------
.text:000105BA
.text:000105BA OriginPspExitThread: ; CODE XREF: .text:0001066C p
.text:000105BA ; DATA XREF: InlineHookPspExitThread+81 o
.text:000105BA mov edi, edi
.text:000105BA ; ---------------------------------------------------------------------------
.text:000105BC aRrrrnotepad_exe db '烫烫烫烫烫烫悙悙notepad.exe',0
.text:000105D8 aNotepad_exe db 'notepad.exe',0 ; DATA XREF: .text:00010649 o
.text:000105E4 db 6 dup(0CCh)
.text:000105EA ; ---------------------------------------------------------------------------
.text:000105EA
.text:000105EA loc_105EA: ; DATA XREF: InlineHookPspExitThread+2A o
.text:000105EA mov edi, edi
.text:000105EC push ebp
.text:000105ED mov ebp, esp ;看到这个心里就有底了,标准的函数头
…………………………………………………………………………略去
上面的转换还是有点问题,别着急,一会儿弄
再看刚才的代码,这回就很清楚了
; void __cdecl InlineHookPspExitThread()
InlineHookPspExitThread proc near
mov edi, edi
push esi
push edi
mov edi, PspExitThread
mov eax, [edi]
mov OriginCode_1_, eax ; 保存10字节
mov eax, [edi+4]
mov OriginCode_2_, eax
mov ax, [edi+8]
mov orgincode_3_, ax
mov byte ptr PushMyAddrCode, 68h
mov edi, offset MyPspExitThread ; hook函数地址
mov PushMyAddrCode+1, edi ; ...
mov RETNcode, 0C3h
mov Nopx4Code, 90909090h
mov byte ptr PushPspAdd10, 68h
mov edi, PspExitThread
add edi, 0Ah
mov PushPspAdd10+1, edi ; push &(PspExitThread + 0Ah)
mov RETNcode1, 0C3h
mov esi, offset SpinLock
mov ecx, esi ; SpinLock
call ds:KfAcquireSpinLock
mov byte_10CB8, al
mov eax, cr0
and eax, 0FFFEFFFFh
mov cr0, eax
cli
mov edi, offset OriginPspExitThread
mov eax, OriginCode_1_
mov [edi], eax
mov eax, OriginCode_2_
mov [edi+4], eax
mov ax, orgincode_3_
mov [edi+8], ax
add edi, 0Ah
mov eax, PushPspAdd10
mov [edi], eax ; 写入原始10字节 然后Pushpsp+10
mov ax, word ptr RETN_0
mov [edi+4], ax
mov edi, PspExitThread
mov eax, PushMyAddrCode
mov [edi], eax
mov ax, word ptr rETN_0
mov [edi+4], ax
mov eax, Nopx4Code ; 剩余字节nop掉
mov [edi+6], eax
sti
mov eax, cr0
or eax, 10000h
mov cr0, eax
mov dl, byte_10CB8
pop edi
mov ecx, esi
pop esi
jmp ds:KfReleaseSpinLock
InlineHookPspExitThread endp
下面看原始那段转换的代码
text:000105BA OriginPspExitThread: ; CODE XREF: .text:0001066C p
.text:000105BA ; DATA XREF: InlineHookPspExitThread+81 o
.text:000105BA mov edi, edi
.text:000105BA ; ---------------------------------------------------------------------------
.text:000105BC db 0CCh
.text:000105BD db 0CCh ; ?
.text:000105BE db 0CCh ; ?
.text:000105BF db 0CCh ; ?
.text:000105C0 db 0CCh ; ?
.text:000105C1 db 0CCh ; ?
.text:000105C2 db 0CCh ; ? ; 原始十字节 push xxxxxxxx retn 0
.text:000105C3 db 0CCh ; ? ; 。
.text:000105C4 db 0CCh ; ?
.text:000105C5 db 0CCh ; ?
.text:000105C6 db 0CCh ; ?
.text:000105C7 db 0CCh ; ?
.text:000105C8 db 90h ; ?
.text:000105C9 db 90h ; ?
.text:000105CA db 90h ; ?
.text:000105CB db 90h ; ?
.text:000105CC NotePad db 6Eh ; n ; DATA XREF: .text:00010638 o
.text:000105CD db 6Fh ; o
.text:000105CE db 74h ; t
.text:000105CF db 65h ; e
.text:000105D0 db 70h ; p
.text:000105D1 db 61h ; a
.text:000105D2 db 64h ; d
.text:000105D3 db 2Eh ; .
.text:000105D4 db 65h ; e
.text:000105D5 db 78h ; x
.text:000105D6 db 65h ; e
.text:000105D7 db 0
.text:000105D8 aNotepad_exe db 'notepad.exe',0 ; DATA XREF: .text:00010649 o
.text:000105E4 db 0CCh
.text:000105E5 db 0CCh ; ?
.text:000105E6 db 0CCh ; ?
.text:000105E7 db 0CCh ; ?
.text:000105E8 db 0CCh ; ?
.text:000105E9 db 0CCh ; ?
.text:000105EA ; ---------------------------------------------------------------------------
.text:000105EA
.text:000105EA MyPspExitThread: ; DATA XREF: InlineHookPspExitThread+2A o
.text:000105EA mov edi, edi
.text:000105EC push ebp
.text:000105ED mov ebp, esp
.text:000105EF push ebx
.text:000105F0 push esi
.text:000105F1 push edi
.text:000105F2 push 10h
.text:000105F4 call ds:IoGetCurrentProcess
.text:000105FA add eax, uNameOffset
.text:00010600 mov esi, ds:strncpy
.text:00010606 push eax
.text:00010607 mov edi, offset CurrentEXEname
.text:0001060C push edi
.text:0001060D call esi ; strncpy ; 当前进程名
.text:0001060F add esp, 0Ch
.text:00010612 push 10h
.text:00010614 call KeGetCurrentThread
.text:00010619 push eax
.text:0001061A call ds:IoThreadToProcess
.text:00010620 add eax, uNameOffset
.text:00010626 mov ebx, 10CF4h
.text:0001062B push eax
.text:0001062C push ebx
.text:0001062D call esi ; strncpy
.text:0001062F mov esi, ds:_strnicmp ; 比较名字
.text:00010635 push 0Ch
.text:00010637 push edi
.text:00010638 push offset NotePad
.text:0001063D call esi ; _strnicmp
.text:0001063F add esp, 18h
.text:00010642 test eax, eax
.text:00010644 jz short loc_10657
.text:00010646 push 0Ch
.text:00010648 push ebx
.text:00010649 push offset aNotepad_exe ; "notepad.exe"
.text:0001064E call esi ; _strnicmp
.text:00010650 add esp, 0Ch
.text:00010653 test eax, eax
.text:00010655 jnz short loc_10669
.text:00010657
.text:00010657 loc_10657: ; CODE XREF: .text:00010644 j
.text:00010657 xor eax, eax ; 如果匹配 那么就等待object
.text:00010659 push eax
.text:0001065A push 1
.text:0001065C push eax
.text:0001065D push eax
.text:0001065E push offset Event
.text:0001065E ; ---------------------------------------------------------------------------
.text:00010663 db 0FFh
.text:00010664 db 15h
.text:00010665 dd offset KeWaitForSingleObject
.text:00010669 ; ---------------------------------------------------------------------------
.text:00010669
.text:00010669 loc_10669: ; CODE XREF: .text:00010655 j
.text:00010669 push dword ptr [ebp+8]
.text:0001066C call OriginPspExitThread
也就是防杀,进程保护其实没什么意思,对于病毒,与其防止别人杀你进程,倒不如在R3下迅速处理完自己的工作然后撤退,没理由让exe长驻。盗号木马的话倒是要长期监视,但也没必要保护进程。要保护进程的大多是安全产品,也没必要做的太底层,不稳定了就得不偿失。防住R3层面的就可以了,然后再保护住自己窗口,句柄,最最重要的就是截住加载驱动的接口,如果用户愿意让驱动加载那出什么事就不怪你了。
大概说下结束进程的流程,结束进程似乎没有创建进程那么繁琐。
R3:
TerminateProcess→NtTerminateProcess→sysenter
R0:
ZwTerminateProcess→SSDT→NtTerminateProcess→PspTerminateThreadByPointer→KeInitializeApc→PspExitThread
大概是这样,古老的方法也挺多,R0下SSDT Hook NtOpenProcess NtTerminateProcess;Hook ObReferenceObjectByHandle;Hook PspTerminateThreadByPointer,Hook Ke(i)InitializeApc先进的可能也有,我还没来得及逆…
Hook PspExitThread的不多,原因自然是太低层太不稳定,而且由于是以APC的形式插入线程,Hook之后也不是很方便。花了点时间逆了 diyhack的进程挑战赛的驱动,(一年以前的东西了。。不过那会儿本小菜正在高三的水深火热之中,毛都不懂),这个驱动就是Hook PspExitThread,其实没什么东西,文件也小到2.5k。功能就是防止结束notepad,首先扔到IDA里,第一次逆向驱动,所以就多逆了很多,把整体驱动框架都逆向了出来,以后只要逆向自己感兴趣的部分就好了,顺便记录下第一次逆向驱动的全过程。
IDA加载后找到函数参考的start函数就是程序开始的位置了
为什么比较了两次文件名?分别用了KeGetCurrentThread IoGetCurrentProcess ?这么做是为了防止别人DKOM 把线程所属指向别的进程然后结束掉
到此也就大概明白了,其实是在PsExitThread中无限等待事件触发,只到UnHook时触发才恢复。
IDA很方便,但是静态分析弊端也很大,但是在动态分析没效果的情况下就需要有一点耐性去在大脑里模拟程序的运行了。
其实这种挑战挺有意思的,算是锻炼思维,无聊的解决方法就很多,有点意思的算是MJ说的搜索data里的event然后唤醒线程