У меня есть приложение, запущенное другим процессом, который загружает некоторые DLL (с большим количеством экспортируемых функций) и вызывает их через некоторое время после запуска.Так как здесь мы не можем использовать традиционную перехват API (заменяя указатель на функцию) и я не использую trampoline (потому что для получения длины инструкции требуется дизассемблер), я написал код для исправления файла dll, поэтому все экспортируемые функции будутначать с кодов операций «бесконечного перехода»: {0xEB, 0xFE}.
Последовательность действий:
- файл DLL исправления
- процесс запуска, который в конечном итоге вызоветнаше приложение
- открывает дескриптор процесса для нашего приложения
- перечисляет потоки, чтобы найти IP-адрес, на котором застрял процесс
- , теперь мы можем присоединить отладчик и выполнить один шаг через функцию, или мыможет "пропустить" его
Вот код для "пропуска" функции:
extern "C" void asm_proc();
#define PATCH_SIZE 2
UCHAR g_Patch[PATCH_SIZE] = { 0xEB, 0xFE };
void SkipFunction(FUNCTION_ITEM *func, HANDLE hProcess, HANDLE hThread)
{
CONTEXT lcContext;
UCHAR *ReturnAddress;
UCHAR *TargetAddress;
UCHAR *BlockAddress;
DWORD ProcSize;
UCHAR Ret;
Ret = 0xC3;
ProcSize = 14;
lcContext.ContextFlags = CONTEXT_ALL;
if (!GetThreadContext(hThread, &lcContext)) DbgRaiseAssertionFailure();
BlockAddress = (UCHAR*)VirtualAllocEx(hProcess, NULL, sizeof(ReturnAddress) + ProcSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
TargetAddress = BlockAddress + sizeof(ReturnAddress);
ReadProcessMemoryEx(hProcess, lcContext.Rsp, (UCHAR*)&ReturnAddress, sizeof(ReturnAddress));
WriteProcessMemoryEx(hProcess, lcContext.Rsp, (UCHAR*)&TargetAddress, sizeof(TargetAddress));
WriteProcessMemoryEx(hProcess, (DWORD64)BlockAddress, (UCHAR*)&ReturnAddress, sizeof(ReturnAddress));
// disable incremental linking to make it work
WriteProcessMemoryEx(hProcess, (DWORD64)TargetAddress, (UCHAR*)asm_proc, ProcSize);
WriteProcessMemoryEx(hProcess, func->Rip, func->OriginalBytes, PATCH_SIZE);
while (TRUE)
{
Sleep(100);
lcContext.ContextFlags = CONTEXT_ALL;
if (!GetThreadContext(hThread, &lcContext)) DbgRaiseAssertionFailure();
if (lcContext.Rip == (DWORD64)(TargetAddress + 12)) break;
}
WriteProcessMemoryEx(hProcess, func->Rip, g_Patch, PATCH_SIZE);
WriteProcessMemoryEx(hProcess, (DWORD64)(TargetAddress + 12), &Ret, sizeof(Ret));
VirtualFreeEx(hProcess, BlockAddress, 0, MEM_RELEASE);
}
И asm_proc код функции:
PUBLIC asm_proc
.code
asm_proc PROC
call $+5
pop rcx
sub rcx, 13
push qword ptr [rcx]
jmp $
asm_proc ENDP
END
func-> Rip содержит ip-адрес исправленной функции func-> OriginalBytes содержит байты исходной функции в том виде, в каком они появились в исходном файле dll
В основном мы выделяемпамяти с VirtualAllocEx, скопируйте наш код и оригинальный адрес возврата, подставьте адрес возврата в стек и введите бесконечный цикл, чтобы мы могли снова исправить функцию.Этот подход должен работать для однопотокового перехвата (мне не нужно обрабатывать несколько потоков).
Однако иногда приложение просто падает, когда мы «пропускаем» функцию, и я не понимаю, почему это происходит.Что вы можете сказать по этому поводу?Что здесь не так?Спасибо.