ReadProcessMemory调用过程


ReadProcessMemory调用过程

基本用法

根据进程读句柄取内存空间

BOOL ReadProcessMemory(
    HANDLE hProcess,  //进程句柄
    LPCVOID lpBaseAddress, // 内存地址
    LPVOID lpBuffer, // 接收的内容,缓冲区指针
    DWORD nSize, // 读取的字节数,如果为0直接返回
    LPDWORD lpNumberOfBytesRead //指向传输到指定缓冲区的字节数的指针
                             //如果lpNumberOfBytesRead为空,则忽略该参数
)//  //成功返回1 失败返回0

函数分析

ReadProcessMemory函数

由反汇编可以看出,ReadProcessMemory函数直接调用了NtReadVirtualMemory,而NtReadVirtualMemory在ntdll.dll中,ntdll又与ntoskrnl.exe对应,在ntoskrnl.exe中找到NtReadVirtualMemory

NtReadVirtualMemory

经过一系列初始化,函数调用了MmCopyVirtualMemory

push    eax      // HandleInformation
lea     eax, [ebp+Object]
push    eax      // Object
push    dword ptr [ebp+AccessMode] // AccessMode
push    _PsProcessType  // ObjectType
push    10h      // DesiredAccess
push    [ebp+ProcessHandle] // Handle
call    _ObReferenceObjectByHandle@24 // 跟据进程句柄获取EPROCESS结构的指针
mov     [ebp+var_Eprocess], eax
test    eax, eax 
jnz     short loc_4B1592 //如果_ObReferenceObjectByHandle返回0 则调用_MmCopyVirtualMemory
lea     eax, [ebp+var_size]
//--------------------------------------传入参数-------------------------------------
push    eax      // BytesRead
push    dword ptr [ebp+AccessMode] // AccessMode
push    esi      // NumberOfBytesToRead
push    [ebp+Buffer]    // Buffer
push    [edi+_KTHREAD.ApcState.Process] // CurrentProcess
push    [ebp+BaseAddress] // BaseAddress
push    [ebp+Object]    // Process
//--------------------------------------传入参数-------------------------------------
call    _MmCopyVirtualMemory@28 // // 调用_MmCopyVirtualMemory
mov     [ebp+var_Eprocess], eax
mov     ecx, [ebp+Object] // Object
call    @ObfDereferenceObject@4 // ObfDereferenceObject(x)

MmCopyVirtualMemory

mov     edi, edi
push    ebp
mov     ebp, esp
cmp     [ebp+Length], 0 //如果要读取的长度为0则直接返回
jz      loc_526787
push    ebx
mov     ebx, [ebp+process]
mov     ecx, ebx
mov     eax, large fs:_KPCR.PrcbData.CurrentThread
cmp     ebx, [eax+_KTHREAD.ApcState.Process] 
jnz     short loc_4A22F8
mov     ecx, [ebp+CurrentProcess]//如果目的线程和当前线程相同,将目标线程指针替换成当前线程

loc_4A22F8:     
add     ecx, 80h //    RunRef
mov     [ebp+process], ecx
call    @ExAcquireRundownProtection@4
test    al, al
jz      loc_52678E
cmp     [ebp+Length], 1FFh
push    esi //保存esi环境
push    edi //保存edi环境
mov     edi, [ebp+arg_18]
ja      loc_4ADEF1  //如果要读取的长度大于1FF,则调用_MiDoMappedCopy

loc_4A2320:      
push    edi      // BytesRead
push    dword ptr [ebp+AccessMode]
push    [ebp+Length]    // Length
push    [ebp+Address]   // Address
push    [ebp+CurrentProcess] // TAEGET_PROCESS
push    [ebp+BaseAddress] // int
push    ebx      // PRKPROCESS
call    _MiDoPoolCopy@28 // // 否则调用_MiDoPoolCopy
mov     esi, eax

loc_4A2338:     
mov     ecx, [ebp+process] // RunRef
call    @ExReleaseRundownProtection@4 // ExReleaseRundownProtection(x)
pop     edi
mov     eax, esi
pop     esi

loc_4A2344:    
pop     ebx

loc_4A2345:     
pop     ebp
retn    1Ch

MiDoMappedCopy

