NetRoc's Blog

N-Tech

 

另一种读写进程内存空间的方法

cc682

http://netroc682.spaces.live.com/

丢一个比较难防的读写其他进程内存空间的方法出来,嘿嘿。貌似我还没想到什么好办法可以防住的。

内存空间不能跨进程访问的原因主要在于不同进程都有自己的页目录和页表。进程切换的很大一块也就是切换掉页目录。

Windows自己的ReadProcessMemory最终也是通过KeStackAttachProcess附加到目标进程空间执行拷贝的。但是中间的N个内核函数调用现在被很多保护系统Hook掉并保护起来了,所以要通过这层层关卡读到东西还是不那么简单的。Unhook?自己实现内核函数?都很麻烦。

其实,仅仅是需要拿到目标进程的页目录,然后直接从目标地址拷贝出来而已,完全可以用简单得多的办法来达到这个目的。

所有的XXAttachProcess函数最终都通过_KiSwapProcess切换进程环境,_KiSwapProcess中会将目标进程的页目录指针放入CR3。这个过程我们可以自己来实现,唯一需要的就是拿到目标进程的EPROCESS而已,而这有无数种方法可以达到。整个过程基本上没有能当作非正常访问的证据以阻止掉的。

首先来看一下几个内核结构:

每个进程都有一个内核里面的进程结构_EPROCESS,前面几个字段如下:

