谈谈NP和HS的通用unhook
祝各位新年快乐![](https://i-blog.csdnimg.cn/blog_migrate/e87e6cce2e2f771bbfbe7acedf9a3459.png)
一边听音乐一边写代码真的很舒服阿很舒服
先简单说以下,NP和HS都hook了以下一些SSDT
什么是SSDT?我不想说那些复杂的理论,希望以简单的事实帮助介绍
先向大S道歉,下文如有冒犯,还请大S原谅
怎么说呢,SSDT本身就是一个地址表,我们当他是个地址簿,也就是写了每个人的地址的记录表
今天,偶想给大S送新年礼物,怎么送到大S家里呢?就得查看地址簿了
上面是这样写的 大S: 游戏达人,外挂高人,破解牛人,脱壳圣人;分身有如月,堀北真希,花泽类,猪的理想;实际上是个阳光宝宝,吃的的白白胖胖的,每天都很开心;家住上海市X区X路X楼,进门左转、左转、再左转,敲门说找S.H.E就行了。
标准SSDT hook的方式就是改变这个地址,HS就是八地址改写成abnlab实验室,如果你照着走就到abnlab实验室了
他们就要检查你了,这么大一箱礼物?是不是带了炸弹阿,识别你的身份阿,但是这样怎么能行呢,经过他们一审核
我的新年礼物岂不是要过年以后才能到了?不行不行
只能先联系大S,大S说家在上海X别墅区X,偶于是就上门了,到了别墅,门卫不让偶进去阿,理由是偶没有邀请函
; MmDeleteTeb(x,x)+Ep ...
.text:004129B3
.text:004129B3 BugCheckParameter1= dword ptr 8
.text:004129B3 arg_4 = dword ptr 0Ch
.text:004129B3
.text:004129B3 ; FUNCTION CHUNK AT .text:00445DD6 SIZE 00000049 BYTES
.text:004129B3
.text:004129B3 mov edi, edi
.text:004129B5 push ebp
.text:004129B6 mov ebp, esp
.text:004129B8 push esi
.text:004129B9 push edi
.text:004129BA mov eax, large fs:124h
.text:004129C0 mov edi, [ebp+BugCheckParameter1]
.text:004129C3 mov esi, eax
.text:004129C5 cmp [esi+44h], edi
.text:004129C8 jz short loc_4129FF
.text:004129CA cmp byte ptr [esi+165h], 0
.text:004129D1 jnz loc_445DD6
.text:004129D7 mov eax, large fs:994h
.text:004129DD test eax, eax
.text:004129DF jnz loc_445DD6
.text:004129E5 call ds:__imp__KeRaiseIrqlToDpcLevel@0 ; KeRaiseIrqlToDpcLevel()
.text:004129EB mov byte ptr [ebp+BugCheckParameter1], al
.text:004129EE lea eax, [esi+14Ch]
.text:004129F4 push eax
.text:004129F5 push [ebp+BugCheckParameter1]
.text:004129F8 push edi
.text:004129F9 push esi
.text:004129FA call _KiAttachProcess@16 ; KiAttachProcess(x,x,x,x)
.text:004129FF
.text:004129FF loc_4129FF: ; CODE XREF: KeAttachProcess(x)+15j
.text:004129FF pop edi
.text:00412A00 pop esi
.text:00412A01 pop ebp
.text:00412A02 retn 4
.text:00412A02 _KeAttachProcess@4 endp
.text:004189DB ; __stdcall KeStackAttachProcess(x, x)
.text:004189DB public _KeStackAttachProcess@8
.text:004189DB _KeStackAttachProcess@8 proc near ; CODE XREF: MmAttachSession(x,x)+58p
.text:004189DB ; MiAttachToSecureProcessInSession(x)+43p ...
.text:004189DB
.text:004189DB arg_0 = dword ptr 8
.text:004189DB arg_4 = dword ptr 0Ch
.text:004189DB
.text:004189DB mov edi, edi
.text:004189DD push ebp
.text:004189DE mov ebp, esp
.text:004189E0 push esi
.text:004189E1 push edi
.text:004189E2 mov eax, large fs:124h
.text:004189E8 mov esi, eax
.text:004189EA mov eax, large fs:994h
.text:004189F0 test eax, eax
.text:004189F2 jnz loc_445DF1
.text:004189F8 mov edi, [ebp+arg_0]
.text:004189FB cmp [esi+44h], edi
.text:004189FE jz short loc_418A34
.text:00418A00 call ds:__imp__KeRaiseIrqlToDpcLevel@0 ; KeRaiseIrqlToDpcLevel()
.text:00418A06 cmp byte ptr [esi+165h], 0
.text:00418A0D mov byte ptr [ebp+arg_0], al
.text:00418A10 jnz loc_445E0D
.text:00418A16 lea eax, [esi+14Ch]
.text:00418A1C push eax
.text:00418A1D push [ebp+arg_0]
.text:00418A20 push edi
.text:00418A21 push esi
.text:00418A22 call _KiAttachProcess@16 ; KiAttachProcess(x,x,x,x)
.text:00418A27 mov eax, [ebp+arg_4]
.text:00418A2A and dword ptr [eax+10h], 0
.text:00418A2E
.text:00418A2E loc_418A2E: ; CODE XREF: KeStackAttachProcess(x,x)+63j
.text:00418A2E ; KeAttachProcess(x)+33467j
.text:00418A2E pop edi
.text:00418A2F pop esi
.text:00418A30 pop ebp
.text:00418A31 retn 8
我们有以下办法
1.直接执行函数+5字节处
这样可以,但是有其他问题,如果NP把函数hook改到函数中间或者末尾,我们很难定位
2.执行底层函数KiAttachProcess
KiAttachProcess(EPROCESS *Process,Irql)
.text:00412855 ; __stdcall KiAttachProcess(x, x, x, x)
.text:00412855 _KiAttachProcess@16 proc near ; CODE XREF: KeAttachProcess(x)+47p
.text:00412855 ; KeStackAttachProcess(x,x)+47p ...
.text:00412855
.text:00412855 arg_0 = dword ptr 8
.text:00412855 arg_4 = dword ptr 0Ch
.text:00412855 arg_8 = byte ptr 10h
.text:00412855 arg_C = dword ptr 14h
.text:00412855
.text:00412855 ; FUNCTION CHUNK AT .text:00445D4F SIZE 0000001E BYTES
.text:00412855 ; FUNCTION CHUNK AT .text:00445D72 SIZE 00000064 BYTES
.text:00412855
.text:00412855 mov edi, edi
.text:00412857 push ebp
.text:00412858 mov ebp, esp
.text:0041285A push ebx
.text:0041285B mov ebx, [ebp+arg_4]
.text:0041285E inc word ptr [ebx+60h]
.text:00412862 push esi
.text:00412863 mov esi, [ebp+arg_0]
.text:00412866 push edi
.text:00412867 push [ebp+arg_C]
.text:0041286A lea edi, [esi+34h]
.text:0041286D push edi
.text:0041286E call _KiMoveApcState@8 ; KiMoveApcState(x,x)
.text:00412873 mov [edi+4], edi
.text:00412876 mov [edi], edi
.text:00412878 lea eax, [esi+3Ch]
.text:0041287B mov [eax+4], eax
.text:0041287E mov [eax], eax
.text:00412880 lea eax, [esi+14Ch]
.text:00412886 cmp [ebp+arg_C], eax
.text:00412889 mov [esi+44h], ebx
.text:0041288C mov byte ptr [esi+48h], 0
.text:00412890 mov byte ptr [esi+49h], 0
.text:00412894 mov byte ptr [esi+4Ah], 0
.text:00412898 jnz short loc_4128AD
.text:0041289A mov [esi+138h], eax
.text:004128A0 mov [esi+13Ch], edi
.text:004128A6 mov byte ptr [esi+165h], 1
.text:004128AD
.text:004128AD loc_4128AD: ; CODE XREF: KiAttachProcess(x,x,x,x)+43j
.text:004128AD cmp byte ptr [ebx+65h], 0
.text:004128B1 jnz loc_445D72
.text:004128B7 lea esi, [ebx+40h]
.text:004128BA
.text:004128BA loc_4128BA: ; CODE XREF: KiAttachProcess(x,x,x,x)+33513j
.text:004128BA mov eax, [esi]
.text:004128BC cmp eax, esi
.text:004128BE jnz loc_445D4F
.text:004128C4 mov eax, [ebp+arg_C]
.text:004128C7 push dword ptr [eax+10h]
.text:004128CA push ebx
.text:004128CB call _KiSwapProcess@8 ; KiSwapProcess(x,x)
.text:004128D0 mov cl, [ebp+arg_8]
.text:004128D3 call @KiUnlockDispatcherDatabase@4 ; KiUnlockDispatcherDatabase(x)
.text:004128D8
.text:004128D8 loc_4128D8: ; CODE XREF: .text:00445D6Dj
.text:004128D8 ; KiAttachProcess(x,x,x,x)+3357Cj
.text:004128D8 pop edi
.text:004128D9 pop esi
.text:004128DA pop ebx
.text:004128DB pop ebp
.text:004128DC retn 10h
.text:004128DC _KiAttachProcess@16 endp
.text:004128DC
或者执行更加底层函数KiSwapProcess
.text:00404AC4 ; __stdcall KiSwapProcess(x, x)
.text:00404AC4 _KiSwapProcess@8 proc near ; CODE XREF: KiAttachProcess(x,x,x,x)+76p
.text:00404AC4 ; KeDetachProcess()+73p ...
.text:00404AC4
.text:00404AC4 arg_0 = dword ptr 4
.text:00404AC4
.text:00404AC4 mov edx, [esp+arg_0]
.text:00404AC8 xor eax, eax
.text:00404ACA cmp [edx+20h], ax
.text:00404ACE jz short loc_404AFF
.text:00404AD0 mov ecx, ds:0FFDFF03Ch
.text:00404AD6 mov eax, [edx+20h]
.text:00404AD9 mov [ecx+48h], eax
.text:00404ADC mov eax, [edx+24h]
.text:00404ADF mov [ecx+4Ch], eax
.text:00404AE2 mov ecx, ds:0FFDFF038h
.text:00404AE8 mov eax, [edx+28h]
.text:00404AEB mov [ecx+108h], eax
.text:00404AF1 mov eax, [edx+2Ch]
.text:00404AF4 mov [ecx+10Ch], eax
.text:00404AFA mov eax, 48h
.text:00404AFF
.text:00404AFF loc_404AFF: ; CODE XREF: KiSwapProcess(x,x)+Aj
.text:00404AFF lldt ax
.text:00404B02 mov ecx, ds:0FFDFF040h
.text:00404B08 mov edx, [esp+arg_0]
.text:00404B0C xor eax, eax
.text:00404B0E mov gs, ax
.text:00404B10 mov eax, [edx+18h]
.text:00404B13 mov [ecx+1Ch], eax
.text:00404B16 mov cr3, eax
.text:00404B19 mov ax, [edx+30h]
.text:00404B1D mov [ecx+66h], ax
.text:00404B21 retn 8
.text:00404B21 _KiSwapProcess@8 endp
.text:00404B21
我们仔细看代码,就是更改cr3
我们自己用代码完成-以下代码参考sinister
介绍比较多,如果不想看的话可以直接复制代码
下面的代码完成2个函数模拟,感谢KiSSinGGer大哥写的代码
NTSTATUS
WriteProcessMem(
IN HANDLE hProcess,
IN PVOID DstAddr,
IN PVOID SrcAddr,
IN ULONG Size
)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
PEPROCESS Process = NULL;
KAPC_STATE ApcState;
PMDL pMdl = NULL;
PCHAR MemBlock = NULL;
PCHAR MappedDstAddr = NULL;
if( SrcAddr == NULL || DstAddr == NULL || Size == 0 )
return Status;
Status = ObReferenceObjectByHandle(
hProcess,
PROCESS_VM_WRITE|PROCESS_VM_READ,
NULL,
KernelMode,
&Process,
NULL
);
if( !NT_SUCCESS(Status) )
{
DbgMsg( "ObReferenceObjectByHandle Failed./n" );
return Status;
}
//
// Get Current Process' Memory Block
//
MemBlock = ExAllocatePoolWithTag( NonPagedPool, Size, '-_-!');
if( MemBlock == NULL )
{
Status = STATUS_UNSUCCESSFUL;
goto __EXIT2;
}
__try
{
if( MmIsAddressValid(SrcAddr) )
{
ProbeForRead( SrcAddr, Size, sizeof(CHAR) );
RtlCopyMemory( MemBlock, SrcAddr, Size );
}
else
{
DbgMsg( "SrcAddr not valid./n" );
Status = STATUS_UNSUCCESSFUL;
goto __EXIT2;
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
DbgMsg( "Read SrcAddr raise an exception./n" );
Status = STATUS_UNSUCCESSFUL;
goto __EXIT2;
}
//
// Attach to Dst Process
//
KeStackAttachProcess( &(Process->Pcb), &ApcState);
__try
{
pMdl = IoAllocateMdl( DstAddr, Size, FALSE, FALSE, NULL );
if( pMdl )
{
MmProbeAndLockPages (pMdl, KernelMode, IoWriteAccess);
MappedDstAddr = MmMapLockedPagesSpecifyCache (pMdl,KernelMode,MmCached,NULL,FALSE,NormalPagePriority);
if( MappedDstAddr )
{
RtlCopyMemory( MappedDstAddr, MemBlock, Size );
Status = STATUS_SUCCESS;
goto __EXIT1;
}
else
Status = STATUS_UNSUCCESSFUL;
}
else
Status = STATUS_UNSUCCESSFUL;
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
DbgMsg( "Process DstAddr raise an exception./n" );
Status = STATUS_UNSUCCESSFUL;
goto __EXIT1;
}
__EXIT1:
//
// Detach to Dst Process
//
KeUnstackDetachProcess( &ApcState );
if( MappedDstAddr )
{
MmUnmapLockedPages( MappedDstAddr, pMdl);
}
if( pMdl )
{
MmUnlockPages( pMdl );
IoFreeMdl( pMdl );
}
__EXIT2:
if( Process )
{
ObDereferenceObject( Process );
}
if( MemBlock )
{
ExFreePool( MemBlock );
}
return Status;
}
NTSTATUS
ReadProcessMem(
IN HANDLE hProcess,
IN PVOID BaseAddr,
IN PVOID Buffer,
IN ULONG Size
)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
PEPROCESS Process = NULL;
KAPC_STATE ApcState;
PCHAR MemBlock = NULL;
if( BaseAddr == NULL || Buffer == NULL || Size == 0 )
return Status;
Status = ObReferenceObjectByHandle(
hProcess,
PROCESS_VM_WRITE|PROCESS_VM_READ,
NULL,
KernelMode,
&Process,
NULL
);
if( !NT_SUCCESS(Status) )
{
DbgMsg( "ObReferenceObjectByHandle Failed./n" );
return Status;
}
MemBlock = ExAllocatePoolWithTag( NonPagedPool, Size, '-_-!');
if( MemBlock == NULL )
{
ObDereferenceObject( Process );
return STATUS_UNSUCCESSFUL;
}
KeStackAttachProcess( &(Process->Pcb), &ApcState);
__try
{
if( MmIsAddressValid(BaseAddr) )
{
ProbeForRead( BaseAddr, Size, sizeof(CHAR) );
RtlCopyMemory( MemBlock, BaseAddr, Size );
}
else
{
DbgMsg( "BaseAddr not valid./n" );
Status = STATUS_UNSUCCESSFUL;
goto __EXIT2;
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
DbgMsg( "Process BaseAddr raise an exception./n" );
Status = STATUS_UNSUCCESSFUL;
goto __EXIT2;
}
KeUnstackDetachProcess( &ApcState );
__try
{
if( MmIsAddressValid(Buffer) )
{
ProbeForWrite( Buffer, Size, sizeof(CHAR));
RtlCopyMemory( Buffer, MemBlock, Size );
}
else
{
DbgMsg( "Buffer not valid./n" );
Status = STATUS_UNSUCCESSFUL;
goto __EXIT2;
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
DbgMsg( "Process Buffer raise an exception./n" );
Status = STATUS_UNSUCCESSFUL;
goto __EXIT2;
}
__EXIT2:
KeUnstackDetachProcess( &ApcState );
if( Process )
{
ObDereferenceObject( Process );
}
if( MemBlock )
{
ExFreePool( MemBlock );
}
return Status;
}
下面的代码完成最后一个事情
HANDLE ZwOpenProcess(HANDLE ProcessId)
{
PEPROCESS Process;
NTSTATUS St;
HANDLE hProcess = NULL;
PsLookupProcessByProcessId(ProcessId, &Process);
ObOpenObjectByPointer(Process, 0, NULL, 0, NULL, UserMode, &hProcess);
ObDereferenceObject(Process);
return hProcess;
}
很好,一些hook函数我们完成了模拟这些内核hook已经对我们不在有意义了
R3部分,这个只有NP才有,我们需要拦截线程创建函数禁止np注入邪恶模块给我们的od
Win32在创建用户态线程的时候,大致流程如下
CreateThread (kernel32.dll)
CreateRemoteThread (kernel32.dll)
NtCreateThread (ntoskrnl.exe)
PspCreateThread (ntos/ps/create.c:237)
PspCreateThread函数在创建用户态线程时,使用PspUserThreadStartup函数(ntos/ps/
create.c:1639)作为入口函数参数,线程被创建后直接进入此函数。PspUserThreadStartu
p函数对非僵死线程和没有结束的线程初始化其APC;
对于载入和卸载DLL,实际的调用流程如下:
LoadLibrary (kernel32.dll)
LoadLibraryEx (kernel32.dll)
BasepLoadLibraryAsDataFile (kernel32.dll)
NtMapViewOfSection (ntos/mm/mapview.c:204)
注:Kernel APC与User APC的区别:
1)前者是在APC_LEVEL上运行,后者是在PASSIVE_LEVEL上运行
2)只有KTHREAD中的AlertAble或KTHREAD-ApcState-UserApcPending
为1的情况下,User APC中的Routine才会被执行,Kernel APC没有这个限制
我之所以不使用广为人知的PsSetCreateProcessNotifyRoutine,PsSetCreateThreadNotifyRoutine
PsSetLoadImageNotifyRoutine。 原因很简单,他们都是插入APC,本质上,其实线程,进程,模块
已经建立或者载入,不过没有开始运行--原因是resume的时候会先执行APC
我们需要从本质上禁止插入邪恶模块,而不是插入1个没有开始运行的模块
当然我也尝试过在apc中无效化模块(故意填充0),但是发现不起作用,邪恶模块还是运行了
还有一个问题是我们不得不关注的——运行程序的时候父进程会“帮助”子进程创建子进程的主要线程(因为这个时候子进程还没有线程,所以不可能自己创建),所以这就引出了另外一个问题——如何判断这个远程线程创建是注入还是正常的程序运行呢?
简单分析下我们不难发现,二者的区别在于将被创建线程的进程是否还存在其他线程——因为我们的代码是在NtCreateThread之前执行的,所以如果是正常运行的程序的话,这个时候它不应当有任何的线程,而线程注入则不同,线程注入的话目标进程应当已经有了至少一个线程(一个主线程和若干个附属线程(或者没有附属线程)
我们需要检查EPROCESS结构的+0x190 ThreadListHead : _LIST_ENTRY
EPROCESS 结构是一个很大的结构,而且不同版本系统结构还不一样
typedef struct _EPROCESS {
/*000*/ BYTE Pcb[0x6C];
/*06C*/ NTSTATUS ExitStatus;
/*070*/ KEVENT LockEvent;
/*080*/ DWORD LockCount;
/*084*/ DWORD dw084;
/*088*/ LARGE_INTEGER CreateTime;
/*090*/ LARGE_INTEGER ExitTime;
/*098*/ PVOID LockOwner;
/*09C*/ DWORD UniqueProcessId;
/*0A0*/ LIST_ENTRY ActiveProcessLinks; // see PsActiveListHead
/*0A8*/ DWORD QuotaPeakPoolUsage[2]; // NP, P
/*0B0*/ DWORD QuotaPoolUsage[2]; // NP, P
/*0B8*/ DWORD PagefileUsage;
/*0BC*/ DWORD CommitCharge;
/*0C0*/ DWORD PeakPagefileUsage;
/*0C4*/ DWORD PeakVirtualSize;
/*0C8*/ LARGE_INTEGER VirtualSize;
/*0D0*/ MMSUPPORT Vm;
/*100*/ LIST_ENTRY SessionProcessLinks;
/*108*/ DWORD dw108[6];
/*120*/ PVOID DebugPort;
/*124*/ PVOID ExceptionPort;
/*128*/ PVOID ObjectTable;
/*12C*/ PVOID Token;
/*130*/ FAST_MUTEX WorkingSetLock;
/*150*/ DWORD WorkingSetPage;
/*154*/ BOOLEAN ProcessOutswapEnabled;
/*155*/ BOOLEAN ProcessOutswapped;
/*156*/ BOOLEAN AddressSpaceInitialized;
/*157*/ BOOLEAN AddressSpaceDeleted;
/*158*/ FAST_MUTEX AddressCreationLock;
/*178*/ KSPIN_LOCK HyperSpaceLock;
/*17C*/ DWORD ForkInProgress;
/*180*/ WORD VmOperation;
/*182*/ BOOLEAN ForkWasSuccessful;
/*183*/ BYTE MmAgressiveWsTrimMask;
/*184*/ DWORD VmOperationEvent;
/*188*/ PVOID PaeTop;
/*18C*/ DWORD LastFaultCount;
/*190*/ DWORD ModifiedPageCount;
/*194*/ PVOID VadRoot;
/*198*/ PVOID VadHint;
/*19C*/ PVOID CloneRoot;
/*1A0*/ DWORD NumberOfPrivatePages;
/*1A4*/ DWORD NumberOfLockedPages;
/*1A8*/ WORD NextPageColor;
/*1AA*/ BOOLEAN ExitProcessCalled;
/*1AB*/ BOOLEAN CreateProcessReported;
/*1AC*/ HANDLE SectionHandle;
/*1B0*/ PVOID Peb;
/*1B4*/ PVOID SectionBaseAddress;
/*1B8*/ PVOID QuotaBlock;
/*1BC*/ NTSTATUS LastThreadExitStatus;
/*1C0*/ DWORD WorkingSetWatch;
/*1C4*/ HANDLE Win32WindowStation;
/*1C8*/ DWORD InheritedFromUniqueProcessId;
/*1CC*/ ACCESS_MASK GrantedAccess;
/*1D0*/ DWORD DefaultHardErrorProcessing; // HEM_*
/*1D4*/ DWORD LdtInformation;
/*1D8*/ PVOID VadFreeHint;
/*1DC*/ DWORD VdmObjects;
/*1E0*/ PVOID DeviceMap;
/*1E4*/ DWORD SessionId;
/*1E8*/ LIST_ENTRY PhysicalVadList;
/*1F0*/ PVOID PageDirectoryPte;
/*1F4*/ DWORD dw1F4;
/*1F8*/ DWORD PaePageDirectoryPage;
/*1FC*/ CHAR ImageFileName[16];
/*20C*/ DWORD VmTrimFaultValue;
/*210*/ BYTE SetTimerResolution;
/*211*/ BYTE PriorityClass;
/*212*/ WORD SubSystemVersion;
/*214*/ PVOID Win32Process;
/*218*/ PVOID Job;
/*21C*/ DWORD JobStatus;
/*220*/ LIST_ENTRY JobLinks;
/*228*/ PVOID LockedPagesList;
/*22C*/ PVOID SecurityPort;
/*230*/ PVOID Wow64;
/*234*/ DWORD dw234;
/*238*/ IO_COUNTERS IoCounters;
/*268*/ DWORD CommitChargeLimit;
/*26C*/ DWORD CommitChargePeak;
/*270*/ LIST_ENTRY ThreadListHead;
/*278*/ PVOID VadPhysicalPagesBitMap;
/*27C*/ DWORD VadPhysicalPages;
/*280*/ DWORD AweLock;
/*284*/ } EPROCESS, *PEPROCESS;
代码如下
//==========================================
BOOLEAN
ProcessNoThread( PEPROCESS Process)
{
PLIST_ENTRY Entry;
PLIST_ENTRY ThreadListEntry;
PLIST_ENTRY ListHead;
ThreadListEntry = (PLIST_ENTRY)((ULONG)Process + ThreadListHead);
Entry = ThreadListEntry->Flink;
return (Entry==ThreadListEntry);
}
也就是判断ProcessNoThread了,如果建立进程就通过,反之则拦截
一些结构很复杂很难,偶也不想多写,其实偶也没很明白,更加不敢写,
详细的查阅其他资料把,
最后各位新年快乐
![](https://i-blog.csdnimg.cn/blog_migrate/e87e6cce2e2f771bbfbe7acedf9a3459.png)
![](https://i-blog.csdnimg.cn/blog_migrate/e87e6cce2e2f771bbfbe7acedf9a3459.png)
![](https://i-blog.csdnimg.cn/blog_migrate/e87e6cce2e2f771bbfbe7acedf9a3459.png)
一边听音乐一边写代码真的很舒服阿很舒服
先简单说以下,NP和HS都hook了以下一些SSDT
什么是SSDT?我不想说那些复杂的理论,希望以简单的事实帮助介绍
先向大S道歉,下文如有冒犯,还请大S原谅
![](https://i-blog.csdnimg.cn/blog_migrate/a1d6c3f54c23a6e01253364743545e13.png)
怎么说呢,SSDT本身就是一个地址表,我们当他是个地址簿,也就是写了每个人的地址的记录表
今天,偶想给大S送新年礼物,怎么送到大S家里呢?就得查看地址簿了
上面是这样写的 大S: 游戏达人,外挂高人,破解牛人,脱壳圣人;分身有如月,堀北真希,花泽类,猪的理想;实际上是个阳光宝宝,吃的的白白胖胖的,每天都很开心;家住上海市X区X路X楼,进门左转、左转、再左转,敲门说找S.H.E就行了。
标准SSDT hook的方式就是改变这个地址,HS就是八地址改写成abnlab实验室,如果你照着走就到abnlab实验室了
他们就要检查你了,这么大一箱礼物?是不是带了炸弹阿,识别你的身份阿,但是这样怎么能行呢,经过他们一审核
我的新年礼物岂不是要过年以后才能到了?不行不行
只能先联系大S,大S说家在上海X别墅区X,偶于是就上门了,到了别墅,门卫不让偶进去阿,理由是偶没有邀请函
; MmDeleteTeb(x,x)+Ep ...
.text:004129B3
.text:004129B3 BugCheckParameter1= dword ptr 8
.text:004129B3 arg_4 = dword ptr 0Ch
.text:004129B3
.text:004129B3 ; FUNCTION CHUNK AT .text:00445DD6 SIZE 00000049 BYTES
.text:004129B3
.text:004129B3 mov edi, edi
.text:004129B5 push ebp
.text:004129B6 mov ebp, esp
.text:004129B8 push esi
.text:004129B9 push edi
.text:004129BA mov eax, large fs:124h
.text:004129C0 mov edi, [ebp+BugCheckParameter1]
.text:004129C3 mov esi, eax
.text:004129C5 cmp [esi+44h], edi
.text:004129C8 jz short loc_4129FF
.text:004129CA cmp byte ptr [esi+165h], 0
.text:004129D1 jnz loc_445DD6
.text:004129D7 mov eax, large fs:994h
.text:004129DD test eax, eax
.text:004129DF jnz loc_445DD6
.text:004129E5 call ds:__imp__KeRaiseIrqlToDpcLevel@0 ; KeRaiseIrqlToDpcLevel()
.text:004129EB mov byte ptr [ebp+BugCheckParameter1], al
.text:004129EE lea eax, [esi+14Ch]
.text:004129F4 push eax
.text:004129F5 push [ebp+BugCheckParameter1]
.text:004129F8 push edi
.text:004129F9 push esi
.text:004129FA call _KiAttachProcess@16 ; KiAttachProcess(x,x,x,x)
.text:004129FF
.text:004129FF loc_4129FF: ; CODE XREF: KeAttachProcess(x)+15j
.text:004129FF pop edi
.text:00412A00 pop esi
.text:00412A01 pop ebp
.text:00412A02 retn 4
.text:00412A02 _KeAttachProcess@4 endp
.text:004189DB ; __stdcall KeStackAttachProcess(x, x)
.text:004189DB public _KeStackAttachProcess@8
.text:004189DB _KeStackAttachProcess@8 proc near ; CODE XREF: MmAttachSession(x,x)+58p
.text:004189DB ; MiAttachToSecureProcessInSession(x)+43p ...
.text:004189DB
.text:004189DB arg_0 = dword ptr 8
.text:004189DB arg_4 = dword ptr 0Ch
.text:004189DB
.text:004189DB mov edi, edi
.text:004189DD push ebp
.text:004189DE mov ebp, esp
.text:004189E0 push esi
.text:004189E1 push edi
.text:004189E2 mov eax, large fs:124h
.text:004189E8 mov esi, eax
.text:004189EA mov eax, large fs:994h
.text:004189F0 test eax, eax
.text:004189F2 jnz loc_445DF1
.text:004189F8 mov edi, [ebp+arg_0]
.text:004189FB cmp [esi+44h], edi
.text:004189FE jz short loc_418A34
.text:00418A00 call ds:__imp__KeRaiseIrqlToDpcLevel@0 ; KeRaiseIrqlToDpcLevel()
.text:00418A06 cmp byte ptr [esi+165h], 0
.text:00418A0D mov byte ptr [ebp+arg_0], al
.text:00418A10 jnz loc_445E0D
.text:00418A16 lea eax, [esi+14Ch]
.text:00418A1C push eax
.text:00418A1D push [ebp+arg_0]
.text:00418A20 push edi
.text:00418A21 push esi
.text:00418A22 call _KiAttachProcess@16 ; KiAttachProcess(x,x,x,x)
.text:00418A27 mov eax, [ebp+arg_4]
.text:00418A2A and dword ptr [eax+10h], 0
.text:00418A2E
.text:00418A2E loc_418A2E: ; CODE XREF: KeStackAttachProcess(x,x)+63j
.text:00418A2E ; KeAttachProcess(x)+33467j
.text:00418A2E pop edi
.text:00418A2F pop esi
.text:00418A30 pop ebp
.text:00418A31 retn 8
我们有以下办法
1.直接执行函数+5字节处
这样可以,但是有其他问题,如果NP把函数hook改到函数中间或者末尾,我们很难定位
2.执行底层函数KiAttachProcess
KiAttachProcess(EPROCESS *Process,Irql)
.text:00412855 ; __stdcall KiAttachProcess(x, x, x, x)
.text:00412855 _KiAttachProcess@16 proc near ; CODE XREF: KeAttachProcess(x)+47p
.text:00412855 ; KeStackAttachProcess(x,x)+47p ...
.text:00412855
.text:00412855 arg_0 = dword ptr 8
.text:00412855 arg_4 = dword ptr 0Ch
.text:00412855 arg_8 = byte ptr 10h
.text:00412855 arg_C = dword ptr 14h
.text:00412855
.text:00412855 ; FUNCTION CHUNK AT .text:00445D4F SIZE 0000001E BYTES
.text:00412855 ; FUNCTION CHUNK AT .text:00445D72 SIZE 00000064 BYTES
.text:00412855
.text:00412855 mov edi, edi
.text:00412857 push ebp
.text:00412858 mov ebp, esp
.text:0041285A push ebx
.text:0041285B mov ebx, [ebp+arg_4]
.text:0041285E inc word ptr [ebx+60h]
.text:00412862 push esi
.text:00412863 mov esi, [ebp+arg_0]
.text:00412866 push edi
.text:00412867 push [ebp+arg_C]
.text:0041286A lea edi, [esi+34h]
.text:0041286D push edi
.text:0041286E call _KiMoveApcState@8 ; KiMoveApcState(x,x)
.text:00412873 mov [edi+4], edi
.text:00412876 mov [edi], edi
.text:00412878 lea eax, [esi+3Ch]
.text:0041287B mov [eax+4], eax
.text:0041287E mov [eax], eax
.text:00412880 lea eax, [esi+14Ch]
.text:00412886 cmp [ebp+arg_C], eax
.text:00412889 mov [esi+44h], ebx
.text:0041288C mov byte ptr [esi+48h], 0
.text:00412890 mov byte ptr [esi+49h], 0
.text:00412894 mov byte ptr [esi+4Ah], 0
.text:00412898 jnz short loc_4128AD
.text:0041289A mov [esi+138h], eax
.text:004128A0 mov [esi+13Ch], edi
.text:004128A6 mov byte ptr [esi+165h], 1
.text:004128AD
.text:004128AD loc_4128AD: ; CODE XREF: KiAttachProcess(x,x,x,x)+43j
.text:004128AD cmp byte ptr [ebx+65h], 0
.text:004128B1 jnz loc_445D72
.text:004128B7 lea esi, [ebx+40h]
.text:004128BA
.text:004128BA loc_4128BA: ; CODE XREF: KiAttachProcess(x,x,x,x)+33513j
.text:004128BA mov eax, [esi]
.text:004128BC cmp eax, esi
.text:004128BE jnz loc_445D4F
.text:004128C4 mov eax, [ebp+arg_C]
.text:004128C7 push dword ptr [eax+10h]
.text:004128CA push ebx
.text:004128CB call _KiSwapProcess@8 ; KiSwapProcess(x,x)
.text:004128D0 mov cl, [ebp+arg_8]
.text:004128D3 call @KiUnlockDispatcherDatabase@4 ; KiUnlockDispatcherDatabase(x)
.text:004128D8
.text:004128D8 loc_4128D8: ; CODE XREF: .text:00445D6Dj
.text:004128D8 ; KiAttachProcess(x,x,x,x)+3357Cj
.text:004128D8 pop edi
.text:004128D9 pop esi
.text:004128DA pop ebx
.text:004128DB pop ebp
.text:004128DC retn 10h
.text:004128DC _KiAttachProcess@16 endp
.text:004128DC
或者执行更加底层函数KiSwapProcess
.text:00404AC4 ; __stdcall KiSwapProcess(x, x)
.text:00404AC4 _KiSwapProcess@8 proc near ; CODE XREF: KiAttachProcess(x,x,x,x)+76p
.text:00404AC4 ; KeDetachProcess()+73p ...
.text:00404AC4
.text:00404AC4 arg_0 = dword ptr 4
.text:00404AC4
.text:00404AC4 mov edx, [esp+arg_0]
.text:00404AC8 xor eax, eax
.text:00404ACA cmp [edx+20h], ax
.text:00404ACE jz short loc_404AFF
.text:00404AD0 mov ecx, ds:0FFDFF03Ch
.text:00404AD6 mov eax, [edx+20h]
.text:00404AD9 mov [ecx+48h], eax
.text:00404ADC mov eax, [edx+24h]
.text:00404ADF mov [ecx+4Ch], eax
.text:00404AE2 mov ecx, ds:0FFDFF038h
.text:00404AE8 mov eax, [edx+28h]
.text:00404AEB mov [ecx+108h], eax
.text:00404AF1 mov eax, [edx+2Ch]
.text:00404AF4 mov [ecx+10Ch], eax
.text:00404AFA mov eax, 48h
.text:00404AFF
.text:00404AFF loc_404AFF: ; CODE XREF: KiSwapProcess(x,x)+Aj
.text:00404AFF lldt ax
.text:00404B02 mov ecx, ds:0FFDFF040h
.text:00404B08 mov edx, [esp+arg_0]
.text:00404B0C xor eax, eax
.text:00404B0E mov gs, ax
.text:00404B10 mov eax, [edx+18h]
.text:00404B13 mov [ecx+1Ch], eax
.text:00404B16 mov cr3, eax
.text:00404B19 mov ax, [edx+30h]
.text:00404B1D mov [ecx+66h], ax
.text:00404B21 retn 8
.text:00404B21 _KiSwapProcess@8 endp
.text:00404B21
我们仔细看代码,就是更改cr3
我们自己用代码完成-以下代码参考sinister
引用:
当调用此函数时先把要切换的进程的内核堆栈数加一(EPROCESS->Stack
Count),并从形参中取得当前线程保存 APC 状态的位置,(ETHREAD->SaveApcState)
得到当前线程 APC 状态(ETHREAD->ApcState),来调用 KiMoveApcState() 函数,将当
前线程APC 状态 (ETHREAD->ApcState) 复制到当前线程 SavedApcState 处保存。然后
将当前线程内核 APC 的 Progress 状态、内核模式 APC 的 Pending 状态、用户模式
APC 的 Pending 态均设置为 FALSE(ETHREAD->KernelApcInProgress、ETHREAD->Kernel
ApcPending、ETHREAD->UserApcPending),并初始化当前线程内核模式与用户模式 APC
状态链(ETHREAD->>ApcState.ApcListHead[KernelMode]、ETHREAD->ApcState.ApcList
Head[UserMode]),然后将当前线程所在进程(ETHREAD->ApcState->EPROCESS)设置为
要切换的进程(EPROCESS),比较当前线程保存 APC 状态(ETHREAD->SavedApcState)
是否与形参中(SavedApcState)要输出的保存 APC 态相等,如相等则需要将当前线程
APC 状态与保存 APC 状态(ETHREAD->ApcState、ETHREAD->SavedApcState)分别赋与
当前线程的 ApcStatePointer[0] 与 ApcStatePointer[1],(ApcStatePointer 是
KAPC_STATE 结构数组而 KAPC_STATE 又是由 LIST_ENTRY 链表描述的)然后在设置当前
线程 APC 状态索引为 1(ETHREAD->ApcStateIndex)。
复制内容到剪贴板
代码:
KiAttachProcess(EPROCESS *Process,Irql){
//CurThread=fs:124h
//CurProcess=CurThread->ApcState.Process;
if(CurProcess!=Process){
if(CurProcess->ApcStateIndex || KPCR->DpcRoutineActive)KeBugCheckEx...
}
//if we already in process's context
if(CurProcess==Process){KiUnlockDispatcherDatabase(Irql);return;}
Process->StackCount++;
KiMoveApcState(&CurThread->ApcState,&CurThread->SavedApcState);
// init lists
CurThread->ApcState.ApcListHead[0].Blink=&CurThread->ApcState.ApcListHead[0];
CurThread->ApcState.ApcListHead[0].Flink=&CurThread->ApcState.ApcListHead[0];
CurThread->ApcState.ApcListHead[1].Blink=&CurThread->ApcState.ApcListHead[1];
CurThread->ApcState.ApcListHead[1].Flink=&CurThread->ApcState.ApcListHead[1];;
//fill curtheads's fields
CurThread->ApcState.Process=Process;
CurThread->ApcState.KernelApcInProgress=0;
CurThread->ApcState.KernelApcPending=0;
CurThread->ApcState.UserApcPending=0;
CurThread->ApcState.ApcStatePointer.SavedApcState=&CurThread->SavedApcState;
CurThread->ApcState.ApcStatePointer.ApcState=&CurThread->ApcState;
CurThread->ApcStateIndex=1;
//if process ready, just swap it...
if(!Process->State)//state==0, ready
{
KiSwapProcess(Process,CurThread->SavedApcState.Process);
KiUnlockDispatcherDatabase(Irql);
return;
}
CurThread->State=1; //ready?
CurThread->ProcessReadyQueue=1;
//put Process in Thread's waitlist
CurThread->WaitListEntry.Flink=&Process->ReadyListHead.Flink;
CurThread->WaitListEntry.Blink=Process->ReadyListHead.Blink;
Process->ReadyListHead.Flink->Flink=&CurThread->WaitListEntry.Flink;
Process->ReadyListHead.Blink=&CurThread->WaitListEntry.Flink;
// else, move process to swap list and wait
if(Process->State==1){//idle?
Process->State=2; //trans
Process->SwapListEntry.Flink=&KiProcessInSwapListHead.Flink;
Process->SwapListEntry.Blink=KiProcessInSwapListHead.Blink;
KiProcessInSwapListHead.Blink=&Process->SwapListEntry.Flink;
KiSwapEvent.Header.SignalState=1;
if(KiSwapEvent.Header.WaitListHead.Flink!=&KiSwapEvent.Header.WaitListHead.
Flink)
KiWaitTest(&KiSwapEvent,0xa); //fastcall
}
CurThread->WaitIrql=Irql;
KiSwapThread();
return;
}
引用:
从这个函数可以得到以下结论。进程可以处于以下状态——0(准备),1(Idle),2(Tra
ns——切换)。这证实了高层次的信息。KiAttachProcess使用了另外两个函数KiSwapProce
ss和KiSwapThread。
/************************* KiSwapProcess ****************************/
KiSwapProcess(EPROCESS* NewProcess, EPROCESS* OldProcess)
{
// just reload cr3 and small work with TSS
// TSS=KPCR->TSS;
// xor eax,eax
// mov gs,ax
TSS->CR3=NewProcess->DirectoryTableBase;//0x1c
// mov cr3,NewProcess->DirectoryTableBase
TSS->IopmOffset=NewProcess->IopmOffset;//0x66
if(WORD(NewProcess->LdtDescriptor)==0){lldt 0x00; return;//}
//GDT=KPCR->GDT;
(QWORD)GDT->0x48=(QWORD)NewProcess->LdtDescriptor;
(QWORD)GDT->0x108=(QWORD)NewProcess->Int21Descriptor;
lldt 0x48;
return;
}
切换进程上下文。正如我所料,这个函数只是重新加载CR3寄存器,再加上一点相关的操作。
例如,用IopmOffset域的值建立TSS中的I/O位图的偏移。还必需将选择子的值加载到ldt(只
用于VDM session)。
/************************* SwapContext ******************************/
SwapContext(NextThread,CurThread,WaitIrql)
{
NextThread.State=ThreadStateRunning; //2
KPCR.DebugActive=NextThread.DebugActive;
cli();
//Save Stack
CurThread.KernelStack=esp;
//Set stack
KPCR.StackLimit=NextThread.StackLimit;
KPCR.StackBase=NextThread.InitialStack;
tmp=NextThread.InitialStack-0x70;
newcr0=cr0&0xfffffff1|NextThread.NpxState|*(tmp+0x6c);
if(newcr0!=cr0)reloadcr0();
if(!*(tmp-0x1c)&0x20000)tmp-=0x10;
TSS=KPCB.TSS;
TSS->ESP0=tmp;
//set pTeb
KPCB.Self=NextThread.pTeb;
esp=NextThread.KernelStack;
sti();
//correct GDT
GDT=KPCB.GDT;
WORD(GDT->0x3a)=NextThread.pTeb;
BYTE(GDT->0x3c)=NextThread.pTeb>>16;
BYTE(GDT->0x3f)=NextThread.pTeb>>24;
//if we must swap processes, do it (like KiSwapProcess)
if(CurThread.ApcState.Process!=NextThread.ApcState.Process)
{
//******** like KiSwapProcess
}
NextThread->ContextSwitches++;
KPCB->KeContextSwitches++;
if(!NextThread->ApcState.KernelApcPending)return 0;
//popf;
// jnz HalRequestSoftwareInterrupt// return 0
return 1;
}
切换堆栈,修正GDT,以使FS寄存器指向TEB。如果线程属于当前进程,则不进行上下文切换
。否则,进行的操作和KiSwapProcess中的大致差不多。
![](https://i-blog.csdnimg.cn/blog_migrate/e8e1fac9b3c6c663a0f9a9ef45f35054.png)
下面的代码完成2个函数模拟,感谢KiSSinGGer大哥写的代码
NTSTATUS
WriteProcessMem(
IN HANDLE hProcess,
IN PVOID DstAddr,
IN PVOID SrcAddr,
IN ULONG Size
)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
PEPROCESS Process = NULL;
KAPC_STATE ApcState;
PMDL pMdl = NULL;
PCHAR MemBlock = NULL;
PCHAR MappedDstAddr = NULL;
if( SrcAddr == NULL || DstAddr == NULL || Size == 0 )
return Status;
Status = ObReferenceObjectByHandle(
hProcess,
PROCESS_VM_WRITE|PROCESS_VM_READ,
NULL,
KernelMode,
&Process,
NULL
);
if( !NT_SUCCESS(Status) )
{
DbgMsg( "ObReferenceObjectByHandle Failed./n" );
return Status;
}
//
// Get Current Process' Memory Block
//
MemBlock = ExAllocatePoolWithTag( NonPagedPool, Size, '-_-!');
if( MemBlock == NULL )
{
Status = STATUS_UNSUCCESSFUL;
goto __EXIT2;
}
__try
{
if( MmIsAddressValid(SrcAddr) )
{
ProbeForRead( SrcAddr, Size, sizeof(CHAR) );
RtlCopyMemory( MemBlock, SrcAddr, Size );
}
else
{
DbgMsg( "SrcAddr not valid./n" );
Status = STATUS_UNSUCCESSFUL;
goto __EXIT2;
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
DbgMsg( "Read SrcAddr raise an exception./n" );
Status = STATUS_UNSUCCESSFUL;
goto __EXIT2;
}
//
// Attach to Dst Process
//
KeStackAttachProcess( &(Process->Pcb), &ApcState);
__try
{
pMdl = IoAllocateMdl( DstAddr, Size, FALSE, FALSE, NULL );
if( pMdl )
{
MmProbeAndLockPages (pMdl, KernelMode, IoWriteAccess);
MappedDstAddr = MmMapLockedPagesSpecifyCache (pMdl,KernelMode,MmCached,NULL,FALSE,NormalPagePriority);
if( MappedDstAddr )
{
RtlCopyMemory( MappedDstAddr, MemBlock, Size );
Status = STATUS_SUCCESS;
goto __EXIT1;
}
else
Status = STATUS_UNSUCCESSFUL;
}
else
Status = STATUS_UNSUCCESSFUL;
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
DbgMsg( "Process DstAddr raise an exception./n" );
Status = STATUS_UNSUCCESSFUL;
goto __EXIT1;
}
__EXIT1:
//
// Detach to Dst Process
//
KeUnstackDetachProcess( &ApcState );
if( MappedDstAddr )
{
MmUnmapLockedPages( MappedDstAddr, pMdl);
}
if( pMdl )
{
MmUnlockPages( pMdl );
IoFreeMdl( pMdl );
}
__EXIT2:
if( Process )
{
ObDereferenceObject( Process );
}
if( MemBlock )
{
ExFreePool( MemBlock );
}
return Status;
}
NTSTATUS
ReadProcessMem(
IN HANDLE hProcess,
IN PVOID BaseAddr,
IN PVOID Buffer,
IN ULONG Size
)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
PEPROCESS Process = NULL;
KAPC_STATE ApcState;
PCHAR MemBlock = NULL;
if( BaseAddr == NULL || Buffer == NULL || Size == 0 )
return Status;
Status = ObReferenceObjectByHandle(
hProcess,
PROCESS_VM_WRITE|PROCESS_VM_READ,
NULL,
KernelMode,
&Process,
NULL
);
if( !NT_SUCCESS(Status) )
{
DbgMsg( "ObReferenceObjectByHandle Failed./n" );
return Status;
}
MemBlock = ExAllocatePoolWithTag( NonPagedPool, Size, '-_-!');
if( MemBlock == NULL )
{
ObDereferenceObject( Process );
return STATUS_UNSUCCESSFUL;
}
KeStackAttachProcess( &(Process->Pcb), &ApcState);
__try
{
if( MmIsAddressValid(BaseAddr) )
{
ProbeForRead( BaseAddr, Size, sizeof(CHAR) );
RtlCopyMemory( MemBlock, BaseAddr, Size );
}
else
{
DbgMsg( "BaseAddr not valid./n" );
Status = STATUS_UNSUCCESSFUL;
goto __EXIT2;
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
DbgMsg( "Process BaseAddr raise an exception./n" );
Status = STATUS_UNSUCCESSFUL;
goto __EXIT2;
}
KeUnstackDetachProcess( &ApcState );
__try
{
if( MmIsAddressValid(Buffer) )
{
ProbeForWrite( Buffer, Size, sizeof(CHAR));
RtlCopyMemory( Buffer, MemBlock, Size );
}
else
{
DbgMsg( "Buffer not valid./n" );
Status = STATUS_UNSUCCESSFUL;
goto __EXIT2;
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
DbgMsg( "Process Buffer raise an exception./n" );
Status = STATUS_UNSUCCESSFUL;
goto __EXIT2;
}
__EXIT2:
KeUnstackDetachProcess( &ApcState );
if( Process )
{
ObDereferenceObject( Process );
}
if( MemBlock )
{
ExFreePool( MemBlock );
}
return Status;
}
下面的代码完成最后一个事情
HANDLE ZwOpenProcess(HANDLE ProcessId)
{
PEPROCESS Process;
NTSTATUS St;
HANDLE hProcess = NULL;
PsLookupProcessByProcessId(ProcessId, &Process);
ObOpenObjectByPointer(Process, 0, NULL, 0, NULL, UserMode, &hProcess);
ObDereferenceObject(Process);
return hProcess;
}
很好,一些hook函数我们完成了模拟这些内核hook已经对我们不在有意义了
R3部分,这个只有NP才有,我们需要拦截线程创建函数禁止np注入邪恶模块给我们的od
Win32在创建用户态线程的时候,大致流程如下
CreateThread (kernel32.dll)
CreateRemoteThread (kernel32.dll)
NtCreateThread (ntoskrnl.exe)
PspCreateThread (ntos/ps/create.c:237)
PspCreateThread函数在创建用户态线程时,使用PspUserThreadStartup函数(ntos/ps/
create.c:1639)作为入口函数参数,线程被创建后直接进入此函数。PspUserThreadStartu
p函数对非僵死线程和没有结束的线程初始化其APC;
对于载入和卸载DLL,实际的调用流程如下:
LoadLibrary (kernel32.dll)
LoadLibraryEx (kernel32.dll)
BasepLoadLibraryAsDataFile (kernel32.dll)
NtMapViewOfSection (ntos/mm/mapview.c:204)
注:Kernel APC与User APC的区别:
1)前者是在APC_LEVEL上运行,后者是在PASSIVE_LEVEL上运行
2)只有KTHREAD中的AlertAble或KTHREAD-ApcState-UserApcPending
为1的情况下,User APC中的Routine才会被执行,Kernel APC没有这个限制
我之所以不使用广为人知的PsSetCreateProcessNotifyRoutine,PsSetCreateThreadNotifyRoutine
PsSetLoadImageNotifyRoutine。 原因很简单,他们都是插入APC,本质上,其实线程,进程,模块
已经建立或者载入,不过没有开始运行--原因是resume的时候会先执行APC
我们需要从本质上禁止插入邪恶模块,而不是插入1个没有开始运行的模块
当然我也尝试过在apc中无效化模块(故意填充0),但是发现不起作用,邪恶模块还是运行了
还有一个问题是我们不得不关注的——运行程序的时候父进程会“帮助”子进程创建子进程的主要线程(因为这个时候子进程还没有线程,所以不可能自己创建),所以这就引出了另外一个问题——如何判断这个远程线程创建是注入还是正常的程序运行呢?
简单分析下我们不难发现,二者的区别在于将被创建线程的进程是否还存在其他线程——因为我们的代码是在NtCreateThread之前执行的,所以如果是正常运行的程序的话,这个时候它不应当有任何的线程,而线程注入则不同,线程注入的话目标进程应当已经有了至少一个线程(一个主线程和若干个附属线程(或者没有附属线程)
我们需要检查EPROCESS结构的+0x190 ThreadListHead : _LIST_ENTRY
EPROCESS 结构是一个很大的结构,而且不同版本系统结构还不一样
typedef struct _EPROCESS {
/*000*/ BYTE Pcb[0x6C];
/*06C*/ NTSTATUS ExitStatus;
/*070*/ KEVENT LockEvent;
/*080*/ DWORD LockCount;
/*084*/ DWORD dw084;
/*088*/ LARGE_INTEGER CreateTime;
/*090*/ LARGE_INTEGER ExitTime;
/*098*/ PVOID LockOwner;
/*09C*/ DWORD UniqueProcessId;
/*0A0*/ LIST_ENTRY ActiveProcessLinks; // see PsActiveListHead
/*0A8*/ DWORD QuotaPeakPoolUsage[2]; // NP, P
/*0B0*/ DWORD QuotaPoolUsage[2]; // NP, P
/*0B8*/ DWORD PagefileUsage;
/*0BC*/ DWORD CommitCharge;
/*0C0*/ DWORD PeakPagefileUsage;
/*0C4*/ DWORD PeakVirtualSize;
/*0C8*/ LARGE_INTEGER VirtualSize;
/*0D0*/ MMSUPPORT Vm;
/*100*/ LIST_ENTRY SessionProcessLinks;
/*108*/ DWORD dw108[6];
/*120*/ PVOID DebugPort;
/*124*/ PVOID ExceptionPort;
/*128*/ PVOID ObjectTable;
/*12C*/ PVOID Token;
/*130*/ FAST_MUTEX WorkingSetLock;
/*150*/ DWORD WorkingSetPage;
/*154*/ BOOLEAN ProcessOutswapEnabled;
/*155*/ BOOLEAN ProcessOutswapped;
/*156*/ BOOLEAN AddressSpaceInitialized;
/*157*/ BOOLEAN AddressSpaceDeleted;
/*158*/ FAST_MUTEX AddressCreationLock;
/*178*/ KSPIN_LOCK HyperSpaceLock;
/*17C*/ DWORD ForkInProgress;
/*180*/ WORD VmOperation;
/*182*/ BOOLEAN ForkWasSuccessful;
/*183*/ BYTE MmAgressiveWsTrimMask;
/*184*/ DWORD VmOperationEvent;
/*188*/ PVOID PaeTop;
/*18C*/ DWORD LastFaultCount;
/*190*/ DWORD ModifiedPageCount;
/*194*/ PVOID VadRoot;
/*198*/ PVOID VadHint;
/*19C*/ PVOID CloneRoot;
/*1A0*/ DWORD NumberOfPrivatePages;
/*1A4*/ DWORD NumberOfLockedPages;
/*1A8*/ WORD NextPageColor;
/*1AA*/ BOOLEAN ExitProcessCalled;
/*1AB*/ BOOLEAN CreateProcessReported;
/*1AC*/ HANDLE SectionHandle;
/*1B0*/ PVOID Peb;
/*1B4*/ PVOID SectionBaseAddress;
/*1B8*/ PVOID QuotaBlock;
/*1BC*/ NTSTATUS LastThreadExitStatus;
/*1C0*/ DWORD WorkingSetWatch;
/*1C4*/ HANDLE Win32WindowStation;
/*1C8*/ DWORD InheritedFromUniqueProcessId;
/*1CC*/ ACCESS_MASK GrantedAccess;
/*1D0*/ DWORD DefaultHardErrorProcessing; // HEM_*
/*1D4*/ DWORD LdtInformation;
/*1D8*/ PVOID VadFreeHint;
/*1DC*/ DWORD VdmObjects;
/*1E0*/ PVOID DeviceMap;
/*1E4*/ DWORD SessionId;
/*1E8*/ LIST_ENTRY PhysicalVadList;
/*1F0*/ PVOID PageDirectoryPte;
/*1F4*/ DWORD dw1F4;
/*1F8*/ DWORD PaePageDirectoryPage;
/*1FC*/ CHAR ImageFileName[16];
/*20C*/ DWORD VmTrimFaultValue;
/*210*/ BYTE SetTimerResolution;
/*211*/ BYTE PriorityClass;
/*212*/ WORD SubSystemVersion;
/*214*/ PVOID Win32Process;
/*218*/ PVOID Job;
/*21C*/ DWORD JobStatus;
/*220*/ LIST_ENTRY JobLinks;
/*228*/ PVOID LockedPagesList;
/*22C*/ PVOID SecurityPort;
/*230*/ PVOID Wow64;
/*234*/ DWORD dw234;
/*238*/ IO_COUNTERS IoCounters;
/*268*/ DWORD CommitChargeLimit;
/*26C*/ DWORD CommitChargePeak;
/*270*/ LIST_ENTRY ThreadListHead;
/*278*/ PVOID VadPhysicalPagesBitMap;
/*27C*/ DWORD VadPhysicalPages;
/*280*/ DWORD AweLock;
/*284*/ } EPROCESS, *PEPROCESS;
代码如下
//==========================================
BOOLEAN
ProcessNoThread( PEPROCESS Process)
{
PLIST_ENTRY Entry;
PLIST_ENTRY ThreadListEntry;
PLIST_ENTRY ListHead;
ThreadListEntry = (PLIST_ENTRY)((ULONG)Process + ThreadListHead);
Entry = ThreadListEntry->Flink;
return (Entry==ThreadListEntry);
}
也就是判断ProcessNoThread了,如果建立进程就通过,反之则拦截
一些结构很复杂很难,偶也不想多写,其实偶也没很明白,更加不敢写,
详细的查阅其他资料把,
最后各位新年快乐
![](https://i-blog.csdnimg.cn/blog_migrate/e87e6cce2e2f771bbfbe7acedf9a3459.png)
![](https://i-blog.csdnimg.cn/blog_migrate/e87e6cce2e2f771bbfbe7acedf9a3459.png)