push    0ACh
push    offset stru_41C3D0
call    __SEH_prolog
xor     edi, edi
mov     [ebp+Status], edi // STATUS_SUCCESS
mov     eax, [ebp+arg_BaseAddress]
mov     [ebp+CurrentAddress], eax
mov     eax, [ebp+Address]
mov     [ebp+TargetAddress], eax
mov     eax, 0E000h     // MI_MAPPED_COPY_PAGES * PAGE_SIZE
bufferSize = ecx
TotalSize = eax
mov     bufferSize, [ebp+Length]
cmp     bufferSize, TotalSize
ja      short loc_4ADD8A // 如果要读取的长度小于等于0xe000,则将要读取的长度设定为0xe000
mov     TotalSize, bufferSize

loc_4ADD8A:      
lea     ebx, [ebp+MemoryDescriptorList] // MDL是用来建立一块虚拟地址空间与物理页面之间的映射
mov     [ebp+MDL], ebx
mov     [ebp+remainingSize], bufferSize
mov     [ebp+CurrentSize], TotalSize
mov     [ebp+FailedInProbe], edi
mov     [ebp+BadAddress], edi // 0
mov     [ebp+HaveBadAddress], edi // 0

loc_4ADDA2:     
mov     eax, [ebp+remainingSize] // while(remainingSize > 0)
cmp     eax, edi
jbe     loc_4ADEDF      // return
cmp     eax, [ebp+CurrentSize]
jb      loc_50DFB6      // 如果RemainingSize < CurrentSize,则CurrentSize = RemainingSize

loc_4ADDB6:     
lea     eax, [ebp+ApcState]
push    eax      // ApcState
push    [ebp+PROCESS]   // PROCESS
call    _KeStackAttachProcess@8 // 将本进程附加到目标进程中
mov     [ebp+BaseAddress], edi
mov     [ebp+PagesLocked], edi
mov     [ebp+var_4C], edi
mov     [ebp+ms_exc.registration.TryLevel], edi
mov     eax, [ebp+arg_BaseAddress]
xor     esi, esi
inc     esi
cmp     [ebp+CurrentAddress], eax
True = esi
jnz     short loc_4ADE05 // 初始化MemoryDescriptorList
cmp     [ebp+AccessMode], 0
jz      short loc_4ADE05 // 初始化MemoryDescriptorList
mov     [ebp+FailedInProbe], True
mov     eax, [ebp+Length]
cmp     eax, edi
jz      short loc_4ADE02
mov     ecx, [ebp+arg_BaseAddress]
add     eax, ecx
cmp     eax, ecx
jb      loc_50DFBE
cmp     eax, _MmUserProbeAddress
ja      loc_50DFBE

loc_4ADE02:      
mov     [ebp+FailedInProbe], edi

loc_4ADE05:      
mov     [ebx+_MDL.Next], edi // 初始化MemoryDescriptorList
mov     eax, [ebp+CurrentAddress]
and     eax, 0FFFh
mov     ecx, [ebp+CurrentSize]
lea     edx, [eax+ecx+0FFFh]
shr     edx, 0Ch
lea     edx, [edx*4+1Ch]
mov     [ebx+_MDL.Size], dx
mov     [ebx+_MDL.MdlFlags], di
mov     edx, [ebp+CurrentAddress]
and     edx, 0FFFFF000h
mov     [ebx+_MDL.StartVa], edx
mov     [ebx+_MDL.ByteOffset], eax
mov     [ebx+_MDL.ByteCount], ecx
push    edi      // Operation
push    dword ptr [ebp+AccessMode] // AccessMode
push    ebx      // MemoryDescriptorList
call    _MmProbeAndLockPages@12 // MmProbeAndLockPages(x,x,x)
mov     [ebp+PagesLocked], True
push    20h      // Priority -> HighPagePriority
push    edi      // BugCheckOnFailure -> False
push    edi      // RequestedAddress -> NULL
push    esi      // CacheType -> MmCached
push    edi      // AccessMode -> KernelMode
push    ebx      // MemoryDescriptorList
call    _MmMapLockedPagesSpecifyCache@24 
mov     [ebp+BaseAddress], eax
cmp     eax, edi
jz      loc_526621

