首页 > 其他分享 >13.4 DirectX内部劫持绘制

13.4 DirectX内部劫持绘制

时间:2023-10-24 09:58:06浏览次数:34  
标签:劫持 return 函数 JmpCode 13.4 NULL 绘制 sizeof DirectX

相对于外部绘图技术的不稳定性,内部绘制则显得更加流程与稳定,在Dx9环境中,函数EndScene是在绘制3D场景后,用于完成将最终的图像渲染到屏幕的一系列操作的函数。它会将缓冲区中的图像清空,设置视口和其他渲染状态,执行顶点和像素着色器,最后在后台缓冲区中生成一张完整的渲染图像,然后将其呈现到屏幕上,完成一次绘制操作。

EndSceneIDirect3DDevice943个函数,我们通过对该函数进行挂钩,并将该函数绘制之前的流程劫持到自身进程内的MyEndScene函数内做图形的增加工作,当我们增加好所需功能后再将该函数指向原来的函数入口,此时EndScene函数再次渲染则会出现我们所新增的功能,利用这种方式即可实现屏幕图形绘制效果,至于笔者是如何确定该函数是第43个的,读者可以在IDirect3DDevice9上面右键查看定义,至此即可看到函数所在位置;

13.4.1 封装Hook劫持功能

首先要实现劫持需要封装钩子函数,如下代码片段则是一个简单通用的钩子结构体的封装,该结构体在此处其实是当作类来使用了,其中读者只需要调用JmpCode()函数则可自动将需要跳转的内存地址与JMP指令相结合,当有了跳转指令的机器码后,则我们只需要通过VirtualProtect设置内存属性为可写,并通过调用memcpy函数即可实现对特定内存的地址替换功能,如下代码中hook()函数用于挂钩,unhook()函数则用于摘除,代码比较通用读者可应用于任何一个领域。

// ---------------------------------------------------------------------------------
// 挂钩摘钩结构体
// ---------------------------------------------------------------------------------

#pragma pack(push)
#pragma pack(1)
#ifndef _WIN64
struct JmpCode
{
private:
    const BYTE jmp;
    DWORD address;

public:
    JmpCode(DWORD srcAddr, DWORD dstAddr) : jmp(0xE9)
    {
        setAddress(srcAddr, dstAddr);
    }

    void setAddress(DWORD srcAddr, DWORD dstAddr)
    {
        address = dstAddr - srcAddr - sizeof(JmpCode);
    }
};
#else
struct JmpCode
{
private:
    BYTE jmp[6];
    uintptr_t address;

public:
    JmpCode(uintptr_t srcAddr, uintptr_t dstAddr)
    {
        static const BYTE JMP[] = { 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00 };
        memcpy(jmp, JMP, sizeof(jmp));
        setAddress(srcAddr, dstAddr);
    }

    void setAddress(uintptr_t srcAddr, uintptr_t dstAddr)
    {
        address = dstAddr;
    }
};
#endif
#pragma pack(pop)

// ---------------------------------------------------------------------------------
// Hook挂钩与摘够函数
// ---------------------------------------------------------------------------------

// 开始Hook
int hook(void* originalFunction, void* hookFunction, BYTE* oldCode)
{
    JmpCode code((uintptr_t)originalFunction, (uintptr_t)hookFunction);
    DWORD oldProtect, oldProtect2;

    // 设置内存保护方式为可读写
    if (VirtualProtect(originalFunction, sizeof(code), PAGE_EXECUTE_READWRITE, &oldProtect))
    {
        memcpy(oldCode, originalFunction, sizeof(code));
        memcpy(originalFunction, &code, sizeof(code));

        // 恢复内存保护方式
        if (VirtualProtect(originalFunction, sizeof(code), oldProtect, &oldProtect2))
        {
            return 1;
        }
    }
    return 0;
}

