首页
社区
课程
招聘
[原创]不走寻常路,不修改代码段的DWM绘制
发表于: 2024-9-17 07:13 2728

[原创]不走寻常路,不修改代码段的DWM绘制

2024-9-17 07:13
2728

偶然发现dwm进程的Present函数调用_guard_dispatch_icall(控制流防护,微软很早就启用的一种安全防护机制,它主要是用于在发生间接跳转时,检查目标地址的合法性),这个函数在ntdll.dll里面(函数名是LdrpDispatchUserCallTarget),一般作为8字节函数地址被保存到PE的数据段。


那么就有趣起来了,直接替换数据段的函数地址来hook Present达到绘制的效果(也可以直接去ntdll里面挂钩),具体要怎么做呢?
具体流程为下:
1.拿到__guard_dispatch_icall_fptr,其内容替换成我们的函数指针
2.我们需要保存各个寄存器,在跳转回去之前恢复寄存器
3.通过返回地址判断是不是present,就是call cs:__guard_dispatch_icall_fptr + 6
4.SwapChain一般都是作为参数传进Present,直接寄存器拿
5.绘制完我们的东西后直接jmp rax或者调用ntdll的_guard_dispatch_icall

ok,流程已经很明白了,操作起来也是特别简单
获取__guard_dispatch_icall_fptr:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
auto d2d1 = (UINT64)GetModuleHandleA("d2d1.dll");
 
auto DrawingContext = FindPatternImage(d2d1, "\x48\x8D\x05\x00\x00\x00\x00\x33\xED\x48\x8D\x71\x08", "xxx????xxxxxx");
 
if (!DrawingContext) return FALSE;
 
DrawingContext += + *(int*)(DrawingContext + 3) + 7;
 
auto PresentMultiplaneOverlay = ((UINT64*)DrawingContext)[7];
 
auto LdrpDispatchUserCallTarget = FindPattern(PresentMultiplaneOverlay, 0x50, "\xFF\x15", "xx");
 
if (!LdrpDispatchUserCallTarget) return FALSE;
 
PresentCall = LdrpDispatchUserCallTarget + 6;
 
LdrpDispatchUserCallTarget += +*(int*)(LdrpDispatchUserCallTarget + 2) + 6;

修改为我们的函数指针:

1
2
3
4
5
6
7
8
auto Protect = 0ul;
 
if (VirtualProtect((PVOID)LdrpDispatchUserCallTarget, 8, PAGE_READWRITE, &Protect)) {
 
    _InterlockedExchangePointer((PVOID*)LdrpDispatchUserCallTarget, AsmLdrpDispatchUserCallTarget);
 
    return VirtualProtect((PVOID)LdrpDispatchUserCallTarget, 8, Protect, &Protect);
}

我们的代理函数汇编代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
AsmLdrpDispatchUserCallTarget PROC
 
    sub rsp, 100h
    mov [rsp + 00h], rax
    mov [rsp + 08h], rcx
    mov [rsp + 10h], rdx
    mov [rsp + 18h], rbx
    mov [rsp + 20h], rbp
    mov [rsp + 28h], rsi
    mov [rsp + 30h], rdi
    mov [rsp + 38h], r8
    mov [rsp + 40h], r9
    mov [rsp + 48h], r10
    mov [rsp + 50h], r11
    mov [rsp + 58h], r12
    mov [rsp + 60h], r13
    mov [rsp + 68h], r14
    mov [rsp + 70h], r15
 
    mov rax, [rsp + 100h]   ; return address
    mov rcx, [PresentCall]  ; call __guard_dispatch_icall_fptr + 6
 
    cmp rax, rcx
    jne JmpOriginal
 
    mov rcx, rdx            ; SwapChain
 
    sub rsp, 08h
    call Present
    add rsp, 08h
 
JmpOriginal:
    mov rax, [rsp + 00h]
    mov rcx, [rsp + 08h]
    mov rdx, [rsp + 10h]
    mov rbx, [rsp + 18h]
    mov rbp, [rsp + 20h]
    mov rsi, [rsp + 28h]
    mov rdi, [rsp + 30h]
    mov r8, [rsp + 38h]
    mov r9, [rsp + 40h]
    mov r10, [rsp + 48h]
    mov r11, [rsp + 50h]
    mov r12, [rsp + 58h]
    mov r13, [rsp + 60h]
    mov r14, [rsp + 68h]
    mov r15, [rsp + 70h]
 
    add rsp, 100h
 
    jmp rax
