Reputation: 53
Im trying to run machine code on windows for testing purposes,
i got a working prototype the only thing not working are EFlags
SetThreadContext seems to NOT apply EFlags to the thread correctly,
If i modify the EFlags in the CONTEXT Structure to include the carry flag cntx->EFlags |= 1;
and apply it with SetThreadContext and set a breakpoint at the adress of the threads execution with gdb and dump the eflags using info registers eflags
it does NOT include CF (Carry Flag)
My current Prototype:
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;
}
What i also have tryed is setting bit 1 additionaly (eflag |= 2
) because i have read its reserved but no change
Im running
"\x49\x83\xD7\x00\xEB\xFA"
==
start:
adc R15, 0
jmp start
Expectet output that R15 is 1 (Carry Flag added then cleared, R15 will not change because its adding 0)
Actual Output: R15 is 0
Debugging:
put gdb breakpoint at adress of func
dump EFlags, see CF (Carry Flag is not set)
(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 ]
As seen the c++ definetly set the carry flag corectly, but its not set in the actual thread
Thats my question WHY does SetThreadContext not set the carry flag corectly in the thread
Aditional Resources i have used:
x86 Flags
GetThreadContext
CreateThread
ResumeThread
SuspendThread
CloseHandle
Upvotes: 5
Views: 129
Reputation: 33754
if you look for RIP
from CONTREXT
you can view that it point to ntdll.RtlUserThreadStart
but not on your func
. and RCX
register is point to your func
. so you set EFlags
at ntdll.RtlUserThreadStart
. before it call your func
the EFlags
of course will change to undefined state. so this and must not work.
however you not need pass EFlags as parameter to shellcode. you can direct call it. of if you want execute it and another thread, you can pass up to 3 parameters ( rcx
, rdx
, r8
in case x64) if execute it in apc callback or single parameter in rcx
Upvotes: 2