// 取消Hook
int unhook(void* originalFunction, BYTE* oldCode)
{
    DWORD oldProtect, oldProtect2;

    // 设置保护方式为可读写
    if (VirtualProtect(originalFunction, sizeof(JmpCode), PAGE_EXECUTE_READWRITE, &oldProtect))
    {
        memcpy(originalFunction, oldCode, sizeof(JmpCode));

        // 恢复内存保护方式
        if (VirtualProtect(originalFunction, sizeof(JmpCode), oldProtect, &oldProtect2))
        {
            return 1;
        }
    }
    return 0;
}

13.4.2 定制MyEndScene

接着就是自定义绘图部分,此处第一个DrawBox绘图函数我们仅仅提供一个方框的绘制,如果需要更多绘制技巧读者可自行尝试实现,这里我们重点看一下MyEndScene函数,该函数是我们的自定义函数,当进程绘图函数被挂钩后,所有调用原函数的请求都会被路由到此函数内,进入此函数内首先通过g_font == NULL判断函数是不是第一次被调用如果是第一次被调用则对当前模块的字体绘制设备等进行初始化,而如果不是第一次绘制则自动流转到else片段内,此块区域内则是我们自己自由发挥的位置,如下代码中我们仅仅是绘制了一段话,并绘制出了两个方框,并没有做其他功能扩展。

void* endSceneAddr = NULL;
BYTE endSceneOldCode[sizeof(JmpCode)];

ID3DXFont* g_font = NULL;
ID3DXLine* d3dLine = NULL;

// ---------------------------------------------------------------------------------
// 绘图函数
// ---------------------------------------------------------------------------------

// 绘制方框
void DrawBox(float x, float y, float width, float height, float w, D3DCOLOR color)
{
    D3DXVECTOR2 points[5];
    points[0] = D3DXVECTOR2(x, y);
    points[1] = D3DXVECTOR2(x + width, y);
    points[2] = D3DXVECTOR2(x + width, y + height);
    points[3] = D3DXVECTOR2(x, y + height);
    points[4] = D3DXVECTOR2(x, y);
    d3dLine->SetWidth(w);
    d3dLine->Draw(points, 5, color);
}

// ---------------------------------------------------------------------------------
// Hook处理函数
// ---------------------------------------------------------------------------------

// 该函数是劫持后的转向函数,这里面可以增加功能
HRESULT STDMETHODCALLTYPE MyEndScene(IDirect3DDevice9* thiz)
{
    // 如果是第一次则初始化绘图库
    if (g_font == NULL)
    {
        // 初始化字体
        D3DXCreateFontA(thiz, 12, 0, FW_HEAVY, 1, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, ANTIALIASED_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "宋体", &g_font);

        // 线条初始化线条
        D3DXCreateLine(thiz, &d3dLine);

        // 临时摘除钩子
        unhook(endSceneAddr, endSceneOldCode);

        // 设置绘图设备
        HRESULT hr = thiz->EndScene();

        // 继续挂钩
        hook(endSceneAddr, MyEndScene, endSceneOldCode);
        return hr;
    }

    // 不是第一次则直接绘图
    else
    {
        // 自定义绘制流程
        // ---------------------------------------------------------------
        static RECT rect = { 0, 0, 200, 200 };

        // 屏幕写字
        g_font->DrawText(NULL, L"Inject D3D hook Success ...", -1, &rect, DT_TOP | DT_LEFT, D3DCOLOR_XRGB(255, 0, 0));

        // 屏幕绘制方框
        DrawBox(25, 30, 60, 120, 2, D3DCOLOR_XRGB(255, 0, 255));
        DrawBox(45, 60, 35, 70, 2, D3DCOLOR_XRGB(255, 69, 0));

        // ---------------------------------------------------------------

        // 恢复钩子
        unhook(endSceneAddr, endSceneOldCode);

        // 执行原函数
        HRESULT hr = thiz->EndScene();

        // 挂钩
        hook(endSceneAddr, MyEndScene, endSceneOldCode);
        return hr;
    }
}