AsmLdrpDispatchUserCallTarget ENDP

Present函数原型:

1
2
3
4
5
6
7
8
9
__int64 __fastcall PresentMultiplaneOverlay(
    DrawingContext* this,
    struct IDXGISwapChainDWM1* a2,
    unsigned int a3,
    unsigned int a4,
    unsigned int a5,
    const void* a6,
    const struct _DXGI_PRESENT_MULTIPLANE_OVERLAY* a7,
    unsigned int a8);

先判断返回地址是否是call __guard_dispatch_icall_fptr + 6,是的话就跳到我们的Present函数,直接将SwapChain作为参数传给我们的Present函数。
至此hook已经完成,剩下的就是初始化绘制,推荐使用比较方便的ImGui,附上效果图:

测试环境:win10 22h2,win11需要hook别的函数,在dwmcore.dll里面
完整代码:https://github.com/Yukin02/Dwm-Overlay
帮忙点个星星呗~


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2024-9-17 17:03 被Mashiro~编辑 ,原因:
收藏
免费 8
支持
分享
最新回复 (20)
雪    币: 1810
活跃值: (4020)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
感谢分享
2024-9-17 09:47
0
雪    币: 9024
活跃值: (6245)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
3
思路很好,感谢分享
2024-9-17 16:24
0
雪    币: 0
活跃值: (81)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
牛逼 学废了
2024-9-17 20:12
0
雪    币: 220
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
5
NIUBI,上次看到这么好的东西还是在上次
2024-9-17 20:42
0
雪    币: 2541
活跃值: (1933)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
吴迪
2024-9-17 20:53
0
雪    币: 0
活跃值: (75)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
吴迪
2024-9-18 16:24
0
雪    币: 232
活跃值: (380)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
思路不错!
2024-9-18 17:46
0
雪    币: 1532
活跃值: (4588)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
9

dwm不跑这段代码咋办

2024-9-18 21:13
0
雪    币: 452
活跃值: (626)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
逆向爱好者 dwm不跑这段代码咋办
没进DrawContext::Present吧
2024-9-18 22:29
0
雪    币: 1532
活跃值: (4588)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
11
Mashiro~ 没进DrawContext::Present吧
都没跑,原本就不绘制
2024-9-19 07:31
0
雪    币: 452
活跃值: (626)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12

检查一下dwm的绘制提交流程, 给DrawContext::Present改一个字节,看看dwm会不会蹦

最后于 2024-9-19 09:23 被Mashiro~编辑 ,原因:
2024-9-19 09:19
0
雪    币: 452
活跃值: (626)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
逆向爱好者 都没跑,原本就不绘制
先看看dwm绘制提交走的哪个Present,源码里面的是DrawContext::Present,大部分win10都是走的这个函数
2024-9-19 09:22
0
雪    币: 1532
活跃值: (4588)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
14
你搜的函数在我电脑都没反应  
2024-9-19 11:02
0
雪    币: 452
活跃值: (626)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
逆向爱好者 你搜的函数在我电脑都没反应
那就找调用的present啊,我这个特征码是老外找的
2024-9-19 13:34
0
雪    币: 1532
活跃值: (4588)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
16
Mashiro~ 那就找调用的present啊,我这个特征码是老外找的
不会
2024-9-19 18:50
0
雪    币: 452
活跃值: (626)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
逆向爱好者 不会[em_18]
绘制提交的present不是在d2d1.dll就是在dwmcore.dll,拖到ida里面直接找present函数,然后ce挨个试,直接清空函数头部看看dwm进程会不会蹦,饭都喂到嘴上了
2024-9-19 19:00
0
雪    币: 1532
活跃值: (4588)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
18
Mashiro~ 绘制提交的present不是在d2d1.dll就是在dwmcore.dll,拖到ida里面直接找present函数,然后ce挨个试,直接清空函数头部看看dwm进程会不会蹦,饭都喂到嘴上了
函数名叫啥
2024-9-19 19:08
0
雪    币: 1282
活跃值: (4560)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
19
温馨提示:这个段是read only的噢
2024-9-19 20:31
0
雪    币: 11
活跃值: (802)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
rdata段只读的
2024-9-19 22:37
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
21
dwm不支持全屏是啥情况,我记得之前可以无边框,但是现在无边框也没效果,一弄成窗口就可以了
2024-10-22 15:52
0
游客
登录 | 注册 方可回帖
返回
//