loc_4ADE61: 
lea     eax, [ebp+ApcState]
push    eax      // ApcState
call    _KeUnstackDetachProcess@4 // KeUnstackDetachProcess(x)
lea     eax, [ebp+ApcState]
push    eax      // ApcState
push    [ebp+CurrentProcess] // PROCESS
call    _KeStackAttachProcess@8 // KeStackAttachProcess(x,x)
mov     eax, [ebp+arg_BaseAddress]
cmp     [ebp+CurrentAddress], eax
jnz     short loc_4ADE96
cmp     [ebp+AccessMode], 0
jz      short loc_4ADE96
mov     [ebp+FailedInProbe], esi
push    esi      // Alignment
push    [ebp+Length]    // Length
push    [ebp+Address]   // Address
call    _ProbeForWrite@12 // ProbeForWrite(x,x,x)
mov     [ebp+FailedInProbe], edi

loc_4ADE96:      

mov     [ebp+var_4C], esi
mov     ecx, [ebp+CurrentSize]
mov     esi, [ebp+BaseAddress]
mov     edi, [ebp+TargetAddress]
mov     eax, ecx
shr     ecx, 2
rep movsd
mov     ecx, eax
and     ecx, 3
rep movsb
or      [ebp+ms_exc.registration.TryLevel], 0FFFFFFFFh
lea     eax, [ebp+ApcState]
push    eax      // ApcState
call    _KeUnstackDetachProcess@4 // KeUnstackDetachProcess(x)
push    ebx      // MemoryDescriptorList
push    [ebp+BaseAddress] // BaseAddress
call    _MmUnmapLockedPages@8 // MmUnmapLockedPages(x,x)
push    ebx      // MemoryDescriptorList
call    _MmUnlockPages@4 // MmUnlockPages(x)
mov     eax, [ebp+CurrentSize]
sub     [ebp+remainingSize], eax
add     [ebp+CurrentAddress], eax
add     [ebp+TargetAddress], eax
xor     edi, edi
jmp     loc_4ADDA2      // while(remainingSize > 0)
// ---------------------------------------------------------------------------

loc_4ADEDF:     
mov     eax, [ebp+BytesRead]
mov     ecx, [ebp+Length]
mov     [eax], ecx
xor     eax, eax

loc_4ADEE9:     
call    __SEH_epilog
retn    1Ch

MiDoPoolCopy

TotalSize = edi

push    248h
push    offset stru_41D0D8
call    __SEH_prolog
mov     eax, large fs:_KPCR.PrcbData.CurrentThread
mov     eax, [ebp+SourceAddress]
mov     [ebp+CurrentAddress], eax
mov     eax, [ebp+TargetAddress]
mov     [ebp+CurrentTargetAddress], eax
mov     eax, 10000h     // MI_MAX_TRANSFER_SIZE = 64 * 1024
mov     TotalSize, eax
cmp     [ebp+BufferSize], eax // if (BufferSize <= MI_MAX_TRANSFER_SIZE) TotalSize = BufferSize//
ja      short loc_4AD3B7
mov     TotalSize, [ebp+BufferSize]

loc_4AD3B7:      
and     [ebp+HavePoolAddress], 0
mov     esi, 200h// MI_POOL_COPY_BYTES
cmp     [ebp+BufferSize], esi
ja      loc_5266C7      // ExAllocatePoolWithTag

loc_4AD3C9:      
lea     eax, [ebp+StackBuffer]
mov     [ebp+PoolAddress], eax

loc_4AD3D2:      
xor     eax, eax
mov     [ebp+Status], eax
mov     [ebp+_], eax
mov     ecx, [ebp+BufferSize]
mov     [ebp+RemainingSize], ecx
mov     ebx, TotalSize
mov     [ebp+FailedInProbe], eax // STATUS_SUCCESS

loc_4AD3E5:      
xor     eax, eax
cmp     [ebp+RemainingSize], eax
ja      loc_4AD496      // RemainingSize < CurrentSize
cmp     [ebp+HavePoolAddress], eax
jnz     loc_526779      // return

loc_4AD3F9:      
mov     eax, [ebp+ReturnSize]
mov     ecx, [ebp+BufferSize]
mov     [eax], ecx
xor     eax, eax

loc_4AD403:      
 // MiDoPoolCopy(x,x,x,x,x,x,x)+793ED↓j
call    __SEH_epilog
retn    1Ch
// ---------------------------------------------------------------------------

loc_4AD40B:     
 // MiDoPoolCopy(x,x,x,x,x,x,x)+13C↓j ...
