 |

24.12.2025, 20:53
|
|
Новичок
Регистрация: 28.07.2024
Сообщений: 3
С нами:
946126
Репутация:
1
|
|
Объясните пожалуйста, как правильно хукать Present (D3D9 Methods Table: [17] Present) под internal cheat.
чтобы рисовать к примеру ImGui, в пресенте, но при это чтобы obs, bandicam и другие подобные приложения с записью экрана который используют d3d9(capture) рисовали чистый кадр на записе
upd.
решил проблему помог @Receiver, отдельная благодарность ему за это
вот сама логика -
SAMP vTable[17]: 0x651261E8 (это SAMP'овский враппер)
реальный Present в d3d9.dll: 0x71046130
логика
1. Игра вызывает vTable[17] → hkPresentSAMP (наш хук, ничего не рендерим)
2. OBS хук (если есть) захватывает чистый кадр
3. SAMP wrapper → вызывает реальный Present в d3d9.dll
4. hkPresentReal (наш хук) - рендерим оверлей
5. Настоящий Present — показываем кадр с оверлеем игроку
OBS захватывает на шаге 2, чит рендерит на шаге 4 - после OBS
|
|
|

24.12.2025, 21:17
|
|
Постоянный
Регистрация: 26.03.2016
Сообщений: 660
С нами:
5332594
Репутация:
163
|
|
OBS перехватывает Present и берёт BackBuffer из основного SwapChain игры. Интернет знает много способов это обойти.
Самый простой - создать собственное окно-оверлей и присвоить ему флаг исключения из захвата:
C++:
Код:
SetWindowDisplayAffinity
(
hWnd
,
WDA_EXCLUDEFROMCAPTURE
)
;
Это будет работать со всеми утилитами захвата, потому что делается на уровне DWM
(на этом же уровне происходит захват всего экрана подобными утилитами захвата).
Если тебя это не устраивает, то возможно для DirectX 9 сработает ранний перехват.
Логика простая - ставишь хук на Present раньше OBS, и он получает кадр без твоей графики,
потому что твой хук по цепочке вызовется позже. Основная идея в том, что OBS грабит кадр в хуке и нужно чтобы твои штучки рисовались после выполнения его хука. Это будет работать только при захвате игры.
Ни один из этих способов не будет работать с Nvidia App / GeForce Experience.
|
|
|

26.12.2025, 13:19
|
|
Постоянный
Регистрация: 24.09.2020
Сообщений: 364
С нами:
2966237
Репутация:
148
|
|
Сообщение от rhjossss
пока что пришел к такому результату -
видно, что моментами чит все таки проскакивает на запись, подскажите в чем проблема
Вот как сейчас работает StreamProof и хук Present:
StreamProof.cpp:
Создаёт отдельное overlay-окно с флагом WDA_EXCLUDEFROMCAPTURE (исключает из захвата OBS/стримов)
Окно создаётся поверх игры с флагами WS_EX_TOPMOST | WS_EX_TRANSPARENT | WS_EX_LAYERED
UpdatePosition() синхронизирует позицию оверлея с окном игры и скрывает его когда игра не в фокусе
Хук Present (Hooks.cpp):
Хук Present (Hooks.cpp)::
Код:
if (StreamProof::IsEnabled())
{
// 1. Презентуем игру БЕЗ оверлея (это захватит OBS)
oPresent(pDevice, pSrcRect, pDestRect, hDestWindow, pDirtyRegion);
// 2. Рисуем оверлей
Cheat::Render();
// 3. Презентуем оверлей в скрытое окно (OBS не захватит)
HWND hOverlay = StreamProof::GetOverlayWindow();
if (hOverlay)
return oPresent(pDevice, nullptr, nullptr, hOverlay, nullptr);
return D3D_OK;
}
Streamproof.cpp:
Код:
#include "main.h"
#ifndef WDA_EXCLUDEFROMCAPTURE
#define WDA_EXCLUDEFROMCAPTURE 0x00000011
#endif
static
LRESULT CALLBACK
OverlayWndProc
(
HWND hWnd
,
UINT uMsg
,
WPARAM wParam
,
LPARAM lParam
)
{
return
DefWindowProcW
(
hWnd
,
uMsg
,
wParam
,
lParam
)
;
}
void
StreamProof
::
Init
(
)
{
}
void
StreamProof
::
Shutdown
(
)
{
if
(
hOverlayWindow
)
{
DestroyWindow
(
hOverlayWindow
)
;
hOverlayWindow
=
nullptr
;
}
bInitialized
=
false
;
}
void
StreamProof
::
CreateOverlayWindow
(
)
{
if
(
hOverlayWindow
)
return
;
HWND hGameWindow
=
*
reinterpret_cast
(
0xC97C1C
)
;
if
(
!
hGameWindow
)
return
;
WNDCLASSEXW wc
=
{
}
;
wc
.
cbSize
=
sizeof
(
WNDCLASSEXW
)
;
wc
.
style
=
CS_HREDRAW
|
CS_VREDRAW
;
wc
.
lpfnWndProc
=
OverlayWndProc
;
wc
.
hInstance
=
GetModuleHandleW
(
nullptr
)
;
wc
.
lpszClassName
=
L
"StreamProofWnd"
;
RegisterClassExW
(
&
wc
)
;
RECT rcGame
;
GetWindowRect
(
hGameWindow
,
&
rcGame
)
;
int
w
=
rcGame
.
right
-
rcGame
.
left
;
int
h
=
rcGame
.
bottom
-
rcGame
.
top
;
// Создаём обычное окно поверх игры
hOverlayWindow
=
CreateWindowExW
(
WS_EX_TOPMOST
|
WS_EX_TRANSPARENT
|
WS_EX_LAYERED
|
WS_EX_NOACTIVATE
,
L
"StreamProofWnd"
,
L
""
,
WS_POPUP
,
rcGame
.
left
,
rcGame
.
top
,
w
,
h
,
nullptr
,
nullptr
,
GetModuleHandleW
(
nullptr
)
,
nullptr
)
;
if
(
hOverlayWindow
)
{
// Главное - исключаем окно из захвата экрана
SetWindowDisplayAffinity
(
hOverlayWindow
,
WDA_EXCLUDEFROMCAPTURE
)
;
// Прозрачность
SetLayeredWindowAttributes
(
hOverlayWindow
,
RGB
(
0
,
0
,
0
)
,
255
,
LWA_ALPHA
)
;
ShowWindow
(
hOverlayWindow
,
SW_SHOWNOACTIVATE
)
;
}
bInitialized
=
true
;
}
void
StreamProof
::
UpdatePosition
(
)
{
if
(
!
hOverlayWindow
)
return
;
HWND hGameWindow
=
*
reinterpret_cast
(
0xC97C1C
)
;
if
(
!
hGameWindow
)
return
;
// Проверяем, активна ли игра (в фокусе)
HWND hForeground
=
GetForegroundWindow
(
)
;
bool
bGameActive
=
(
hForeground
==
hGameWindow
)
;
if
(
!
bGameActive
)
{
// Игра не в фокусе - скрываем оверлей
ShowWindow
(
hOverlayWindow
,
SW_HIDE
)
;
return
;
}
// Игра в фокусе - показываем и обновляем позицию
RECT rcGame
;
GetWindowRect
(
hGameWindow
,
&
rcGame
)
;
SetWindowPos
(
hOverlayWindow
,
HWND_TOPMOST
,
rcGame
.
left
,
rcGame
.
top
,
rcGame
.
right
-
rcGame
.
left
,
rcGame
.
bottom
-
rcGame
.
top
,
SWP_NOACTIVATE
|
SWP_SHOWWINDOW
)
;
}
HWND StreamProof
::
GetOverlayWindow
(
)
{
if
(
!
bEnabled
)
return
nullptr
;
if
(
!
hOverlayWindow
)
CreateOverlayWindow
(
)
;
UpdatePosition
(
)
;
return
hOverlayWindow
;
}
bool
StreamProof
::
IsEnabled
(
)
{
return
bEnabled
;
}
void
StreamProof
::
SetEnabled
(
bool
bEnable
)
{
bEnabled
=
bEnable
;
if
(
bEnabled
&&
!
hOverlayWindow
)
CreateOverlayWindow
(
)
;
if
(
hOverlayWindow
)
ShowWindow
(
hOverlayWindow
,
bEnabled
?
SW_SHOWNOACTIVATE
:
SW_HIDE
)
;
}
Streamproof.h:
Код:
#pragma once
namespace
StreamProof
{
void
Init
(
)
;
void
Shutdown
(
)
;
void
CreateOverlayWindow
(
)
;
void
UpdatePosition
(
)
;
HWND
GetOverlayWindow
(
)
;
bool
IsEnabled
(
)
;
void
SetEnabled
(
bool
bEnabled
)
;
inline
HWND hOverlayWindow
{
nullptr
}
;
inline
bool
bEnabled
{
false
}
;
inline
bool
bInitialized
{
false
}
;
}
Ты можешь поставить хук на оригинальный Present в d3d9.dll, так как в игре несколько экземпляров IDirect3DDevice9. Как я понимаю получается так, что обс по своей сигнатуре находит немного другое место и ставит хук который будет вызываться до твоего рендера и его не будет видно.
Можешь использовать эту функцию:
C++:
[CODE]
std
::
uintptr_t
*
find_d3d9_vtable
(
std
::
size_t len
=
0x128000
)
{
auto
b
=
(
uint8_t
*
)
GetModuleHandleA
(
"d3d9.dll"
)
;
if
(
!
b
)
return
nullptr
;
for
(
size_t i
=
0
;
i
|
|
|

26.12.2025, 22:42
|
|
Постоянный
Регистрация: 26.03.2016
Сообщений: 660
С нами:
5332594
Репутация:
163
|
|
[QUOTE="g305noobo"]
Ты можешь поставить хук на оригинальный Present в d3d9.dll, так как в игре несколько экземпляров IDirect3DDevice9. Как я понимаю получается так, что обс по своей сигнатуре находит немного другое место и ставит хук который будет вызываться до твоего рендера и его не будет видно.
Можешь использовать эту функцию:
C++:
[CODE]
std
::
uintptr_t
*
find_d3d9_vtable
(
std
::
size_t len
=
0x128000
)
{
auto
b
=
(
uint8_t
*
)
GetModuleHandleA
(
"d3d9.dll"
)
;
if
(
!
b
)
return
nullptr
;
for
(
size_t i
=
0
;
i
|
|
|
|
 |
|
Здесь присутствуют: 1 (пользователей: 0 , гостей: 1)
|
|
|
|