nvidia截图分析
n卡截图是通过dwm实现的
其中nvwgf2umx.dll为n卡用户层模块,nvspcap64.dll为n卡截图模块
nvspcap64.dll模块导出了三个函数,其中QueryShadowPlayDdiShimInterface可以查询到接口
可以发现导出一堆代理函数,n卡驱动会调用,其中两很重要
1.CreateSession_V2创建实例
2.DdiPrePresent_V2 present的前回调函数(截图部分)
先看CreateSession_V2
Instance = CShadowPlayProxyShim__CreateInstance();
__int64 __fastcall sub_18003B480(__int64 a1)
{
__int64 v2; // rax
__int64 v3; // rax
CHAR MultiByteStr[272]; // [rsp+70h] [rbp-548h] BYREF
WCHAR Filename[264]; // [rsp+180h] [rbp-438h] BYREF
WCHAR WideCharStr[264]; // [rsp+390h] [rbp-228h] BYREF
*(_QWORD *)a1 = &ShadowPlayDdiShim_vtable;
一直追可以找到 vtable instance 这个后面会经常用到
下面可以研究DdiPrePresent_V2 中是怎么截图的了
return (*(__int64 (__fastcall **)(__int64, __int64 *))(*(_QWORD *)a1 + 0x40LL))(a1, v14);
分析其中调用了一个虚函数,根据刚才的虚函数表找到函数
if ( *(_QWORD *)(v46 + 8) )
{
if ( (unsigned __int8)CSPCaptureScreenShot__Initialise(*(_QWORD *)(a1 + 80), (__int64)PerformanceCount) )
{
CurrentProcessId = GetCurrentProcessId();
if ( *(_BYTE *)(v46 + 56) )
{
v45 = sub_18000BDAC(v46, CurrentProcessId, v47);
goto LABEL_116;
}
}
else
{
sub_180001C21(v48);
sub_180008F6C(7LL, "CSPCaptureScreenShot::CaptureScreenShotWrapper: ScreenShot Initialize failed");
}
}
其中截图部分,通过文字可以知道,sub_18000BDAC是真正的截图部分。
进入,截图函数
v13 = sub_180003E22(v12, 0LL, 0LL);
LABEL_16:
v14 = v13;
if ( !v13 )
goto LABEL_38;
v15 = *(_QWORD *)(a1 + 8);
v25[7] = v13 + 160;
v16 = (*(__int64 (__fastcall **)(__int64, _QWORD *))(*(_QWORD *)v15 + 8LL))(v15, v25);
if ( v16 )
{
dword_1802BFF70 = 7;
if ( dword_1802C00AC <= 7 )
sub_18000A1D7(
&off_1802BFF60,
"CSPCaptureScreenShot::CaptureScreenShot: Screenshot Capture Frame Failed(0x%X)",
v16);
if ( *(_QWORD *)a1 )
{
sub_180009CC3(a1);
return 0;
}
return 0;
}
其中v13是共享buffer,v25[7] = v13 + 160是buffer中的截图部分,下面需要追v16 = (*(__int64 (__fastcall **)(__int64, _QWORD *))(*(_QWORD *)v15 + 8LL))(v15, v25)
这个函数是哪里来的。v15 = (_QWORD )(a1 + 8);
可知v15是a1的一个成员。
回到DdiPrePresent_V2
v45 = sub_18000BDAC(v46, CurrentProcessId, v47);
v46 = *(_QWORD *)(instance + 80);
发现截图函数的a1是instance的偏移80部分,其实a1是CSPCaptureScreenShot的对象。
往上找有这么一行
*(_QWORD *)(*(_QWORD *)(instance + 80) + 8LL) = *(_QWORD *)(*(_QWORD *)(instance + 64) + 24LL);
继续向上
v43 = CSPRendererManager__GetRenderer(*(_QWORD *)(instance + 56), *(_QWORD *)(a2 + 8), 0, 0, 0LL, 0);
*(_QWORD *)(instance + 64) = v43;
可是偏移64是CSPRendererManager,分析CSPRendererManager__GetRenderer
sub_18002DC50
*(_BYTE *)(v15 + 16) = 0;
*(_BYTE *)(v15 + 37) = 0;
*(_QWORD *)(v15 + 8) = a3;
*(_QWORD *)v15 = a2;
*(_DWORD *)(v15 + 20) = a4;
*(_QWORD *)(v15 + 24) = a5;
*(_DWORD *)(v15 + 32) = a6;
*(_BYTE *)(v15 + 46) = 0;
*(_QWORD *)(v15 + 48) = 0LL;
*(_BYTE *)(v15 + 36) = 0;
*(_QWORD *)(v15 + 38) = 0LL;
可知v15 + 24也就是截图调用的对象为 a5
向上查找引用可知sub_18003E640
v43 = CSPRendererManager__GetRenderer(
v36,
*(_QWORD *)(a2 + 8),
*(_QWORD *)(a2 + 24),
*(_DWORD *)(a2 + 40),
*(_QWORD *)(a2 + 32),
*(_DWORD *)(a2 + 16));
其中截图对象为a2 + 32
向上查找分析是个虚函数调用.rdata:0000000180247A88 dq offset sub_18000353F
Instance = CShadowPlayProxyShim__CreateInstance_();
if ( Instance )
{
HIDWORD(CreateSession_V2_Args[0]) = 0;
HIDWORD(CreateSession_V2_Args[2]) = 0;
HIDWORD(CreateSession_V2_Args[5]) = 0;
sub_180008959((__int64)v39, 0LL, 926LL);
v25 = a2[14] == 0;
v26 = (int *)*((_QWORD *)a2 + 6);
CreateSession_V2_Args[1] = *((_QWORD *)a2 + 1);
v37 = !v25;
CreateSession_V2_Args[3] = *((_QWORD *)a2 + 3);
LODWORD(CreateSession_V2_Args[2]) = a2[4];
LODWORD(CreateSession_V2_Args[5]) = a2[8];
LODWORD(CreateSession_V2_Args[0]) = *a2;
v38 = *(_BYTE *)(v9 + 91);
LODWORD(CreateSession_V2_Args[6]) = a2[17];
HIDWORD(CreateSession_V2_Args[6]) = v35[0];
CreateSession_V2_Args[4] = v5;
if ( v26 )
sub_180001203(v39, 100LL, v26, -1LL);
if ( (unsigned int)*a2 >= 0x70340 )
{
if ( *((_WORD *)a2 + 156) )
sub_180001203(v40, 260LL, a2 + 78, -1LL);
v26 = a2 + 28;
if ( *((_WORD *)a2 + 56) )
sub_180001203(v41, 100LL, v26, -1LL);
}
v21 = (*(__int64 (__fastcall **)(__int64, __int64 *, int *))(*(_QWORD *)Instance + 32LL))(
Instance,
CreateSession_V2_Args,
v26);
}
然后发现这个虚函数会被调用CreateSession_V2
CreateSession_V2_Args[4] = v5;
v5 = *((_QWORD *)a2 + 9);
可以找到截图对象是CreateSession_V2是第二个参数的偏移72。截图函数是其第2个虚函数(+8).
sub_180004769(
v19,
6LL,
"CreateSession_V2: Proxyshim instance (0x%x), pArgs->ver = 0x%x, pArgs->hDevice = 0x%x, hLogoRenderer = 0x%x, hSc"
"reenshotCaptureDelegate = 0x%x, pArgs->d3dVersion = 0x%X, pArgs->rendererVersion = 0x%x, pArgs->shadowPlayFlags = 0x%x",
v14,
*(_DWORD *)a2,
*(_QWORD *)(a2 + 8),
*(_QWORD *)(a2 + 24),
v5,
*(_DWORD *)(a2 + 16),
*(_DWORD *)(a2 + 32),
*(_DWORD *)(a2 + 68));
}
可知该对象名字叫hSreenshotCaptureDelegate