我正在尝试在 Windows 上运行机器代码以进行测试,
我有一个工作原型,唯一不工作的是 EFlags
SetThreadContext 似乎NOT 将 EFlags 正确应用到线程,
如果我修改 CONTEXT 结构中的 EFlags 以包含进位标志
cntx->EFlags |= 1;
并将其与 SetThreadContext 一起应用,并使用 gdb 在线程执行的地址处设置断点并使用 info registers eflags
转储 eflags,它不包括CF(携带旗帜)
我当前的原型:
typedef struct machine_code_t {
char *machine_code;
size_t machine_code_size;
} machine_code;
char code_adc[] = "\x49\x83\xD7\x00\xEB\xFA";
#define IM_CODE code_adc
int main(void) {
CONTEXT *final_context = nullptr;
HANDLE threadhndle = nullptr;
CONTEXT *context = nullptr;
machine_code machineCode = {.machine_code = IM_CODE, .machine_code_size = sizeof(IM_CODE)};
auto func = reinterpret_cast<LPTHREAD_START_ROUTINE>(VirtualAlloc(nullptr, machineCode.machine_code_size,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE));
if (func == nullptr) {
printf("VirtualAlloc failed\n");
return 1;
}
if (std::memcpy(reinterpret_cast<void *>(func), machineCode.machine_code, machineCode.machine_code_size) ==
nullptr) {
printf("memcpy failed\n");
return 1;
}
threadhndle = CreateThread(nullptr, 0, func, nullptr, CREATE_SUSPENDED, &g_targetThreadId);
if (threadhndle == nullptr) {
printf("CreateThread failed\n");
return 1;
}
context = (CONTEXT *) calloc(1, sizeof(CONTEXT));
if (context == nullptr) {
printf("malloc failed\n");
return 1;
}
context->ContextFlags = CONTEXT_ALL;
if (GetThreadContext(threadhndle, context) == 0) {
printf("GetThreadContext failed\n");
goto clean;
}
context->EFlags |= 1; // Set the carry flag
context->R14 = 100;
context->ContextFlags = CONTEXT_ALL;
if (SetThreadContext(threadhndle, context) == 0) {
printf("SetThreadContext failed\n");
goto clean;
}
if (ResumeThread(threadhndle) == -1) {
printf("ResumeThread failed\n");
goto clean;
}
Sleep(100);
if (SuspendThread(threadhndle) == -1) {
printf("SuspendThread failed\n");
goto clean;
}
final_context = (CONTEXT *) calloc(1, sizeof(CONTEXT));
if (final_context == nullptr) {
printf("malloc failed\n");
goto clean;
}
final_context->ContextFlags = CONTEXT_FULL;
if (GetThreadContext(threadhndle, final_context) == 0) {
printf("GetThreadContext failed\n");
goto clean;
}
printf("Initial R15: %lld\n", context->R15);
printf("Final R15: %lld\n", final_context->R15);
clean:
free(context);
if (final_context != nullptr) free(final_context);
if (threadhndle != nullptr) CloseHandle(threadhndle);
if (func != nullptr) VirtualFree(reinterpret_cast<LPVOID>(func), 0, MEM_RELEASE);
return 0;
}
我还尝试过额外设置位 1 (
eflag |= 2
),因为我已阅读其保留但没有更改
我在跑步
"\x49\x83\xD7\x00\xEB\xFA"
==
start:
adc R15, 0
jmp start
预计输出R15为1(进位标志加后清零,R15不会改变,因为加了0)
实际输出:R15为0
调试:
将 gdb 断点置于
func
地址
转储 EFlags,请参阅 CF(未设置进位标志)
(gdb) break *0x00..... //adress stored in func variable
// Wait for hit on breakpoint should show [New Thread ...] then hit the brakpoint
(gdb) x/i $pc // should show adc $0x0,%r15 (reversed in comparison to intel syntax but still correct)
(gdb) info registers eflags // should show something like [ PF ZF IF ]
正如所见,c++明确地设置了进位标志,但它没有在实际线程中设置
这就是我的问题为什么 SetThreadContext 没有在线程中正确设置进位标志
如果您从
RIP
查找 CONTREXT
,您可以看到它指向 ntdll.RtlUserThreadStart
,但不在您的 func
上。和 RCX
寄存器指向您的 func
。所以您将 EFlags
设置为 ntdll.RtlUserThreadStart
。在它调用您的 func
之前,EFlags
当然会更改为未定义状态。所以这一定行不通。
但是您不需要将 EFlags 作为参数传递给 shellcode。你可以直接调用它。如果你想执行它和另一个线程,如果在 apc 回调中执行,或者在
rcx
中执行单个参数,则最多可以传递 3 个参数(
rdx
、
r8
、
rcx
,如果是 x64)