13.4.3 初始化与绘制图形

继续向下则是initHookThread函数,该函数内我们自行创建了一个具有空类名的隐藏窗口,并通过调用Direct3DCreate9实现了对Dx9引擎的初始化,通过调用(*(void***)device)[42]的方式我们即可获取到当前内存中endSceneAddr的原始地址,有了这个地址则直接对其进行Hook替换,此时当有新的请求访问该函数时则会自动路由到MyEndSceneAddr函数内。

// 初始化Hook线程
DWORD WINAPI initHookThread(LPVOID dllMainThread)
{
    WaitForSingleObject(dllMainThread, INFINITE);
    CloseHandle(dllMainThread);

    WNDCLASSEX wc = {};
    wc.cbSize = sizeof(wc);
    wc.style = CS_OWNDC;
    wc.hInstance = GetModuleHandle(NULL);
    wc.lpfnWndProc = DefWindowProc;
    wc.lpszClassName = _T("LySharkWindow");

    // 注册窗口类
    if (RegisterClassEx(&wc) == 0)
    {
        return 0;
    }

    // 创建窗口
    HWND hwnd = CreateWindowEx(0, wc.lpszClassName, _T(""), WS_OVERLAPPEDWINDOW, 0, 0, 640, 480, NULL, NULL, wc.hInstance, NULL);
    if (hwnd == NULL)
    {
        return 0;
    }

    // 初始化D3D
    IDirect3D9* d3d9 = Direct3DCreate9(D3D_SDK_VERSION);
    if (d3d9 == NULL)
    {
        DestroyWindow(hwnd);
        return 0;
    }

    D3DPRESENT_PARAMETERS pp = {};
    pp.Windowed = TRUE;
    pp.SwapEffect = D3DSWAPEFFECT_COPY;

    // 创建设备
    IDirect3DDevice9* device;
    if (FAILED(d3d9->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &pp, &device)))
    {
        d3d9->Release();
        DestroyWindow(hwnd);
        return 0;
    }

    // 开始劫持 EndScene
    // EndScene是IDirect3DDevice9第43个函数
    endSceneAddr = (*(void***)device)[42];

    // 开始挂钩
    hook(endSceneAddr, MyEndScene, endSceneOldCode);

    // 释放
    d3d9->Release();
    device->Release();
    DestroyWindow(hwnd);
    return 0;
}

有了上述代码基础,接着读者只需要增加一个DLL头,在入口处通过DuplicateHandle得到当前线程的线程ID,并调用CreateThread创建新线程,此时劫持也就正式生效了。

// dll入口
BOOL APIENTRY DllMain(HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        HANDLE curThread;

        // 获取当前线程ID
        if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &curThread, SYNCHRONIZE, FALSE, 0))
        {
            return FALSE;
        }

        // DllMain中不能使用COM组件 所以要在另一个线程初始化
        CloseHandle(CreateThread(NULL, 0, initHookThread, curThread, 0, NULL));
        break;

    case DLL_PROCESS_DETACH:
        if (endSceneAddr != NULL)
        {
            unhook(endSceneAddr, endSceneOldCode);
        }
        break;
    }
    return TRUE;
}

至此,读者可使用任意一款注入软件将编译好的hook.dll文件注入到目标进程内,此时会发现窗体上新增加了一行文字和两个方框,至此绘制实现;

本文作者: 王瑞
本文链接: https://www.lyshark.com/post/ca456002.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

标签:劫持,return,函数,JmpCode,13.4,NULL,绘制,sizeof,DirectX
From: https://www.cnblogs.com/LyShark/p/17784031.html