mov     ecx, ebx // RtlCopyMemory -> memcpy
mov     esi, [ebp+CurrentAddress]
mov     edi, [ebp+PoolAddress]
mov     eax, ecx
shr     ecx, 2
rep movsd // 把ESI的内容拷贝到EDI中
 // 把数据读到临时空间,这个临时空间是在内核堆栈或内核的内存中,即高2G的位置
mov     ecx, eax
and     ecx, 3
rep movsb // 对齐
lea     eax, [ebp+ApcState]
push    eax      // ApcState
call    _KeUnstackDetachProcess@4 // KeUnstackDetachProcess(x)
lea     eax, [ebp+ApcState]
push    eax      // ApcState
push    [ebp+TAEGET_PROCESS] // PROCESS
call    _KeStackAttachProcess@8 // KeStackAttachProcess(x,x)
mov     eax, [ebp+SourceAddress]
cmp     [ebp+CurrentAddress], eax
jnz     loc_4E24CD
cmp     [ebp+PreviousMode], 0
jz      loc_4E24CD
xor     esi, esi
inc     esi
mov     [ebp+FailedInProbe], esi
push    esi      // Alignment
push    [ebp+BufferSize] // Length
push    [ebp+TargetAddress] // Address
call    _ProbeForWrite@12 // ProbeForWrite(x,x,x)
and     [ebp+FailedInProbe], 0

loc_4AD462:   
mov     [ebp+var_3C], esi
mov     ecx, ebx
mov     esi, [ebp+PoolAddress]
mov     edi, [ebp+CurrentTargetAddress]
mov     eax, ecx
shr     ecx, 2
rep movsd
mov     ecx, eax
and     ecx, 3
rep movsb
or      [ebp+Exception.registration.TryLevel], 0FFFFFFFFh
lea     eax, [ebp+ApcState]
push    eax      // ApcState
call    _KeUnstackDetachProcess@4 // KeUnstackDetachProcess(x)
sub     [ebp+RemainingSize], ebx
add     [ebp+CurrentAddress], ebx
add     [ebp+CurrentTargetAddress], ebx
jmp     loc_4AD3E5
// ---------------------------------------------------------------------------

loc_4AD496:      
cmp     [ebp+RemainingSize], ebx
jb      loc_5266E7      // CurrentSize = RemainingSize//

loc_4AD49F:    
0 = esi
lea     eax, [ebp+ApcState]
push    eax      // ApcState
push    [ebp+SourceProcess] // PROCESS
call    _KeStackAttachProcess@8 // KeStackAttachProcess(x,x)
xor     esi, esi
mov     [ebp+var_3C], 0
mov     [ebp+Exception.registration.TryLevel], 0
mov     ecx, [ebp+SourceAddress]
cmp     [ebp+CurrentAddress], ecx // (CurrentAddress == SourceAddress) && (PreviousMode != KernelMode)
jnz     loc_4AD40B      // RtlCopyMemory -> memcpy
cmp     [ebp+PreviousMode], 0 //  KernelMode
jz      loc_4AD40B      // RtlCopyMemory -> memcpy
mov     [ebp+FailedInProbe], 1
mov     eax, [ebp+BufferSize]
cmp     eax, 0
jz      short loc_4AD4ED
add     eax, ecx
cmp     eax, ecx
jb      loc_4E24D5
cmp     eax, _MmUserProbeAddress
ja      loc_4E24D5

loc_4AD4ED:     
mov     [ebp+FailedInProbe], esi
jmp     loc_4AD40B      // RtlCopyMemory -> memcpy

KeStackAttachProcess

mov     edi, edi
push    ebp
mov     ebp, esp
push    esi
push    edi
mov     eax, large fs:_KPCR.PrcbData.CurrentThread
mov     esi, eax
mov     eax, large fs:_KPCR.PrcbData.DpcRoutineActive
test    eax, eax
jnz     loc_44A944
mov     edi, [ebp+PROCESS]
cmp     [esi+_KTHREAD.ApcState.Process], edi
jz      loc_41D0C8
xor     ecx, ecx
call    ds:__imp_@KeAcquireQueuedSpinLockRaiseToSynch@4 
cmp     [esi+_KTHREAD.ApcStateIndex], OriginalApcEnvironment
mov     byte ptr [ebp+PROCESS], al
jnz     loc_431CA8 //如果当前线程以及被附加上,则直接将ApcState传入_KiAttachProcess,否则传入SavedApcState
lea     eax, [esi+_KTHREAD.SavedApcState]
push    eax
push    [ebp+PROCESS]
push    edi
push    esi
call    _KiAttachProcess@16 // 把其他进程加载到本进程
mov     eax, [ebp+ApcState]
and     [eax+_KAPC_STATE.Process], 0

