ntdll閿欒鎬庝箞瑙e喅 (ntdll浣跨敤)

顾名思义我们今天讨论的是应用层软件的调试,且是64位的软件,当一个软件发生错误或者异常时(不管是无意的还是刻意的)首先这个错误信息会先在内核中处理,如果是驱动程序产生的异常或者错误,能处理就处理不能处理就直接蓝屏,如果是应用程序的话,在内核中分配一些诸如ExceptionRecord、ContextRecord的结构体后就返回到用户层面继续处理了。

内核函数 KiExceptionDispatch在检测到异常或错误来自用户空间时,就会调用用户层的KiUserExceptionDispatcher,这个函数是用户层面的异常错误总入口,位于NTDLL.DLL中,我们编写软件时用到的SEH异常处理,都是由这个函数来实现的。

NTDLL异常处理在调试中的应用

上图是win10的目前最新的版本

NTDLL异常处理在调试中的应用

我们看下它的导出函数KiUserExceptionDispatcher,其中RtlDispatchException就是核心的异常处理过程,RtlGuardRestoreContext 函数是调用我们提供给SEH的安全返回地址。

现在市面上的商业软件不管32位还是64位,都有防跟踪、防逆向的指令在保护自身代码,很多调试器也有相应的插件提供过保护,32位系统下,基本上OD可以过所有保护。但64位系统下就不行了,其中win64对内核有更加严格的保护,不像32位下你可以随时接管中断接口、异常接口、SSDT 等,64位系统下你这样做分分钟蓝屏,PatchGuard了解一下。

基于这个原因,其实我们可以从ntdll的RtlDispatchException函数入手,我们挂钩RtlDispatchException,接管用户层面的所有异常,然后过滤出我们感兴趣的程序即可。

NTDLL异常处理在调试中的应用

我们增加一个新的节区(fix)调整节区属性为可读、可写、可执行、可共享(应用程序加载NTDLL时不会为这个节区额外分配空间,直接映射我们分配的空间)

NTDLL异常处理在调试中的应用

NTDLL异常处理在调试中的应用

手动修改call的跳转地址。然后自己编写过滤代码,将编好的代码用ue写入我们新建的节区就可以了。假如我们要跟踪一个license 授权文件的解密流程,那就需要挂钩ZwCreateFile函数,获取到文件句柄后,挂钩ZwReadFile函数。方法和RtlDispatchException一样,直接把指令call到我们提前定义的空间地址上。整个过程就是在真实的环境中运行,软件自身的反调试功能统统失效,因为我们就没用市面上的任何调试器,我们直接通过和NTDLL的自定义节区(fix)交换数据来获取调试数据。

很多软件喜欢用VMP来加壳,我们就针对VMP来简单说下调试过程吧。VMP壳软件在启动过程中自身会调用一些函数来检测电脑的运行环境,如果电脑处在调试模式,或者检测到调试端口、事件之类,就会异常退出,我们没用调试器所以就不用关心这些事情,唯一要做的是VMP对自陷指令的处理,比如:int1 、int 3。我们不是接管了RtlDispatchException嘛,所以VMP 产生的异常指令,都会被我们过滤到,把这些过滤到的异常指令全部保存下来,将来在动态跟踪时,遇到这些指令就放行。

pushfq

or byte ptr [rsp+1],1 ;单步

popfq

持续产生int1 中断,就可以把整个代码流程给抓取出来。用正确的授权走一遍,再用错误的走一遍,比对一下不同点很快就可以找到关键 jz jnz 指令点。

align 16

org 300h

dq 0cccccccccccccccch

dq 0cccccccccccccccch

_DispatchException proc

LOCAL @OBJECT_ATTRIBUTES :OBJECT_ATTRIBUTES ;30h

LOCAL @seh_64_exe_UNICODE_STRING :UNICODE_STRING ;<?>;10h

LOCAL ip:qword

LOCAL @SectionHandle:qword

LOCAL @LARGE_INTEGER:qword

LOCAL @SectionOffset:qword

LOCAL @ViewSize:qword

LOCAL @viewBaseAddress:qword

LOCAL @PEB_LDR_DATA :qword

LOCAL @NEXT_PEB_LDR_DATA:qword ;要调试的程序的文件名

LOCAL @NEXT__for_all_PEB_LDR_DATA:qword ;要调试的程序所包含的全部DLL信息

LOCAL @flags_debug_or_nodebug:qword

LOCAL @hevent_read_for_seh_64 :qword

LOCAL @hevent_write_for_ntdll :qword ;NTDLL可以写数据了

LOCAL @DllBase :qword

LOCAL _rcx:qword; ExceptionRecord

LOCAL _rdx:qword;ContextRecord

LOCAL _rsi:qword

LOCAL _rdi:qword

LOCAL _r8:qword

LOCAL _r9:qword

LOCAL _r10:qword

LOCAL _r11:qword

LOCAL _r12:qword, _r13:qword, _r14:qword, _r15:qword

LOCAL @ObjectDirectory:qword

LOCAL @count:qword

mov _rcx,rcx

mov _rdx,rdx

mov _rsi,rsi

mov _rdi,rdi

mov _r8,r8

mov _r9,r9

mov _r10,r10

mov _r11,r11

push rcx

push rdx

push rbx

push rbp

push rsi

push rdi

push r12

push r13

push r14

push r15

sub rsp, 80h

call @f

@@:

pop rax

lea rbx,@b

sub rax,rbx

mov ip,rax

mov rsi,[rdx+_CONTEXT._Rip]