typedef struct _EPROCESS {

KPROCESS Pcb;

EX_PUSH_LOCK ProcessLock;

……

Pcb中就包含我们需要的页目录地址:

typedef struct _KPROCESS {

DISPATCHER_HEADER Header;

LIST_ENTRY ProfileListHead;

ULONG_PTR DirectoryTableBase[2];

……

系统一般只使用DirectoryTableBase[0],DirectoryTableBase[1]是用于hyperspace的。我们只需要拿到目标进程里面的DirectoryTableBase[0]值并直接设置到CR3中就可以读取了。

假设要读取PID为1000的进程从0x400000开始的100个字节,只需要按下面的步骤实现:

  1. 获取PID=1000的进程的_EPROCESS,读取_EPROCESS::Pcb::DirectoryTableBase[0]
  2. 将页目录的值mov到cr3中。
  3. 直接将0x400000的地址memcpy到内核空间地址中
  4. 恢复cr3的值。

下面是我测试使用的代码片断,图方便使用了PsLookupProcessByProcessId来获取EPROCESS,实际上这个函数多半被处理过的,所以很可能拿不到。解决的方法之一是可以通过PsSetCreateProcessNotifyRoutine挂一个回调,在里面通过PsLookupProcessByProcessId获取每个进程的EPROCESS地址并缓存起来后面使用。

这是DispatchIoCtrl里面的

switch(irpStack->Parameters.DeviceIoControl.IoControlCode)

    {

    case IOCTL_READMEM:

        pstReadInfo = Irp->AssociatedIrp.SystemBuffer;

        ntStatus = STATUS_SUCCESS;

        if ( pstReadInfo->ulLen > 1000 ||

            irpStack->Parameters.DeviceIoControl.InputBufferLength < pstReadInfo->ulLen ||

            pstReadInfo->ulStart >= (PVOID)0x80000000 ||

            (ULONG)pstReadInfo->ulStart + pstReadInfo->ulLen >= 0x80000000)

        {

            DbgPrint( "Read range invalid\r\n");

            ntStatus = STATUS_UNSUCCESSFUL;

            break;

        }

        __try

        {                                    

            if (PsLookupProcessByProcessId((HANDLE)(pstReadInfo->ulPid),&pstEProcess) == STATUS_SUCCESS)

            {

                pbyBuff = (PUCHAR)ExAllocatePoolWithTag( NonPagedPool, pstReadInfo->ulLen, 'PMRD');

                ASSERT( pbyBuff);

            }

            else

            {

                DbgPrint( "PsLookupProcessByProcessId fail\r\n");

                ntStatus = STATUS_UNSUCCESSFUL;

                break;

            }

            ntStatus = ReadProcessMemory( pstEProcess, pbyBuff, pstReadInfo->ulStart, pstReadInfo->ulLen);

            if ( ntStatus == STATUS_SUCCESS)

            {

                RtlCopyMemory( pstReadInfo, pbyBuff, pstReadInfo->ulLen);

            }

            else

            {

                ntStatus = STATUS_UNSUCCESSFUL;

                break;

            }

            ExFreePool( pbyBuff);

            pbyBuff = NULL;

        }

        __except(1)

        {

            ntStatus=STATUS_UNSUCCESSFUL;

        }

        break;

……

这是读取内存的:

NTSTATUS ReadProcessMemory( PEPROCESS pstEProcess, PUCHAR pucBuff, PVOID pStart, ULONG ulLen)

{

    PKPROCESS pstKProcess = NULL;

    PEPROCESS pstCurrent = NULL;

    ULONG ulPDT = 0;

    ULONG ulOldCr3 = 0;

 

    pstKProcess = &pstEProcess->Pcb;

    ulPDT = pstKProcess->DirectoryTableBase[0];

 

    //load cr3

    _asm

    {

        cli;

        mov eax, cr3;

        mov ulOldCr3, eax;

        mov eax, ulPDT;

        mov cr3,eax

    }

    _asm sti;

    RtlCopyMemory( pucBuff, pStart, ulLen); //直接复制内存

    _asm cli;

    _asm

    {

        mov eax, ulOldCr3;

        mov cr3,eax;

        sti;

    }

    return STATUS_SUCCESS;

}

这个代码有些细节并没有处理,只是尝试了一下新的访问进程内存的方式。除了拿EPROCESS之外,中间没有使用任何系统函数,所以常规办法基本上是没有办法挡住的。进程保护技术看来实现起来难度还是很大啊。还是那句话,一点突破,满盘皆输。

posted on 2008-05-16 17:14 NetRoc 阅读(3235) 评论(15)  编辑 收藏 引用

评论

# re: 另一种读写进程内存空间的方法 2008-05-28 00:28 fly

能传上来源代码马 呵呵
  回复  更多评论   

# re: 另一种读写进程内存空间的方法 2008-05-28 10:14 NetRoc/cc682

@fly
呵呵,核心的代码都在里面了。我也只是试验用的,没有完善。如果需要的话留一个邮箱,我发给你吧。  回复  更多评论   

# re: 另一种读写进程内存空间的方法 2008-05-28 13:02 fly

taxueliuyun@sina.com 谢谢了 呵呵 能读取qq2008的内存马  回复  更多评论   

# re: 另一种读写进程内存空间的方法 2009-02-06 23:36 fixfix

可以给份源码我不

fixfix0011@126.com

我看雪ID也是这个  回复  更多评论   

# re: 另一种读写进程内存空间的方法 2009-02-13 12:52 小兵

请给份源码我,谢谢
188636329@qq.com  回复  更多评论   

# re: 另一种读写进程内存空间的方法 2009-02-15 17:11 derek

也请给份源码我,谢谢

derek125@qq.com  回复  更多评论   

# re: 另一种读写进程内存空间的方法 2009-02-26 10:26 lunlun

请给我份源码, 谢谢

lileiletter@gmailcom
  回复  更多评论   

# re: 另一种读写进程内存空间的方法 2009-04-24 15:18 RR

pxw2000@MSN.COM  回复  更多评论   

# re: 另一种读写进程内存空间的方法 2009-05-02 19:04 Post

能给我份源代码不?
ctperson@163.com
谢谢  回复  更多评论   

# re: 另一种读写进程内存空间的方法 2009-06-22 09:41 吴毅

credit_soft@msn.com ,向你学习,能传一份代码给我,谢谢  回复  更多评论   

# re: 另一种读写进程内存空间的方法 2009-09-09 17:29 jmp117

您好,关于这个文章的代码可以发一份吗?

邮箱: jmp117@126.com


想学习一下啦~~~谢谢!  回复  更多评论   

# re: 另一种读写进程内存空间的方法 2011-05-02 21:57 493114

求来学下,谢谢了:493114201@qq.com  回复  更多评论   

# # re:另一种读写进程内存空间的方法 2012-05-21 17:40 messiah

感谢分享! 想学习一下^0^ 谢谢!568344571@qq.com  回复  更多评论   

# re: 另一种读写进程内存空间的方法 2013-04-19 13:49 RK

这里有个问题
KPROCESS.DirectoryTableBase[0]里存的是页目录或者页目录索引的物理地址的高位,如何保证在读写的时候该进程的页目录在物理内存中?
另外关闭写保护和切换到操作最好提升IRQL,防止被APC之类的中断  回复  更多评论   

只有注册用户登录后才能发表评论。

导航

统计

常用链接

留言簿(7)

随笔档案(99)

文章分类(35)

文章档案(32)

Friends

Mirror

搜索

最新评论

阅读排行榜

评论排行榜