Ctrl-F9和F9OllyDbg之间有什么区别,除了第一个命令将在下一RET条指令上停止程序执行而第二个命令将在下一个断点/程序终止时停止程序执行?
我只是注意到调试应用程序在执行以下代码时在这些情况下给出了不同的结果(函数调用后不同的寄存器值等):
; ...
CALL module_name.some_address
TEST EAX, EAX
; ...
我已经在每条指令上设置了断点,并注意到当我F9在第一个断点之后按下或按下F7和Ctrl-时,行为是不同的F9。
为什么 ?
Ctrl-F9和F9OllyDbg之间有什么区别,除了第一个命令将在下一RET条指令上停止程序执行而第二个命令将在下一个断点/程序终止时停止程序执行?
我只是注意到调试应用程序在执行以下代码时在这些情况下给出了不同的结果(函数调用后不同的寄存器值等):
; ...
CALL module_name.some_address
TEST EAX, EAX
; ...
我已经在每条指令上设置了断点,并注意到当我F9在第一个断点之后按下或按下F7和Ctrl-时,行为是不同的F9。
为什么 ?
我不知道这个特性在 Ollydbg 中是如何实现的,但是我们可以看看它是如何在 x64_dbg 中实现的,因为它是开源的。
在Ctrl- F9(执行到返回)功能被实现cbDebugRtr在x64_dbg_dbg/debugger_commands.cpp。我们可以看到它调用StepOver了TitanEngine提供的方法。
CMDRESULT cbDebugRtr(int argc, char* argv[])
{
StepOver((void*)cbRtrStep);
cbDebugRun(argc, argv);
return STATUS_CONTINUE;
}
在 TitanEngine 文档中,StepOver 的描述如下:
步进功能
StepOver 通过单步执行调用来跟踪代码。此跟踪函数在调用后设置一个 INT3 断点,用于调用您的回调。不能保证代码执行将从该调用返回,因此不能保证您的回调将永远被调用。断点仅运行一次,并在回调完成后删除。
TitanEngine也是开源的,这里是StepOverin的实现TitanEngine / TitanEngine.Debugger.Control.cpp
__declspec(dllexport) void TITCALL StepOver(LPVOID StepCallBack)
{
ULONG_PTR ueCurrentPosition = GetContextData(UE_CIP);
unsigned char instr[16];
MemoryReadSafe(dbgProcessInformation.hProcess, (void*)ueCurrentPosition, instr, sizeof(instr), 0);
char* DisassembledString = (char*)StaticDisassembleEx(ueCurrentPosition, (LPVOID)instr);
if(strstr(DisassembledString, "CALL") || strstr(DisassembledString, "REP") || strstr(DisassembledString, "PUSHF"))
{
ueCurrentPosition += StaticLengthDisassemble((void*)instr);
SetBPX(ueCurrentPosition, UE_BREAKPOINT_TYPE_INT3 + UE_SINGLESHOOT, StepCallBack);
}
else
StepInto(StepCallBack);
}
在每一步之后,都会调用回调函数cbRtrStep来检查当前指令是否为 a RET,如果是,则停止调试器。( x64_dbg_dbg/debugger.cpp)
void cbRtrStep()
{
unsigned int cipch = getCIPch();
if(cipch == 0xC3 or cipch == 0xC2)
cbRtrFinalStep();
else
StepOver((void*)cbRtrStep);
}
所以,总而言之,Ctrl-F9正在执行步进,直到遇到RET指令。如果按下F7和Ctrl-F9调试器将单步执行该函数的指令;您正在调用的函数可能正在执行在此函数之后设置断点时未触发的调试器检测。