lea rdi, track_point

add rdi,rax

lea rbx, track_point_end

add rbx,rax

.if << cmp rsi,rdi>>, ABOVE?||EQUAL? ;可能产生异常的地方,处理方法

.if <<cmp rsi,rbx>> , BELOW?||EQUAL? ;如果是我们自己的RtlDispatchException 产生的异常,那就跳过这个异常的记录, mov rsi,[rsi+ _CONTEXT._Rip]

;.if <<test rsi,rsi>>, NOZERO?

;REP MOVSB 这个很容易产生异常

;.else

;mov qword ptr [rdi],0

;.endif

lea rbx,track_point_end

mov [rdx+_CONTEXT._Rip],rbx

add rsp,80h

pop r15

pop r14

pop r13

pop r12

pop rdi

pop rsi

pop rbp

pop rbx

pop rdx

pop rcx

mov r8,_r8

mov r9,_r9

mov r10,_r10

mov r11,_r11

; mov rax,1 ;RtlDispatchException 返回1 之后RtlRestoreContext 继续执行

; ret ;不能直接返回:RtlRestoreContext 已经被我们HOOK,会生成单步中断

mov rax,ip

lea rax,[rax+78e9b39fh] ; RtlRestoreContext

;text:0000000078E9BCC1 E8 D9 F6 FF FF call RtlRestoreContext ; VOID WINAPI RtlRestoreContext( ;文件地址: 4b0c1 更改为E8 3afb1500 call near ptr qword_78FFb800

mov rcx,_rcx

mov rdx,_rdx

mov rcx,rdx ;context

xor rdx,rdx

leave

;jmp qword ptr _r11

jmp rax

.endif

.endif

mov rsi,[rdx+_CONTEXT._Rip]

lea rdi, copy_rip

add rdi,rax

lea rbx, copy_rip_end

add rbx,rax

.if << cmp rsi,rdi>>, ABOVE?||EQUAL? ;可能产生异常的地方,处理方法

.if <<cmp rsi,rbx>> , BELOW?||EQUAL? ;如果是我们自己的RtlDispatchException 产生的异常,那就跳过这个异常的记录, mov rsi,[rsi+ _CONTEXT._Rip]

;.if <<test rsi,rsi>>, NOZERO?

;REP MOVSB 这个很容易产生异常

;.else

;mov qword ptr [rdi],0

;.endif

lea rbx,copy_rip_end

mov [rdx+_CONTEXT._Rip],rbx

add rsp,80h

pop r15

pop r14

pop r13

pop r12

pop rdi

pop rsi

pop rbp

pop rbx

pop rdx

pop rcx

mov r8,_r8

mov r9,_r9

mov r10,_r10

mov r11,_r11

; mov rax,1 ;RtlDispatchException 返回1 之后RtlRestoreContext 继续执行

; ret ;不能直接返回:RtlRestoreContext 已经被我们HOOK,会生成单步中断

mov rax,ip

lea rax,[rax+78e9b39fh] ; RtlRestoreContext

;text:0000000078E9BCC1 E8 D9 F6 FF FF call RtlRestoreContext ; VOID WINAPI RtlRestoreContext( ;文件地址: 4b0c1 更改为E8 3afb1500 call near ptr qword_78FFb800

mov rcx,_rcx

mov rdx,_rdx

mov rcx,rdx ;context

xor rdx,rdx

leave

;jmp qword ptr _r11

jmp rax

.endif

.endif

lea rdi,copy_in_data_rbp

add rdi,rax

lea rbx,copy_in_rbp_end

add rbx,rax

.if << cmp rsi,rdi>>, ABOVE?||EQUAL? ;可能产生异常的地方,处理方法

.if <<cmp rsi,rbx>> ,BELOW?||EQUAL?

mov rax,ip

lea rbx,copy_in_rbp_end

mov [rdx+_CONTEXT._Rip],rbx

add rsp,80h

pop r15

pop r14

pop r13

pop r12

pop rdi

pop rsi

pop rbp

pop rbx

pop rdx

pop rcx

mov r8,_r8

mov r9,_r9

mov r10,_r10

mov r11,_r11

; mov rax,1 ;RtlDispatchException 返回1 之后RtlRestoreContext 继续执行

; ret ;不能直接返回:RtlRestoreContext 已经被我们HOOK,会生成单步中断

mov rax,ip

lea rax,[rax+78e9b39fh] ; RtlRestoreContext

;text:0000000078E9BCC1 E8 D9 F6 FF FF call RtlRestoreContext ; VOID WINAPI RtlRestoreContext( ;文件地址: 4b0c1 更改为E8 3afb1500 call near ptr qword_78FFb800

mov rcx,_rcx

mov rdx,_rdx

mov rcx,rdx ;context

xor rdx,rdx

leave

;jmp qword ptr _r11

jmp rax

.endif

.endif

lea rdi,[rax+78ffa060h]

lea rbx,[rax+78ffa0e0h]

.if << cmp rsi,rdi>>, ABOVE?||EQUAL?

.if <<cmp rsi,rbx>> ,BELOW?||EQUAL?

mov rax,ip

lea rbx,[rax+78ffa0e0h] ;这里存放的是ret指令(POP RCX POP RDI POP RSI RET

mov [rdx+_CONTEXT._Rip],rbx

add rsp,80h

pop r15

pop r14

pop r13

pop r12

pop rdi

pop rsi

pop rbp

pop rbx

pop rdx

pop rcx

mov r8,_r8

mov r9,_r9

mov r10,_r10

mov r11,_r11

; mov rax,1 ;RtlDispatchException 返回1 之后RtlRestoreContext 继续执行

; ret ;不能直接返回:RtlRestoreContext 已经被我们HOOK,会生成单步中断

mov rax,ip

lea rax,[rax+78e9b39fh] ; RtlRestoreContext

;text:0000000078E9BCC1 E8 D9 F6 FF FF call RtlRestoreContext ; VOID WINAPI RtlRestoreContext( ;文件地址: 4b0c1 更改为E8 3afb1500 call near ptr qword_78FFb800

mov rcx,_rcx

mov rdx,_rdx

mov rcx,rdx ;context

xor rdx,rdx

leave

;jmp qword ptr _r11

jmp rax

.endif

.endif

.if <<cmp [rdx+_CONTEXT._Rax],12121212h>>,ZERO?

.if <<cmp [rdx+ _CONTEXT._Rbx],23232323h>>,ZERO?

.if <<cmp [rdx+_CONTEXT._Rcx],34343434h>>,ZERO?

.if <<cmp [rdx+ _CONTEXT._Rdx],45454545h>>,ZERO?

;确认是我们的调试器发出的指令

mov rax,[rdx+_CONTEXT._R12]

mov @flags_debug_or_nodebug,rax

mov rax,[rdx+_CONTEXT._R13] ;sizeof_file

mov @hevent_read_for_seh_64,0

mov rax,[rdx+_CONTEXT._R14]

mov @hevent_write_for_ntdll,0

mov rax,ip

lea rax, [rax+78efb2f0h];

call rax ;ntdll!RtlGetCurrentPeb

.if <<test rax,rax>>,NOZERO?

mov rax,[rax+18h] ;_PEB_LDR_DATA

.if <<test rax,rax>>,NOZERO?

lea rcx, [rax+10h]

mov @PEB_LDR_DATA,rcx

mov rax,[rax+10h];_PEB_LDR_DATA.InLoadOrderModuleList;InLoadOrderModuleList : _LIST_ENTRY

.if <<test rax,rax>>,NOZERO?

.while <<cmp rax,@PEB_LDR_DATA>>,NOZERO?

lea rax,[rax+LIST_ENTRY.Flink]

mov @NEXT_PEB_LDR_DATA,rax

mov rbx,[rax+30h];_LDR_DATA_TABLE_ENTRY.DllBase

mov @DllBase,rbx

lea rax,[rax+58h]; _LDR_DATA_TABLE_ENTRY.BaseDllName : _UNICODE_STRING

; mov rsi,[rax+_UNICODE_STRING.Buffer]

; invoke MessageBoxW,0,rsi,addr ldrtest,MB_OK

movzx rcx,[rax+_UNICODE_STRING._Length]

mov rsi,[rax+_UNICODE_STRING.Buffer]

mov rax,ip

lea rdi ,[rax+78ffa010h] ;szMMFName db "seh_64*ex.e**64---",0

cld

repz cmpsb

.if ZERO? ;是seh_64发出的异常指令

mov rdx,_rdx

lea rsi , [rdx+_CONTEXT._Rsi];_rsi--_r11 总共30H 存放有要调试的程序

mov ecx,30h

mov rax,ip

lea rdi ,[rax+78ffa030h+8]

cld

rep movsb

mov rcx,[rdx+_CONTEXT._R15]

mov qword ptr [rax+78ffa030h],rcx

lea rsi , [rdx+_CONTEXT.___u38.data.__s1._Xmm0]

lea rdi ,[rax+78ffa200h]

mov rcx,20h

cld

rep movsq

mov rcx,@flags_debug_or_nodebug

mov [rax+78ffa100h],rcx ;1自动跟踪MT5 或 0只是检测它的异常断点 -1,动态调试

mov rdx,_rdx

mov rcx,8989898989898989h

mov [rdx+_CONTEXT._Rax],rcx

mov rcx,9a9a9a9a9a9a9a9ah

mov [rdx+_CONTEXT._Rbx],rcx

mov rcx,0ababababababababh

mov [rdx+_CONTEXT._Rcx],rcx

mov rcx,0bcbcbcbcbcbcbcbch

mov [rdx+_CONTEXT._Rdx],rcx

mov rax,ip

mov rcx,78ffc000h

add rcx,rax

mov [rdx+_CONTEXT._Rsi],rcx ;@viewBaseAddress

mov rcx,78ffa0f8h

add rcx,rax

mov [rdx+_CONTEXT._Rdi],rcx ;@hevent_write_for_ntdll;NTDLL可以写数据了

mov rcx,78ffa0f0h

add rcx,rax

mov [rdx+_CONTEXT._R9],rcx ;;hevent_read_for_seh_64

mov rcx,78ffc000h

add rcx,rax

mov @viewBaseAddress,rcx

lock and qword ptr [rax+78ffa008h],0;@viewBaseAddress

lock or [rax+78ffa008h],rcx

lock and qword ptr [rax+78ffa000h],0;第一次运行需要获取程序需要的DLL信息

lock and qword ptr [rax+78ffa110h],0;进程ID

lock and qword ptr [rax+78ffa120h],0;78ffa120h license.lic 文件句柄

mov rcx,[rdx+_CONTEXT._R13] ;sizeof_file

mov qword ptr [rax+78ffa118h],rcx;被调试程序大小

mov rcx,[rdx+_CONTEXT._R14];

lock and qword ptr [rax+78ffa108h],0

lock or qword ptr [rax+78ffa108h],rcx;总开关,系统在初始化时,不需要任何检测的,我们的调试器还没有启动呢!

add qword ptr [rdx+_CONTEXT._Rip],8 ; C6 04 25 00 00 00 00 00 mov byte ptr ds:0, 0

mov rax,ip

add rsp,80h

pop r15

pop r14

pop r13

pop r12

pop rdi

pop rsi

pop rbp

pop rbx

pop rdx

pop rcx

mov r8,_r8

mov r9,_r9

mov r10,_r10

mov r11,_r11

mov rax,1 ;RtlDispatchException 返回1 之后RtlRestoreContext 继续执行

ret

.endif

mov rax, @NEXT_PEB_LDR_DATA

mov rax,[rax]

.endw

.endif

.endif

.endif

.endif

.endif

.endif

.endif

;是否MT5产生的异常

mov rax,ip

mov rcx,[rax+78ffa108h] ;总开关,系统在初始化时,不需要任何检测了

.if <<test rcx,rcx>>, ZERO?

jmp exit

.endif

mov rax,ip

mov rcx,[rax+78ffa110h]

mov rax,qword ptr gs:[30h] ;_TEB. _NT_TIB.self

mov eax,dword ptr [rax+40h];GetCurrentProcessId ;ClientId._CLIENT_ID.UniqueProcess

.if <<test rcx,rcx>>, NOZERO? ;进程ID

.if <<cmp rcx,rax>>, NOZERO?

jmp exit ;不是我们要处理的程序

.endif

.else ;如果进程ID为空说明之前还没有捕获到MT5的异常 这里继续在本次异常里尝试

mov rax,ip

lea rax, [rax+78efb2f0h];

call rax ;ntdll!RtlGetCurrentPeb

.if <<test rax,rax>>,NOZERO?

mov rax,[rax+18h] ;_PEB_LDR_DATA

.if <<test rax,rax>>,NOZERO?

lea rcx, [rax+10h]

mov @PEB_LDR_DATA,rcx

mov rax,[rax+10h];_PEB_LDR_DATA.InLoadOrderModuleList;InLoadOrderModuleList : _LIST_ENTRY

.if <<test rax,rax>>,NOZERO?

.while <<cmp rax,@PEB_LDR_DATA>>,NOZERO?

; lea rax,[rax+LIST_ENTRY.Flink];;其实这里还是== lea rax,[rax] ;LIST_ENTRY.Flink == 0

mov @NEXT_PEB_LDR_DATA,rax

mov rbx,[rax+30h];_LDR_DATA_TABLE_ENTRY.DllBase

mov @DllBase,rbx

lea rax,[rax+58h]; _LDR_DATA_TABLE_ENTRY.BaseDllName : _UNICODE_STRING

movzx rcx,[rax+_UNICODE_STRING._Length]

mov rbx,ip

.if <<cmp rcx,[rbx+78ffa030h]>>,zero? ; 要调试的程序名长度

mov rsi,[rax+_UNICODE_STRING.Buffer]

mov rax,ip

lea rdi ,[rax+78ffa030h+8]

cld

repz cmpsb

.if ZERO? ;MT5的异常;这里找到MT5了

;保存加载的DLL信息和进程ID

mov rax,ip

mov rcx,@DllBase

mov [rax+78ffa0e8h],rcx

mov rcx,[rax+78ffa000h]

.if <<test rcx,rcx>>,ZERO? ;第一次运行需要获取程序需要的DLL信息

comment ~

lea rcx,@seh_64_exe_UNICODE_STRING

lea rdx, [rax+78ffa010h+8]

lea rax,[rax+78e9f5d0h]

call rax ;RtlInitUnicodeString

lea rcx,@ObjectDirectory

mov rax,ip

lea rax, [rax+78EF7FA0h];

call rax;BaseGetNamedObjectDirectory

mov rax,ip

lea rdx,@seh_64_exe_UNICODE_STRING

lea rcx,@OBJECT_ATTRIBUTES

mov rax,@ObjectDirectory

InitializeObjectAttributes rcx, rdx, OBJ_OPENIF, rax, NULL;OBJ_OPENIF equ 80h

lea r8,@OBJECT_ATTRIBUTES

mov rdx,EVENT_ALL_ACCESS

lea rcx,@hevent_read_for_seh_64

mov rax,ip

lea rax, [rax+78e9c130h]

call rax ;ZwOpenEvent returns STATUS_SUCCESS on success

.if <<test rax,rax>>, NOZERO?

jmp exit

.endif

mov rax,ip

mov rcx,@hevent_read_for_seh_64;

mov [rax+78ffa0f0h],rcx

lea rcx,@seh_64_exe_UNICODE_STRING

lea rdx, [rax+78ffa010h+10H]

lea rax,[rax+78e9f5d0h]

call rax ;RtlInitUnicodeString

lea rcx,@ObjectDirectory

mov rax,ip

lea rax, [rax+78EF7FA0h];

call rax;BaseGetNamedObjectDirectory

mov rax,ip

lea rdx,@seh_64_exe_UNICODE_STRING

lea rcx,@OBJECT_ATTRIBUTES

mov rax,@ObjectDirectory

InitializeObjectAttributes rcx, rdx, OBJ_OPENIF, rax, NULL;OBJ_OPENIF equ 80h

lea r8,@OBJECT_ATTRIBUTES

mov rdx,EVENT_ALL_ACCESS

lea rcx,@hevent_write_for_ntdll

mov rax,ip

lea rax, [rax+78e9c130h]

call rax ;ZwOpenEvent returns STATUS_SUCCESS on success

.if <<test rax,rax>>, NOZERO?

jmp exit

.endif

mov rax,ip

mov rcx,@hevent_write_for_ntdll;NTDLL可以写数据了

mov [rax+78ffa0f8h],rcx

lea rcx,@seh_64_exe_UNICODE_STRING

lea rdx, [rax+78ffa010h]

lea rax,[rax+78e9f5d0h]

call rax ;RtlInitUnicodeString

lea rcx,@ObjectDirectory

mov rax,ip

lea rax, [rax+78EF7FA0h];

call rax;BaseGetNamedObjectDirectory

mov rax,ip

lea rdx,@seh_64_exe_UNICODE_STRING

lea rcx,@OBJECT_ATTRIBUTES

mov rax,@ObjectDirectory

InitializeObjectAttributes rcx, rdx, OBJ_OPENIF, rax, NULL;OBJ_OPENIF equ 80h

lea r8,@OBJECT_ATTRIBUTES

mov rdx,SECTION_MAP_WRITE or SECTION_MAP_READ ;SECTION_MAP_WRITE equ 2h SECTION_MAP_READ equ 4h

lea rcx,@SectionHandle

mov rax,ip

lea rax, [rax+78e9c0a0h]

call rax ;ZwOpenSection returns STATUS_SUCCESS on success

.if <<test rax,rax>>,ZERO?

mov rcx,@SectionHandle

mov rdx,-1;ProcessHandle

lea r8,@viewBaseAddress

mov @viewBaseAddress,0

mov r9,0;ZeroBits,

mov qword ptr [rsp+20h],0;CommitSize

lea rax,@SectionOffset

mov @SectionOffset,0

mov qword ptr [rsp+28h],rax;SectionOffset

lea rax,@ViewSize

mov @ViewSize,0

mov qword ptr [rsp+30h],rax;ViewSize 分配的尺寸

mov qword ptr [rsp+38h],1;ViewShare equ 1

mov qword ptr [rsp+40h],0

mov qword ptr [rsp+48h],PAGE_READWRITE ; equ 4

mov dword ptr [rsp+4ch],-1

mov rax,ip

lea rax, [rax+78e9bfb0h]

call rax ;ntdll!ZwMapViewOfSection PROTO64 external,ZwMapViewOfSection,

; :QWORD,:QWORD,:QWORD,:QWORD,:QWORD,:QWORD,:QWORD,:QWORD,:QWORD,:QWORD

.if <<test rax,rax>>,ZERO?

comment ~

mov rax,ip

mov rcx,1

mov @SectionHandle,rcx

; lock and qword ptr [rax+78ffa000h],0;第一次运行需要获取程序需要的DLL信息

; lock or [rax+78ffa000h],rcx

lock xchg [rax+78ffa000h],rcx

mov rax,ip

lea rax, [rax+78efb2f0h];

call rax ;ntdll!RtlGetCurrentPeb

.if <<test rax,rax>>,NOZERO?

mov rax,[rax+18h] ;_PEB_LDR_DATA

.if <<test rax,rax>>,NOZERO?

lea rcx, [rax+10h]

mov @PEB_LDR_DATA,rcx

mov rax,[rax+10h];_PEB_LDR_DATA.InLoadOrderModuleList;InLoadOrderModuleList : _LIST_ENTRY

.if <<test rax,rax>>,NOZERO?

;把找到的dll信息发给调试器

mov @NEXT_PEB_LDR_DATA,rax

; mov rax,ip

; mov rcx,[rax+78ffa0f8h];;NTDLL可以写数据了

; mov rdx,0

; mov r8,0

; lea rax, [rax+78e9bd70h];

; call rax ;ntdll!ZwWaitForSingleObject

mov rax,ip

@@:

lock bts qword ptr [rax+78ffa0f8h], 0 ; BTS就是根据位偏移值从位串中取出一位放入CF中,然后将位串中的该位置成1

jnc @f

_wait:

pause

wait

fwait

test qword ptr [rax+78ffa0f8h], 1

jnz _wait

jmp @b

@@:

mov rax, @NEXT_PEB_LDR_DATA

mov rbx,0

.while <<cmp rax,@PEB_LDR_DATA>>,NOZERO?

; lea rax,[rax+LIST_ENTRY.Flink]

mov @NEXT_PEB_LDR_DATA,rax

mov rdx,[rax+30h];_LDR_DATA_TABLE_ENTRY.DllBase

lea rax,[rax+58h]; _LDR_DATA_TABLE_ENTRY.BaseDllName : _UNICODE_STRING

movzx rcx,[rax+_UNICODE_STRING._Length];我们设置的长度是40h unicode模式 是ASCII码长度的2倍

.if <<cmp rcx,40h>>, BELOW?||EQUAL?

mov rsi,[rax+_UNICODE_STRING.Buffer]

mov rax,ip

mov rdi,[rax+78ffa008h];@viewBaseAddress

mov [rdi][rbx][dllinformation.flags],2

mov [rdi][rbx][dllinformation.dllbase],rdx

mov [rdi][rbx][dllinformation.dllnamelength],rcx

lea rdi,[rdi][rbx][dllinformation.dllname]

cld

rep movsb

add rbx,sizeof dllinformation

.if <<cmp rbx,40*sizeof dllinformation>>, ABOVE?

.break

.endif

.endif

mov rax,@NEXT_PEB_LDR_DATA

mov rax,[rax+LIST_ENTRY.Flink]

.endw

mov rcx,qword ptr gs:[30h]

mov ecx,dword ptr [rcx+40h];GetCurrentProcessId

mov rax,ip

lock xchg [rax+78ffa110h],rcx;把进程id保存起来,下次直接通过ID比较

; mov rcx,[rax+78ffa0f0h];hevent_read_for_seh_64

; mov rdx,0

; mov rax,ip

; lea rax, [rax+78e9be10h];ZwSetEvent

; call rax

mov rax,ip

lock and qword ptr [rax+78ffa0f0h], 0 ; release spin lock hevent_read_for_seh_64 客户端可以读数据了

.break ;跳出上一层循环

.else

jmp error

.endif

.else

jmp error

.endif

.else

jmp error

.endif

.else

.break ;如果已经设置好了,就不需要重复设置了

.endif

.endif

.endif

mov rax,@NEXT_PEB_LDR_DATA

mov rax,[rax+LIST_ENTRY.Flink]

.endw

mov rax,ip

mov rcx,[rax+78ffa110h];进程ID

.if <<test rcx,rcx>>, ZERO?

;没有填充进程ID

jmp exit

.endif

.else

jmp exit

.endif

.else

jmp exit

.endif

.else

jmp exit

.endif

.endif

;这里确定是MT5后继续执行

mov rax,ip

mov rdi,[rax+78ffa0e8h] ;mt5DllBase

mov @DllBase,rdi

@@:

lock bts qword ptr [rax+78ffa0f8h], 0 ; BTS就是根据位偏移值从位串中取出一位放入CF中,然后将位串中的该位置成1 ;;;;NTDLL可以写数据事件

jnc @f

_wait1:

pause

wait

fwait

test qword ptr [rax+78ffa0f8h], 1

jnz _wait1

jmp @b

@@:

mov rax,ip

mov rdi,[rax+78ffa008h];@viewBaseAddress

mov rax,qword ptr gs:[30h]

mov eax,dword ptr [rax+40h];GetCurrentProcessId

mov qword ptr [rdi][recvfromntddd.flage_CONTEXT_EFlags],0

mov qword ptr [rdi][recvfromntddd.flags],1

mov qword ptr [rdi][recvfromntddd.from_processid],rax

mov rax,qword ptr gs:[30h]

mov eax,dword ptr [rax+48h];GetCurrentThreadId;_CLIENT_ID.UniqueThread

mov qword ptr [rdi][recvfromntddd.from_threadid],rax

mov rax,@DllBase

mov qword ptr [rdi][recvfromntddd.dllbase],rax

lea rdi, [rdi+recvfromntddd.from_EXCEPTION_RECORD]

mov rsi,_rcx

mov rcx,sizeof EXCEPTION_RECORD

.if <<test rsi,rsi>>, NOZERO?

cld

REP MOVSB

.else

mov rax,0cccccccccccccccch

cld

rep stosb

;mov qword ptr [rdi],rax

.endif

mov rax,ip

mov rdi,[rax+78ffa008h];@viewBaseAddress

lea rdi, [rdi+recvfromntddd.from__CONTEXT]

mov rcx,sizeof _CONTEXT

mov rsi,_rdx

.if <<test rsi,rsi>>, NOZERO?

cld

REP MOVSB

.else

mov al,90h

cld

rep stosb

; mov qword ptr [rdi],rax

;mov qword ptr [rdi],0

.endif

; mov rcx,sizeof DISPATCHER_CONTEXT

; mov rsi,_r9

; .if <<test rsi,rsi>>, NOZERO?

; REP MOVSB

; .endif

; mov rsi,_r8

mov rax,ip

mov rdi,[rax+78ffa008h];@viewBaseAddress

lea rdi, [rdi+recvfromntddd.from__Rip]

mov rcx,11h

mov rsi,_rdx

mov rsi,[rsi+ _CONTEXT._Rip]

copy_rip::

.if <<test rsi,rsi>>, NOZERO?

REP MOVSB

.else

copy_rip_end::

mov rax,ip

mov rdi,[rax+78ffa008h];@viewBaseAddress

lea rdi, [rdi+recvfromntddd.from__Rip]

mov rax,9090909090909090h

mov qword ptr [rdi],rax

mov word ptr [rdi],0cd04h;int 4

mov qword ptr [rdi+8],rax

mov byte ptr [rdi+10h],90h

.endif

mov rax,ip

mov rdi,[rax+78ffa008h];@viewBaseAddress

lea rdi, [rdi+recvfromntddd.RBP_0]

mov rcx,2h

mov rsi,_rdx

mov rsi,[rsi+ _CONTEXT._Rbp]

copy_in_data_rbp::

.if <<test rsi,rsi>>, NOZERO?

REP MOVSQ ;这里有可能RBP的地址会产生异常

.else

copy_in_rbp_end::

mov rax,ip

mov rdi,[rax+78ffa008h];@viewBaseAddress

lea rdi, [rdi+recvfromntddd.RBP_0]

mov rax,9090909090909090h

mov qword ptr [rdi],rax

mov qword ptr [rdi+8], rax

.endif

comment ~

;mov qword ptr[rsp+30],0

;mov qword ptr[rsp+28],SEC_COMMIT

;mov qword ptr [rsp+20],PAGE_READWRITE

;lea r9,@LARGE_INTEGER

;mov qword ptr [r9],1000h

;lea r8,@OBJECT_ATTRIBUTES

;mov rdx,0f0007h

;lea rcx,@SectionHandle

;mov rax,ip

;lea rax, [rax+78e9c1d0h]

;call rax ;ZwCreateSection PROTO64 external,ZwCreateSection, :QWORD,:QWORD,:QWORD,:QWORD,:QWORD,:QWORD,:QWORD

;test eax,eax

;lea rax,[rax+78e78120h] ; RtlpExecuteHandlerForException 文件地址 1795a :E8 C1 FB 00 00 被替换为:text:0000000078E6855A E8 A1 1D 19 00 call near ptr qword_78FFA300

; lea rax,[rax+78e68150h] ;

;RtlpCallVectoredHandlers ;0000000078E681ED E8 5E FF FF FF 文件地址: 175ED 更改为E8 0e211900 call near ptr qword_78FFA300 call RtlpCallVectoredHandlers 已经替换了78E681ED的地址

; mov _r11,rax

;pop rax

;要对VEH作个检测

; mov r13d,2 ;VEH: 向量化异常处理程序(进程相关) mov r13d,3 对VCH检测

; mov rax, qword ptr gs:[30h] ;teb

; mov r12, [rax+60h] ;peb

; mov eax, [r12+50h] ;ProcessUsingVEH

; bt eax, r13d ; ;BT 把 eax 的第3位(或者 第2位根据 r8+2决定)复制到 CF 从0 开始排列

; +0x050 ProcessUsingVEH : Pos 2, 1 Bit

; +0x050 ProcessUsingVCH : Pos 3, 1 Bit

; VEH: 向量化异常处理程序(进程相关)

; VCH: 同上,也是向量化异常处理程序,

; 不过它总是在最后被调用(进程相关)

;VEH,VCH:保存在ntdll.dll模块的_LdrpVectorHandlerList全局变量里

; .if CARRY?

; .endif

;0F 82 FD 16 00 00 jb loc_78E6988D

comment ~

mov rax,ip

mov rcx,[rax+78ffa100h] ;1自动跟踪MT5 或 0只是检测它的异常断点 -1,动态调试

.if <<test rcx,rcx>>, GREATER?

;自动跟踪MT5

mov rax,ip

mov rsi,[rax+78ffa008h];@viewBaseAddress

lea rsi,[rsi+4096+80h] ;这里存入的是官方的异常断点,这个值应该是个偏移量,因为程序每次装入的地址应该不一样。

;前4 个字节是异常总共占用的大小

mov ecx,[rsi+orgbreak.count_except]

mov rbx,0

mov rdi,_rdx

mov rdi,[rdi+ _CONTEXT._Rip]

.while <<test rcx,rcx>>,NOZERO?

mov rax,[rsi+orgbreak.except_point][rbx]

add rax, @DllBase

.if <<cmp rdi,rax>>, ZERO? ;官方的断点需要原程序自己去处理

mov r8d,[rsi+orgbreak.except_flags][rbx]

;官方的断点要在 RtlpCallVectoredHandlers 里标记 还要在RtlRestoreContext里标记

mov rax,ip

lock and qword ptr [rax+78ffa0f0h], 0 ; release spin lock,通知seh_64 接收数据

add rsp,80h

pop r15

pop r14

pop r13

pop r12

pop rdi

pop rsi

pop rbp

pop rbx

pop rdx

pop rcx

mov r8,_r8

mov r9,_r9

mov r10,_r10

mov r11,_r11

mov rax,ip

lea rax,[rax+78e681d0h] ; RtlDispatchException

leave

pushfq

; or byte ptr [rsp+1],1 ;单步

popfq

jmp rax

;这里不会返回了

.endif

add rbx,sizeof orgbreak

dec rcx

.endw

mov rsi,_rdx

mov rax,[rsi+ _CONTEXT._Rip]

track_point::

.if <<cmp byte ptr [rax],09ch>>, ZERO?;;9c pushfq 如果当前INT1中断的地址内容是 pushf

sub qword ptr [rsi+ _CONTEXT._Rsp],8 ;模拟pushfq 的执行

mov eax,[rsi+ _CONTEXT.EFlags]

and rax,0fffffffffffffeffh ;把tf 置0 if(中断标志) 用sti cli 修改,用户模式没有权限

mov rbx,[rsi+ _CONTEXT._Rsp]

mov [rbx],rax

inc [rsi+ _CONTEXT._Rip];模拟pushf 之后 跳过pushf 的执行

mov rax,ip

mov rdi,[rax+78ffa008h];@viewBaseAddress

mov qword ptr [rdi][recvfromntddd.flage_CONTEXT_EFlags],1;我们模拟pufhf 的执行 所以加个标志通知 seh_64。exe 处理这个情况

track_point_end::

and [rsi+ _CONTEXT.EFlags],0fffffeffh ;把tf和IF 置0

OR [rsi+ _CONTEXT.EFlags],100H

mov rax,ip

mov rdi,[rax+78ffa008h];@viewBaseAddress

mov rax,[rsi+ _CONTEXT._Rip]

jmp track_point ;或许有多个pushfq

;ID VIP VIF AC VM RF NT IOPL OF DF IF TF SF ZF AF PF CF

;21 20 19 18 17 16 14 13–12 11 10 9 8 7 6 4 2 0

.elseif <<cmp word ptr [rax],09cf3h>>, ZERO?;;9c

inc [rsi+ _CONTEXT._Rip];模拟pushf 之后 跳过pushf 的执行

mov rax,[rsi+ _CONTEXT._Rip]

jmp track_point

.elseif <<cmp word ptr [rax],09c2eh>>, ZERO?;;9c

inc [rsi+ _CONTEXT._Rip];模拟pushf 之后 跳过pushf 的执行

mov rax,[rsi+ _CONTEXT._Rip]

jmp track_point

.elseif <<cmp word ptr [rax],09c40h>>, ZERO?;;9c

inc [rsi+ _CONTEXT._Rip];模拟pushf 之后 跳过pushf 的执行

mov rax,[rsi+ _CONTEXT._Rip]

jmp track_point

.else

OR [rsi+ _CONTEXT.EFlags],100H

.endif

mov rax,ip

lock and qword ptr [rax+78ffa0f0h], 0 ; release spin lock,通知seh_64 接收数据 release spin lock,通知seh_64 接收数据 release spin lock,通知seh_64 接收数据

add rsp,80h

pop r15

pop r14

pop r13

pop r12

pop rdi

pop rsi

pop rbp

pop rbx

pop rdx

pop rcx

mov r8,_r8

mov r9,_r9

mov r10,_r10

mov r11,_r11

mov rax,1 ;RtlDispatchException 返回1 之后RtlRestoreContext 继续执行 我们HOOK的 RtlRestoreContext 会设置单步

ret ;返回后继续调用RtlRestoreContext 这个RtlRestoreContext 调用不会再产生 异常,(我们没有设置 单步标志)下一次的异常是在返回用户地址后产生的

.elseif <<test rcx,rcx>>, zero? ;1要调试MT5 或 0只是检测它的异常断点 -1,动态调试

mov rax,ip

lock and qword ptr [rax+78ffa0f0h], 0 ; release spin lock,通知seh_64 接收数据

.else

mov rax,ip

lock and qword ptr [rax+78ffa0f0h], 0 ; release spin lock,通知seh_64 接收数据 release spin lock,通知seh_64 接收数据 release spin lock,通知seh_64 接收数据

_80h_instruct:

mov rax,ip

@@:

lock bts qword ptr [rax+78ffa0f8h], 0 ; BTS就是根据位偏移值从位串中取出一位放入CF中,然后将位串中的该位置成1

jnc @f

_wait2:

pause

wait

fwait

test qword ptr [rax+78ffa0f8h], 1

jnz _wait2

jmp @b

@@:

mov rax,ip

mov rsi,[rax+78ffa008h];@viewBaseAddress 78ffc000

mov qword ptr [rsi][recvfromntddd.flags],0

lea rsi,[rsi+4096] ;78ffd000h单步调试指令区 sizeof == 80h

.if <<cmp byte ptr [rsi],0>>,NOZERO? ;这里是指令区

lea rdi,[rax+78ffa060h]

mov rcx,10h

rep movsq ;读80h个指令

mov rcx,_rcx

mov rdx,_rdx

lea rbx,[rax+78ffa060h]

call rbx;qword ptr [rax+78ffa060h] ;80H字节的指令区

mov rax,ip

lock and qword ptr [rax+78ffa0f0h], 0 ; release spin lock,通知seh_64 接收数据 release spin lock,通知seh_64 接收数据 release spin lock,通知seh_64 接收数据

jmp _80h_instruct

nop

.else

mov rax,ip

lock and qword ptr [rax+78ffa0f0h], 0 ; release spin lock,通知seh_64 接收数据 release spin lock,通知seh_64 接收数据 release spin lock,通知seh_64 接收数据

.endif

add rsp,80h

pop r15

pop r14

pop r13

pop r12

pop rdi

pop rsi

pop rbp

pop rbx

pop rdx

pop rcx

mov r8,_r8

mov r9,_r9

mov r10,_r10

mov r11,_r11

; mov rax,1 ;RtlDispatchException 返回1 之后RtlRestoreContext 继续执行

; ret ;不能直接返回:RtlRestoreContext 已经被我们HOOK,会生成单步中断

mov rax,ip

lea rax,[rax+78e9b39fh] ; RtlRestoreContext

;text:0000000078E9BCC1 E8 D9 F6 FF FF call RtlRestoreContext ; VOID WINAPI RtlRestoreContext( ;文件地址: 4b0c1 更改为E8 3afb1500 call near ptr qword_78FFb800

mov rcx,_rcx

mov rdx,_rdx

mov rcx,rdx ;context

xor rdx,rdx

leave

;jmp qword ptr _r11

jmp rax

.endif

error_DispatchException::

error:

exit: ;不是我们需要的

add rsp,80h

pop r15

pop r14

pop r13

pop r12

pop rdi

pop rsi

pop rbp

pop rbx

pop rdx

pop rcx

mov r8,_r8

mov r9,_r9

mov r10,_r10

mov r11,_r11

mov rax,ip

;lea rax,[rax+78e78120h] ; RtlpExecuteHandlerForException 文件地址 1795a :E8 C1 FB 00 00 被替换为:text:0000000078E6855A E8 A1 1D 19 00 call near ptr qword_78FFA300

;lea rax,[rax+78e68150h] ;

;RtlpCallVectoredHandlers ;0000000078E681ED E8 5E FF FF FF 文件地址: 175ED 更改为E8 0e211900 call near ptr qword_78FFA300 call RtlpCallVectoredHandlers 已经替换了78E681ED的地址

lea rax,[rax+78e681d0h] ; RtlDispatchException

;.text:0000000078E9BCB3 E8 18 C5 FC FF call RtlDispatchException ;文件地址: 4b0b4 更改为E8 48e61500 call near ptr qword_78FFA300

; mov _r11,rax

;pop rax

;jmp qword ptr _r11

leave

jmp rax

;call rax 这里不能用CALL 会破坏堆栈平衡

ret

_DispatchException endp