
Реализация Detours (x64/x86), использующая только импорт ntdll
сделать IAT доступным для записи.
смотрите simple.cpp
TimedSleepEx вызывает настоящий API SleepEx через указатель на функцию TrueSleepEx.``` static DWORD (WINAPI * TrueSleepEx)(DWORD dwMilliseconds, BOOL bAlertable) = SleepEx;
DWORD WINAPI TimedSleepEx(DWORD dwMilliseconds, BOOL bAlertable) { ... DWORD ret = TrueSleepEx(dwMilliseconds, bAlertable); ... }
**_не делайте этого !_** компоновщик уже определён```
EXTERN_C PVOID __imp_SleepEx = SleepEx;
переменная ( для x86 это будет __imp__SleepEx@8 и нужно использовать``` __pragma(comment(linker, "/alternatename:___imp_SleepEx=__imp__SleepEx@8"))
так используйте ***__imp_SleepEx*** вместо ***TrueSleepEx*** и просто вызывайте API привычным и удобным способом.
любая импортированная api SomeApi вызывается через указатель ***__imp_SomeApi*** (отложенная загрузка через ***__imp_load_SomeApi***). используйте это!
пример```
//EXTERN_C extern PVOID __imp_SleepEx;
DECLARE_T_HOOK(SleepEx, 8)
DWORD WINAPI hook_SleepEx( _In_ DWORD dwMilliseconds, _In_ BOOL bAlertable )
{
ULONG ret = SleepEx(dwMilliseconds, bAlertable);
DbgPrint("%s(%x, %x) = %x\n", __FUNCTION__, dwMilliseconds, bAlertable, ret);
return ret;
}
void DetoursDemo()
{
DBG_PRINT_ON();
if (0 <= TrInit())
{
ThreadInfo* pti;
SuspendAll(&pti);
DbgPrint("before: SleepEx = 0x%p\n", SleepEx);
TrHook(&__imp_SleepEx, hook_SleepEx, pti);
DbgPrint("after: SleepEx = 0x%p\n", SleepEx);
ResumeAndFree(pti);
union {
PVOID pv;
DWORD (WINAPI * fn_SleepEx)( _In_ DWORD dwMilliseconds, _In_ BOOL bAlertable );
};
if (pv = GetProcAddress(GetModuleHandleW(L"kernel32"), "SleepEx"))
{
if (QueueUserAPC((PAPCFUNC)OutputDebugStringA, GetCurrentThread(), (ULONG_PTR)"asynchronous procedure call...\n"))
{
fn_SleepEx(INFINITE, TRUE);
}
}
}
}
типичный вывод отладки:``` NT::ThreadInfo::ThreadInfo<000002196427D6A0>(0000000000000814) NT::ThreadInfo::ThreadInfo<000002196427DBC0>(0000000000000128) NT::ThreadInfo::ThreadInfo<000002196427E0E0>(00000000000023D0) before: SleepEx = 0x00007FFED3DB06B0 0x00007FFED3DB06B0 -> 0x00007FF78BEB06C0 -> 0x00007FFE5394FFB8 [0x00007FFE5394FF98] after: SleepEx = 0x00007FFE5394FFB8 NT::ThreadInfo::~ThreadInfo<000002196427E0E0>(00000000000023D0) NT::ThreadInfo::~ThreadInfo<000002196427DBC0>(0000000000000128) NT::ThreadInfo::~ThreadInfo<000002196427D6A0>(0000000000000814) asynchronous procedure call... NT::hook_SleepEx(ffffffff, 1) = c0
ещё один пример, с unhook:```
DWORD WINAPI hook_SleepEx( _In_ DWORD dwMilliseconds, _In_ BOOL bAlertable )
{
// don't use any [TrueSleepEx](https://github.com/microsoft/Detours/blob/main/samples/simple/simple.cpp#L23) !!
ULONG ret = SleepEx(dwMilliseconds, bAlertable);
DbgPrint("%s(%x, %x) = %x\n", __FUNCTION__, dwMilliseconds, bAlertable, ret);
return ret;
}
int
WINAPI
hook_MessageBoxW(
_In_opt_ HWND hWnd,
_In_opt_ PCWSTR lpText,
_In_opt_ PCWSTR lpCaption,
_In_ UINT uType)
{
// don't use any [Real_MessageBoxW](https://raw.githubusercontent.com/microsoft/Detours/main/samples/traceapi/_win32.cpp) !!
int ret = MessageBoxW(hWnd, lpText, __FUNCTIONW__, uType);
DbgPrint("%s(\"%S\", \"%S\") = %x\n", __FUNCTION__, lpText, lpCaption, ret);
return ret;
}
DECLARE_T_HOOK(SleepEx, 8)
DECLARE_T_HOOK(MessageBoxW, 16)
T_HOOKS_BEGIN(gHooks)
T_HOOK(SleepEx),
T_HOOK(MessageBoxW),
T_HOOKS_END()
void DemoApiCalls()
{
union {
PVOID pv;
DWORD (WINAPI * fn_SleepEx)( _In_ DWORD dwMilliseconds, _In_ BOOL bAlertable );
int (WINAPI * fn_MessageBoxW)( _In_opt_ HWND hWnd, _In_opt_ LPCWSTR lpText, _In_opt_ LPCWSTR lpCaption, _In_ UINT uType);
};
if (pv = GetProcAddress(GetModuleHandleW(L"kernel32"), "SleepEx"))
{
if (QueueUserAPC((PAPCFUNC)OutputDebugStringA, GetCurrentThread(), (ULONG_PTR)"asynchronous procedure call...\n"))
{
fn_SleepEx(INFINITE, TRUE);
}
}
if (pv = GetProcAddress(GetModuleHandleW(L"user32"), "MessageBoxW"))
{
fn_MessageBoxW(0, L"[some text]", 0, MB_ICONINFORMATION);
}
}
void DetoursDemo()
{
DBG_PRINT_ON();
if (0 <= TrInit())
{
ThreadInfo* pti;
SuspendAll(&pti);
TrHook(gHooks, _countof(gHooks), pti);
ResumeAndFree(pti);
DemoApiCalls();
SuspendAll(&pti);
TrUnHook(gHooks, _countof(gHooks), pti);
ResumeAndFree(pti);
DemoApiCalls();
}
}
NT::ThreadInfo::ThreadInfo<0000026DEA5AD6D0>(00000000000017E8)
NT::ThreadInfo::ThreadInfo<0000026DEA5ADBF0>(00000000000013E0)
NT::ThreadInfo::ThreadInfo<0000026DEA5AE110>(00000000000002D4)
0x00007FFED3DB06B0 -> 0x00007FF693E10790 -> 0x00007FFE5394FFB8 [0x00007FFE5394FF98]
0x00007FFED41CA160 -> 0x00007FF693E10720 -> 0x00007FFE5430FFB8 [0x00007FFE5430FF98]
NT::ThreadInfo::~ThreadInfo<0000026DEA5AE110>(00000000000002D4)
NT::ThreadInfo::~ThreadInfo<0000026DEA5ADBF0>(00000000000013E0)
NT::ThreadInfo::~ThreadInfo<0000026DEA5AD6D0>(00000000000017E8)
asynchronous procedure call...
NT::hook_SleepEx(ffffffff, 1) = c0
---------------------------
NT::hook_MessageBoxW
---------------------------
[some text]
---------------------------
OK
---------------------------
NT::hook_MessageBoxW("[some text]", "(null)") = 1
NT::ThreadInfo::ThreadInfo<0000026DEA5C7370>(0000000000001A84)
NT::ThreadInfo::ThreadInfo<0000026DEA5C7890>(0000000000000D1C)
NT::ThreadInfo::ThreadInfo<0000026DEA5E2950>(00000000000020C8)
NT::ThreadInfo::ThreadInfo<0000026DEA5E2E70>(0000000000000D18)
NT::ThreadInfo::ThreadInfo<0000026DEA5E3390>(0000000000001974)
NT::ThreadInfo::ThreadInfo<0000026DEA5DD3D0>(00000000000023E0)
NT::ThreadInfo::ThreadInfo<0000026DEA5DD8F0>(0000000000001C58)
NT::ThreadInfo::~ThreadInfo<0000026DEA5DD8F0>(0000000000001C58)
NT::ThreadInfo::~ThreadInfo<0000026DEA5DD3D0>(00000000000023E0)
NT::ThreadInfo::~ThreadInfo<0000026DEA5E3390>(0000000000001974)
NT::ThreadInfo::~ThreadInfo<0000026DEA5E2E70>(0000000000000D18)
NT::ThreadInfo::~ThreadInfo<0000026DEA5E2950>(00000000000020C8)
NT::ThreadInfo::~ThreadInfo<0000026DEA5C7890>(0000000000000D1C)
NT::ThreadInfo::~ThreadInfo<0000026DEA5C7370>(0000000000001A84)
asynchronous procedure call...
---------------------------
Error
---------------------------
[some text]
---------------------------
OK
---------------------------
включить / отключить вывод отладочной информации (по умолчанию отключено)
перечислить потоки в процессе через NtGetNextThread проверить, является ли поток текущим ( ZwQueryInformationThread(ThreadBasicInformation) ) приостановить поток ZwSuspendThread сохранить его контекст ZwGetContextThread связать информацию о потоке (дескриптор/контекст) со списком
обойти список потоков ( pti ) возобновить поток закрыть его дескриптор освободить память