由枚举模块到ring0内存结构 (分析NtQueryVirtualMemory)
是由获得进程模块而引发的一系列的问题,首先,在ring3层下枚举进程模块有ToolHelp,Psapi,还可以通过在ntdll中获得ZwQuerySystemInformation的函数地址来枚举,其中ZwQueryInformationProcess相当于是调用系统服务函数,其内部实现就是遍历PEB中的Moudle链表,
kd> dt _PEB
+0x00c Ldr : Ptr32 _PEB_LDR_DATA
kd> dt _PEB_LDR_DATA
nt!_PEB_LDR_DATA
+0x000 Length : Uint4B
+0x004 Initialized : UChar
+0x008 SsHandle : Ptr32 Void
+0x00c InLoadOrderModuleList : _LIST_ENTRY //按模块的加载顺序
+0x014 InMemoryOrderModuleList : _LIST_ENTRY //按模块在内存中的地址顺序
+0x01c InInitializationOrderModuleList : _LIST_ENTRY //按初始化顺序
+0x024 EntryInProgress : Ptr32 Void
+0x028 ShutdownInProgress : UChar
+0x02c ShutdownThreadId : Ptr32 Void
整个链的结构就是下面这张图
typedef struct _PEB_LDR_DATA32 //xp sp3 x86 { ULONG Length; 0x00 BOOLEAN Initialized; 0x04 HANDLE SsHandle; 0x08 LIST_ENTRY InLoadOrderModuleList; 0x0c //按模块的加载顺序 LIST_ENTRY InMemoryOrderModuleList; 0x14 //按模块在内存中的地址顺序 LIST_ENTRY InInitializationOrderModuleList; 0x1c //按初始化顺序 PVOID EntryInProgress; 0x24 } PEB_LDR_DATA32, *PPEB_LDR_DATA32; typedef struct _PEB_LDR_DATA64 //win7 sp1 x64 { ULONG Length; BOOLEAN Initialized; HANDLE SsHandle; LIST_ENTRY64 InLoadOrderModuleList; LIST_ENTRY64 InMemoryOrderModuleList; LIST_ENTRY64 InInitializationOrderModuleList; PVOID EntryInProgress; BOOLEAN ShutdownInProgress; PVOID ShutdownThreadId; } PEB_LDR_DATA64, *PPEB_LDR_DATA64; 图中所示的_LDR_MOUDLE的结构如下: typedef struct _LDR_DATA_TABLE_ENTRY32 { //xp sp3 x86 LIST_ENTRY InLoadOrderLinks; LIST_ENTRY InMemoryOrderLinks; LIST_ENTRY InInitializationOrderLinks; PVOID DllBase; PVOID EntryPoint; ULONG SizeOfImage; UNICODE_STRING FullDllName; UNICODE_STRING BaseDllName; ULONG Flags; USHORT LoadCount; USHORT TlsIndex; union { LIST_ENTRY HashLinks; struct { PVOID SectionPointer; ULONG CheckSum; }; }; union { struct { ULONG TimeDateStamp; }; struct { PVOID LoadedImports; }; }; } LDR_DATA_TABLE_ENTRY32,*PLDR_DATA_TABLE_ENTRY32; typedef struct _LDR_DATA_TABLE_ENTRY64 //win7 sp1 x64 { LIST_ENTRY64 InLoadOrderLinks; LIST_ENTRY64 InMemoryOrderLinks; LIST_ENTRY64 InInitializationOrderLinks; PVOID DllBase; PVOID EntryPoint; ULONG SizeOfImage; UNICODE_STRING FullDllName; UNICODE_STRING BaseDllName; ULONG Flags; USHORT LoadCount; USHORT TlsIndex; PVOID SectionPointer; ULONG CheckSum; PVOID LoadedImports; PVOID EntryPointActivationContext; PVOID PatchInformation; LIST_ENTRY64 ForwarderLinks; LIST_ENTRY64 ServiceTagLinks; LIST_ENTRY64 StaticLinks; PVOID ContextInformation; ULONG64 OriginalBase; LARGE_INTEGER LoadTime; } LDR_DATA_TABLE_ENTRY64, *PLDR_DATA_TABLE_ENTRY64;
很清晰的结构,只要遍历链表就能获得所有的模块的信息,具体的代码就不给出了。问题产生了,就像利用ActiveList来遍历进程一样,存在被断链的可能,如果被断链了,遍历Ldr的方法就失效了,模块也就被隐藏了。
那针对断链的隐藏进程有没有好的方法呢,答案是肯定的,这就利用到了进程中的内存管理。
我们知道Windows对于可执行映像都是采用内存映射文件的方式,而映射区对象(SectioinObject)中会存有与之关联的FileObject的指针,而文件对象中的域
+0x030 FileName : _UNICODE_STRING
存放的就是整个映像文件的路径,所以我们可以来暴力搜索进程内存来得到进程模块,这里我们可以借助一个SSDT的函数,NtQueryVirtualMemory() 来枚举进程空间内的所有内存的信息,怎么获得NtQueryVirtualMemory函数地址我就不赘述了,
函数原型:
typedef NTSTATUS (*pfnNtQueryVirtualMemory)(HANDLE ProcessHandle,PVOID BaseAddress, MEMORY_INFORMATION_CLASS MemoryInformationClass, PVOID MemoryInformation, SIZE_T MemoryInformationLength, PSIZE_T ReturnLength);
其中的MEMORY_INFORMATION_CLASS 是一个枚举量,来表示查询的是哪种信息
Wrk中搜的枚举类型
typedef enum _MEMORY_INFORMATION_CLASS { MemoryBasicInformation #if DEVL ,MemoryWorkingSetInformation #endif ,MemoryMappedFilenameInformation ,MemoryRegionInformation ,MemoryWorkingSetExInformation } MEMORY_INFORMATION_CLASS;
而我自己一般用的就是下面的简化版
typedef enum _MEMORY_INFORMATION_CLASS { MemoryBasicInformation, //内存基本信息 MemoryWorkingSetList, MemorySectionName //内存映射文件名信息 }MEMORY_INFORMATION_CLASS;
当我们使用MemoryBasicInformation 来查询时,MemoryInformation结构如下
typedef struct _MEMORY_BASIC_INFORMATION { PVOID BaseAddress; //查询内存块所占的第一个页面基地址 PVOID AllocationBase; //内存块所占的第一块区域基地址,小于等于BaseAddress, DWORD AllocationProtect; //区域被初次保留时赋予的保护属性 SIZE_T RegionSize; //从BaseAddress开始,具有相同属性的页面的大小, DWORD State; //页面的状态,有三种可能值MEM_COMMIT、MEM_FREE和MEM_RESERVE DWORD Protect; //页面的属性,其可能的取值与AllocationProtect相同 DWORD Type; //该内存块的类型,有三种可能值:MEM_IMAGE、MEM_MAPPED和MEM_PRIVATE } MEMORY_BASIC_INFORMATION, *PMEMORY_BASIC_INFORMATION;
State指示虚拟内存的状态
MEM_COMMIT 指明已分配物理内存或者系统页文件。
MEM_RESERVE 指明页面被保留,但是没有分配任何物理内存。
MEM_FREE 空闲状态。该区域的虚拟地址没有分配任务物理内存
Type是指示物理存储器的类型
MEM_IMAGE 指明该区域的虚拟地址原先受内存映射的映像文件(如.exe或DLL文件)的支持,但也许不再受映像文件的支持。
例如,当写入模块映像中的全局变量时,“写入时拷贝”的机制将由页文件来支持特定的页面,而不是受原始映像文件的支持。
MEM_MAPPED 该区域的虚拟地址原先是受内存映射的数据文件的支持,但也许不再受数据文件的支持。
例如,数据文件可以使用“写入时拷贝”的保护属性来映射。对文件的任何写入操作都将导致页文件而不是原始数据支持特定的页面。
MEM_PRIVATE 指明该内存区域是私有的。不被其他进程共享。
用MemorySectionName 查询时MemoryInformation结构如下
typedef struct _MEMORY_SECTION_NAME { UNICODE_STRING Name; WCHAR Buffer[_MAX_OBJECT_NAME]; }MEMORY_SECTION_NAME,*PMEMORY_SECTION_NAME;
下面给出主要代码,思路就是调用NtQueryVirtualMemory来暴力遍历进程的所有内存
NTSTATUS EnumMoudleByNtQueryVirtualMemory(ULONG ProcessId) { NTSTATUS Status; PEPROCESS EProcess = NULL; HANDLE hProcess = NULL; ULONG_PTR HighUserAddress = 0; ULONG ulRet = 0; #ifdef _WIN64 HighUserAddress = 0x80000000000; #else HighUserAddress = 0x80000000; #endif if (ProcessId) { Status = PsLookupProcessByProcessId((HANDLE)ProcessId, &EProcess); if (!NT_SUCCESS(Status)) { return Status; } } if (IsRealProcess(EProcess)) //判断是否为僵尸进程,我只是判断了对象类型和句柄表是否存在 { ObfDereferenceObject(EProcess); Status = ObOpenObjectByPointer(EProcess, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, GENERIC_ALL, *PsProcessType, KernelMode, &hProcess ); if (NT_SUCCESS(Status)) { ULONG_PTR ulBase = 0; //改变PreviousMode PETHREAD EThread = PsGetCurrentThread(); CHAR PreMode = ChangePreMode(EThread); //KernelMode do { MEMORY_BASIC_INFORMATION mbi = {0}; Status = NtQueryVirtualMemory(hProcess, (PVOID)ulBase, MemoryBasicInformation, &mbi, sizeof(MEMORY_BASIC_INFORMATION), &ulRet); if (NT_SUCCESS(Status)) { //如果是Image 再查询SectionName,即FileObject Name if (mbi.Type==MEM_IMAGE) { MEMORY_SECTION_NAME msn = {0}; Status = NtQueryVirtualMemory(hProcess, (PVOID)ulBase, MemorySectionName, &msn, sizeof(MEMORY_SECTION_NAME), &ulRet); if (NT_SUCCESS(Status)) { UNICODE_STRING DosName; DbgPrint("SectionName:%wZ\r\n",&(msn.SectionFileName)); // DbgPrint("MoudleName:%S\r\n",msn.SectionFileName); RtlVolumeDeviceToDosName_(&(msn.SectionFileName),&DosName); DbgPrint("SectionName:%wZ\r\n",&(DosName)); } } ulBase += mbi.RegionSize; } else ulBase += PAGE_SIZE; } while (ulBase < (ULONG_PTR)HighUserAddress); NtClose(hProcess); RecoverPreMode(EThread,PreMode); } } return Status; }
然后我们进一步探索NtQueryVirtualMemory的内部实现原理,先重点关注NtQueryVirtualMemory关于MemorySectionName==2 方式的处理流程,这里只以Win7 x86 Sp1为例列出我们要用到的几个结构,如果对于Windows 底层的内存管理想要深入了解,可以阅读《Windows内核原理及实现》和《Windows内核情景分析》中内存管理的相关章节。
我们可以看到EProcess进程体的VadRoot
kd> dt _eprocess +0x278 VadRoot : _MM_AVL_TABLE
kd> dt _MMADDRESS_NODE
nt!_MMADDRESS_NODE
+0x000 u1 : <unnamed-tag>
+0x004 LeftChild : Ptr32 _MMADDRESS_NODE
+0x008 RightChild : Ptr32 _MMADDRESS_NODE
+0x00c StartingVpn : Uint4B
+0x010 EndingVpn : Uint4B
可以看到Win 7Sp1 下的两种结构非常简单,提供不了太多有效的信息,我们再来看看xp sp3下的结构
kd> dt _eprocess
+0x11c VadRoot : Ptr32 Void
只是一个32位的指针,但是我们知道这个指针是指向_MMVAD结构,形成一颗二叉树结构
kd> dt _mmvad
nt!_MMVAD
+0x000 StartingVpn : Uint4B
+0x004 EndingVpn : Uint4B
+0x008 Parent : Ptr32 _MMVAD
+0x00c LeftChild : Ptr32 _MMVAD
+0x010 RightChild : Ptr32 _MMVAD
+0x014 u : __unnamed
+0x018 ControlArea : Ptr32 _CONTROL_AREA
+0x01c FirstPrototypePte : Ptr32 _MMPTE
+0x020 LastContiguousPte : Ptr32 _MMPTE
+0x024 u2 : __unnamed
然后尝试着在Win 7下输入 dt _MMVAD
kd> dt _mmvad
nt!_MMVAD
+0x000 StartingVpn : Uint4B
+0x004 EndingVpn : Uint4B
+0x008 Parent : Ptr32 _MMVAD
+0x00c LeftChild : Ptr32 _MMVAD
+0x010 RightChild : Ptr32 _MMVAD
+0x014 u : __unnamed
+0x018 ControlArea : Ptr32 _CONTROL_AREA
+0x01c FirstPrototypePte : Ptr32 _MMPTE
+0x020 LastContiguousPte : Ptr32 _MMPTE
+0x024 u2 : __unnamed
对比两个结构,发现了什么,没错,_MMADRESS_NODE只是_MMVAD结构体的前一部分,真正的内存块的节点还是_MMVAD结构
我们可以以calc.exe为例来体验一下整个流程 Win7 x86 sp1
kd> !process 0 0 **** NT ACTIVE PROCESS DUMP **** PROCESS 87b73cf8 SessionId: 1 Cid: 0dd8 Peb: 7ffdf000 ParentCid: 07f4 DirBase: 7f3455e0 ObjectTable: 97934428 HandleCount: 79. Image: calc.exe
kd> dt _eprocess 87b73cf8
+0x278 VadRoot : _MM_AVL_TABLE
kd> dt _MM_AVL_TABLE 87b73cf8+0x278
nt!_MM_AVL_TABLE
+0x000 BalancedRoot : _MMADDRESS_NODE
+0x014 DepthOfTree : 0y01001 (0x9)
+0x014 Unused : 0y000
+0x014 NumberGenericTableElements : 0y000000000000000010011111 (0x9f)
+0x018 NodeHint : 0x876c4ec8 Void
+0x01c NodeFreeHint : (null)
kd> dt _MMADDRESS_NODE 87b73cf8+0x278 nt!_MMADDRESS_NODE +0x000 u1 : <unnamed-tag> +0x004 LeftChild : (null) +0x008 RightChild : 0x87c327e0 _MMADDRESS_NODE +0x00c StartingVpn : 0 +0x010 EndingVpn : 0
注意Win 7下的根结点只利用了右子树,想当与跟节点的右子树才是真正意义上的AVL树的根节点,继续跟入:
kd> dt _MMVAD 0x87c327e0 nt!_MMVAD +0x000 u1 : <unnamed-tag> +0x004 LeftChild : 0x87ac3248 _MMVAD +0x008 RightChild : 0x87c2b3b8 _MMVAD +0x00c StartingVpn : 0xd80 +0x010 EndingVpn : 0xe3f +0x014 u : <unnamed-tag> +0x018 PushLock : _EX_PUSH_LOCK +0x01c u5 : <unnamed-tag> +0x020 u2 : <unnamed-tag> +0x024 Subsection : 0x880a3410 _SUBSECTION //我们接下来要跟的就是这个结构 +0x024 MappedSubsection : 0x880a3410 _MSUBSECTION +0x028 FirstPrototypePte : 0x961b69c8 _MMPTE +0x02c LastContiguousPte : 0xfffffffc _MMPTE +0x030 ViewLinks : _LIST_ENTRY [ 0x880a3408 - 0x880a3408 ] +0x038 VadsProcess : 0x87b73cf9 _EPROCESS
kd> dt _SUBSECTION 0x880a3410 nt!_SUBSECTION +0x000 ControlArea : 0x880a33c0 _CONTROL_AREA //跟这个结构 +0x004 SubsectionBase : 0x961b69c8 _MMPTE +0x008 NextSubsection : 0x880a3430 _SUBSECTION +0x00c PtesInSubsection : 1 +0x010 UnusedPtes : 0 +0x010 GlobalPerSessionHead : (null) +0x014 u : <unnamed-tag> +0x018 StartingSector : 0 +0x01c NumberOfFullSectors : 2
kd> dt _CONTROL_AREA 0x880a33c0 nt!_CONTROL_AREA +0x000 Segment : 0x961b6998 _SEGMENT +0x004 DereferenceList : _LIST_ENTRY [ 0x0 - 0x880d7104 ] +0x00c NumberOfSectionReferences : 1 +0x010 NumberOfPfnReferences : 0x86 +0x014 NumberOfMappedViews : 1 +0x018 NumberOfUserReferences : 2 +0x01c u : <unnamed-tag> +0x020 FlushInProgressCount : 0 +0x024 FilePointer : _EX_FAST_REF //关联的文件对象就在这个结构里面了 +0x028 ControlAreaLock : 0n0 +0x02c ModifiedWriteCount : 0 +0x02c StartingFrame : 0 +0x030 WaitingForDeletion : (null) +0x034 u2 : <unnamed-tag> +0x040 LockedPages : 0n1 +0x048 ViewList : _LIST_ENTRY [ 0x87c32810 - 0x87c32810 ]
kd> dt _EX_FAST_REF 0x880a33c0+0x24 nt!_EX_FAST_REF +0x000 Object : 0x880a1b43 Void //关联的文件对象 +0x000 RefCnt : 0y011 +0x000 Value : 0x880a1b43
注意,这里关联的文件对象要&0x0FFFFFFF8(最后三位清零)才是正真的文件对象
kd> !object 0x880a1b43&0x0fffffff8 Object: 880a1b40 Type: (863ff650) File ObjectHeader: 880a1b28 (new version) HandleCount: 0 PointerCount: 4 Directory Object: 00000000 Name: \Windows\System32\calc.exe {HarddiskVolume1} //文件对象名,完整路径
了解了这些结构体之后,再看NtQueryVirtualMemory函数关于MemorySectionName的处理流程,贴出ida分析的过程,只分析了我们关注的MemorySectionName的方式,wrk中也有源码
PAGE:0063F242 ; NTSTATUS __stdcall NtQueryVirtualMemory(HANDLE ProcessHandle, PVOID BaseAddress, MEMORY_INFORMATION_CLASS MemoryInformationClass, PVOID MemoryInformation, ULONG MemoryInformationLength, PULONG ReturnLength) PAGE:0063F242 _NtQueryVirtualMemory@24 proc near PAGE:0063F242 ; DATA XREF: .text:0045CB68o PAGE:0063F242 PAGE:0063F242 var_7C= dword ptr -7Ch PAGE:0063F242 var_78= dword ptr -78h PAGE:0063F242 var_74= dword ptr -74h PAGE:0063F242 var_70= dword ptr -70h PAGE:0063F242 var_64= dword ptr -64h PAGE:0063F242 ApcState= dword ptr -60h PAGE:0063F242 var_48= dword ptr -48h PAGE:0063F242 var_44= dword ptr -44h PAGE:0063F242 var_40= dword ptr -40h PAGE:0063F242 var_3C= dword ptr -3Ch PAGE:0063F242 var_38= dword ptr -38h PAGE:0063F242 Object= dword ptr -34h PAGE:0063F242 AccessMode= byte ptr -30h PAGE:0063F242 var_2C= dword ptr -2Ch PAGE:0063F242 var_28= dword ptr -28h PAGE:0063F242 var_24= dword ptr -24h PAGE:0063F242 EProcess= dword ptr -20h PAGE:0063F242 var_1C??Attached= dword ptr -1Ch PAGE:0063F242 ms_exc= CPPEH_RECORD ptr -18h PAGE:0063F242 ProcessHandle= dword ptr 8 PAGE:0063F242 BaseAddress= dword ptr 0Ch PAGE:0063F242 MemoryInformationClass= dword ptr 10h PAGE:0063F242 MemoryInformation= dword ptr 14h PAGE:0063F242 MemoryInformationLength= dword ptr 18h PAGE:0063F242 ReturnLength= dword ptr 1Ch PAGE:0063F242 PAGE:0063F242 6A 6C push 6Ch PAGE:0063F244 68 18 0F 45 00 push offset stru_450F18 PAGE:0063F249 E8 D2 4F E1 FF call __SEH_prolog4 PAGE:0063F24E 33 F6 xor esi, esi PAGE:0063F250 89 75 E4 mov [ebp+var_1C??Attached], esi PAGE:0063F253 89 75 DC mov [ebp+var_24], esi PAGE:0063F256 8B 45 10 mov eax, [ebp+MemoryInformationClass] PAGE:0063F259 2B C6 sub eax, esi ; Handler MemoryBasicInformation PAGE:0063F25B 74 28 jz short Handler_MemoryBasicInformation PAGE:0063F25D 48 dec eax ; Handler MemoryWorkingSetList PAGE:0063F25E 74 1F jz short Handler_MemoryWorkingSetList PAGE:0063F260 48 dec eax PAGE:0063F261 74 32 jz short loc_63F295 ; Handler_MemoryMappedFilenameInformation PAGE:0063F263 48 dec eax PAGE:0063F264 74 13 jz short loc_63F279 PAGE:0063F266 48 dec eax PAGE:0063F267 74 0A jz short loc_63F273 PAGE:0063F269 B8 03 00 00 C0 mov eax, 0C0000003h PAGE:0063F26E E9 D6 06 00 00 jmp loc_63F949 PAGE:0063F273 ; --------------------------------------------------------------------------- PAGE:0063F273 PAGE:0063F273 loc_63F273: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+25j PAGE:0063F273 83 7D 18 08 cmp [ebp+MemoryInformationLength], 8 PAGE:0063F277 EB 10 jmp short loc_63F289 PAGE:0063F279 ; --------------------------------------------------------------------------- PAGE:0063F279 PAGE:0063F279 loc_63F279: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+22j PAGE:0063F279 83 7D 18 10 cmp [ebp+MemoryInformationLength], 10h PAGE:0063F27D EB 0A jmp short loc_63F289 PAGE:0063F27F ; --------------------------------------------------------------------------- PAGE:0063F27F PAGE:0063F27F Handler_MemoryWorkingSetList: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+1Cj PAGE:0063F27F 83 7D 18 04 cmp [ebp+MemoryInformationLength], 4 PAGE:0063F283 EB 04 jmp short loc_63F289 PAGE:0063F285 ; --------------------------------------------------------------------------- PAGE:0063F285 PAGE:0063F285 Handler_MemoryBasicInformation: PAGE:0063F285 ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+19j PAGE:0063F285 83 7D 18 1C cmp [ebp+MemoryInformationLength], 1Ch PAGE:0063F289 PAGE:0063F289 loc_63F289: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+35j PAGE:0063F289 ; NtQueryVirtualMemory(x,x,x,x,x,x)+3Bj PAGE:0063F289 ; NtQueryVirtualMemory(x,x,x,x,x,x)+41j PAGE:0063F289 73 0A jnb short loc_63F295 PAGE:0063F28B B8 04 00 00 C0 mov eax, 0C0000004h PAGE:0063F290 E9 B4 06 00 00 jmp loc_63F949 PAGE:0063F295 ; --------------------------------------------------------------------------- PAGE:0063F295 PAGE:0063F295 loc_63F295: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+1Fj PAGE:0063F295 ; NtQueryVirtualMemory(x,x,x,x,x,x):loc_63F289j PAGE:0063F295 64 8B 1D 24 01 00+mov ebx, large fs:124h ; Handler_MemoryMappedFilenameInformation PAGE:0063F29C 8A 83 3A 01 00 00 mov al, [ebx+13Ah] ; FS寄存器指向当前CPU的KPCR KPCR+0x124指向当前线程对象 PAGE:0063F29C ; +0x13A获取先前的调用模式<KernelMode/UserMode> PAGE:0063F2A2 88 45 D0 mov [ebp+AccessMode], al PAGE:0063F2A5 84 C0 test al, al ; KernelMode=0,UserMode=1 PAGE:0063F2A7 74 50 jz short loc_63F2F9 PAGE:0063F2A9 89 75 FC mov [ebp+ms_exc.registration.TryLevel], esi ; 很明显的异常处理,与我们分析无关,忽略 PAGE:0063F2AC 6A 04 push 4 ; Alignment PAGE:0063F2AC ; UserMode PAGE:0063F2AE FF 75 18 push [ebp+MemoryInformationLength] ; Length PAGE:0063F2B1 8B 7D 14 mov edi, [ebp+MemoryInformation] PAGE:0063F2B4 57 push edi ; Address PAGE:0063F2B5 E8 B3 E0 FB FF call _ProbeForWrite@12 ; 查看用户态内存是否可写 PAGE:0063F2BA 8B 4D 1C mov ecx, [ebp+ReturnLength] PAGE:0063F2BD 3B CE cmp ecx, esi ; 此时的esi=0,即比较ReturnLength是否为0 PAGE:0063F2BF 74 0F jz short loc_63F2D0 ; ReturnLength==0则跳转 PAGE:0063F2C1 A1 04 07 56 00 mov eax, ds:_MmUserProbeAddress ; 检查此地址是否高于用户边界地址, PAGE:0063F2C1 ; _MmUserProbeAddress == 7fff0000 PAGE:0063F2C6 3B C8 cmp ecx, eax PAGE:0063F2C8 72 02 jb short loc_63F2CC ; 低于的话则跳转,正常来讲一定会跳 PAGE:0063F2CA 8B C8 mov ecx, eax ; 异常处理,忽略 PAGE:0063F2CC PAGE:0063F2CC loc_63F2CC: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+86j PAGE:0063F2CC 8B 01 mov eax, [ecx] PAGE:0063F2CE 89 01 mov [ecx], eax PAGE:0063F2D0 PAGE:0063F2D0 loc_63F2D0: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+7Dj PAGE:0063F2D0 C7 45 FC FE FF FF+mov [ebp+ms_exc.registration.TryLevel], 0FFFFFFFEh ; 很明显的SEH处理, PAGE:0063F2D0 FF ; 改变TryLevel,忽略 PAGE:0063F2D7 EB 23 jmp short loc_63F2FC ; 一般参数没问题,直接跳转 PAGE:0063F2D9 ; --------------------------------------------------------------------------- PAGE:0063F2D9 PAGE:0063F2D9 loc_63F2D9: ; DATA XREF: .text:stru_450F18o PAGE:0063F2D9 8B 45 EC mov eax, [ebp+ms_exc.exc_ptr] ; 很明显,如果参数有问题,就直接返回了 PAGE:0063F2DC 8B 00 mov eax, [eax] PAGE:0063F2DE 8B 00 mov eax, [eax] PAGE:0063F2E0 89 45 D4 mov [ebp+var_2C], eax PAGE:0063F2E3 33 C0 xor eax, eax PAGE:0063F2E5 40 inc eax PAGE:0063F2E6 C3 retn PAGE:0063F2E7 ; --------------------------------------------------------------------------- PAGE:0063F2E7 PAGE:0063F2E7 loc_63F2E7: ; DATA XREF: .text:stru_450F18o PAGE:0063F2E7 8B 65 E8 mov esp, [ebp+ms_exc.old_esp] ; Exception handler 0 for function 63F242 PAGE:0063F2EA 8B 45 D4 mov eax, [ebp+var_2C] PAGE:0063F2ED PAGE:0063F2ED loc_63F2ED: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+3DBj PAGE:0063F2ED ; NtQueryVirtualMemory(x,x,x,x,x,x)+55Bj PAGE:0063F2ED ; NtQueryVirtualMemory(x,x,x,x,x,x)+620j PAGE:0063F2ED C7 45 FC FE FF FF+mov [ebp+ms_exc.registration.TryLevel], 0FFFFFFFEh PAGE:0063F2F4 E9 50 06 00 00 jmp loc_63F949 PAGE:0063F2F9 ; --------------------------------------------------------------------------- PAGE:0063F2F9 PAGE:0063F2F9 loc_63F2F9: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+65j PAGE:0063F2F9 8B 7D 14 mov edi, [ebp+MemoryInformation] ; KernelMode PAGE:0063F2FC PAGE:0063F2FC loc_63F2FC: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+95j PAGE:0063F2FC 8B 45 0C mov eax, [ebp+BaseAddress] ; 还是参数的校验,我们要查询的虚拟地址 PAGE:0063F2FF 8B 0D FC 06 56 00 mov ecx, ds:_MmHighestUserAddress ; MmHighestUserAddress==0x7ffeffff PAGE:0063F2FF ; (这是用户空间可以访问的最高地址, PAGE:0063F2FF ; 逻辑上的而已,上面还有个64K的禁区(飞地)就到内核空间了) PAGE:0063F305 3B C1 cmp eax, ecx PAGE:0063F307 76 0A jbe short loc_63F313 PAGE:0063F309 B8 0D 00 00 C0 mov eax, 0C000000Dh ; Status的返回值 STATUS_INVALID_PARAMETER(参数无效) PAGE:0063F30E E9 36 06 00 00 jmp loc_63F949 ; 返回 PAGE:0063F313 ; --------------------------------------------------------------------------- PAGE:0063F313 PAGE:0063F313 loc_63F313: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+C5j PAGE:0063F313 8D B1 00 00 FF FF lea esi, [ecx-10000h] ; esi==0x7ffdffff PAGE:0063F319 89 75 D8 mov [ebp+var_28], esi ; 保存允许查询的最高地址,作为查询的虚拟地址的终结点 PAGE:0063F31C BA 00 F0 FF FF mov edx, 0FFFFF000h PAGE:0063F321 3B C6 cmp eax, esi ; 判断查询的虚拟地址是否高于终结点 PAGE:0063F323 0F 87 A6 05 00 00 ja loc_63F8CF ; 高于,跳转,等会再看具体处理 PAGE:0063F329 89 45 14 mov [ebp+MemoryInformation], eax ; 将地址保存 PAGE:0063F32C 21 55 14 and [ebp+MemoryInformation], edx ; edi==0FFFFF000h 取整得到虚拟地址的内存页首地址 PAGE:0063F32F 81 7D 14 00 00 FE+cmp [ebp+MemoryInformation], 7FFE0000h ; 判断地址是否大于SharedUserData PAGE:0063F32F 7F ; 也就是说SharedUserData为最后一个合法的用户边界 PAGE:0063F336 0F 84 93 05 00 00 jz loc_63F8CF PAGE:0063F33C 83 7D 08 FF cmp [ebp+ProcessHandle], 0FFFFFFFFh PAGE:0063F340 75 08 jnz short loc_63F34A ; 不是本身的进程就跳转 PAGE:0063F342 8B 73 50 mov esi, [ebx+50h] ; ebx为线程对象,+0x50就为进程对象 PAGE:0063F345 89 75 E0 mov [ebp+EProcess], esi PAGE:0063F348 EB 2A jmp short loc_63F374 PAGE:0063F34A ; --------------------------------------------------------------------------- PAGE:0063F34A PAGE:0063F34A loc_63F34A: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+FEj PAGE:0063F34A 6A 00 push 0 ; HandleInformation PAGE:0063F34C 8D 45 CC lea eax, [ebp+Object] PAGE:0063F34F 50 push eax ; Object PAGE:0063F350 FF 75 D0 push dword ptr [ebp+AccessMode] ; AccessMode PAGE:0063F353 FF 35 2C 00 56 00 push ds:_PsProcessType ; ObjectType PAGE:0063F359 68 00 04 00 00 push 400h ; DesiredAccess PAGE:0063F35E FF 75 08 push [ebp+ProcessHandle] ; Handle PAGE:0063F361 E8 F6 D0 FB FF call _ObReferenceObjectByHandle@24 ; ObReferenceObjectByHandle(x,x,x,x,x,x) PAGE:0063F366 8B 75 CC mov esi, [ebp+Object] PAGE:0063F369 89 75 E0 mov [ebp+EProcess], esi PAGE:0063F36C 85 C0 test eax, eax PAGE:0063F36E 0F 8C D5 05 00 00 jl loc_63F949 PAGE:0063F374 PAGE:0063F374 loc_63F374: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+106j PAGE:0063F374 83 7D 10 04 cmp [ebp+MemoryInformationClass], 4 ; MemoryRegionInformation方式查询 PAGE:0063F378 75 4D jnz short loc_63F3C7 PAGE:0063F37A 56 push esi PAGE:0063F37B 8B 5D 18 mov ebx, [ebp+MemoryInformationLength] PAGE:0063F37E 53 push ebx PAGE:0063F37F 8B CF mov ecx, edi PAGE:0063F381 E8 FF B8 DE FF call _MiGetWorkingSetInfoList@12 ; MiGetWorkingSetInfoList(x,x,x) PAGE:0063F386 8B F8 mov edi, eax PAGE:0063F388 83 7D 08 FF cmp [ebp+ProcessHandle], 0FFFFFFFFh PAGE:0063F38C 74 07 jz short loc_63F395 PAGE:0063F38E 8B CE mov ecx, esi ; Object PAGE:0063F390 E8 98 70 E1 FF call @ObfDereferenceObject@4 ; ObfDereferenceObject(x) PAGE:0063F395 PAGE:0063F395 loc_63F395: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+14Aj PAGE:0063F395 85 FF test edi, edi PAGE:0063F397 7D 07 jge short loc_63F3A0 PAGE:0063F399 8B C7 mov eax, edi PAGE:0063F39B E9 A9 05 00 00 jmp loc_63F949 PAGE:0063F3A0 ; --------------------------------------------------------------------------- PAGE:0063F3A0 PAGE:0063F3A0 loc_63F3A0: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+155j PAGE:0063F3A0 C7 45 FC 02 00 00+mov [ebp+ms_exc.registration.TryLevel], 2 PAGE:0063F3A7 8B 45 1C mov eax, [ebp+ReturnLength] PAGE:0063F3AA 85 C0 test eax, eax PAGE:0063F3AC 74 0B jz short loc_63F3B9 PAGE:0063F3AE 89 18 mov [eax], ebx PAGE:0063F3B0 EB 07 jmp short loc_63F3B9 PAGE:0063F3B2 ; --------------------------------------------------------------------------- PAGE:0063F3B2 PAGE:0063F3B2 loc_63F3B2: ; DATA XREF: .text:stru_450F18o PAGE:0063F3B2 33 C0 xor eax, eax ; Exception filter 2 for function 63F242 PAGE:0063F3B4 40 inc eax PAGE:0063F3B5 C3 retn PAGE:0063F3B6 ; --------------------------------------------------------------------------- PAGE:0063F3B6 PAGE:0063F3B6 loc_63F3B6: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x):loc_63F40Ej PAGE:0063F3B6 ; DATA XREF: .text:stru_450F18o PAGE:0063F3B6 8B 65 E8 mov esp, [ebp+ms_exc.old_esp] ; Exception handler 2 for function 63F242 PAGE:0063F3B9 PAGE:0063F3B9 loc_63F3B9: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+16Aj PAGE:0063F3B9 ; NtQueryVirtualMemory(x,x,x,x,x,x)+16Ej PAGE:0063F3B9 ; NtQueryVirtualMemory(x,x,x,x,x,x)+1B9j PAGE:0063F3B9 ; NtQueryVirtualMemory(x,x,x,x,x,x)+1C6j PAGE:0063F3B9 ; NtQueryVirtualMemory(x,x,x,x,x,x):loc_63F5EEj PAGE:0063F3B9 ; NtQueryVirtualMemory(x,x,x,x,x,x)+3B8j PAGE:0063F3B9 ; NtQueryVirtualMemory(x,x,x,x,x,x)+3D2j PAGE:0063F3B9 ; NtQueryVirtualMemory(x,x,x,x,x,x)+52Cj PAGE:0063F3B9 ; NtQueryVirtualMemory(x,x,x,x,x,x)+538j PAGE:0063F3B9 ; NtQueryVirtualMemory(x,x,x,x,x,x)+552j PAGE:0063F3B9 ; NtQueryVirtualMemory(x,x,x,x,x,x)+617j PAGE:0063F3B9 C7 45 FC FE FF FF+mov [ebp+ms_exc.registration.TryLevel], 0FFFFFFFEh PAGE:0063F3C0 33 C0 xor eax, eax PAGE:0063F3C2 E9 82 05 00 00 jmp loc_63F949 PAGE:0063F3C7 ; --------------------------------------------------------------------------- PAGE:0063F3C7 PAGE:0063F3C7 loc_63F3C7: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+136j PAGE:0063F3C7 83 7D 10 01 cmp [ebp+MemoryInformationClass], 1 PAGE:0063F3CB 75 43 jnz short loc_63F410 PAGE:0063F3CD 56 push esi ; BugCheckParameter1 PAGE:0063F3CE FF 75 18 push [ebp+MemoryInformationLength] ; int PAGE:0063F3D1 8B CF mov ecx, edi PAGE:0063F3D3 E8 A3 96 E7 FF call _MiGetWorkingSetInfo@12 ; MiGetWorkingSetInfo(x,x,x) PAGE:0063F3D8 8B D8 mov ebx, eax PAGE:0063F3DA 83 7D 08 FF cmp [ebp+ProcessHandle], 0FFFFFFFFh PAGE:0063F3DE 74 07 jz short loc_63F3E7 PAGE:0063F3E0 8B CE mov ecx, esi ; Object PAGE:0063F3E2 E8 46 70 E1 FF call @ObfDereferenceObject@4 ; ObfDereferenceObject(x) PAGE:0063F3E7 PAGE:0063F3E7 loc_63F3E7: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+19Cj PAGE:0063F3E7 85 DB test ebx, ebx PAGE:0063F3E9 0F 8C 58 05 00 00 jl loc_63F947 PAGE:0063F3EF C7 45 FC 03 00 00+mov [ebp+ms_exc.registration.TryLevel], 3 PAGE:0063F3F6 8B 45 1C mov eax, [ebp+ReturnLength] PAGE:0063F3F9 85 C0 test eax, eax PAGE:0063F3FB 74 BC jz short loc_63F3B9 PAGE:0063F3FD 8B 0F mov ecx, [edi] PAGE:0063F3FF 8D 0C 8D 04 00 00+lea ecx, ds:4[ecx*4] PAGE:0063F406 89 08 mov [eax], ecx PAGE:0063F408 EB AF jmp short loc_63F3B9 PAGE:0063F40A ; --------------------------------------------------------------------------- PAGE:0063F40A PAGE:0063F40A loc_63F40A: ; DATA XREF: .text:stru_450F18o PAGE:0063F40A 33 C0 xor eax, eax ; Exception filter 3 for function 63F242 PAGE:0063F40C 40 inc eax PAGE:0063F40D C3 retn PAGE:0063F40E ; --------------------------------------------------------------------------- PAGE:0063F40E PAGE:0063F40E loc_63F40E: ; DATA XREF: .text:stru_450F18o PAGE:0063F40E EB A6 jmp short loc_63F3B6 ; Exception handler 3 for function 63F242 PAGE:0063F410 ; --------------------------------------------------------------------------- PAGE:0063F410 PAGE:0063F410 loc_63F410: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+189j PAGE:0063F410 83 7D 08 FF cmp [ebp+ProcessHandle], 0FFFFFFFFh PAGE:0063F414 74 11 jz short loc_63F427 ; 如果不是自己进程本身就靠挂到其他进程空间 PAGE:0063F416 8D 45 A0 lea eax, [ebp+ApcState] PAGE:0063F419 50 push eax ; ApcState PAGE:0063F41A 56 push esi ; Process PAGE:0063F41B E8 7A 36 E2 FF call _KeStackAttachProcess@8 ; KeStackAttachProcess(x,x) PAGE:0063F420 C7 45 E4 01 00 00+mov [ebp+var_1C??Attached], 1 PAGE:0063F427 PAGE:0063F427 loc_63F427: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+1D2j PAGE:0063F427 66 FF 8B 86 00 00+dec word ptr [ebx+86h] ; ???? PAGE:0063F42E 8D 86 00 01 00 00 lea eax, [esi+100h] ; +0x100 AddressCreationLock : _EX_PUSH_LOCK PAGE:0063F42E ; 获取进程的地址空间锁,因为涉及到遍历地址空间, PAGE:0063F42E ; 就不可以在遍历的时候地址空间发生变化 PAGE:0063F434 6A 11 push 11h PAGE:0063F436 59 pop ecx ; ???应该是原子锁的问题,不是很清楚 PAGE:0063F437 8B D0 mov edx, eax PAGE:0063F439 33 C0 xor eax, eax PAGE:0063F43B F0 0F B1 0A lock cmpxchg [edx], ecx PAGE:0063F43F 85 C0 test eax, eax PAGE:0063F441 74 0B jz short loc_63F44E PAGE:0063F443 8D 8E 00 01 00 00 lea ecx, [esi+100h] PAGE:0063F449 E8 C7 79 E4 FF call @ExfAcquirePushLockShared@4 ; ExfAcquirePushLockShared(x) PAGE:0063F44E PAGE:0063F44E loc_63F44E: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+1FFj PAGE:0063F44E 80 8B 89 02 00 00+or byte ptr [ebx+289h], 4 ; ??? PAGE:0063F44E 04 ; +0x289 OwnsSessionWorkingSetShared : Pos 0, 1 Bit PAGE:0063F44E ; +0x289 OwnsProcessAddressSpaceExclusive : Pos 1, 1 Bit PAGE:0063F44E ; +0x289 OwnsProcessAddressSpaceShared : Pos 2, 1 Bit PAGE:0063F44E ; +0x289 SuppressSymbolLoad : Pos 3, 1 Bit PAGE:0063F44E ; +0x289 Prefetching : Pos 4, 1 Bit PAGE:0063F44E ; +0x289 OwnsDynamicMemoryShared : Pos 5, 1 Bit PAGE:0063F44E ; +0x289 OwnsChangeControlAreaExclusive : Pos 6, 1 Bit PAGE:0063F44E ; +0x289 OwnsChangeControlAreaShared : Pos 7, 1 Bi PAGE:0063F455 F6 86 70 02 00 00+test byte ptr [esi+270h], 20h ; ???判断此进程的空间是否准备删除,EPROCESS->VmDeleted,如果删除就返回了 PAGE:0063F455 20 ; +0x270 Flags : Uint4B PAGE:0063F455 ; +0x270 CreateReported : Pos 0, 1 Bit PAGE:0063F455 ; +0x270 NoDebugInherit : Pos 1, 1 Bit PAGE:0063F455 ; +0x270 ProcessExiting : Pos 2, 1 Bit PAGE:0063F455 ; +0x270 ProcessDelete : Pos 3, 1 Bit PAGE:0063F455 ; +0x270 Wow64SplitPages : Pos 4, 1 Bit PAGE:0063F455 ; +0x270 VmDeleted : Pos 5, 1 Bit PAGE:0063F455 ; +0x270 OutswapEnabled : Pos 6, 1 Bit PAGE:0063F455 ; +0x270 Outswapped : Pos 7, 1 Bit PAGE:0063F455 ; +0x270 ForkFailed : Pos 8, 1 Bit PAGE:0063F455 ; +0x270 Wow64VaSpace4Gb : Pos 9, 1 Bit PAGE:0063F455 ; +0x270 AddressSpaceInitialized : Pos 10, 2 Bits PAGE:0063F455 ; +0x270 SetTimerResolution : Pos 12, 1 Bit PAGE:0063F455 ; +0x270 BreakOnTermination : Pos 13, 1 Bit PAGE:0063F455 ; +0x270 DeprioritizeViews : Pos 14, 1 Bit PAGE:0063F455 ; +0x270 WriteWatch : Pos 15, 1 Bit PAGE:0063F455 ; +0x270 ProcessInSession : Pos 16, 1 Bit PAGE:0063F455 ; +0x270 OverrideAddressSpace : Pos 17, 1 Bit PAGE:0063F455 ; +0x270 HasAddressSpace : Pos 18, 1 Bit PAGE:0063F455 ; +0x270 LaunchPrefetched : Pos 19, 1 Bit PAGE:0063F455 ; +0x270 InjectInpageErrors : Pos 20, 1 Bit PAGE:0063F455 ; +0x270 VmTopDown : Pos 21, 1 Bit PAGE:0063F455 ; +0x2 PAGE:0063F45C 74 67 jz short loc_63F4C5 ; 正常流程跳转 PAGE:0063F45E 33 C9 xor ecx, ecx ; 肯定是如果准备删除了这里会需要处理资源 PAGE:0063F460 8D 86 00 01 00 00 lea eax, [esi+100h] PAGE:0063F466 8B D0 mov edx, eax PAGE:0063F468 6A 11 push 11h PAGE:0063F46A 58 pop eax PAGE:0063F46B F0 0F B1 0A lock cmpxchg [edx], ecx PAGE:0063F46F 83 F8 11 cmp eax, 11h PAGE:0063F472 74 0B jz short loc_63F47F PAGE:0063F474 8D 8E 00 01 00 00 lea ecx, [esi+100h] PAGE:0063F47A E8 7B 78 E4 FF call @ExfReleasePushLockShared@4 ; //释放锁 PAGE:0063F47F PAGE:0063F47F loc_63F47F: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+230j PAGE:0063F47F 80 A3 89 02 00 00+and byte ptr [ebx+289h], 0FBh PAGE:0063F486 66 FF 83 86 00 00+inc word ptr [ebx+86h] PAGE:0063F48D 0F B7 83 86 00 00+movzx eax, word ptr [ebx+86h] PAGE:0063F494 66 85 C0 test ax, ax PAGE:0063F497 75 0C jnz short loc_63F4A5 PAGE:0063F499 83 C3 40 add ebx, 40h PAGE:0063F49C 39 1B cmp [ebx], ebx PAGE:0063F49E 74 05 jz short loc_63F4A5 PAGE:0063F4A0 E8 D2 DE DE FF call _KiCheckForKernelApcDelivery@0 ; KiCheckForKernelApcDelivery() PAGE:0063F4A5 PAGE:0063F4A5 loc_63F4A5: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+255j PAGE:0063F4A5 ; NtQueryVirtualMemory(x,x,x,x,x,x)+25Cj PAGE:0063F4A5 F6 45 E4 01 test byte ptr [ebp+var_1C??Attached], 1 PAGE:0063F4A9 74 10 jz short loc_63F4BB PAGE:0063F4AB 8D 45 A0 lea eax, [ebp+ApcState] PAGE:0063F4AE 50 push eax PAGE:0063F4AF E8 9E 37 E2 FF call _KeUnstackDetachProcess@4 ; KeUnstackDetachProcess(x) PAGE:0063F4B4 8B CE mov ecx, esi ; Object PAGE:0063F4B6 E8 72 6F E1 FF call @ObfDereferenceObject@4 ; ObfDereferenceObject(x) PAGE:0063F4BB PAGE:0063F4BB loc_63F4BB: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+267j PAGE:0063F4BB B8 0A 01 00 C0 mov eax, 0C000010Ah ; STATUS_PROCESS_IS_TERMINATING PAGE:0063F4C0 E9 84 04 00 00 jmp loc_63F949 PAGE:0063F4C5 ; --------------------------------------------------------------------------- PAGE:0063F4C5 PAGE:0063F4C5 loc_63F4C5: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+21Aj PAGE:0063F4C5 F7 86 8C 02 00 00+test dword ptr [esi+28Ch], 0FFFFFF00h ; esi==Process PAGE:0063F4C5 00 FF FF FF ; +0x28c实际+0x014 NumberGenericTableElements : Pos 8, 24 Bits PAGE:0063F4C5 ; 判断节点数量 PAGE:0063F4CF 74 3D jz short loc_63F50E PAGE:0063F4D1 8B 45 E0 mov eax, [ebp+EProcess] PAGE:0063F4D4 8B B0 80 02 00 00 mov esi, [eax+280h] ; esi == Process->VadRoot->RightChild PAGE:0063F4DA 8B 45 0C mov eax, [ebp+BaseAddress] PAGE:0063F4DD 8B C8 mov ecx, eax PAGE:0063F4DF C1 E9 0C shr ecx, 0Ch ; 右移12位,取得高20位,获得页帧编号 PAGE:0063F4DF ; ecx == 查询地址的页帧编号 PAGE:0063F4E2 EB 1E jmp short loc_63F502 PAGE:0063F4E4 ; --------------------------------------------------------------------------- PAGE:0063F4E4 PAGE:0063F4E4 loc_63F4E4: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+2C2j PAGE:0063F4E4 8B 56 0C mov edx, [esi+0Ch] ; 遍历AVL树 PAGE:0063F4E7 3B CA cmp ecx, edx PAGE:0063F4E9 72 09 jb short loc_63F4F4 PAGE:0063F4EB 3B 4E 10 cmp ecx, [esi+10h] PAGE:0063F4EE 76 18 jbe short loc_63F508 ; ??? PAGE:0063F4F0 3B CA cmp ecx, edx PAGE:0063F4F2 73 05 jnb short loc_63F4F9 PAGE:0063F4F4 PAGE:0063F4F4 loc_63F4F4: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+2A7j PAGE:0063F4F4 8B 56 04 mov edx, [esi+4] PAGE:0063F4F7 EB 03 jmp short loc_63F4FC PAGE:0063F4F9 ; --------------------------------------------------------------------------- PAGE:0063F4F9 PAGE:0063F4F9 loc_63F4F9: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+2B0j PAGE:0063F4F9 8B 56 08 mov edx, [esi+8] PAGE:0063F4FC PAGE:0063F4FC loc_63F4FC: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+2B5j PAGE:0063F4FC 85 D2 test edx, edx PAGE:0063F4FE 74 15 jz short loc_63F515 PAGE:0063F500 8B F2 mov esi, edx PAGE:0063F502 PAGE:0063F502 loc_63F502: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+2A0j PAGE:0063F502 85 F6 test esi, esi ; 判断子节点是否有效 PAGE:0063F504 75 DE jnz short loc_63F4E4 ; 如果有效就跳转,遍历AVL树 PAGE:0063F506 EB 0D jmp short loc_63F515 ; 退出循环 PAGE:0063F508 ; --------------------------------------------------------------------------- PAGE:0063F508 PAGE:0063F508 loc_63F508: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+2ACj PAGE:0063F508 83 4D E4 02 or [ebp+var_1C??Attached], 2 ; 如果之前有靠挂,Attached为1,此时or 2 ,Attached==3 PAGE:0063F508 ; 如果没有靠挂,此时Attached = 2 PAGE:0063F50C EB 07 jmp short loc_63F515 PAGE:0063F50E ; --------------------------------------------------------------------------- PAGE:0063F50E PAGE:0063F50E loc_63F50E: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+28Dj PAGE:0063F50E 33 F6 xor esi, esi PAGE:0063F510 33 C9 xor ecx, ecx PAGE:0063F512 8B 45 0C mov eax, [ebp+BaseAddress] PAGE:0063F515 PAGE:0063F515 loc_63F515: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+2BCj PAGE:0063F515 ; NtQueryVirtualMemory(x,x,x,x,x,x)+2C4j PAGE:0063F515 ; NtQueryVirtualMemory(x,x,x,x,x,x)+2CAj PAGE:0063F515 F6 45 E4 02 test byte ptr [ebp+var_1C??Attached], 2 ; 一般情况此时Attached=3,跳转发生 PAGE:0063F519 0F 85 0D 01 00 00 jnz loc_63F62C PAGE:0063F51F 85 F6 test esi, esi PAGE:0063F521 75 09 jnz short loc_63F52C PAGE:0063F523 PAGE:0063F523 loc_63F523: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+2FAj PAGE:0063F523 8B 75 D8 mov esi, [ebp+var_28] PAGE:0063F526 2B 75 14 sub esi, [ebp+MemoryInformation] PAGE:0063F529 46 inc esi PAGE:0063F52A EB 25 jmp short loc_63F551 PAGE:0063F52C ; --------------------------------------------------------------------------- PAGE:0063F52C PAGE:0063F52C loc_63F52C: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+2DFj PAGE:0063F52C 8B 46 0C mov eax, [esi+0Ch] PAGE:0063F52F 3B C1 cmp eax, ecx PAGE:0063F531 73 16 jnb short loc_63F549 PAGE:0063F533 8B CE mov ecx, esi PAGE:0063F535 E8 E2 8D E4 FF call @MiGetNextNode@4 ; MiGetNextNode(x) PAGE:0063F53A 85 C0 test eax, eax PAGE:0063F53C 74 E5 jz short loc_63F523 PAGE:0063F53E 8B 70 0C mov esi, [eax+0Ch] PAGE:0063F541 C1 E6 0C shl esi, 0Ch PAGE:0063F544 2B 75 14 sub esi, [ebp+MemoryInformation] PAGE:0063F547 EB 08 jmp short loc_63F551 PAGE:0063F549 ; --------------------------------------------------------------------------- PAGE:0063F549 PAGE:0063F549 loc_63F549: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+2EFj PAGE:0063F549 C1 E0 0C shl eax, 0Ch PAGE:0063F54C 2B 45 14 sub eax, [ebp+MemoryInformation] PAGE:0063F54F 8B F0 mov esi, eax PAGE:0063F551 PAGE:0063F551 loc_63F551: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+2E8j PAGE:0063F551 ; NtQueryVirtualMemory(x,x,x,x,x,x)+305j PAGE:0063F551 33 C9 xor ecx, ecx PAGE:0063F553 8B 45 E0 mov eax, [ebp+EProcess] PAGE:0063F556 05 00 01 00 00 add eax, 100h PAGE:0063F55B 8B D0 mov edx, eax PAGE:0063F55D 6A 11 push 11h PAGE:0063F55F 58 pop eax PAGE:0063F560 F0 0F B1 0A lock cmpxchg [edx], ecx PAGE:0063F564 83 F8 11 cmp eax, 11h PAGE:0063F567 74 0E jz short loc_63F577 PAGE:0063F569 8B 4D E0 mov ecx, [ebp+EProcess] PAGE:0063F56C 81 C1 00 01 00 00 add ecx, 100h PAGE:0063F572 E8 83 77 E4 FF call @ExfReleasePushLockShared@4 ; ExfReleasePushLockShared(x) PAGE:0063F577 PAGE:0063F577 loc_63F577: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+325j PAGE:0063F577 80 A3 89 02 00 00+and byte ptr [ebx+289h], 0FBh PAGE:0063F57E 66 FF 83 86 00 00+inc word ptr [ebx+86h] PAGE:0063F585 0F B7 83 86 00 00+movzx eax, word ptr [ebx+86h] PAGE:0063F58C 66 85 C0 test ax, ax PAGE:0063F58F 75 0C jnz short loc_63F59D PAGE:0063F591 83 C3 40 add ebx, 40h PAGE:0063F594 39 1B cmp [ebx], ebx PAGE:0063F596 74 05 jz short loc_63F59D PAGE:0063F598 E8 DA DD DE FF call _KiCheckForKernelApcDelivery@0 ; KiCheckForKernelApcDelivery() PAGE:0063F59D PAGE:0063F59D loc_63F59D: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+34Dj PAGE:0063F59D ; NtQueryVirtualMemory(x,x,x,x,x,x)+354j PAGE:0063F59D F6 45 E4 01 test byte ptr [ebp+var_1C??Attached], 1 PAGE:0063F5A1 74 11 jz short loc_63F5B4 PAGE:0063F5A3 8D 45 A0 lea eax, [ebp+ApcState] PAGE:0063F5A6 50 push eax PAGE:0063F5A7 E8 A6 36 E2 FF call _KeUnstackDetachProcess@4 ; KeUnstackDetachProcess(x) PAGE:0063F5AC 8B 4D E0 mov ecx, [ebp+EProcess] ; Object PAGE:0063F5AF E8 79 6E E1 FF call @ObfDereferenceObject@4 ; ObfDereferenceObject(x) PAGE:0063F5B4 PAGE:0063F5B4 loc_63F5B4: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+35Fj PAGE:0063F5B4 33 C9 xor ecx, ecx PAGE:0063F5B6 39 4D 10 cmp [ebp+MemoryInformationClass], ecx PAGE:0063F5B9 75 67 jnz short loc_63F622 PAGE:0063F5BB 83 65 E4 FD and [ebp+var_1C??Attached], 0FFFFFFFDh PAGE:0063F5BF C7 45 FC 04 00 00+mov [ebp+ms_exc.registration.TryLevel], 4 PAGE:0063F5C6 89 4F 04 mov [edi+4], ecx PAGE:0063F5C9 89 4F 08 mov [edi+8], ecx PAGE:0063F5CC 8B 45 14 mov eax, [ebp+MemoryInformation] PAGE:0063F5CF 89 07 mov [edi], eax PAGE:0063F5D1 89 77 0C mov [edi+0Ch], esi PAGE:0063F5D4 C7 47 10 00 00 01+mov dword ptr [edi+10h], 10000h PAGE:0063F5DB C7 47 14 01 00 00+mov dword ptr [edi+14h], 1 PAGE:0063F5E2 89 4F 18 mov [edi+18h], ecx PAGE:0063F5E5 83 4D E4 02 or [ebp+var_1C??Attached], 2 PAGE:0063F5E9 8B 45 1C mov eax, [ebp+ReturnLength] PAGE:0063F5EC 3B C1 cmp eax, ecx PAGE:0063F5EE PAGE:0063F5EE loc_63F5EE: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+5FDj PAGE:0063F5EE 0F 84 C5 FD FF FF jz loc_63F3B9 PAGE:0063F5F4 C7 00 1C 00 00 00 mov dword ptr [eax], 1Ch PAGE:0063F5FA E9 BA FD FF FF jmp loc_63F3B9 PAGE:0063F5FF ; --------------------------------------------------------------------------- PAGE:0063F5FF PAGE:0063F5FF loc_63F5FF: ; DATA XREF: .text:stru_450F18o PAGE:0063F5FF 8B 45 EC mov eax, [ebp+ms_exc.exc_ptr] ; Exception filter 4 for function 63F242 PAGE:0063F602 8B 00 mov eax, [eax] PAGE:0063F604 8B 00 mov eax, [eax] PAGE:0063F606 89 45 C8 mov [ebp+var_38], eax PAGE:0063F609 33 C0 xor eax, eax PAGE:0063F60B 40 inc eax PAGE:0063F60C C3 retn PAGE:0063F60D ; --------------------------------------------------------------------------- PAGE:0063F60D PAGE:0063F60D loc_63F60D: ; DATA XREF: .text:stru_450F18o PAGE:0063F60D 8B 65 E8 mov esp, [ebp+ms_exc.old_esp] ; Exception handler 4 for function 63F242 PAGE:0063F610 F6 45 E4 02 test byte ptr [ebp+var_1C??Attached], 2 PAGE:0063F614 0F 85 9F FD FF FF jnz loc_63F3B9 PAGE:0063F61A 8B 45 C8 mov eax, [ebp+var_38] PAGE:0063F61D E9 CB FC FF FF jmp loc_63F2ED PAGE:0063F622 ; --------------------------------------------------------------------------- PAGE:0063F622 PAGE:0063F622 loc_63F622: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+377j PAGE:0063F622 ; NtQueryVirtualMemory(x,x,x,x,x,x)+629j PAGE:0063F622 B8 41 01 00 C0 mov eax, 0C0000141h PAGE:0063F627 E9 1D 03 00 00 jmp loc_63F949 PAGE:0063F62C ; --------------------------------------------------------------------------- PAGE:0063F62C PAGE:0063F62C loc_63F62C: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+2D7j PAGE:0063F62C 25 00 F0 FF FF and eax, 0FFFFF000h ; eax == BaseAddress,这里取BaseAddress的虚拟内存页的首地址 PAGE:0063F631 89 45 0C mov [ebp+BaseAddress], eax PAGE:0063F634 89 45 84 mov [ebp+var_7C], eax PAGE:0063F637 8B 46 0C mov eax, [esi+0Ch] ; 取得此子节点的起始编号 MMVAD->StartingVpn PAGE:0063F63A C1 E0 0C shl eax, 0Ch ; 左移12位,取得虚拟内存页的页首地址 PAGE:0063F63D 89 45 88 mov [ebp+var_78], eax PAGE:0063F640 8B 46 14 mov eax, [esi+14h] ; 取得MMVAD->u 其实是此节点的内存描述信息, PAGE:0063F640 ; u是一个联合体,表示一个Flags PAGE:0063F643 8B C8 mov ecx, eax ; ???对Flag进行某种运算 PAGE:0063F645 C1 E9 18 shr ecx, 18h PAGE:0063F648 83 E1 1F and ecx, 1Fh PAGE:0063F64B 8B 0C 8D E8 1E 44+mov ecx, ds:_MmProtectToValue[ecx*4] ; 很明显是取数组中的元素, PAGE:0063F64B 00 ; 可以在Windbg中kd> dd MmProtectToValue查看 PAGE:0063F64B ; 其实MmProtectToValue就是一个数组,保存内存页的保护属性, PAGE:0063F64B ; 前面的Flags其实就是得到数组的下标,然后从中取出值 PAGE:0063F652 89 4D 8C mov [ebp+var_74], ecx ; 保存查询地址的内存页属性 PAGE:0063F655 85 C0 test eax, eax PAGE:0063F657 79 09 jns short loc_63F662 PAGE:0063F659 C7 45 9C 00 00 02+mov [ebp+var_64], 20000h PAGE:0063F660 EB 61 jmp short loc_63F6C3 PAGE:0063F662 ; --------------------------------------------------------------------------- PAGE:0063F662 PAGE:0063F662 loc_63F662: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+415j PAGE:0063F662 25 00 00 70 00 and eax, (offset loc_6FFFFD+3) ; ???? PAGE:0063F667 2D 00 00 20 00 sub eax, 200000h PAGE:0063F66C F7 D8 neg eax ; ????指令不懂 PAGE:0063F66E 1B C0 sbb eax, eax PAGE:0063F670 25 00 00 04 FF and eax, 0FF040000h PAGE:0063F675 05 00 00 00 01 add eax, 1000000h PAGE:0063F67A 89 45 9C mov [ebp+var_64], eax ; 之前就是对Flag进行某种运算,再保存 PAGE:0063F67D 83 7D 10 02 cmp [ebp+MemoryInformationClass], 2 ; 我们就是2 PAGE:0063F681 75 40 jnz short loc_63F6C3 PAGE:0063F683 8B 46 24 mov eax, [esi+24h] ; MMVAD->Subsection->ControlArea PAGE:0063F686 83 38 00 cmp dword ptr [eax], 0 ; 判断CONTROL_AREA->Segment 是否有效 PAGE:0063F689 74 2B jz short loc_63F6B6 ; 无效则跳转 PAGE:0063F68B 8B 00 mov eax, [eax] PAGE:0063F68D 89 45 08 mov [ebp+ProcessHandle], eax ; 取出来,保存 PAGE:0063F690 8B 40 24 mov eax, [eax+24h] ; CONTROL_AREA->FilePointer 结构类型_EX_FAST_REF PAGE:0063F693 83 E0 F8 and eax, 0FFFFFFF8h ; 去除标志,只要是CONTROL_AREA下的文件指针, PAGE:0063F693 ; 全部都要这样去除标志,得到文件对象 PAGE:0063F696 89 45 DC mov [ebp+var_24], eax ; 保存这块虚拟内存的文件对象FilePointer PAGE:0063F699 74 1B jz short loc_63F6B6 PAGE:0063F69B 8B 45 08 mov eax, [ebp+ProcessHandle] PAGE:0063F69E 83 C0 24 add eax, 24h ; PEX_FAST_REF结构 PAGE:0063F6A1 50 push eax PAGE:0063F6A2 E8 5F F5 E1 FF call @ObFastReferenceObject@4 ; 快速引用一个对象 PAGE:0063F6A7 85 C0 test eax, eax ; 判断文件对象是否有效 PAGE:0063F6A9 75 08 jnz short loc_63F6B3 ; 有效跳转 PAGE:0063F6AB FF 75 08 push [ebp+ProcessHandle] PAGE:0063F6AE E8 A3 93 E5 FF call _MiReferenceControlAreaFile@4 ; MiReferenceControlAreaFile(x) PAGE:0063F6B3 PAGE:0063F6B3 loc_63F6B3: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+467j PAGE:0063F6B3 89 45 DC mov [ebp+var_24], eax ; 保存_File_Object PAGE:0063F6B6 PAGE:0063F6B6 loc_63F6B6: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+447j PAGE:0063F6B6 ; NtQueryVirtualMemory(x,x,x,x,x,x)+457j PAGE:0063F6B6 83 7D DC 00 cmp [ebp+var_24], 0 ; 判断对象引用是否成功 PAGE:0063F6BA 75 07 jnz short loc_63F6C3 ; 引用成功则跳转 PAGE:0063F6BC C7 45 DC 01 00 00+mov [ebp+var_24], 1 PAGE:0063F6C3 PAGE:0063F6C3 loc_63F6C3: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+41Ej PAGE:0063F6C3 ; NtQueryVirtualMemory(x,x,x,x,x,x)+43Fj PAGE:0063F6C3 ; NtQueryVirtualMemory(x,x,x,x,x,x)+478j PAGE:0063F6C3 83 7D 10 03 cmp [ebp+MemoryInformationClass], 3 ; 我们的方式是2 PAGE:0063F6C7 0F 85 D5 00 00 00 jnz loc_63F7A2 ; 不为 3 就跳转 PAGE:0063F6CD 8B 4E 10 mov ecx, [esi+10h] PAGE:0063F6D0 2B 4E 0C sub ecx, [esi+0Ch] PAGE:0063F6D3 41 inc ecx PAGE:0063F6D4 C1 E1 0C shl ecx, 0Ch PAGE:0063F6D7 89 4D 10 mov [ebp+MemoryInformationClass], ecx PAGE:0063F6DA 33 D2 xor edx, edx PAGE:0063F6DC 8B 45 E0 mov eax, [ebp+EProcess] PAGE:0063F6DF 05 00 01 00 00 add eax, 100h PAGE:0063F6E4 8B F0 mov esi, eax PAGE:0063F6E6 6A 11 push 11h PAGE:0063F6E8 58 pop eax PAGE:0063F6E9 F0 0F B1 16 lock cmpxchg [esi], edx PAGE:0063F6ED 83 F8 11 cmp eax, 11h PAGE:0063F6F0 74 11 jz short loc_63F703 PAGE:0063F6F2 8B 4D E0 mov ecx, [ebp+EProcess] PAGE:0063F6F5 81 C1 00 01 00 00 add ecx, 100h PAGE:0063F6FB E8 FA 75 E4 FF call @ExfReleasePushLockShared@4 ; ExfReleasePushLockShared(x) PAGE:0063F700 8B 4D 10 mov ecx, [ebp+MemoryInformationClass] PAGE:0063F703 PAGE:0063F703 loc_63F703: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+4AEj PAGE:0063F703 80 A3 89 02 00 00+and byte ptr [ebx+289h], 0FBh PAGE:0063F70A 66 FF 83 86 00 00+inc word ptr [ebx+86h] PAGE:0063F711 0F B7 83 86 00 00+movzx eax, word ptr [ebx+86h] PAGE:0063F718 66 85 C0 test ax, ax PAGE:0063F71B 75 0F jnz short loc_63F72C PAGE:0063F71D 83 C3 40 add ebx, 40h PAGE:0063F720 39 1B cmp [ebx], ebx PAGE:0063F722 74 08 jz short loc_63F72C PAGE:0063F724 E8 4E DC DE FF call _KiCheckForKernelApcDelivery@0 ; KiCheckForKernelApcDelivery() PAGE:0063F729 8B 4D 10 mov ecx, [ebp+MemoryInformationClass] PAGE:0063F72C PAGE:0063F72C loc_63F72C: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+4D9j PAGE:0063F72C ; NtQueryVirtualMemory(x,x,x,x,x,x)+4E0j PAGE:0063F72C F6 45 E4 01 test byte ptr [ebp+var_1C??Attached], 1 PAGE:0063F730 74 14 jz short loc_63F746 PAGE:0063F732 8D 45 A0 lea eax, [ebp+ApcState] PAGE:0063F735 50 push eax PAGE:0063F736 E8 17 35 E2 FF call _KeUnstackDetachProcess@4 ; KeUnstackDetachProcess(x) PAGE:0063F73B 8B 4D E0 mov ecx, [ebp+EProcess] ; Object PAGE:0063F73E E8 EA 6C E1 FF call @ObfDereferenceObject@4 ; ObfDereferenceObject(x) PAGE:0063F743 8B 4D 10 mov ecx, [ebp+MemoryInformationClass] PAGE:0063F746 PAGE:0063F746 loc_63F746: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+4EEj PAGE:0063F746 83 65 E4 FD and [ebp+var_1C??Attached], 0FFFFFFFDh PAGE:0063F74A C7 45 FC 05 00 00+mov [ebp+ms_exc.registration.TryLevel], 5 PAGE:0063F751 8B 45 88 mov eax, [ebp+var_78] PAGE:0063F754 89 07 mov [edi], eax PAGE:0063F756 8B 45 8C mov eax, [ebp+var_74] PAGE:0063F759 89 47 04 mov [edi+4], eax PAGE:0063F75C 8B 45 9C mov eax, [ebp+var_64] PAGE:0063F75F 89 47 08 mov [edi+8], eax PAGE:0063F762 89 4F 0C mov [edi+0Ch], ecx PAGE:0063F765 83 4D E4 02 or [ebp+var_1C??Attached], 2 PAGE:0063F769 8B 45 1C mov eax, [ebp+ReturnLength] PAGE:0063F76C 85 C0 test eax, eax PAGE:0063F76E 0F 84 45 FC FF FF jz loc_63F3B9 PAGE:0063F774 C7 00 10 00 00 00 mov dword ptr [eax], 10h PAGE:0063F77A E9 3A FC FF FF jmp loc_63F3B9 PAGE:0063F77F ; --------------------------------------------------------------------------- PAGE:0063F77F PAGE:0063F77F loc_63F77F: ; DATA XREF: .text:stru_450F18o PAGE:0063F77F 8B 45 EC mov eax, [ebp+ms_exc.exc_ptr] ; Exception filter 5 for function 63F242 PAGE:0063F782 8B 00 mov eax, [eax] PAGE:0063F784 8B 00 mov eax, [eax] PAGE:0063F786 89 45 C4 mov [ebp+var_3C], eax PAGE:0063F789 33 C0 xor eax, eax PAGE:0063F78B 40 inc eax PAGE:0063F78C C3 retn PAGE:0063F78D ; --------------------------------------------------------------------------- PAGE:0063F78D PAGE:0063F78D loc_63F78D: ; DATA XREF: .text:stru_450F18o PAGE:0063F78D 8B 65 E8 mov esp, [ebp+ms_exc.old_esp] ; Exception handler 5 for function 63F242 PAGE:0063F790 F6 45 E4 02 test byte ptr [ebp+var_1C??Attached], 2 PAGE:0063F794 0F 85 1F FC FF FF jnz loc_63F3B9 PAGE:0063F79A 8B 45 C4 mov eax, [ebp+var_3C] PAGE:0063F79D E9 4B FB FF FF jmp loc_63F2ED PAGE:0063F7A2 ; --------------------------------------------------------------------------- PAGE:0063F7A2 PAGE:0063F7A2 loc_63F7A2: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+485j PAGE:0063F7A2 83 7D 10 00 cmp [ebp+MemoryInformationClass], 0 ; 我们是2,继续跳转 PAGE:0063F7A6 75 12 jnz short loc_63F7BA PAGE:0063F7A8 56 push esi PAGE:0063F7A9 FF 75 0C push [ebp+BaseAddress] PAGE:0063F7AC 8D 45 84 lea eax, [ebp+var_7C] PAGE:0063F7AF E8 7E B2 E4 FF call _MiQueryAddressSpan@12 ; MiQueryAddressSpan(x,x,x) PAGE:0063F7B4 2B 45 84 sub eax, [ebp+var_7C] PAGE:0063F7B7 89 45 90 mov [ebp+var_70], eax PAGE:0063F7BA PAGE:0063F7BA loc_63F7BA: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+564j PAGE:0063F7BA 33 C9 xor ecx, ecx PAGE:0063F7BC 8B 45 E0 mov eax, [ebp+EProcess] PAGE:0063F7BF 05 00 01 00 00 add eax, 100h ; +0x100 AddressCreationLock : _EX_PUSH_LOCK PAGE:0063F7BF ; 地址空间锁 PAGE:0063F7C4 8B D0 mov edx, eax PAGE:0063F7C6 6A 11 push 11h PAGE:0063F7C8 58 pop eax PAGE:0063F7C9 F0 0F B1 0A lock cmpxchg [edx], ecx PAGE:0063F7CD 83 F8 11 cmp eax, 11h PAGE:0063F7D0 74 0E jz short loc_63F7E0 PAGE:0063F7D2 8B 4D E0 mov ecx, [ebp+EProcess] PAGE:0063F7D5 81 C1 00 01 00 00 add ecx, 100h PAGE:0063F7DB E8 1A 75 E4 FF call @ExfReleasePushLockShared@4 ; ExfReleasePushLockShared(x) PAGE:0063F7E0 PAGE:0063F7E0 loc_63F7E0: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+58Ej PAGE:0063F7E0 80 A3 89 02 00 00+and byte ptr [ebx+289h], 0FBh PAGE:0063F7E7 66 FF 83 86 00 00+inc word ptr [ebx+86h] PAGE:0063F7EE 0F B7 83 86 00 00+movzx eax, word ptr [ebx+86h] PAGE:0063F7F5 66 85 C0 test ax, ax PAGE:0063F7F8 75 0C jnz short loc_63F806 PAGE:0063F7FA 83 C3 40 add ebx, 40h PAGE:0063F7FD 39 1B cmp [ebx], ebx PAGE:0063F7FF 74 05 jz short loc_63F806 PAGE:0063F801 E8 71 DB DE FF call _KiCheckForKernelApcDelivery@0 ; KiCheckForKernelApcDelivery() PAGE:0063F806 PAGE:0063F806 loc_63F806: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+5B6j PAGE:0063F806 ; NtQueryVirtualMemory(x,x,x,x,x,x)+5BDj PAGE:0063F806 F6 45 E4 01 test byte ptr [ebp+var_1C??Attached], 1 ; 判断进程是否靠挂 PAGE:0063F80A 74 11 jz short loc_63F81D PAGE:0063F80C 8D 45 A0 lea eax, [ebp+ApcState] PAGE:0063F80F 50 push eax PAGE:0063F810 E8 3D 34 E2 FF call _KeUnstackDetachProcess@4 ; KeUnstackDetachProcess(x) PAGE:0063F815 8B 4D E0 mov ecx, [ebp+EProcess] ; Object PAGE:0063F818 E8 10 6C E1 FF call @ObfDereferenceObject@4 ; 解除对进程对象的引用 PAGE:0063F81D PAGE:0063F81D loc_63F81D: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+5C8j PAGE:0063F81D 83 7D 10 00 cmp [ebp+MemoryInformationClass], 0 PAGE:0063F821 75 44 jnz short loc_63F867 PAGE:0063F823 83 65 E4 FD and [ebp+var_1C??Attached], 0FFFFFFFDh PAGE:0063F827 C7 45 FC 06 00 00+mov [ebp+ms_exc.registration.TryLevel], 6 PAGE:0063F82E 6A 07 push 7 PAGE:0063F830 59 pop ecx PAGE:0063F831 8D 75 84 lea esi, [ebp+var_7C] PAGE:0063F834 F3 A5 rep movsd PAGE:0063F836 83 4D E4 02 or [ebp+var_1C??Attached], 2 PAGE:0063F83A 8B 45 1C mov eax, [ebp+ReturnLength] PAGE:0063F83D 85 C0 test eax, eax PAGE:0063F83F E9 AA FD FF FF jmp loc_63F5EE PAGE:0063F844 ; --------------------------------------------------------------------------- PAGE:0063F844 PAGE:0063F844 loc_63F844: ; DATA XREF: .text:stru_450F18o PAGE:0063F844 8B 45 EC mov eax, [ebp+ms_exc.exc_ptr] ; Exception filter 6 for function 63F242 PAGE:0063F847 8B 00 mov eax, [eax] PAGE:0063F849 8B 00 mov eax, [eax] PAGE:0063F84B 89 45 C0 mov [ebp+var_40], eax PAGE:0063F84E 33 C0 xor eax, eax PAGE:0063F850 40 inc eax PAGE:0063F851 C3 retn PAGE:0063F852 ; --------------------------------------------------------------------------- PAGE:0063F852 PAGE:0063F852 loc_63F852: ; DATA XREF: .text:stru_450F18o PAGE:0063F852 8B 65 E8 mov esp, [ebp+ms_exc.old_esp] ; Exception handler 6 for function 63F242 PAGE:0063F855 F6 45 E4 02 test byte ptr [ebp+var_1C??Attached], 2 PAGE:0063F859 0F 85 5A FB FF FF jnz loc_63F3B9 PAGE:0063F85F 8B 45 C0 mov eax, [ebp+var_40] PAGE:0063F862 E9 86 FA FF FF jmp loc_63F2ED PAGE:0063F867 ; --------------------------------------------------------------------------- PAGE:0063F867 PAGE:0063F867 loc_63F867: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+5DFj PAGE:0063F867 83 7D DC 00 cmp [ebp+var_24], 0 ; 又是对文件对象的判断 PAGE:0063F86B 0F 84 B1 FD FF FF jz loc_63F622 ; 为NULL,跳转 PAGE:0063F871 83 7D DC 01 cmp [ebp+var_24], 1 PAGE:0063F875 75 0A jnz short loc_63F881 ; 有效,则跳转 PAGE:0063F877 B8 98 00 00 C0 mov eax, 0C0000098h ; STATUS_FILE_INVALID 错误码 PAGE:0063F87C E9 C8 00 00 00 jmp loc_63F949 PAGE:0063F881 ; --------------------------------------------------------------------------- PAGE:0063F881 PAGE:0063F881 loc_63F881: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+633j PAGE:0063F881 8D 45 BC lea eax, [ebp+var_44] PAGE:0063F884 50 push eax ; ReturnLength PAGE:0063F885 FF 75 18 push [ebp+MemoryInformationLength] ; Length PAGE:0063F888 57 push edi ; ObjectNameInfo PAGE:0063F889 FF 75 DC push [ebp+var_24] ; Object PAGE:0063F88C E8 0D 10 FE FF call _ObQueryNameString@16 ; 在wrk中搜函数原型 PAGE:0063F891 8B F0 mov esi, eax PAGE:0063F893 8B 4D DC mov ecx, [ebp+var_24] ; Object PAGE:0063F896 E8 92 6B E1 FF call @ObfDereferenceObject@4 ; 释放对文件对象的引用 PAGE:0063F89B 8B 45 1C mov eax, [ebp+ReturnLength] ; 判断传入的返回值是否为NULL PAGE:0063F89E 85 C0 test eax, eax PAGE:0063F8A0 74 29 jz short loc_63F8CB ; 为NULL,则跳转 PAGE:0063F8A2 C7 45 FC 07 00 00+mov [ebp+ms_exc.registration.TryLevel], 7 PAGE:0063F8A9 8B 4D BC mov ecx, [ebp+var_44] ; 实际字符串的长度 PAGE:0063F8AC 89 08 mov [eax], ecx ; 返回实际字符串的长度 PAGE:0063F8AE EB 14 jmp short loc_63F8C4 PAGE:0063F8B0 ; --------------------------------------------------------------------------- PAGE:0063F8B0 PAGE:0063F8B0 loc_63F8B0: ; DATA XREF: .text:stru_450F18o PAGE:0063F8B0 8B 45 EC mov eax, [ebp+ms_exc.exc_ptr] ; Exception filter 7 for function 63F242 PAGE:0063F8B3 8B 00 mov eax, [eax] PAGE:0063F8B5 8B 00 mov eax, [eax] PAGE:0063F8B7 89 45 B8 mov [ebp+var_48], eax PAGE:0063F8BA 33 C0 xor eax, eax PAGE:0063F8BC 40 inc eax PAGE:0063F8BD C3 retn PAGE:0063F8BE ; --------------------------------------------------------------------------- PAGE:0063F8BE PAGE:0063F8BE loc_63F8BE: ; DATA XREF: .text:stru_450F18o PAGE:0063F8BE 8B 65 E8 mov esp, [ebp+ms_exc.old_esp] ; Exception handler 7 for function 63F242 PAGE:0063F8C1 8B 75 B8 mov esi, [ebp+var_48] PAGE:0063F8C4 PAGE:0063F8C4 loc_63F8C4: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+66Cj PAGE:0063F8C4 C7 45 FC FE FF FF+mov [ebp+ms_exc.registration.TryLevel], 0FFFFFFFEh PAGE:0063F8CB PAGE:0063F8CB loc_63F8CB: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+65Ej PAGE:0063F8CB 8B C6 mov eax, esi ; ObQueryNameString 返回的status PAGE:0063F8CD EB 7A jmp short loc_63F949 PAGE:0063F8CF ; --------------------------------------------------------------------------- PAGE:0063F8CF PAGE:0063F8CF loc_63F8CF: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+E1j PAGE:0063F8CF ; NtQueryVirtualMemory(x,x,x,x,x,x)+F4j PAGE:0063F8CF BB 41 01 00 C0 mov ebx, 0C0000141h PAGE:0063F8D4 83 7D 10 00 cmp [ebp+MemoryInformationClass], 0 PAGE:0063F8D8 75 6D jnz short loc_63F947 PAGE:0063F8DA C7 45 FC 01 00 00+mov [ebp+ms_exc.registration.TryLevel], 1 PAGE:0063F8E1 46 inc esi PAGE:0063F8E2 89 77 04 mov [edi+4], esi PAGE:0063F8E5 6A 02 push 2 PAGE:0063F8E7 5B pop ebx PAGE:0063F8E8 89 5F 08 mov [edi+8], ebx PAGE:0063F8EB 8B F0 mov esi, eax PAGE:0063F8ED 23 F2 and esi, edx PAGE:0063F8EF 89 37 mov [edi], esi PAGE:0063F8F1 23 C2 and eax, edx PAGE:0063F8F3 2B C8 sub ecx, eax PAGE:0063F8F5 41 inc ecx PAGE:0063F8F6 89 4F 0C mov [edi+0Ch], ecx PAGE:0063F8F9 C7 47 10 00 20 00+mov dword ptr [edi+10h], 2000h PAGE:0063F900 C7 47 14 01 00 00+mov dword ptr [edi+14h], 1 PAGE:0063F907 C7 47 18 00 00 02+mov dword ptr [edi+18h], 20000h PAGE:0063F90E 8B 45 1C mov eax, [ebp+ReturnLength] PAGE:0063F911 85 C0 test eax, eax PAGE:0063F913 74 06 jz short loc_63F91B PAGE:0063F915 C7 00 1C 00 00 00 mov dword ptr [eax], 1Ch PAGE:0063F91B PAGE:0063F91B loc_63F91B: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+6D1j PAGE:0063F91B B8 00 00 FE 7F mov eax, 7FFE0000h PAGE:0063F920 3B F0 cmp esi, eax PAGE:0063F922 75 1A jnz short loc_63F93E PAGE:0063F924 89 47 04 mov [edi+4], eax PAGE:0063F927 89 5F 14 mov [edi+14h], ebx PAGE:0063F92A B8 00 10 00 00 mov eax, 1000h PAGE:0063F92F 89 47 0C mov [edi+0Ch], eax PAGE:0063F932 89 47 10 mov [edi+10h], eax PAGE:0063F935 EB 07 jmp short loc_63F93E PAGE:0063F937 ; --------------------------------------------------------------------------- PAGE:0063F937 PAGE:0063F937 loc_63F937: ; DATA XREF: .text:stru_450F18o PAGE:0063F937 33 C0 xor eax, eax ; Exception filter 1 for function 63F242 PAGE:0063F939 40 inc eax PAGE:0063F93A C3 retn PAGE:0063F93B ; --------------------------------------------------------------------------- PAGE:0063F93B PAGE:0063F93B loc_63F93B: ; DATA XREF: .text:stru_450F18o PAGE:0063F93B 8B 65 E8 mov esp, [ebp+ms_exc.old_esp] ; Exception handler 1 for function 63F242 PAGE:0063F93E PAGE:0063F93E loc_63F93E: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+6E0j PAGE:0063F93E ; NtQueryVirtualMemory(x,x,x,x,x,x)+6F3j PAGE:0063F93E C7 45 FC FE FF FF+mov [ebp+ms_exc.registration.TryLevel], 0FFFFFFFEh PAGE:0063F945 33 DB xor ebx, ebx PAGE:0063F947 PAGE:0063F947 loc_63F947: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+1A7j PAGE:0063F947 ; NtQueryVirtualMemory(x,x,x,x,x,x)+696j PAGE:0063F947 8B C3 mov eax, ebx PAGE:0063F949 PAGE:0063F949 loc_63F949: ; CODE XREF: NtQueryVirtualMemory(x,x,x,x,x,x)+2Cj PAGE:0063F949 ; NtQueryVirtualMemory(x,x,x,x,x,x)+4Ej PAGE:0063F949 ; NtQueryVirtualMemory(x,x,x,x,x,x)+B2j PAGE:0063F949 ; NtQueryVirtualMemory(x,x,x,x,x,x)+CCj PAGE:0063F949 ; NtQueryVirtualMemory(x,x,x,x,x,x)+12Cj PAGE:0063F949 ; NtQueryVirtualMemory(x,x,x,x,x,x)+159j PAGE:0063F949 ; NtQueryVirtualMemory(x,x,x,x,x,x)+180j PAGE:0063F949 ; NtQueryVirtualMemory(x,x,x,x,x,x)+27Ej PAGE:0063F949 ; NtQueryVirtualMemory(x,x,x,x,x,x)+3E5j PAGE:0063F949 ; NtQueryVirtualMemory(x,x,x,x,x,x)+63Aj PAGE:0063F949 ; NtQueryVirtualMemory(x,x,x,x,x,x)+68Bj PAGE:0063F949 E8 17 49 E1 FF call __SEH_epilog4 PAGE:0063F94E C2 18 00 retn 18h PAGE:0063F94E _NtQueryVirtualMemory@24 endp PAGE:0063F94E
下面是wrk中的源代码
NTSTATUS NtQueryVirtualMemory( __in HANDLE ProcessHandle, __in PVOID BaseAddress, __in MEMORY_INFORMATION_CLASS MemoryInformationClass, __out_bcount(MemoryInformationLength) PVOID MemoryInformation, __in SIZE_T MemoryInformationLength, __out_opt PSIZE_T ReturnLength ) /*++ Routine Description: This function provides the capability to determine the state, protection, and type of a region of pages within the virtual address space of the subject process. The state of the first page within the region is determined and then subsequent entries in the process address map are scanned from the base address upward until either the entire range of pages has been scanned or until a page with a nonmatching set of attributes is encountered. The region attributes, the length of the region of pages with matching attributes, and an appropriate status value are returned. If the entire region of pages does not have a matching set of attributes, then the returned length parameter value can be used to calculate the address and length of the region of pages that was not scanned. Arguments: ProcessHandle - An open handle to a process object. BaseAddress - The base address of the region of pages to be queried. This value is rounded down to the next host-page- address boundary. MemoryInformationClass - The memory information class about which to retrieve information. MemoryInformation - A pointer to a buffer that receives the specified information. The format and content of the buffer depend on the specified information class. MemoryBasicInformation - Data type is PMEMORY_BASIC_INFORMATION. MEMORY_BASIC_INFORMATION Structure ULONG RegionSize - The size of the region in bytes beginning at the base address in which all pages have identical attributes. ULONG State - The state of the pages within the region. State Values MEM_COMMIT - The state of the pages within the region is committed. MEM_FREE - The state of the pages within the region is free. MEM_RESERVE - The state of the pages within the region is reserved. ULONG Protect - The protection of the pages within the region. Protect Values PAGE_NOACCESS - No access to the region of pages is allowed. An attempt to read, write, or execute within the region results in an access violation. PAGE_EXECUTE - Execute access to the region of pages is allowed. An attempt to read or write within the region results in an access violation. PAGE_READONLY - Read-only and execute access to the region of pages is allowed. An attempt to write within the region results in an access violation. PAGE_READWRITE - Read, write, and execute access to the region of pages is allowed. If write access to the underlying section is allowed, then a single copy of the pages are shared. Otherwise, the pages are shared read-only/copy-on-write. PAGE_GUARD - Read, write, and execute access to the region of pages is allowed; however, access to the region causes a "guard region entered" condition to be raised in the subject process. PAGE_NOCACHE - Disable the placement of committed pages into the data cache. PAGE_WRITECOMBINE - Disable the placement of committed pages into the data cache, combine the writes as well. ULONG Type - The type of pages within the region. Type Values MEM_PRIVATE - The pages within the region are private. MEM_MAPPED - The pages within the region are mapped into the view of a section. MEM_IMAGE - The pages within the region are mapped into the view of an image section. MemoryInformationLength - Specifies the length in bytes of the memory information buffer. ReturnLength - An optional pointer which, if specified, receives the number of bytes placed in the process information buffer. Return Value: NTSTATUS. Environment: Kernel mode. --*/ { ULONG LocalReturnLength; KPROCESSOR_MODE PreviousMode; PEPROCESS TargetProcess; PETHREAD Thread; NTSTATUS Status; PMMVAD Vad; PVOID Va; PVOID NextVaToQuery; LOGICAL Found; SIZE_T TheRegionSize; ULONG NewProtect; ULONG NewState; PVOID FilePointer; ULONG_PTR BaseVpn; MEMORY_BASIC_INFORMATION Info; PMEMORY_BASIC_INFORMATION BasicInfo; LOGICAL Attached; LOGICAL Leaped; ULONG MemoryInformationLengthUlong; KAPC_STATE ApcState; PETHREAD CurrentThread; PVOID HighestVadAddress; PVOID HighestUserAddress; Found = FALSE; Leaped = TRUE; FilePointer = NULL; // // Make sure the user's buffer is large enough for the requested operation. // // Check argument validity. // switch (MemoryInformationClass) { case MemoryBasicInformation: if (MemoryInformationLength < sizeof(MEMORY_BASIC_INFORMATION)) { return STATUS_INFO_LENGTH_MISMATCH; } break; case MemoryWorkingSetInformation: if (MemoryInformationLength < sizeof(ULONG_PTR)) { return STATUS_INFO_LENGTH_MISMATCH; } break; case MemoryWorkingSetExInformation: if (MemoryInformationLength < sizeof (MEMORY_WORKING_SET_EX_INFORMATION)) { return STATUS_INFO_LENGTH_MISMATCH; } break; case MemoryMappedFilenameInformation: break; default: return STATUS_INVALID_INFO_CLASS; } CurrentThread = PsGetCurrentThread (); PreviousMode = KeGetPreviousModeByThread(&CurrentThread->Tcb); if (PreviousMode != KernelMode) { // // Check arguments. // try { ProbeForWrite(MemoryInformation, MemoryInformationLength, sizeof(ULONG_PTR)); if (ARGUMENT_PRESENT(ReturnLength)) { ProbeForWriteUlong_ptr(ReturnLength); } } except (EXCEPTION_EXECUTE_HANDLER) { // // If an exception occurs during the probe or capture // of the initial values, then handle the exception and // return the exception code as the status value. // return GetExceptionCode(); } } if (BaseAddress > MM_HIGHEST_USER_ADDRESS) { return STATUS_INVALID_PARAMETER; } HighestUserAddress = MM_HIGHEST_USER_ADDRESS; HighestVadAddress = (PCHAR) MM_HIGHEST_VAD_ADDRESS; #if defined(_WIN64) if (ProcessHandle == NtCurrentProcess()) { TargetProcess = PsGetCurrentProcessByThread(CurrentThread); } else { Status = ObReferenceObjectByHandle (ProcessHandle, PROCESS_QUERY_INFORMATION, PsProcessType, PreviousMode, (PVOID *)&TargetProcess, NULL); if (!NT_SUCCESS(Status)) { return Status; } } // // If this is a wow64 process, then return the appropriate highest // user address depending on whether the process has been started with // a 2GB or a 4GB address space. // if (TargetProcess->Wow64Process != NULL) { if (TargetProcess->Flags & PS_PROCESS_FLAGS_WOW64_4GB_VA_SPACE) { HighestUserAddress = (PVOID) ((ULONG_PTR)_4gb - X64K - 1); } else { HighestUserAddress = (PVOID) ((ULONG_PTR)_2gb - X64K - 1); } HighestVadAddress = (PCHAR)HighestUserAddress - X64K; if (BaseAddress > HighestUserAddress) { if (ProcessHandle != NtCurrentProcess()) { ObDereferenceObject (TargetProcess); } return STATUS_INVALID_PARAMETER; } } #endif if ((BaseAddress > HighestVadAddress) || (PAGE_ALIGN(BaseAddress) == (PVOID)MM_SHARED_USER_DATA_VA)) { // // Indicate a reserved area from this point on. // Status = STATUS_INVALID_ADDRESS; if (MemoryInformationClass == MemoryBasicInformation) { try { ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->AllocationBase = (PCHAR) HighestVadAddress + 1; ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->AllocationProtect = PAGE_READONLY; ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->BaseAddress = PAGE_ALIGN(BaseAddress); ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->RegionSize = ((PCHAR)HighestUserAddress + 1) - (PCHAR)PAGE_ALIGN(BaseAddress); ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->State = MEM_RESERVE; ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->Protect = PAGE_NOACCESS; ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->Type = MEM_PRIVATE; if (ARGUMENT_PRESENT(ReturnLength)) { *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION); } if (PAGE_ALIGN(BaseAddress) == (PVOID)MM_SHARED_USER_DATA_VA) { // // This is the page that is double mapped between // user mode and kernel mode. // ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->AllocationBase = (PVOID)MM_SHARED_USER_DATA_VA; ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->Protect = PAGE_READONLY; ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->RegionSize = PAGE_SIZE; ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->State = MEM_COMMIT; } } except (EXCEPTION_EXECUTE_HANDLER) { // // Just return success. // NOTHING; } Status = STATUS_SUCCESS; } #if defined(_WIN64) if (ProcessHandle != NtCurrentProcess()) { ObDereferenceObject (TargetProcess); } #endif return Status; } #if !defined(_WIN64) if (ProcessHandle == NtCurrentProcess()) { TargetProcess = PsGetCurrentProcessByThread(CurrentThread); } else { Status = ObReferenceObjectByHandle (ProcessHandle, PROCESS_QUERY_INFORMATION, PsProcessType, PreviousMode, (PVOID *)&TargetProcess, NULL); if (!NT_SUCCESS(Status)) { return Status; } } #endif if (MemoryInformationClass == MemoryWorkingSetExInformation) { Status = MiGetWorkingSetInfoList ( (PMEMORY_WORKING_SET_EX_INFORMATION) MemoryInformation, MemoryInformationLength, TargetProcess); if (ProcessHandle != NtCurrentProcess()) { ObDereferenceObject (TargetProcess); } // // If MiGetWorkingSetInfoList failed then inform the caller. // if (!NT_SUCCESS(Status)) { return Status; } try { if (ARGUMENT_PRESENT (ReturnLength)) { *ReturnLength = MemoryInformationLength; } } except (EXCEPTION_EXECUTE_HANDLER) { NOTHING; } return STATUS_SUCCESS; } if (MemoryInformationClass == MemoryWorkingSetInformation) { Status = MiGetWorkingSetInfo ( (PMEMORY_WORKING_SET_INFORMATION) MemoryInformation, MemoryInformationLength, TargetProcess); if (ProcessHandle != NtCurrentProcess()) { ObDereferenceObject (TargetProcess); } // // If MiGetWorkingSetInfo failed then inform the caller. // if (!NT_SUCCESS(Status)) { return Status; } try { if (ARGUMENT_PRESENT(ReturnLength)) { *ReturnLength = ((((PMEMORY_WORKING_SET_INFORMATION) MemoryInformation)->NumberOfEntries - 1) * sizeof(ULONG_PTR)) + sizeof(MEMORY_WORKING_SET_INFORMATION); } } except (EXCEPTION_EXECUTE_HANDLER) { } return STATUS_SUCCESS; } // // If the specified process is not the current process, attach // to the specified process. // if (ProcessHandle != NtCurrentProcess()) { KeStackAttachProcess (&TargetProcess->Pcb, &ApcState); Attached = TRUE; } else { Attached = FALSE; } // // Get working set mutex and block APCs. // LOCK_ADDRESS_SPACE (TargetProcess); // // Make sure the address space was not deleted, if so, return an error. // if (TargetProcess->Flags & PS_PROCESS_FLAGS_VM_DELETED) { UNLOCK_ADDRESS_SPACE (TargetProcess); if (Attached == TRUE) { KeUnstackDetachProcess (&ApcState); ObDereferenceObject (TargetProcess); } return STATUS_PROCESS_IS_TERMINATING; } // // Locate the VAD that contains the base address or the VAD // which follows the base address. // if (TargetProcess->VadRoot.NumberGenericTableElements != 0) { Vad = (PMMVAD) TargetProcess->VadRoot.BalancedRoot.RightChild; BaseVpn = MI_VA_TO_VPN (BaseAddress); while (TRUE) { if (Vad == NULL) { break; } if ((BaseVpn >= Vad->StartingVpn) && (BaseVpn <= Vad->EndingVpn)) { Found = TRUE; break; } if (BaseVpn < Vad->StartingVpn) { if (Vad->LeftChild == NULL) { break; } Vad = Vad->LeftChild; } else { ASSERT (BaseVpn > Vad->EndingVpn); if (Vad->RightChild == NULL) { break; } Vad = Vad->RightChild; } } } else { Vad = NULL; BaseVpn = 0; } if (!Found) { // // There is no virtual address allocated at the base // address. Return the size of the hole starting at // the base address. // if (Vad == NULL) { TheRegionSize = (((PCHAR)HighestVadAddress + 1) - (PCHAR)PAGE_ALIGN(BaseAddress)); } else { if (Vad->StartingVpn < BaseVpn) { // // We are looking at the Vad which occupies the range // just before the desired range. Get the next Vad. // Vad = MiGetNextVad (Vad); if (Vad == NULL) { TheRegionSize = (((PCHAR)HighestVadAddress + 1) - (PCHAR)PAGE_ALIGN(BaseAddress)); } else { TheRegionSize = (PCHAR)MI_VPN_TO_VA (Vad->StartingVpn) - (PCHAR)PAGE_ALIGN(BaseAddress); } } else { TheRegionSize = (PCHAR)MI_VPN_TO_VA (Vad->StartingVpn) - (PCHAR)PAGE_ALIGN(BaseAddress); } } UNLOCK_ADDRESS_SPACE (TargetProcess); if (Attached == TRUE) { KeUnstackDetachProcess (&ApcState); ObDereferenceObject (TargetProcess); } // // Establish an exception handler and write the information and // returned length. // if (MemoryInformationClass == MemoryBasicInformation) { BasicInfo = (PMEMORY_BASIC_INFORMATION) MemoryInformation; Found = FALSE; try { BasicInfo->AllocationBase = NULL; BasicInfo->AllocationProtect = 0; BasicInfo->BaseAddress = PAGE_ALIGN(BaseAddress); BasicInfo->RegionSize = TheRegionSize; BasicInfo->State = MEM_FREE; BasicInfo->Protect = PAGE_NOACCESS; BasicInfo->Type = 0; Found = TRUE; if (ARGUMENT_PRESENT(ReturnLength)) { *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION); } } except (EXCEPTION_EXECUTE_HANDLER) { // // Just return success if the BasicInfo was successfully // filled in. // if (Found == FALSE) { return GetExceptionCode (); } } return STATUS_SUCCESS; } return STATUS_INVALID_ADDRESS; } // // Found a VAD. // Va = PAGE_ALIGN(BaseAddress); Info.BaseAddress = Va; Info.AllocationBase = MI_VPN_TO_VA (Vad->StartingVpn); Info.AllocationProtect = MI_CONVERT_FROM_PTE_PROTECTION ( Vad->u.VadFlags.Protection); // // There is a page mapped at the base address. // if ((Vad->u.VadFlags.PrivateMemory) || (Vad->u.VadFlags.VadType == VadRotatePhysical)) { Info.Type = MEM_PRIVATE; } else { if (Vad->u.VadFlags.VadType == VadImageMap) { Info.Type = MEM_IMAGE; } else { Info.Type = MEM_MAPPED; } if (MemoryInformationClass == MemoryMappedFilenameInformation) { if (Vad->ControlArea != NULL) { FilePointer = Vad->ControlArea->FilePointer; } if (FilePointer == NULL) { FilePointer = (PVOID)1; } else { ObReferenceObject (FilePointer); } } } Thread = PsGetCurrentThread (); LOCK_WS_SHARED (Thread, TargetProcess); Info.State = MiQueryAddressState (Va, Vad, TargetProcess, &Info.Protect, &NextVaToQuery); Va = NextVaToQuery; while (MI_VA_TO_VPN (Va) <= Vad->EndingVpn) { NewState = MiQueryAddressState (Va, Vad, TargetProcess, &NewProtect, &NextVaToQuery); if ((NewState != Info.State) || (NewProtect != Info.Protect)) { // // The state for this address does not match, calculate // size and return. // Leaped = FALSE; break; } Va = NextVaToQuery; } UNLOCK_WS_SHARED (Thread, TargetProcess); // // We may have aggressively leaped past the end of the VAD. Shorten the // Va here if we did. // if (Leaped == TRUE) { Va = MI_VPN_TO_VA (Vad->EndingVpn + 1); } Info.RegionSize = ((PCHAR)Va - (PCHAR)Info.BaseAddress); // // A range has been found, release the mutexes, detach from the // target process and return the information. // UNLOCK_ADDRESS_SPACE (TargetProcess); if (Attached == TRUE) { KeUnstackDetachProcess (&ApcState); ObDereferenceObject (TargetProcess); } if (MemoryInformationClass == MemoryBasicInformation) { Found = FALSE; try { *(PMEMORY_BASIC_INFORMATION)MemoryInformation = Info; Found = TRUE; if (ARGUMENT_PRESENT(ReturnLength)) { *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION); } } except (EXCEPTION_EXECUTE_HANDLER) { // // Just return success if the BasicInfo was successfully // filled in. // if (Found == FALSE) { return GetExceptionCode (); } } return STATUS_SUCCESS; } // // Try to return the name of the file that is mapped. // if (FilePointer == NULL) { return STATUS_INVALID_ADDRESS; } if (FilePointer == (PVOID)1) { return STATUS_FILE_INVALID; } MemoryInformationLengthUlong = (ULONG)MemoryInformationLength; if ((SIZE_T)MemoryInformationLengthUlong < MemoryInformationLength) { return STATUS_INVALID_PARAMETER_5; } // // We have a referenced pointer to the file. Call ObQueryNameString // and get the file name. // Status = ObQueryNameString (FilePointer, (POBJECT_NAME_INFORMATION) MemoryInformation, MemoryInformationLengthUlong, &LocalReturnLength); ObDereferenceObject (FilePointer); if (ARGUMENT_PRESENT (ReturnLength)) { try { *ReturnLength = LocalReturnLength; } except (EXCEPTION_EXECUTE_HANDLER) { Status = GetExceptionCode (); } } return Status; }
所以,我们也可以自己来遍历二叉树来获得进程的所有模块,而不用NtQueryVirtualMemory函数,就是一个AVL树的遍历,主要代码如下
typedef struct _MMADDRESS_NODE // 0x14 { union u1{ULONG u1;}; // +0x0(0x4) struct _MMADDRESS_NODE* LeftChild; // +0x4(0x4) struct _MMADDRESS_NODE* RightChild; // +0x8(0x4) ULONG StartingVpn; // +0xc(0x4) ULONG EndingVpn; // +0x10(0x4) }MMADDRESS_NODE,*PMMADDRESS_NODE; #pragma pack(push,1) typedef struct _EX_FAST_REF { union { PVOID Object; ULONG_PTR RefCnt:3; ULONG_PTR Value; }; } EX_FAST_REF, *PEX_FAST_REF; #pragma pack(pop) struct _SEGMENT // 0x38 { struct _CONTROL_AREA* ControlArea; // +0x0(0x4) ULONG TotalNumberOfPtes; // +0x4(0x4) ULONG SegmentFlags; // +0x8(0x4) ULONG NumberOfCommittedPages; // +0xc(0x4) ULONGLONG SizeOfSegment; // +0x10(0x8) union { struct _MMEXTEND_INFO* ExtendInfo; // +0x18(0x4) void* BasedAddress; // +0x18(0x4) }; EX_PUSH_LOCK SegmentLock; // +0x1c(0x4) ULONG u1; // +0x20(0x4) ULONG u2; // +0x24(0x4) struct _MMPTE* PrototypePte; // +0x28(0x4) //ULONGLONG ThePtes[0x1]; // +0x30(0x8) }; struct _CONTROL_AREA // 0x50 { struct _SEGMENT* Segment; // +0x0(0x4) struct _LIST_ENTRY DereferenceList; // +0x4(0x8) ULONG NumberOfSectionReferences; // +0xc(0x4) ULONG NumberOfPfnReferences; // +0x10(0x4) ULONG NumberOfMappedViews; // +0x14(0x4) ULONG NumberOfUserReferences; // +0x18(0x4) ULONG u; // +0x1c(0x4) ULONG FlushInProgressCount; // +0x20(0x4) struct _EX_FAST_REF FilePointer; // +0x24(0x4) }; struct _SUBSECTION // 0x20 { struct _CONTROL_AREA* ControlArea; // +0x0(0x4) struct _MMPTE* SubsectionBase; // +0x4(0x4) struct _SUBSECTION* NextSubsection; // +0x8(0x4) ULONG PtesInSubsection; // +0xc(0x4) ULONG UnusedPtes; // +0x10(0x4) struct _MM_AVL_TABLE* GlobalPerSessionHead; // +0x10(0x4) union u{ULONG u;}; // +0x14(0x4) ULONG StartingSector; // +0x18(0x4) ULONG NumberOfFullSectors; // +0x1c(0x4) }; typedef struct _MMVAD // 0x3c { ULONG u1; // +0x0(0x4) struct _MMVAD* LeftChild; // +0x4(0x4) struct _MMVAD* RightChild; // +0x8(0x4) ULONG StartingVpn; // +0xc(0x4) ULONG EndingVpn; // +0x10(0x4) ULONG u; // +0x14(0x4) EX_PUSH_LOCK PushLock; // +0x18(0x4) ULONG u5; // +0x1c(0x4) ULONG u2; // +0x20(0x4) struct _SUBSECTION* Subsection; // +0x24(0x4) struct _MSUBSECTION* MappedSubsection; // +0x24(0x4) struct _MMPTE* FirstPrototypePte; // +0x28(0x4) struct _MMPTE* LastContiguousPte; // +0x2c(0x4) struct _LIST_ENTRY ViewLinks; // +0x30(0x8) struct _EPROCESS* VadsProcess; // +0x38(0x4) }MMVAD; typedef struct _MM_AVL_TABLE // 0x20 { struct _MMADDRESS_NODE BalancedRoot; // +0x0(0x14) ULONG DepthOfTree; // +0x14(0x4) ULONG Unused; // +0x14(0x4) ULONG NumberGenericTableElements; // +0x14(0x4) void* NodeHint; // +0x18(0x4) void* NodeFreeHint; // +0x1c(0x4) }MM_AVL_TABLE,*PMMAVL_TABLE; #define GetVadRoot(eprocess) ((PVOID)((char*)eprocess+0x278))
VOID PrintTree(MMVAD* Root) { POBJECT_NAME_INFORMATION Str=(POBJECT_NAME_INFORMATION )ExAllocatePool(PagedPool,800); ULONG RetLen=0; if(!Str||!Root) return ; RtlZeroMemory(Str,800);//递归要节省堆栈资源,不要大量使用局部变量 __try { if(MmIsAddressValid(Root->Subsection)&&MmIsAddressValid(Root->Subsection->ControlArea)) { if(MmIsAddressValid((PVOID)Root->Subsection->ControlArea->FilePointer.Value)) { //最后三位清零 PFILE_OBJECT pFileObj=(PFILE_OBJECT)((Root->Subsection->ControlArea->FilePointer.Value>>3)<<3); if(MmIsAddressValid(pFileObj)) { NTSTATUS Status=ObQueryNameString(pFileObj,Str,800,&RetLen); if(NT_SUCCESS(Status)) { DbgPrint("Base:%08X Size:%dKb Name:%wZ\r\n",Root->Subsection->ControlArea->Segment->BasedAddress,\ (Root->Subsection->ControlArea->Segment->SizeOfSegment)/0x1000, \ (PUNICODE_STRING)(&(Str->Name))); } else { DbgPrint("不能获取到对象!%08X\n",Status); } } } } } __except(1) { DbgPrint("Invalid Address!\n"); } ExFreePool(Str); __try { if(MmIsAddressValid(Root->LeftChild)) PrintTree(Root->LeftChild); if(MmIsAddressValid(Root->RightChild)) PrintTree(Root->RightChild); } __except(1) { DbgPrint("Exception!"); return; } } VOID ShowProcessModule(ULONG Pid) { #ifdef _DBG _asm int 3 #endif PMMAVL_TABLE Table; PEPROCESS Epr=0; if(NT_SUCCESS(PsLookupProcessByProcessId((HANDLE)Pid,&Epr))) { KeAttachProcess(Epr); Table=(PMMAVL_TABLE)GetVadRoot(Epr); if(Table->BalancedRoot.LeftChild) PrintTree((MMVAD*)Table->BalancedRoot.LeftChild); if(Table->BalancedRoot.RightChild) PrintTree((MMVAD*)Table->BalancedRoot.RightChild); KeDetachProcess(); } }