Created
January 12, 2025 20:33
-
-
Save JonathonReinhart/4ea0199b1771790141f4c26de207ad58 to your computer and use it in GitHub Desktop.
Self-debugging on windows
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-FileCopyrightText: © 2012 Jonathon Reinhart <[email protected]> | |
// SPDX-License-Identifier: MIT | |
#include "Breakpoints.h" | |
#include "util.h" | |
#include <stdio.h> | |
#include <stdlib.h> | |
// This causes UD2 opcodes to be used instead of INT3. | |
#define USE_UD2_BREAKPOINT 1 | |
#ifdef USE_UD2_BREAKPOINT | |
# define BP_OPCODE UD2_OPCODE // 0F 0B ud2 | |
# define BP_DTYPE UINT16 | |
#else | |
#define BP_OPCOE 0xCC // CC int3 | |
# define BP_DTYPE BYTE | |
#endif //USE_UD2_BREAKPOINT | |
#define BP_SIZE sizeof(BP_DTYPE) | |
#define EFLAGS_TF (1<<8) // Trap flag | |
#define EFLAGS_RF (1<<16) // Resume flag | |
typedef struct _Breakpoint Breakpoint; | |
struct _Breakpoint{ | |
Breakpoint* next; | |
LPVOID address; | |
BYTE orig[BP_SIZE]; | |
BreakpointHandler handler; | |
}; | |
Breakpoint* m_breakpoints; | |
static Breakpoint* _LookupBreakpoint(LPVOID addr) { | |
Breakpoint* i; | |
for (i = m_breakpoints; i; i = i->next) { | |
if (i->address == addr) return i; | |
} | |
return NULL; | |
} | |
static void _PlaceBreakpoint(Breakpoint* bp) { | |
memcpy(bp->orig, (LPVOID)bp->address, BP_SIZE); | |
*(BP_DTYPE*)(bp->address) = BP_OPCODE; | |
} | |
static void _RestoreInstruction(Breakpoint* bp) { | |
memcpy((LPVOID)bp->address, bp->orig, BP_SIZE); | |
} | |
void SetBreakpoint(LPVOID addr, BreakpointHandler handler) { | |
Breakpoint* bp; | |
// TODO: Does a breakpoint already exist for this address? | |
bp = malloc_s(sizeof(*bp)); | |
bp->address = addr; | |
bp->handler = handler; | |
bp->next = NULL; | |
_PlaceBreakpoint(bp); | |
// Add it to the list | |
if (!m_breakpoints) { | |
m_breakpoints = bp; // new head | |
} | |
else { | |
Breakpoint* i; | |
for (i=m_breakpoints; i->next; i = i->next) ; | |
i->next = bp; // at end | |
} | |
} | |
BOOL RemoveBreakpoint(LPVOID addr) { | |
Breakpoint* i; | |
Breakpoint** prev = &m_breakpoints; | |
for (i = m_breakpoints; i; prev = &i->next, i = i->next) { | |
if (i->address == addr) break; | |
} | |
if (!i) return FALSE; | |
_RestoreInstruction(i); | |
*prev = i->next; | |
free(i); | |
return TRUE; | |
} | |
// The breakpoint that we are currently resuming from. | |
// Use this to re-set the breakpoint after continuing and setting the TRAP flag. | |
static Breakpoint* m_pendingStepBP = NULL; | |
BOOL HandleSingleStepException(PEXCEPTION_POINTERS ei, LPVOID ip) { | |
printf("Single-step at %p: ", ip); | |
if (!m_pendingStepBP) { | |
printf("Not ours!\n"); | |
return FALSE; // Not ours! | |
} | |
printf("\n"); | |
// We've just executed an instruction that needs a breakpoint re-placed. | |
_PlaceBreakpoint(m_pendingStepBP); | |
m_pendingStepBP = NULL; | |
return TRUE; | |
} | |
BOOL HandleBreakpointException(PEXCEPTION_POINTERS ei, LPVOID ip) { | |
DWORD handler_result; | |
CONTEXT* ctx = ei->ContextRecord; | |
Breakpoint* bp; | |
printf("Breakpoint at %p: ", ip); | |
bp = _LookupBreakpoint(ip); | |
if (!bp) { | |
printf("Not found in list!\n"); | |
return FALSE; // Not ours! | |
} | |
printf("\n"); | |
_RestoreInstruction(bp); | |
// Call the handler | |
if (bp->handler) | |
handler_result = bp->handler(ip, ctx); | |
else | |
handler_result = BP_KEEP; | |
if (handler_result != BP_REMOVE) { | |
// Before continuing, set the trap flag (TF) so we break on the next instruction. | |
ctx->EFlags |= EFLAGS_TF; | |
m_pendingStepBP = bp; | |
} | |
return TRUE; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-FileCopyrightText: © 2012 Jonathon Reinhart <[email protected]> | |
// SPDX-License-Identifier: MIT | |
#pragma once | |
#include <Windows.h> | |
#define BP_KEEP 0 // Leave the breakpoint in | |
#define BP_REMOVE 1 // Remove the breakpoint | |
#define UD2_OPCODE 0x0B0F // 0F 0B UD2 | |
typedef DWORD (*BreakpointHandler)(LPVOID ip, CONTEXT* ctx); | |
void SetBreakpoint(LPVOID addr, BreakpointHandler handler); | |
BOOL RemoveBreakpoint(LPVOID addr); | |
BOOL HandleBreakpointException(PEXCEPTION_POINTERS ei, LPVOID ip); | |
BOOL HandleSingleStepException(PEXCEPTION_POINTERS ei, LPVOID ip); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-FileCopyrightText: © 2012 Jonathon Reinhart <[email protected]> | |
// SPDX-License-Identifier: MIT | |
#include <Windows.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include "ExceptionHandling.h" | |
#include "Breakpoints.h" | |
#include "util.h" | |
#include "udis86.h" | |
#ifdef _WIN64 | |
# define CTX_IP(ctx) ctx->Rip | |
# define CTX_AX(ctx) ctx->Rax | |
# define CTX_CX(ctx) ctx->Rcx | |
# define CTX_DX(ctx) ctx->Rdx | |
#else | |
# define CTX_IP(ctx) ctx->Eip | |
# define CTX_AX(ctx) ctx->Eax | |
# define CTX_CX(ctx) ctx->Ecx | |
# define CTX_DX(ctx) ctx->Edx | |
#endif | |
ud_t g_ud; | |
static const char* GetAccessViolationFaultOperation(DWORD type) { | |
switch (type) { | |
case EXCEPTION_READ_FAULT: return "Read"; | |
case EXCEPTION_WRITE_FAULT: return "Write"; | |
case EXCEPTION_EXECUTE_FAULT: return "Execute"; | |
default: return "(???)"; | |
} | |
} | |
// Returns TRUE if handled. | |
BOOL _ExAccessViolation(PEXCEPTION_POINTERS ei, LPVOID ip) { | |
unsigned int i; | |
DWORD type = (DWORD)ei->ExceptionRecord->ExceptionInformation[0]; | |
LPVOID addr = (LPVOID)ei->ExceptionRecord->ExceptionInformation[1]; | |
printf("Access violation: Tried to %s at 0x%p\n", | |
GetAccessViolationFaultOperation(type), addr); | |
if (type == EXCEPTION_EXECUTE_FAULT) | |
return FALSE; // nothing we can do | |
printf(" Instruction @ " PTR_FMT ": ", ip); | |
for (i=0; i<ud_insn_len(&g_ud); i++) | |
printf("%02X ", ud_insn_ptr(&g_ud)[i]); | |
printf(" %s\n", ud_insn_asm(&g_ud)); | |
return FALSE; | |
} | |
// Returns TRUE if handled. | |
BOOL _ExPrivilegedInstruction(PEXCEPTION_POINTERS ei, LPVOID ip) { | |
BOOL handled = FALSE; | |
unsigned int i; | |
CONTEXT* ctx = ei->ContextRecord; | |
printf("Exception: Privileged instruction @ " PTR_FMT ": ", ip); | |
for (i=0; i<ud_insn_len(&g_ud); i++) | |
printf("%02X ", ud_insn_ptr(&g_ud)[i]); | |
printf(" %s\n", ud_insn_asm(&g_ud)); | |
switch (ud_insn_mnemonic(&g_ud)) { | |
} | |
// Advance | |
if (handled) | |
CTX_IP(ctx) += ud_insn_len(&g_ud); | |
return handled; | |
} | |
BOOL _ExIllegalInstruction(PEXCEPTION_POINTERS ei, LPVOID ip) { | |
BOOL result; | |
CONTEXT* ctx = ei->ContextRecord; | |
if (*(UINT16*)ip == UD2_OPCODE) { // 0F 0B UD2 | |
result = HandleBreakpointException(ei, ip); | |
return result; | |
} | |
return FALSE; | |
} | |
LONG CALLBACK VectoredHandler(PEXCEPTION_POINTERS ExceptionInfo) | |
{ | |
// TODO: Clear direction flag! | |
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms679274.aspx | |
DWORD code = ExceptionInfo->ExceptionRecord->ExceptionCode; | |
LPVOID ip = ExceptionInfo->ExceptionRecord->ExceptionAddress; | |
BOOL handled = FALSE; | |
ud_set_input_buffer(&g_ud, ip, 20); // TODO: what to use for size? | |
ud_disassemble(&g_ud); | |
switch (code) { | |
case STATUS_PRIVILEGED_INSTRUCTION: | |
handled = _ExPrivilegedInstruction(ExceptionInfo, ip); | |
break; | |
case STATUS_ACCESS_VIOLATION: | |
handled = _ExAccessViolation(ExceptionInfo, ip); | |
break; | |
case STATUS_ILLEGAL_INSTRUCTION: | |
handled = _ExIllegalInstruction(ExceptionInfo, ip); | |
break; | |
case STATUS_BREAKPOINT: | |
handled = HandleBreakpointException(ExceptionInfo, ip); | |
break; | |
case STATUS_SINGLE_STEP: | |
handled = HandleSingleStepException(ExceptionInfo, ip); | |
break; | |
} | |
if (handled) return EXCEPTION_CONTINUE_EXECUTION; | |
// Maybe longjmp here? | |
printf("\n\nERROR: Failed to handle exception. Aborting.\n"); | |
getchar(); | |
exit(4); | |
return EXCEPTION_CONTINUE_SEARCH; | |
} | |
static PVOID m_hHandler = NULL; | |
void EnableExceptionHandling(void) { | |
m_hHandler = AddVectoredExceptionHandler(1, VectoredHandler); | |
// Initialize disassembler | |
ud_init(&g_ud); | |
ud_set_mode(&g_ud, sizeof(void*)*8); | |
ud_set_vendor(&g_ud, UD_VENDOR_ANY); | |
ud_set_syntax(&g_ud, UD_SYN_INTEL); | |
} | |
void DisableExceptionHandling(void) { | |
RemoveVectoredExceptionHandler(m_hHandler); | |
m_hHandler = NULL; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-FileCopyrightText: © 2012 Jonathon Reinhart <[email protected]> | |
// SPDX-License-Identifier: MIT | |
#ifndef _EXCEPTIONHANDLING_H_ | |
#define _EXCEPTIONHANDLING_H_ | |
void EnableExceptionHandling(void); | |
void DisableExceptionHandling(void); | |
#endif // _EXCEPTIONHANDLING_H_ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
MIT License | |
Copyright (c) 2012 Jonathon Reinhart | |
Permission is hereby granted, free of charge, to any person obtaining a copy | |
of this software and associated documentation files (the "Software"), to deal | |
in the Software without restriction, including without limitation the rights | |
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
copies of the Software, and to permit persons to whom the Software is | |
furnished to do so, subject to the following conditions: | |
The above copyright notice and this permission notice shall be included in all | |
copies or substantial portions of the Software. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
SOFTWARE. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment