內核知識第十講,內核結構體簡介.以及自己實現內存讀寫功能.
前言:
不知道大家學習逆向技術的時候.有沒有聽說過什么驅動過保護. 什么驅動讀寫xxx的.而今天就講解一下其原理.
PS: 講解原理還有簡單的Demo. 請大家學習逆向技術.並做好信息安全. 而不是要求大家去做黑產.
PS: 只講今天表中有用的結構體.不會每個成員都會介紹.
一丶ReadProcessMemory 和WriteProcessMemory 的小知識.
我們都知道 Ring3讀寫別人進程的內存.都是用這兩個API進行操作的. 但是游戲為了保護自己.對這兩個API進行了HOOK. 進而使自己的游戲進程不會被更改.
但是我們有沒有想過. 我們是否可以自己實現這個API.
首先隨便找一個API. 看看作用.
BOOL ReadProcessMemory( HANDLE hProcess, // handle to the process LPCVOID lpBaseAddress, // base of memory area LPVOID lpBuffer, // data buffer SIZE_T nSize, // number of bytes to read SIZE_T * lpNumberOfBytesRead // number of bytes read );
可以看到.第一個參數是一個句柄. 而OpenProcess獲取句柄的API.也肯定被HOOK了.那我們可以不通過句柄來獲取嗎?
答案是可以的:
我們進了Ring0了. 我們也知道了這些API的本質就是查表. windows對進程管理肯定有一張表格存放這. 那么我們怎么查看.
通過調試虛擬機的XP系統. 查看內核中0環的API.
這里需要介紹一下X指令
x nt!*Read*Mmmory*
*代表通配符. 代表的是我想查找一個API. 前邊我不知道.但是中間我知道.
x指令的作用就是 查找所有有關的API.
其中.有個API.是nt!ZwReadVirtualMemory
我們查看反匯編
發現內部調用一個Call,繼續跟進去
發現操作了fs段寄存器.所以得出結論.fs段寄存器中保存了表首地址.
而這個表.在32位系統下.存放在了FS寄存器中.
二丶通過GDT獲取表內容.
我們知道.FS 里面的內容.在0環中.是存放表的位置. 我們可以看下GDT表中存放的段地址是什么.
由此得出表的首地址是 ffdff000.
這個地址則是我們表的首地址.
DT命令的使用.
我們獲得了表的首地址.但是要對其做解析. dt命令就是解析的.
dt是解析結構體的.
例如:
dt xxx結構體. :那么結構體內容就會顯示出來.偏移也會顯示出來.
dt xxx結構體 地址 : 那么不光顯示偏移.而且成員的地址也會列出來. 注意,地址在前.結構體在后也可以.
三丶_KPCR表.
我們的GDT的段地址就是 _KPCR表.
我們對其解析一下看一下.
通過解析.我們得出了3個重要的地方.
1. 我們的位置保存了GDT表的值.和第二處是一樣的.
3.重要結構體 _KPRCB
四丶_KPRCB表.
通過上圖,我們得出了__KPRCB表.我們在對其解析一下看看.
這個結構體內容比較多.我直接COPY下來了.
kd> dt _KPRCB 0xffdff120 nt!_KPRCB +0x000 MinorVersion : 1 +0x002 MajorVersion : 1 +0x004 CurrentThread : 0x8055a9c0 _KTHREAD //當前的線程. +0x008 NextThread : (null) +0x00c IdleThread : 0x8055a9c0 _KTHREAD +0x010 Number : 0 '' +0x011 Reserved : 0 '' +0x012 BuildType : 2 +0x014 SetMember : 1 +0x018 CpuType : 6 '' +0x019 CpuID : 1 '' +0x01a CpuStep : 0x5e03 +0x01c ProcessorState : _KPROCESSOR_STATE +0x33c KernelReserved : [16] 0 +0x37c HalReserved : [16] 0 +0x3bc PrcbPad0 : [92] "" +0x418 LockQueue : [16] _KSPIN_LOCK_QUEUE +0x498 PrcbPad1 : [8] "" +0x4a0 NpxThread : (null) +0x4a4 InterruptCount : 0 +0x4a8 KernelTime : 0 +0x4ac UserTime : 0 +0x4b0 DpcTime : 0 +0x4b4 DebugDpcTime : 0 +0x4b8 InterruptTime : 0 +0x4bc AdjustDpcThreshold : 0x14 +0x4c0 PageColor : 0 +0x4c4 SkipTick : 0 +0x4c8 MultiThreadSetBusy : 0 '' +0x4c9 Spare2 : [3] "" +0x4cc ParentNode : 0x8055b080 _KNODE +0x4d0 MultiThreadProcessorSet : 1 +0x4d4 MultiThreadSetMaster : (null) +0x4d8 ThreadStartCount : [2] 0 +0x4e0 CcFastReadNoWait : 0 +0x4e4 CcFastReadWait : 0 +0x4e8 CcFastReadNotPossible : 0 +0x4ec CcCopyReadNoWait : 0 +0x4f0 CcCopyReadWait : 0 +0x4f4 CcCopyReadNoWaitMiss : 0 +0x4f8 KeAlignmentFixupCount : 0 +0x4fc KeContextSwitches : 0 +0x500 KeDcacheFlushCount : 0 +0x504 KeExceptionDispatchCount : 0x1f +0x508 KeFirstLevelTbFills : 0 +0x50c KeFloatingEmulationCount : 0 +0x510 KeIcacheFlushCount : 0 +0x514 KeSecondLevelTbFills : 0 +0x518 KeSystemCalls : 0 +0x51c SpareCounter0 : [1] 0 +0x520 PPLookasideList : [16] _PP_LOOKASIDE_LIST +0x5a0 PPNPagedLookasideList : [32] _PP_LOOKASIDE_LIST +0x6a0 PPPagedLookasideList : [32] _PP_LOOKASIDE_LIST +0x7a0 PacketBarrier : 0 +0x7a4 ReverseStall : 0 +0x7a8 IpiFrame : (null) +0x7ac PrcbPad2 : [52] "" +0x7e0 CurrentPacket : [3] (null) +0x7ec TargetSet : 0 +0x7f0 WorkerRoutine : (null) +0x7f4 IpiFrozen : 0 +0x7f8 PrcbPad3 : [40] "" +0x820 RequestSummary : 0 +0x824 SignalDone : (null) +0x828 PrcbPad4 : [56] "" +0x860 DpcListHead : _LIST_ENTRY [ 0xffdff980 - 0xffdff980 ] +0x868 DpcStack : 0x8054f200 Void +0x86c DpcCount : 0 +0x870 DpcQueueDepth : 0 +0x874 DpcRoutineActive : 0 +0x878 DpcInterruptRequested : 0 +0x87c DpcLastCount : 0 +0x880 DpcRequestRate : 0 +0x884 MaximumDpcQueueDepth : 4 +0x888 MinimumDpcRate : 3 +0x88c QuantumEnd : 0 +0x890 PrcbPad5 : [16] "" +0x8a0 DpcLock : 0 +0x8a4 PrcbPad6 : [28] "" +0x8c0 CallDpc : _KDPC +0x8e0 ChainedInterruptList : (null) +0x8e4 LookasideIrpFloat : 0n0 +0x8e8 SpareFields0 : [6] 0 +0x900 VendorString : [13] "GenuineIntel" +0x90d InitialApicId : 0 '' +0x90e LogicalProcessorsPerPhysicalProcessor : 0x1 '' +0x910 MHz : 0 +0x914 FeatureBits : 0xa0013fff +0x918 UpdateSignature : _LARGE_INTEGER 0x6a`00000000 +0x920 NpxSaveArea : _FX_SAVE_AREA +0xb30 PowerState : _PROCESSOR_POWER_STATE //電源狀態.
這個表中重要的結構體就是
_KTHREAD結構體.保存了當前線程的信息.
五丶了解進程和線程的數據關系.
我們知道.一個進程可以有多個線程. 一對多的關系.
而一個線程只屬於一個進程.
所以 進程做外鍵.放到線程表中.
那么得出了數據關系.我們在看_KThread結構體里面是否這樣做.
PS: 也就是說我們通過線程信息.能得到進程信息.
例如圖示:
線程_KThread |
進程 |
Xxx線程 |
A進程 |
Xxx線程 |
A進程 |
六丶_KTHREAD 結構體中的內容.
通過上面解析.我們得出_KThread的位置.我們解析一下看看.
PS結構體也是很多.copy過來.
nt!_KTHREAD +0x000 Header : _DISPATCHER_HEADER +0x010 MutantListHead : _LIST_ENTRY [ 0x8055a9d0 - 0x8055a9d0 ] +0x018 InitialStack : 0x80552200 Void +0x01c StackLimit : 0x8054f200 Void +0x020 Teb : (null) +0x024 TlsArray : (null) +0x028 KernelStack : 0x80551fd4 Void +0x02c DebugActive : 0 '' +0x02d State : 0x2 '' +0x02e Alerted : [2] "" +0x030 Iopl : 0 '' +0x031 NpxState : 0xa '' +0x032 Saturation : 0 '' +0x033 Priority : 31 '' +0x034 ApcState : _KAPC_STATE //APC狀態. +0x04c ContextSwitches : 0 +0x050 IdleSwapBlock : 0 '' +0x051 Spare0 : [3] "" +0x054 WaitStatus : 0n0 +0x058 WaitIrql : 0x2 '' +0x059 WaitMode : 0 '' +0x05a WaitNext : 0 '' +0x05b WaitReason : 0 '' +0x05c WaitBlockList : (null) +0x060 WaitListEntry : _LIST_ENTRY [ 0x0 - 0x0 ] +0x060 SwapListEntry : _SINGLE_LIST_ENTRY +0x068 WaitTime : 0 +0x06c BasePriority : 0 '' +0x06d DecrementCount : 0 '' +0x06e PriorityDecrement : 0 '' +0x06f Quantum : 127 '' +0x070 WaitBlock : [4] _KWAIT_BLOCK +0x0d0 LegoData : (null) +0x0d4 KernelApcDisable : 0 +0x0d8 UserAffinity : 0xffffffff +0x0dc SystemAffinityActive : 0 '' +0x0dd PowerState : 0 '' +0x0de NpxIrql : 0 '' +0x0df InitialNode : 0 '' +0x0e0 ServiceTable : 0x8055b220 Void +0x0e4 Queue : (null) +0x0e8 ApcQueueLock : 0 +0x0f0 Timer : _KTIMER +0x118 QueueListEntry : _LIST_ENTRY [ 0x0 - 0x0 ] +0x120 SoftAffinity : 1 +0x124 Affinity : 1 +0x128 Preempted : 0 '' +0x129 ProcessReadyQueue : 0 '' +0x12a KernelStackResident : 0x1 '' +0x12b NextProcessor : 0 '' +0x12c CallbackStack : (null) +0x130 Win32Thread : (null) +0x134 TrapFrame : (null) +0x138 ApcStatePointer : [2] 0x8055a9f4 _KAPC_STATE +0x140 PreviousMode : 0 '' +0x141 EnableStackSwap : 0x1 '' +0x142 LargeStack : 0 '' +0x143 ResourceIndex : 0 '' +0x144 KernelTime : 0 +0x148 UserTime : 0 +0x14c SavedApcState : _KAPC_STATE +0x164 Alertable : 0 '' +0x165 ApcStateIndex : 0 '' +0x166 ApcQueueable : 0x1 '' +0x167 AutoAlignment : 0 '' +0x168 StackBase : 0x80552200 Void +0x16c SuspendApc : _KAPC +0x19c SuspendSemaphore : _KSEMAPHORE +0x1b0 ThreadListEntry : _LIST_ENTRY [ 0x8055ac70 - 0x8055ac70 ] +0x1b8 FreezeCount : 0 '' +0x1b9 SuspendCount : 0 '' +0x1ba IdealProcessor : 0 '' +0x1bb DisableBoost : 0 ''
我們當前所講重要的成員有一個
_KAPC_STATE, 也就是當前地址+ 0x34的位置. 我們看一下里面存放的是什么.
七丶解析 _KAPC_STATE表的內容.
通過解析表中的內容.我們得出了外鍵. 也就是進程的信息.
_KPROCESS
現在我們解析_KPROCESS
八丶解析_KPROCESS表中的內容
命令:
dt 0x8055ac20 _KPROCESS
其中重要的成員有一個 CR3,也就是我們所說的PDE. 里面保存了當前進程PDE
九丶微軟的隱藏.核心的知識.
通過上面幾張表.我們最終找到了PDE的位置.那么最后我們修改PDE.然后對其讀取內存.則可以自己實現ReadProcessMemory
但是現在微軟對我們隱藏了.也就是說我們的 _KTHREA 和 _KPROCESS 結構體其實都是一小部分.
其實真正的結構體是
_ETHREAD 和_EPROCESS
而_KTHREAD 和_ETHREAD都是這兩個結構體中的第一項成員.
例如:
struct _EPROCESS { _KPROCESS * m_Kprocess } struct _ETHREAD { _KTHREAD * m_KTHREAD }
也就是說.同一個 地址,可以解析為_KPROCESS.也可以解析成_EPROCESS. .線程的同理.
我們重新解析一下.
解析_ETHREAD
PS: 表項太多.直接拷貝
kd> dt 0x8055a9c0 _ETHREAD nt!_ETHREAD +0x000 Tcb : _KTHREAD //第一個成員果然是_KTHREAD +0x1c0 CreateTime : _LARGE_INTEGER 0x0 +0x1c0 NestedFaultCount : 0y00 +0x1c0 ApcNeeded : 0y0 +0x1c8 ExitTime : _LARGE_INTEGER 0x0 +0x1c8 LpcReplyChain : _LIST_ENTRY [ 0x0 - 0x0 ] +0x1c8 KeyedWaitChain : _LIST_ENTRY [ 0x0 - 0x0 ] +0x1d0 ExitStatus : 0n0 +0x1d0 OfsChain : (null) +0x1d4 PostBlockList : _LIST_ENTRY [ 0x0 - 0x0 ] +0x1dc TerminationPort : (null) +0x1dc ReaperLink : (null) +0x1dc KeyedWaitValue : (null) +0x1e0 ActiveTimerListLock : 0 +0x1e4 ActiveTimerListHead : _LIST_ENTRY [ 0x0 - 0x0 ] +0x1ec Cid : _CLIENT_ID +0x1f4 LpcReplySemaphore : _KSEMAPHORE +0x1f4 KeyedWaitSemaphore : _KSEMAPHORE +0x208 LpcReplyMessage : (null) +0x208 LpcWaitingOnPort : (null) +0x20c ImpersonationInfo : (null) +0x210 IrpList : _LIST_ENTRY [ 0x0 - 0x0 ] +0x218 TopLevelIrp : 0 +0x21c DeviceToVerify : (null) +0x220 ThreadsProcess : (null) +0x224 StartAddress : (null) +0x228 Win32StartAddress : (null) +0x228 LpcReceivedMessageId : 0 +0x22c ThreadListEntry : _LIST_ENTRY [ 0x0 - 0x0 ] +0x234 RundownProtect : _EX_RUNDOWN_REF +0x238 ThreadLock : _EX_PUSH_LOCK +0x23c LpcReplyMessageId : 0 +0x240 ReadClusterSize : 0 +0x244 GrantedAccess : 0 +0x248 CrossThreadFlags : 0 +0x248 Terminated : 0y0 +0x248 DeadThread : 0y0 +0x248 HideFromDebugger : 0y0 +0x248 ActiveImpersonationInfo : 0y0 +0x248 SystemThread : 0y0 +0x248 HardErrorsAreDisabled : 0y0 +0x248 BreakOnTermination : 0y0 +0x248 SkipCreationMsg : 0y0 +0x248 SkipTerminationMsg : 0y0 +0x24c SameThreadPassiveFlags : 0 +0x24c ActiveExWorker : 0y0 +0x24c ExWorkerCanWaitUser : 0y0 +0x24c MemoryMaker : 0y0 +0x250 SameThreadApcFlags : 0 +0x250 LpcReceivedMsgIdValid : 0y0 +0x250 LpcExitThreadCalled : 0y0 +0x250 AddressSpaceOwner : 0y0 +0x254 ForwardClusterOnly : 0 '' +0x255 DisablePageFaultClustering : 0 ''
解析_EPROCESS
kd> dt _ePROCESS 0x8055ac20 nt!_EPROCESS +0x000 Pcb : _KPROCESS //第一個也是_KPROCESS +0x06c ProcessLock : _EX_PUSH_LOCK +0x070 CreateTime : _LARGE_INTEGER 0x0 +0x078 ExitTime : _LARGE_INTEGER 0x0 +0x080 RundownProtect : _EX_RUNDOWN_REF +0x084 UniqueProcessId : (null) //進程的ID +0x088 ActiveProcessLinks : _LIST_ENTRY [ 0x0 - 0x0 ]//雙向鏈表.其中指向了下一個_EPROCESS 鏈表的位置. 也就是+0x88的位置. 我們需要-0x88才到首地址. +0x090 QuotaUsage : [3] 0 +0x09c QuotaPeak : [3] 0 +0x0a8 CommitCharge : 0 +0x0ac PeakVirtualSize : 0 +0x0b0 VirtualSize : 0 +0x0b4 SessionProcessLinks : _LIST_ENTRY [ 0x0 - 0x0 ] +0x0bc DebugPort : (null) //調試事件.我們的進程如果把這個給空.那么任何調試器都會死.什么調試都不管用. +0x0c0 ExceptionPort : (null) +0x0c4 ObjectTable : (null) +0x0c8 Token : _EX_FAST_REF //令牌權限. 可以讓我們的ring3程序變為 system級別的進程.最高級別.比管理員級別還高.但是你ring0.弄這個就意義不大了.除非有特殊需求.而且這個也是病毒作者常用的. +0x0cc WorkingSetLock : _FAST_MUTEX +0x0ec WorkingSetPage : 0xbff80 +0x0f0 AddressCreationLock : _FAST_MUTEX +0x110 HyperSpaceLock : 0 +0x114 ForkInProgress : (null) +0x118 HardwareTrigger : 0 +0x11c VadRoot : (null) +0x120 VadHint : (null) +0x124 CloneRoot : (null) +0x128 NumberOfPrivatePages : 0 +0x12c NumberOfLockedPages : 0 +0x130 Win32Process : (null) +0x134 Job : (null) +0x138 SectionObject : (null) +0x13c SectionBaseAddress : (null) +0x140 QuotaBlock : (null) +0x144 WorkingSetWatch : (null) +0x148 Win32WindowStation : (null) +0x14c InheritedFromUniqueProcessId : (null) +0x150 LdtInformation : (null) +0x154 VadFreeHint : (null) +0x158 VdmObjects : (null) +0x15c DeviceMap : (null) +0x160 PhysicalVadList : _LIST_ENTRY [ 0x8055ad80 - 0x8055ad80 ] +0x168 PageDirectoryPte : _HARDWARE_PTE +0x168 Filler : 0 +0x170 Session : (null) +0x174 ImageFileName : [16] "" //進程名稱 +0x184 JobLinks : _LIST_ENTRY [ 0x0 - 0x0 ] +0x18c LockedPagesList : (null) +0x190 ThreadListHead : _LIST_ENTRY [ 0x0 - 0x0 ] +0x198 SecurityPort : (null) +0x19c PaeTop : (null) +0x1a0 ActiveThreads : 0 +0x1a4 GrantedAccess : 0 +0x1a8 DefaultHardErrorProcessing : 0 +0x1ac LastThreadExitStatus : 0n0 +0x1b0 Peb : (null) +0x1b4 PrefetchTrace : _EX_FAST_REF +0x1b8 ReadOperationCount : _LARGE_INTEGER 0x0 +0x1c0 WriteOperationCount : _LARGE_INTEGER 0x0 +0x1c8 OtherOperationCount : _LARGE_INTEGER 0x0 +0x1d0 ReadTransferCount : _LARGE_INTEGER 0x0 +0x1d8 WriteTransferCount : _LARGE_INTEGER 0x0 +0x1e0 OtherTransferCount : _LARGE_INTEGER 0x0 +0x1e8 CommitChargeLimit : 0 +0x1ec CommitChargePeak : 0 +0x1f0 AweInfo : (null) +0x1f4 SeAuditProcessCreationInfo : _SE_AUDIT_PROCESS_CREATION_INFO +0x1f8 Vm : _MMSUPPORT +0x238 LastFaultCount : 0 +0x23c ModifiedPageCount : 0 +0x240 NumberOfVads : 0 +0x244 JobStatus : 0 +0x248 Flags : 0x800 +0x248 CreateReported : 0y0 +0x248 NoDebugInherit : 0y0 +0x248 ProcessExiting : 0y0 +0x248 ProcessDelete : 0y0 +0x248 Wow64SplitPages : 0y0 +0x248 VmDeleted : 0y0 +0x248 OutswapEnabled : 0y0 +0x248 Outswapped : 0y0 +0x248 ForkFailed : 0y0 +0x248 HasPhysicalVad : 0y0 +0x248 AddressSpaceInitialized : 0y10 +0x248 SetTimerResolution : 0y0 +0x248 BreakOnTermination : 0y0 +0x248 SessionCreationUnderway : 0y0 +0x248 WriteWatch : 0y0 +0x248 ProcessInSession : 0y0 +0x248 OverrideAddressSpace : 0y0 +0x248 HasAddressSpace : 0y0 +0x248 LaunchPrefetched : 0y0 +0x248 InjectInpageErrors : 0y0 +0x248 VmTopDown : 0y0 +0x248 Unused3 : 0y0 +0x248 Unused4 : 0y0 +0x248 VdmAllowed : 0y0 +0x248 Unused : 0y00000 (0) +0x248 Unused1 : 0y0 +0x248 Unused2 : 0y0 +0x24c ExitStatus : 0n0 +0x250 NextPageColor : 0 +0x252 SubSystemMinorVersion : 0 '' +0x253 SubSystemMajorVersion : 0 '' +0x252 SubSystemVersion : 0 +0x254 PriorityClass : 0 '' +0x255 WorkingSetAcquiredUnsafe : 0 '' +0x258 Cookie : 0
至此.我們的表項就寫完了.下面可以通過這些表項.來寫我們自己的ReadProcessMemory了.
十丶實現自己的進程內存讀寫
現在我們要自己對進程的虛擬內存進行讀寫了.
PS: 我們要對CR3進行操作.如果不懂.可以查看前幾篇博客. 內存的分頁管理.進行了解CR3 分頁管理,點擊即可.
思路:
1.遍歷_EPROCESS. 遍歷進程.
2.通過PID.獲取指定進程的CR3. 我們知道.每個進程的CR3不一樣.倘若我們獲取了我們想操作進程的CR3.對其操作.其實就是操作指定進程的物理內存.
3.找到之后對CR3進行操作.
3.1 保存CR3寄存器的原值
3.2 關閉CR0的內存保護屬性,如果寫WriteProcessMemory的是否需要用到
3.3 修改CR3寄存器的原值
3.4寫你的核心代碼.比如給定一個虛擬內存.進行讀寫.
3.5 恢復CR3寄存器的原值
有了思路,我們就可以進行寫代碼的操作了.
1.內核驅動提供公共的接口.
讀取指定PID進程的物理內存:
NTSTATUS MyReadProcessMemory(DWORD dwPID, //指定進程的PID
DWORD dwAdddress, //指定進程的虛擬內存
DWORD dwSize, //指定進程虛擬內存的大小
PVOID lpBuff, //讀取內容的緩沖區
DWORD dwBufSize) //緩沖區的大小.
獲得指定進程PID的PDE
NTSTATUS GetProcessDirBase(DWORD dwPID, //指定進程的PID
PDWORD pDirBase); //傳入傳出參數.指定進程的PDE
沒有寫和三環進行通訊的代碼.只是0環開始測試.入口點調用這個.
MyReadProcessMemory的實現.
實現:
NTSTATUS MyReadProcessMemory(DWORD dwPID, DWORD dwAdddress, DWORD dwSize, PVOID lpBuff, DWORD dwBufSize) { DWORD dwDirBase; NTSTATUS status; DWORD dwOldDirBase; KdBreakPoint(); __try { status = GetProcessDirBase(dwPID, &dwDirBase); if (status != STATUS_SUCCESS) return status; __asm { cli //屏蔽中斷防止線程切換 mov eax, cr0 //關閉內存保護 and eax, not 10000h mov cr0, eax mov eax, cr3 // 保存CR3寄存器原來的值 mov dwOldDirBase, eax //切換CR3 mov eax, dwDirBase //切換CR3的值. CR3的值是我們獲取指定進程的PDE得出的 mov cr3, eax } //讀取內存 //ProbeForRead(dwAdddress, dwSize, 4); if ( dwSize > dwBufSize) //簡單的判斷 dwSize = dwBufSize; RtlCopyMemory(lpBuff, dwAdddress, dwSize);//讀取內存.拷貝到我們的緩沖區中. 因為CR3被更改了.所以說讀取的內存就是指定進程的內存.
__asm { mov eax, dwOldDirBase mov cr3, eax //恢復CR3 mov eax, cr0 //恢復內存保護 or eax, 10000h mov cr0, eax sti //恢復中斷 } } __except(EXCEPTION_EXECUTE_HANDLER ) { dprintf("[MyReadProcessMemory] MyReadProcessMemory __except \r\n"); } return STATUS_SUCCESS; }
GetProcessDirBase的實現.獲取PDE
NTSTATUS GetProcessDirBase(DWORD dwPID, PDWORD pDirBase) { PEPROCESS Process; PEPROCESS CurProcess; CHAR *pszImageName; DWORD dwCurPID; DWORD i; __try { //遍歷EPROCESS __asm { mov eax, fs:[124h] //ETHREAD // 首先 GDT首地址 + 0x120 獲取_KPCR的成員 PrcbData,而這個成員是_KPRCB結構體. 對其取內容加偏移0x4獲得CurrentThread
mov eax, [eax+44h] //EPROCESS *currentHREAD + 0x34 = APCSTATE 首地址*(APCSTATE) + 0X10 = eprocess ,找到外鍵_Eprocess.(也可以解釋為_KPROCESS)
mov Process, eax
}
CurProcess = Process;
i = 0;
do
{
pszImageName = (char*)CurProcess + 0x174; // dt 0x89d14020 _EPROCESS EPROCESS +0x174 = ImageFileName ,通過查表.得出EPROCESS + 0X174得出進程名
dwCurPID = (*(DWORD*)((char*)CurProcess + 0x084));// UniqueProcessId 進程ID, 的除了進程ID
dprintf("[MyReadProcessMemory] {%d} PID=%d ImageName:%s \r\n", i++, dwCurPID, pszImageName);
if (dwCurPID == dwPID) //判斷我們的ID.和遍歷的進程ID是否相等.
{
*pDirBase = (*(DWORD*)((char*)CurProcess + 0x018));//DirectoryTableBase = KPROCESS 0x18 //獲得PDE的值.給我們的參數復制.
dprintf("[MyReadProcessMemory] Find PID=%d DirBase:%p\r\n", dwCurPID, pDirBase);
return STATUS_SUCCESS;
}
CurProcess = (*(DWORD*)((char*)CurProcess + 0x088)) - 0x88; //下一個繼續循環. -0x88上面也說了.主要是鏈表位置的偏移在0x88,而_EPROCESS是在0位置.所以-0x88得出_EPROCESS的位置.
} while (CurProcess != Process);
}
__except(EXCEPTION_EXECUTE_HANDLER )
{
dprintf("[MyReadProcessMemory] GetProcessDirBase __except \r\n"); }
return STATUS_UNSUCCESSFUL;
}