相关文章

  • DHCP欺骗劫持与防御策略
    DHCP欺骗劫持与防御策略一、任务目的掌握DHCP的欺骗原理与DHCP监听配置二、任务设备、设施enspwin10VMwaretyporawin7三、任务拓扑结构图四、基本配置1.接口IP与默认路由配置(在这里同样可以使用ospf,加上反掩码效果一样)R1system-view[Huawei]sysnameR1[R1]inter......
  • 网站被劫持了怎么办
    网站被劫持了怎么办建议新建一个index.html文件,文件中只写几个数字,上传到网站根目录,然后访问网站域名,看看是不是正常,从而可以确定是程序问题还是域名被劫持的问题。如果是域名被劫持,你可以登录你的域名管理控制面板,检查一下解析记录是否正常,如果正常,建议更换dns服务商。如果是网......
  • 线程劫持-进程注入C++示例和检测思考
    线程劫持:运行方法C:\Users\l00379637\source\repos\thread_hijack\x64\Release\thread_hijack.exe18132C:\Users\l00379637\source\repos\injected_dll\x64\Release\injected_dll.dllProcessID:18132Injected!劫持效果: 劫持代码如下:#include<iostream......
  • vue2原理初探-数据代理和数据劫持
    本篇文章主要想简单聊聊vue如何实现数据修改,页面联动的底层原理。当然,篇幅有限,只是自己一些浅显的认知而已,我会从一下几个方面去聊,希望对你有所帮助。几个基础知识点数据代理数据劫持完整demo 一、几个基础知识点1.普通函数和箭头函数的区别我们知道,每个函数执行都会......
  • 权限提升-烂土豆&dll劫持&引号路径&服务权限
    必备知识点:#令牌窃取配合烂土豆提权单纯令牌窃取:web权限或本地提权如配合烂土豆提权:web或数据库等权限 #不带引号服务路径安全问题服务路径提权:web权限或本地提权 #不安全的服务权限配置问题服务权限配置:WEB权限或本地提权(web几率小) #补充说明:dll劫持提权及......
  • 移动端劫持应急
    PC端访问正常,移动端访问出现异常,比如插入弹窗、嵌入式广告和跳转到第三方网站,将干扰用户的正常使用,对用户体验造成极大伤害。现象描述部分网站用户反馈,手机打开网站就会跳转到赌博网站。问题处理访问网站首页,抓取到了一条恶意js:http://js.zadovosnjppnywuz.com/caonima.js我们......
  • glibc2.35-通过tls_dtor_list劫持exit执行流程
    前言glibc2.35删除了malloc_hook、free_hook以及realloc_hook,通过劫持这三个hook函数执行system已经不可行了。传统堆漏洞利用是利用任意地址写改上上述几个hook从而执行system,在移除之后则需要找到同样只需要修改某个地址值并且能够造成程序流劫持的效果。__call_tls_dtors在......
  • glibc2.35-通过tls_dtor_list劫持exit执行流程
    前言glibc2.35删除了malloc_hook、free_hook以及realloc_hook,通过劫持这三个hook函数执行system已经不可行了。传统堆漏洞利用是利用任意地址写改上上述几个hook从而执行system,在移除之后则需要找到同样只需要修改某个地址值并且能够造成程序流劫持的效果。__call_tls_dtors在程序......
  • Node.Js 13.4.18 Docker 部署后无法访问
    将项目Next.js升级后,通过Docker部署无法访问到服务,通过dockerlogsid检查日志,发现没有任何反应,甚至一个访问请求都没看到。一开始怀疑是打包的问题,将Docker容器中的服务文件全部拉下来,在本地跑..诶嘿~正常运行...排除打包问题检查启动日志:readystartedserveron......
  • 设置 X-Frame-Options HTTP 响应头防止点击劫持攻击
    X-Frame-Options是一个HTTP响应头,设置X-Frame-OptionsHTTP响应头为DENY或SAMEORIGIN,用于控制页面是否可以被嵌入到<iframe>,<frame>,<embed>,或<object>等元素中。这有助于防止点击劫持攻击。DENY或SAMEORIGIN分别是什么意思?DENY:当设置为DENY时,页面不......