loc_41CFEE:      
pop     edi
pop     esi
pop     ebp
retn    8

loc_41D0C8:  
mov     eax, [ebp+ApcState]
mov     [eax+_KAPC_STATE.Process], 1
jmp     loc_41CFEE

KiAttachProcess

只要操作进程内存就会调用KiAttachProcess函数,hook KiAttachProcess函数即可知道哪个线程中的进程进来的

mov     edi, edi
push    ebp
mov     ebp, esp
push    ebx
push    esi
mov     esi, [ebp+Thread]
push    edi
push    [ebp+SaveApcState]
mov     edi, [ebp+Process]
inc     [edi+_EPROCESS.Pcb.StackCount] // 当前进程内存中线程的栈计数+1
lea     ebx, [esi+_KTHREAD.ApcState] // ApcListHead[KernelMode]
push    ebx
call    _KiMoveApcState@8 // SaveApcState = ApcState,备份ApcState
mov     [ebx+_LIST_ENTRY.Blink], ebx
mov     [ebx+_LIST_ENTRY.Flink], ebx
lea     eax, [esi+(_KTHREAD.ApcState.ApcListHead.Flink+8)] // ApcListHead[UserMode]  8 = sizeof(LIST_ENTRY) * 1
mov     [eax+_LIST_ENTRY.Blink], eax
mov     [eax+_LIST_ENTRY.Flink], eax
lea     eax, [esi+_KTHREAD.SavedApcState]
cmp     [ebp+SaveApcState], eax
mov     [esi+_KTHREAD.ApcState.Process], edi // 更改为要读取的进程
mov     [esi+_KTHREAD.ApcState.KernelApcInProgress], 0
mov     [esi+_KTHREAD.ApcState.KernelApcPending], 0
mov     [esi+_KTHREAD.ApcState.UserApcPending], 0
jnz     short loc_41A519 // 判断进程是否在内存中
mov     [esi+_KTHREAD.ApcStatePointer], eax // 14C SavedApcState 替换指针的位置
mov     [esi+(_KTHREAD.ApcStatePointer+4)], ebx // 34 ApcState
mov     [esi+_KTHREAD.ApcStateIndex], AttechedApcEnvironment // 改成挂靠的APC

loc_41A519:      // CODE XREF: KiAttachProcess(x,x,x,x)+43↑j
cmp     [edi+_KPROCESS.State], ProcessInMemory
jnz     loc_44A8A2      // 判断是否在内存中
lea     esi, [edi+40h]

loc_41A526:      // CODE XREF: KiAttachProcess(x,x,x,x)+303D7↓j
mov     eax, [esi+_LIST_ENTRY.Flink]
cmp     eax, esi
jnz     loc_44A87F      // 判断线程的等待链表是否为空
mov     eax, [ebp+SaveApcState]
push    [eax+_KAPC_STATE.Process]
push    edi
call    _KiSwapProcess@8 // KiSwapProcess(x,x)
mov     cl, [ebp+ApcLock]
call    @KiUnlockDispatcherDatabase@4 // KiUnlockDispatcherDatabase(x)

loc_41A544:      // CODE XREF: 0044A89D↓j
 // KiAttachProcess(x,x,x,x)+30463↓j
pop     edi
pop     esi
pop     ebx
pop     ebp
retn    10h

文章作者: Kevin。
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Kevin。 !
评论
 上一篇
Electron写一个API测试工具 Electron写一个API测试工具
简介这个项目是作为我的毕业设计来写的,粗略地实现了HTTP/HTTPS请求的功能,包括请求体、请求头、Get参数、Auth认证、CookieJar、代理、重定向、超时、自定义请求方式等,请求的模块由nodejs中的http、https模块封
2022-07-03
下一篇 
Windows APC机制 Windows APC机制
Windows APC机制APC(Asynchronous Procedure Call),即异步过程调用,也称不确定调用。Windows的APC机制本质上是一种对于应用软件(线程)的“软件中断”机制。与DPC不同,APC是针对线程的,每个
2022-06-24
  目录