update to ida 7.6, add builds

This commit is contained in:
2021-10-31 21:20:46 +02:00
parent e0e0f2be99
commit b1809fe2d9
1408 changed files with 279193 additions and 302468 deletions

176
idasdk76/dbg/win32/makefile Normal file
View File

@@ -0,0 +1,176 @@
include ../../allmake.mak
GOALS-$(BUILD_IDA) += modules # target in $(IDA)module.mak
GOALS-$(BUILD_DBGSRV) += server # target in $(IDA)dbg/server.mak
.PHONY: $(GOALS-1)
all: $(GOALS-1)
#----------------------------------------------------------------------
ifdef __NT__
ifndef __X86__
SERVER = win64_remote$(B)
else
SERVER = win32_remote$(B)
endif
endif
ifdef SERVER
SERVERS += $(call server_exe,$(SERVER))
endif
#----------------------------------------------------------------------
STUB = $(call module_dll,win32_stub)
ifdef BUILD_IDA
ifeq ($(or $(IDAHOME),$(DEMO_OR_FREE)),)
MODULES += $(STUB)
endif
endif
#----------------------------------------------------------------------
USER = $(call module_dll,win32_user)
ifeq ($(and $(BUILD_IDA),$(__NT__)),1)
MODULES += $(USER)
endif
#----------------------------------------------------------------------
# we explicitly added our module targets
NO_DEFAULT_TARGETS = 1
# NOTE: all MODULES must be defined before including plugin.mak.
include ../plugin.mak
# NOTE: target-specific rules and dependencies that use variable
# expansion to name the target (such as "$(MODULE): [...]") must
# come after including plugin.mak
#----------------------------------------------------------------------
# select OBJS common to user plugin and debugger server
BASE_OBJS-$(__NT__) += $(F)win32_debmod$(O)
BASE_OBJS-$(__NT__) += $(F)win32_util$(O)
BASE_OBJS-$(__NT__) += $(F)winbase_debmod$(O)
BASE_OBJS += $(BASE_OBJS-1)
#----------------------------------------------------------------------
SERVER_OBJS += $(F)win32_server$(O)
SERVER_OBJS += $(F)tilfuncs$(O)
SERVER_OBJS += $(BASE_OBJS)
SERVER_STDLIBS += ole32.lib
SERVER_STDLIBS += oleaut32.lib
include ../server.mak
#----------------------------------------------------------------------
STUB_OBJS += $(F)win32_stub$(O)
STUB_OBJS += $(F)w32sehch$(O)
$(STUB): MODULE_OBJS += $(STUB_OBJS)
$(STUB): $(STUB_OBJS)
#----------------------------------------------------------------------
USER_OBJS += $(F)win32_user$(O)
USER_OBJS += $(F)w32sehch$(O)
USER_OBJS += $(BASE_OBJS)
$(USER): MODULE_OBJS += $(USER_OBJS)
$(USER): $(USER_OBJS)
$(USER): STDLIBS += user32.lib
#----------------------------------------------------------------------
include $(IDA)objdir.mak
# MAKEDEP dependency list ------------------
$(F)tilfuncs$(O): $(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp \
$(I)diskio.hpp $(I)err.h $(I)expr.hpp $(I)fpro.h \
$(I)funcs.hpp $(I)ida.hpp $(I)idd.hpp $(I)idp.hpp \
$(I)ins/pc.hpp $(I)intel.hpp $(I)kernwin.hpp \
$(I)lines.hpp $(I)llong.hpp $(I)nalt.hpp $(I)name.hpp \
$(I)netnode.hpp $(I)network.hpp $(I)pro.h $(I)range.hpp \
$(I)segment.hpp $(I)typeinf.hpp $(I)ua.hpp $(I)xref.hpp \
../../ldr/pe/cor.h ../../ldr/pe/corerror.h \
../../ldr/pe/corhdr.h ../../ldr/pe/mycor.h \
../../ldr/pe/pe.h ../../plugins/pdb/common.cpp \
../../plugins/pdb/cvconst.h ../../plugins/pdb/dbghelp.h \
../../plugins/pdb/dia2.h ../../plugins/pdb/idaaccess.hpp \
../../plugins/pdb/msdia.cpp ../../plugins/pdb/msdia.hpp \
../../plugins/pdb/pdb.hpp \
../../plugins/pdb/pdbaccess.hpp \
../../plugins/pdb/pdbida.hpp \
../../plugins/pdb/pdblocal.cpp \
../../plugins/pdb/pdblocal.hpp \
../../plugins/pdb/varser.hpp ../debmod.h tilfuncs.cpp \
tilfuncs.hpp
$(F)w32sehch$(O): $(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp \
$(I)dbg.hpp $(I)fpro.h $(I)funcs.hpp $(I)ida.hpp \
$(I)idd.hpp $(I)idp.hpp $(I)kernwin.hpp $(I)lines.hpp \
$(I)llong.hpp $(I)loader.hpp $(I)nalt.hpp $(I)name.hpp \
$(I)netnode.hpp $(I)pro.h $(I)range.hpp $(I)segment.hpp \
$(I)ua.hpp $(I)xref.hpp w32sehch.cpp w32sehch.h
$(F)win32_debmod$(O): $(I)auto.hpp $(I)bitrange.hpp $(I)bytes.hpp \
$(I)config.hpp $(I)dbg.hpp $(I)diskio.hpp $(I)entry.hpp \
$(I)err.h $(I)exehdr.h $(I)fixup.hpp $(I)fpro.h \
$(I)funcs.hpp $(I)ida.hpp $(I)idd.hpp \
$(I)idp.hpp $(I)kernwin.hpp $(I)lines.hpp $(I)llong.hpp \
$(I)loader.hpp $(I)nalt.hpp $(I)name.hpp $(I)netnode.hpp \
$(I)network.hpp $(I)offset.hpp $(I)pro.h $(I)prodir.h \
$(I)range.hpp $(I)segment.hpp $(I)segregs.hpp $(I)ua.hpp \
$(I)xref.hpp ../../ldr/pe/../idaldr.h \
../../ldr/pe/common.cpp ../../ldr/pe/common.h \
../../ldr/pe/pe.h ../dbg_pe_hlp.cpp ../deb_pc.hpp \
../debmod.h ../pc_debmod.h ../pc_regs.hpp \
win32_debmod.cpp win32_debmod.h win32_debmod_impl.cpp \
win32_rpc.h win32_undoc.h win32_util.hpp \
winbase_debmod.h
$(F)win32_server$(O): $(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp \
$(I)fpro.h $(I)funcs.hpp $(I)ida.hpp $(I)idd.hpp \
$(I)idp.hpp $(I)kernwin.hpp $(I)lines.hpp $(I)llong.hpp \
$(I)nalt.hpp $(I)name.hpp $(I)netnode.hpp \
$(I)network.hpp $(I)pro.h $(I)range.hpp $(I)segment.hpp \
$(I)typeinf.hpp $(I)ua.hpp $(I)xref.hpp \
../../ldr/pe/cor.h ../../ldr/pe/corerror.h \
../../ldr/pe/corhdr.h ../../ldr/pe/mycor.h \
../../ldr/pe/pe.h ../../plugins/pdb/cvconst.h \
../../plugins/pdb/dia2.h ../../plugins/pdb/idaaccess.hpp \
../../plugins/pdb/msdia.hpp ../../plugins/pdb/pdb.hpp \
../../plugins/pdb/pdbaccess.hpp \
../../plugins/pdb/pdbida.hpp \
../../plugins/pdb/pdblocal.hpp ../dbg_rpc_hlp.h \
../deb_pc.hpp ../debmod.h ../pc_debmod.h ../pc_regs.hpp \
tilfuncs.hpp win32_debmod.h win32_rpc.h win32_server.cpp \
win32_util.hpp winbase_debmod.h
$(F)win32_stub$(O): $(I)../ldr/pe/pe.h $(I)../plugins/pdb/pdb.hpp \
$(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp \
$(I)dbg.hpp $(I)err.h $(I)expr.hpp $(I)fpro.h \
$(I)funcs.hpp $(I)ida.hpp $(I)idd.hpp $(I)idp.hpp \
$(I)kernwin.hpp $(I)lines.hpp $(I)llong.hpp \
$(I)loader.hpp $(I)nalt.hpp $(I)name.hpp $(I)netnode.hpp \
$(I)network.hpp $(I)pro.h $(I)range.hpp $(I)segment.hpp \
$(I)segregs.hpp $(I)typeinf.hpp $(I)ua.hpp $(I)xref.hpp \
../../ldr/pe/pe.h ../common_local_impl.cpp \
../common_stub_impl.cpp ../dbg_rpc_client.h \
../dbg_rpc_engine.h ../dbg_rpc_hlp.h ../deb_pc.hpp \
../debmod.h ../pc_local_impl.cpp ../pc_regs.hpp \
../rpc_debmod.h w32sehch.h win32_local_impl.cpp \
win32_rpc.h win32_stub.cpp
$(F)win32_user$(O): $(I)../ldr/pe/pe.h $(I)../plugins/pdb/pdb.hpp \
$(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp \
$(I)dbg.hpp $(I)err.h $(I)expr.hpp $(I)fpro.h \
$(I)funcs.hpp $(I)ida.hpp $(I)idd.hpp $(I)idp.hpp \
$(I)kernwin.hpp $(I)lines.hpp $(I)llong.hpp \
$(I)loader.hpp $(I)nalt.hpp $(I)name.hpp $(I)netnode.hpp \
$(I)network.hpp $(I)pro.h $(I)range.hpp $(I)segment.hpp \
$(I)segregs.hpp $(I)typeinf.hpp $(I)ua.hpp $(I)xref.hpp \
../../ldr/pe/pe.h ../common_local_impl.cpp \
../common_stub_impl.cpp ../dbg_rpc_hlp.h ../deb_pc.hpp \
../debmod.h ../pc_debmod.h ../pc_local_impl.cpp \
../pc_regs.hpp w32sehch.h win32_debmod.h \
win32_local_impl.cpp win32_rpc.h win32_server_stub.cpp \
win32_user.cpp win32_util.hpp winbase_debmod.h
$(F)win32_util$(O): $(I)bytes.hpp $(I)ida.hpp $(I)idd.hpp $(I)kernwin.hpp \
$(I)lines.hpp $(I)llong.hpp $(I)nalt.hpp $(I)netnode.hpp \
$(I)network.hpp $(I)pro.h $(I)range.hpp $(I)segment.hpp \
$(I)ua.hpp $(I)xref.hpp ../deb_pc.hpp ../debmod.h \
../pc_debmod.h ../pc_regs.hpp win32_util.cpp \
win32_util.hpp winbase_debmod.h
$(F)winbase_debmod$(O): $(I)bytes.hpp $(I)ida.hpp $(I)idd.hpp \
$(I)kernwin.hpp $(I)lines.hpp $(I)llong.hpp $(I)nalt.hpp \
$(I)netnode.hpp $(I)network.hpp $(I)pro.h $(I)range.hpp \
$(I)segment.hpp $(I)ua.hpp $(I)xref.hpp ../deb_pc.hpp \
../debmod.h ../pc_debmod.h ../pc_regs.hpp win32_util.hpp \
winbase_debmod.cpp winbase_debmod.h

View File

@@ -0,0 +1,338 @@
#include <pro.h>
#include <name.hpp>
#include <kernwin.hpp>
#include <dbg.hpp>
#include <loader.hpp>
#include "w32sehch.h"
static int req_id = -1;
//-------------------------------------------------------------------------
// represents data to store on x86seh_chooser_t
struct x86seh_entry_t
{
uint32 handler = 0; // address of SEH handler
uint32 stack = 0; // address of SEH chain on the stack
x86seh_entry_t(uint32 _handler, uint32 _stack)
: handler(_handler), stack(_stack) {}
bool operator ==(const x86seh_entry_t &e) const { return handler == e.handler && stack == e.handler; }
};
DECLARE_TYPE_AS_MOVABLE(x86seh_entry_t);
//-------------------------------------------------------------------------
// non-modal exception handler chooser
struct x86seh_chooser_t : public chooser_t
{
protected:
qvector<x86seh_entry_t> list;
qstring title_;
thid_t tid;
static const int widths_[];
static const char *const header_[];
enum { ICON = 144 };
public:
// this object must be allocated using `new`
x86seh_chooser_t(thid_t tid);
virtual ~x86seh_chooser_t()
{
unhook_from_notification_point(
HT_DBG,
dbg_handler, const_cast<char *>(title));
}
ssize_t choose(uint32 addr = uint32(-1)) //lint !e1511 member hides non-virtual member
{
return ::choose(this, &addr);
}
virtual const void *get_obj_id(size_t *len) const override
{
*len = sizeof(tid);
return &tid;
}
virtual size_t idaapi get_count() const override { return list.size(); }
virtual void idaapi get_row(
qstrvec_t *cols,
int *icon_,
chooser_item_attrs_t *attrs,
size_t n) const override;
virtual cbret_t idaapi enter(size_t n) override;
// calculate the location of the item,
virtual ssize_t idaapi get_item_index(const void *item_data) const override;
virtual bool idaapi init() override;
virtual cbret_t idaapi refresh(ssize_t n) override;
ea_t get_stack_addr(int n) const;
protected:
static ssize_t idaapi dbg_handler(void *ud, int notif_code, va_list va);
};
//-------------------------------------------------------------------------
const int x86seh_chooser_t::widths_[] =
{
CHCOL_HEX | 10, // Address
30, // Name
10, // Stack
};
const char *const x86seh_chooser_t::header_[] =
{
"Address", // 0
"Name", // 1
"Stack", // 2
};
static const char seh_widget_title[] = "Structured exception handlers";
//-------------------------------------------------------------------------
inline x86seh_chooser_t::x86seh_chooser_t(thid_t tid_)
: chooser_t(CH_NOBTNS | CH_FORCE_DEFAULT | CH_CAN_REFRESH,
qnumber(widths_), widths_, header_),
tid(tid_)
{
title_.sprnt("[%04X] - %s", tid, seh_widget_title);
title = title_.c_str();
CASSERT(qnumber(widths_) == qnumber(header_));
icon = ICON;
hook_to_notification_point(
HT_DBG,
dbg_handler, const_cast<char *>(title));
}
//-------------------------------------------------------------------------
void idaapi x86seh_chooser_t::get_row(
qstrvec_t *cols_,
int *,
chooser_item_attrs_t *,
size_t n) const
{
// assert: n < list.size()
uint32 addr = list[n].handler;
qstrvec_t &cols = *cols_;
cols[0].sprnt("%08X", addr);
get_nice_colored_name(&cols[1], addr, GNCN_NOCOLOR | GNCN_NOLABEL);
// set Stack column data
cols[2].sprnt("%08X", list[n].stack);
CASSERT(qnumber(header_) == 3);
}
//-------------------------------------------------------------------------
chooser_t::cbret_t idaapi x86seh_chooser_t::enter(size_t n)
{
// assert: n < list.size()
ea_t ea = ea_t(list[n].handler);
if ( !is_code(get_flags(ea)) )
create_insn(ea);
jumpto(ea);
return cbret_t(); // nothing changed
}
//------------------------------------------------------------------------
ssize_t idaapi x86seh_chooser_t::get_item_index(const void *item_data) const
{
if ( list.empty() )
return NO_SELECTION;
const x86seh_entry_t item = *(const x86seh_entry_t *)item_data;
if ( item.handler == uint32(-1) )
return 0; // first item by default
// find `item_script` in the list
const x86seh_entry_t *p = list.find(item);
if ( p != list.end() )
return p - list.begin();
return 0; // first item by default
}
//--------------------------------------------------------------------------
bool idaapi x86seh_chooser_t::init()
{
// rebuild the handlers list
uint64 fs_sel;
ea_t fs_base;
uint32 excr_ea;
list.clear();
if ( !get_reg_val("fs", &fs_sel)
|| internal_get_sreg_base(&fs_base, tid, int(fs_sel)) <= DRC_NONE
|| read_dbg_memory(fs_base, &excr_ea, sizeof(excr_ea)) != sizeof(excr_ea) )
{
warning("Failed to build the SEH list for thread %08X", tid);
return false; // do not show the empty chooser
}
struct EXC_REG_RECORD
{
uint32 prev;
uint32 handler;
};
EXC_REG_RECORD rec;
std::set<uint32> seen;
while ( excr_ea != 0xffffffff )
{
if ( read_dbg_memory(excr_ea, &rec, sizeof(rec)) != sizeof(rec) )
break;
if ( !seen.insert(excr_ea).second )
{
msg("Circular SEH record has been detected\n");
break;
}
list.push_back(x86seh_entry_t(rec.handler, excr_ea));
excr_ea = rec.prev;
}
return true;
}
//------------------------------------------------------------------------
chooser_t::cbret_t idaapi x86seh_chooser_t::refresh(ssize_t n)
{
uint32 item_addr = uint32(-1);
if ( n >= 0 && n < list.size() )
item_addr = list[n].handler; // remember the currently selected handler
init();
if ( n < 0 )
return NO_SELECTION;
ssize_t idx = get_item_index(&item_addr);
// no need to adjust `idx` as get_item_index() returns first item by
// default
return idx;
}
//-------------------------------------------------------------------------
ea_t x86seh_chooser_t::get_stack_addr(int n) const
{
if ( n < list.size() )
return ea_t(list[n].stack);
return BADADDR;
}
//-------------------------------------------------------------------------
ssize_t idaapi x86seh_chooser_t::dbg_handler(void *ud, int code, va_list)
{
if ( code == dbg_suspend_process )
{
const char *ttl = static_cast<const char *>(ud);
refresh_chooser(ttl);
}
return 0;
}
//-------------------------------------------------------------------------
struct stkview_ah_t : public action_handler_t
{
virtual int idaapi activate(action_activation_ctx_t *ctx) override
{
if ( !ctx->chooser_selection.empty() ) // should always be the case
{
size_t idx = ctx->chooser_selection[0];
const x86seh_chooser_t *c = (x86seh_chooser_t *) ctx->source.chooser;
if ( c == nullptr )
return 0;
ea_t ea = c->get_stack_addr(idx);
if ( ea != BADADDR )
{
TWidget *w = find_widget("Stack view");
if ( w != nullptr )
{
activate_widget(w, true);
jumpto(ea);
}
}
}
return 1;
}
virtual action_state_t idaapi update(action_update_ctx_t *ctx) override
{
return ::qstrstr(ctx->widget_title.c_str(), seh_widget_title) != nullptr
? AST_ENABLE_FOR_WIDGET
: AST_DISABLE_FOR_WIDGET;
}
};
static stkview_ah_t stkview_ah;
//-------------------------------------------------------------------------
struct show_window_ah_t : public action_handler_t
{
virtual int idaapi activate(action_activation_ctx_t *) override
{
thid_t tid = get_current_thread();
x86seh_chooser_t *ch = new x86seh_chooser_t(tid);
bool ok = ch->choose() == 0;
if ( ok )
{
TWidget *w = find_widget(ch->title);
if ( w != nullptr )
{
#define ACTION_NAME "x86seh:Stack"
const action_desc_t stkview_ah_action = ACTION_DESC_LITERAL_OWNER(
ACTION_NAME,
"Follow in stack view",
&stkview_ah,
&PLUGIN,
nullptr,
nullptr,
-1,
ADF_OT_PLUGIN);
register_action(stkview_ah_action);
attach_action_to_popup(
w,
nullptr, // make permanent
ACTION_NAME);
}
}
return ok; //-V773 The function was exited without releasing the 'ch' pointer.
} //lint !e429 Custodial pointer 'ch' has not been freed or returned
virtual action_state_t idaapi update(action_update_ctx_t *) override
{
return AST_ENABLE;
}
};
static show_window_ah_t show_window_ah;
//---------------------------------------------------------------------------
void remove_x86seh_menu()
{
if ( req_id != -1 )
{
cancel_exec_request(req_id);
req_id = -1;
}
}
//---------------------------------------------------------------------------
void install_x86seh_menu()
{
// HACK: We queue this request because commdbg apparently enables the debug menus
// just after calling init_debugger().
struct uireq_install_menu_t: public ui_request_t
{
virtual bool idaapi run() override
{
if ( !inf_is_64bit() )
{
register_and_attach_to_menu(
"Debugger/Debugger windows/Stack trace",
"dbg:sehList", "SEH list", NULL, SETMENU_APP,
&show_window_ah,
&PLUGIN,
ADF_OT_PLUGIN);
}
req_id = -1;
return false;
}
};
req_id = execute_ui_requests(new uireq_install_menu_t, NULL);
}

View File

@@ -0,0 +1,7 @@
#ifndef __W32SEHCH__
#define __W32SEHCH__
void install_x86seh_menu();
void remove_x86seh_menu();
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,427 @@
#ifndef __WIN32_DEBUGGER_MODULE__
#define __WIN32_DEBUGGER_MODULE__
#include <windows.h>
#include <Tlhelp32.h>
#include "../../ldr/pe/pe.h"
#include "winbase_debmod.h"
//-V::720 It is advised to utilize the 'SuspendThread' function only when developing a debugger
// Type definitions
class win32_debmod_t;
//--------------------------------------------------------------------------
// image information
struct image_info_t
{
image_info_t() { memset(this, 0, sizeof(*this)); }
image_info_t(win32_debmod_t *);
image_info_t(win32_debmod_t *, ea_t _base, uint32 _imagesize, const qstring &_name);
image_info_t(win32_debmod_t *, const LOAD_DLL_DEBUG_INFO &i, uint32 _imagesize, const char *_name);
image_info_t(win32_debmod_t *, const modinfo_t &m);
win32_debmod_t *sess;
ea_t base;
uval_t imagesize;
qstring name;
LOAD_DLL_DEBUG_INFO dll_info;
};
// key: image base address
typedef std::map<ea_t, image_info_t> images_t;
//-------------------------------------------------------------------------
struct context_holder_t
{
bytevec_t buffer;
PCONTEXT ptr;
context_holder_t() : ptr(NULL) {}
};
//-------------------------------------------------------------------------
struct context_helper_t
{
typedef DWORD64 (WINAPI *PGETENABLEDXSTATEFEATURES)();
PGETENABLEDXSTATEFEATURES pfnGetEnabledXStateFeatures;
typedef BOOL (WINAPI *PINITIALIZECONTEXT)(PVOID Buffer, DWORD ContextFlags, PCONTEXT *Context, PDWORD ContextLength);
PINITIALIZECONTEXT pfnInitializeContext;
typedef BOOL (WINAPI *PGETXSTATEFEATURESMASK)(PCONTEXT Context, PDWORD64 FeatureMask);
PGETXSTATEFEATURESMASK pfnGetXStateFeaturesMask;
typedef PVOID (WINAPI *LOCATEXSTATEFEATURE)(PCONTEXT Context, DWORD FeatureId, PDWORD Length);
LOCATEXSTATEFEATURE pfnLocateXStateFeature;
typedef BOOL (WINAPI *SETXSTATEFEATURESMASK)(PCONTEXT Context, DWORD64 FeatureMask);
SETXSTATEFEATURESMASK pfnSetXStateFeaturesMask;
typedef BOOL (WINAPI *COPYCONTEXT)(PCONTEXT Destination, DWORD ContextFlags, PCONTEXT Source);
COPYCONTEXT pfnCopyContext;
int xstate_context_size;
bool get_xstate_context_size(int *out_ctxsz);
context_helper_t() { clear(); }
bool create_context(context_holder_t *out, int *ctxflags);
bool xstate_helpers_loaded() const { return xstate_context_size > 0; }
void clear();
};
//--------------------------------------------------------------------------
// thread information
struct thread_info_t : public CREATE_THREAD_DEBUG_INFO
{
thread_info_t(
win32_debmod_t *dm,
const CREATE_THREAD_DEBUG_INFO &i,
thid_t t,
wow64_state_t wow64_state);
win32_debmod_t *debmod;
thid_t tid; // thread id
int suspend_count;
ea_t bpt_ea;
int flags;
#define THR_TRACING 0x0001 // expecting a STEP event
#define THR_WOW64 0x0002 // is wow64 process?
#define THR_NEWNAME 0x0004 // thread was renamed
ea_t callgate_ea;
qstring name;
bool read_context(context_holder_t *out, int clsmask);
bool write_context(int clsmask, CONTEXT &ctx);
bool toggle_tbit(bool set_tbit);
bool is_tracing(void) const { return (flags & THR_TRACING) != 0; }
bool is_wow64(void) const { return (flags & THR_WOW64) != 0; }
void set_tracing(void) { flags |= THR_TRACING; }
void clr_tracing(void) { flags &= ~THR_TRACING; }
bool is_new_name(void) const { return (flags & THR_NEWNAME) != 0; }
void clr_new_name(void) { flags &= ~THR_NEWNAME; }
void set_new_name(void) { flags |= THR_NEWNAME; }
};
//--------------------------------------------------------------------------
inline thread_info_t::thread_info_t(
win32_debmod_t *dm,
const CREATE_THREAD_DEBUG_INFO &i,
thid_t t,
wow64_state_t wow64_state)
: CREATE_THREAD_DEBUG_INFO(i), tid(t), suspend_count(0), bpt_ea(BADADDR),
debmod(dm),
flags(wow64_state > 0 ? THR_WOW64 : 0),
callgate_ea(0)
{
}
//--------------------------------------------------------------------------
// Check if the context structure has valid values at the specified portion
// portion is a conbination of CONTEXT_... bitmasks
inline bool has_portion(const CONTEXT &ctx, int portion)
{
return (ctx.ContextFlags & portion & 0xFFFF) != 0;
}
//--------------------------------------------------------------------------
// (tid -> info)
struct threads_t: public std::map<DWORD, thread_info_t>
{
thread_info_t *get(DWORD tid)
{
const iterator it = find(tid);
if ( it == end() )
return NULL;
return &it->second;
}
};
//--------------------------------------------------------------------------
typedef qvector<thread_info_t> threadvec_t;
//--------------------------------------------------------------------------
// structure for the internal breakpoint information for threads
struct internal_bpt_info_t
{
int count; // number of times this breakpoint is 'set'
uchar orig_bytes[BPT_CODE_SIZE]; // original byte values
};
typedef std::map<ea_t, internal_bpt_info_t> bpt_info_t;
//--------------------------------------------------------------------------
typedef int (*process_cb_t)(debmod_t *, PROCESSENTRY32 *pe32, void *ud);
typedef int (*module_cb_t)(debmod_t *, MODULEENTRY32 *me32, void *ud);
//----------------------------------------------------------------------------
// A live PDB session, that will be used remotely (typically by non-windows machines).
struct pdb_remote_session_t;
void close_pdb_remote_session(pdb_remote_session_t *);
// Wow64-specific events
#ifndef STATUS_WX86_BREAKPOINT
# define STATUS_WX86_BREAKPOINT 0x4000001f
#endif
#ifndef STATUS_WX86_SINGLE_STEP
# define STATUS_WX86_SINGLE_STEP 0x4000001e
#endif
//-------------------------------------------------------------------------
struct machine_thread_state_t;
struct machine_float_state_t;
//--------------------------------------------------------------------------
class win32_debmod_t : public winbase_debmod_t
{
typedef winbase_debmod_t inherited;
regctx_t *reg_ctx;
gdecode_t get_debug_event(debug_event_t *event, int timeout_ms);
void check_thread(bool must_be_main_thread) const;
void add_thread(const CREATE_THREAD_DEBUG_INFO &thr_info, thid_t tid);
void install_callgate_workaround(thread_info_t *ti, const debug_event_t *event);
int describe_stack_segment(
thid_t tid,
images_t &thr_ranges,
images_t &cls_ranges,
const _NT_TIB &tib,
const char *pref);
void update_thread_names(thread_name_vec_t *thr_names);
bool get_pe_exports_from_path(
const char *path,
linput_t *li,
ea_t imagebase,
name_info_t &ni,
const char *exported_name=NULL) const;
void _term_reg_ctx(void);
public:
// debugged process information
qstring process_path;
HANDLE thread_handle;
HANDLE redirin_handle;
HANDLE redirout_handle;
attach_status_t attach_status;
HANDLE attach_evid;
int8 expecting_debug_break;
bool stop_at_ntdll_bpts;
images_t curproc; // image of the running process
images_t dlls; // list of loaded DLLs
images_t images; // list of detected PE images
images_t thread_ranges; // list of ranges related to threads
images_t class_ranges; // list of ranges related to class names
easet_t dlls_to_import; // list of dlls to import information from
modinfo_t binary_to_import; // executable to import information from
bpt_info_t thread_bpts;
threads_t threads;
// ID of a thread for which we must emulate a STEP event on XP (using a breakpoint)
thid_t winxp_step_thread;
CREATE_PROCESS_DEBUG_INFO cpdi;
debug_event_t *in_event; // current debug event
bool fake_suspend_event;
bool exiting;
bool pause_requested;
procinfo_vec_t processes;
// threads suspended by the fiber created for restoring broken connections
threadvec_t _suspended_threads;
// event to wait until the broken connection is completely restored
HANDLE broken_event_handle;
context_helper_t context_helper;
// Module specific methods, to be implemented
virtual void idaapi dbg_set_debugging(bool _debug_debugger) override;
virtual drc_t idaapi dbg_init(uint32_t *flags2, qstring *errbuf) override;
virtual void idaapi dbg_term(void) override;
virtual drc_t idaapi dbg_detach_process(void) override;
virtual drc_t idaapi dbg_start_process(
const char *path,
const char *args,
const char *startdir,
int flags,
const char *input_path,
uint32 input_file_crc32,
qstring *errbuf) override;
virtual gdecode_t idaapi dbg_get_debug_event(debug_event_t *event, int timeout_ms) override;
virtual drc_t idaapi dbg_attach_process(
pid_t process_id,
int event_id,
int flags,
qstring *errbuf) override;
virtual drc_t idaapi dbg_prepare_to_pause_process(qstring *errbuf) override;
virtual drc_t idaapi dbg_exit_process(qstring *errbuf) override;
virtual drc_t idaapi dbg_continue_after_event(const debug_event_t *event) override;
virtual void idaapi dbg_stopped_at_debug_event(
import_infos_t *infos,
bool dlls_added,
thread_name_vec_t *thr_names) override;
virtual drc_t idaapi dbg_thread_suspend(thid_t thread_id) override;
virtual drc_t idaapi dbg_thread_continue(thid_t thread_id) override;
virtual drc_t idaapi dbg_set_resume_mode(thid_t thread_id, resume_mode_t resmod) override;
virtual drc_t idaapi dbg_read_registers(
thid_t thread_id,
int clsmask,
regval_t *values,
qstring *errbuf) override;
virtual drc_t idaapi dbg_write_register(
thid_t thread_id,
int reg_idx,
const regval_t *value,
qstring *errbuf) override;
virtual drc_t idaapi dbg_thread_get_sreg_base(ea_t *ea, thid_t thread_id, int sreg_value, qstring *errbuf) override;
virtual drc_t idaapi dbg_get_memory_info(meminfo_vec_t &ranges, qstring *errbuf) override;
virtual ssize_t idaapi dbg_read_memory(ea_t ea, void *buffer, size_t size, qstring *errbuf) override;
virtual ssize_t idaapi dbg_write_memory(ea_t ea, const void *buffer, size_t size, qstring *errbuf) override;
virtual int idaapi dbg_add_bpt(bytevec_t *orig_bytes, bpttype_t type, ea_t ea, int len) override;
virtual int idaapi dbg_del_bpt(bpttype_t type, ea_t ea, const uchar *orig_bytes, int len) override;
virtual int idaapi handle_ioctl(int fn, const void *buf, size_t size, void **outbuf, ssize_t *outsize) override;
//
win32_debmod_t();
~win32_debmod_t() { cleanup(); _term_reg_ctx(); }
virtual void init_reg_ctx(void) override;
virtual void term_reg_ctx(void) override;
bool get_thread_state(
context_holder_t *out_ctxh,
machine_thread_state_t *out_regs,
machine_float_state_t *out_floats,
thid_t tid,
int clsmask);
bool set_thread_state(
const machine_thread_state_t &regs,
const machine_float_state_t &floats,
const context_holder_t &ctxh,
thid_t tid,
int clsmask);
void handle_pdb_thread_request(void *data);
uint32 calc_imagesize(eanat_t base);
bool get_filename_for(
char *buf,
size_t bufsize,
eanat_t image_name_ea,
bool use_unicode,
eanat_t image_base);
ea_t get_dll_export(
const images_t &dlls,
ea_t imagebase,
const char *exported_name);
bool create_process(
const char *path,
const char *args,
const char *startdir,
bool is_gui,
bool hide_window,
PROCESS_INFORMATION *ProcessInformation);
void show_debug_event(const DEBUG_EVENT &ev);
ssize_t _read_memory(eanat_t ea, void *buffer, size_t size, bool suspend = false);
ssize_t _write_memory(eanat_t ea, const void *buffer, size_t size, bool suspend = false);
int rdmsr(int reg, uint64 *value);
int wrmsr(int reg, uint64 value);
int kldbgdrv_access_msr(struct SYSDBG_MSR *msr, bool write);
// !! OVERWRITTEN METHODS !!
bool refresh_hwbpts();
// Utility methods
gdecode_t handle_exception(debug_event_t *event,
const EXCEPTION_RECORD &er,
bool was_thread_bpt,
bool firsttime);
ssize_t access_memory(eanat_t ea, void *buffer, ssize_t size, bool write, bool suspend);
inline void resume_all_threads(bool raw = false);
inline void suspend_all_threads(bool raw = false);
size_t add_dll(image_info_t &ii);
bool module_present(const char *modname);
HANDLE get_thread_handle(thid_t tid);
static int get_dmi_cb(debmod_t *sess, MODULEENTRY32 *me32, void *ud);
void get_debugged_module_info(modinfo_t *dmi);
int for_each_module(DWORD pid, module_cb_t module_cb, void *ud);
bool myCloseHandle(HANDLE &h);
void cleanup(void);
void restore_original_bytes(ea_t ea, bool really_restore = true);
int save_original_bytes(ea_t ea);
bool set_thread_bpt(thread_info_t &ti, ea_t ea);
bool del_thread_bpt(thread_info_t &ti, ea_t ea);
bool del_thread_bpts(ea_t ea);
bool has_bpt_at(ea_t ea);
bool can_access(ea_t addr);
ea_t get_kernel_bpt_ea(ea_t ea, thid_t tid);
void create_attach_event(debug_event_t *event, bool attached);
void create_start_event(debug_event_t *event);
bool check_for_hwbpt(debug_event_t *event, bool is_stepping=false);
ea_t get_region_info(ea_t ea, memory_info_t *info);
bool get_dll_exports(
const images_t &dlls,
ea_t imagebase,
name_info_t &ni,
const char *exported_name = NULL);
bool get_filename_from_process(
eanat_t name_ea,
bool is_unicode,
char *buf,
size_t bufsize);
bool get_debug_string(const DEBUG_EVENT &ev, char *buf, size_t bufsize);
int add_thread_ranges(
thid_t tid,
images_t &thread_ranges,
images_t &class_ranges);
ea_t get_pe_header(eanat_t imagebase, peheader_t *nh);
bool get_pe_export_name_from_process(
eanat_t imagebase,
char *name,
size_t namesize);
void show_exception_record(const EXCEPTION_RECORD &er, int level=0);
eanat_t pstos0(eanat_t ea);
eanat_t s0tops(eanat_t ea);
bool prepare_to_stop_process(debug_event_t *, const threads_t &);
bool disable_hwbpts();
bool enable_hwbpts();
bool may_write(ea_t ea);
LPVOID correct_exe_image_base(LPVOID base);
bool clear_tbit(thread_info_t &th);
void enqueue_event(const debug_event_t &ev, queue_pos_t pos);
void suspend_running_threads(threadvec_t &suspended);
void resume_suspended_threads(threadvec_t suspended) const;
bool reopen_threads(void);
virtual bool idaapi write_registers(
thid_t thread_id,
int start,
int count,
const regval_t *values) override;
virtual bool idaapi dbg_prepare_broken_connection(void) override;
virtual bool idaapi dbg_continue_broken_connection(pid_t pid) override;
qvector<pdb_remote_session_t*> pdb_remote_sessions;
pdb_remote_session_t *get_pdb_session(int id);
void delete_pdb_session(int id);
protected:
virtual int dbg_freeze_threads_except(thid_t tid) override;
virtual int dbg_thaw_threads_except(thid_t tid) override;
};
ea_t s0tops(ea_t ea);
#endif

View File

@@ -0,0 +1,717 @@
//
//
// This file contains win32 specific implementations of win32_debmod class
//
//
#include <diskio.hpp>
#include "win32_rpc.h"
#include "win32_undoc.h"
#include "dbg_pe_hlp.cpp"
struct impfunc_t
{
const char *name;
void *fptr;
};
#define IMPFUNC(x) { TEXT(#x), &x }
//lint -esym(843,ntdll) -esym(844,ntdll) could be const
static HMODULE ntdll = NULL;
static NtSystemDebugControl_t *NtSystemDebugControl;
static NtLoadDriver_t *NtLoadDriver;
static NtUnloadDriver_t *NtUnloadDriver;
static RtlAdjustPrivilege_t *RtlAdjustPrivilege;
static NtCreateFile_t *NtCreateFile;
static NtDeviceIoControlFile_t *NtDeviceIoControlFile;
static const impfunc_t ntfuncs[] =
{
IMPFUNC(NtSystemDebugControl),
IMPFUNC(NtLoadDriver),
IMPFUNC(NtUnloadDriver),
IMPFUNC(RtlAdjustPrivilege),
IMPFUNC(NtCreateFile),
IMPFUNC(NtDeviceIoControlFile),
};
// To read MSRs, we use a local kernel debugger driver provided by Microsoft.
//lint -esym(843,DriverHandle,DriverPath,DriverName) could be const
//lint -esym(844,DriverHandle) could be pointing to const
static HANDLE DriverHandle = NULL;
static UNICODE_STRING DriverPath = RTL_CONSTANT_STRING(L"\\REGISTRY\\MACHINE\\SYSTEM\\CURRENTCONTROLSET\\SERVICES\\kldbgdrv");
static UNICODE_STRING DriverName = RTL_CONSTANT_STRING(L"\\Device\\kldbgdrv");
//--------------------------------------------------------------------------
// PE COMMON HELPER FUNCTIONS
#define lread myread // since we can't use loader_failure()
inline void myread(linput_t *li, void *buf, size_t size)
{
int bytes_read = qlread(li, buf, size);
if ( bytes_read != size )
{
int saved_code = qerrcode();
const char *errmsg = qerrstr();
uint64 pos = qltell(li) - bytes_read;
static const char *const format =
"Read error: %s\n"
"(file position 0x%" FMT_64 "X, wanted 0x%" FMT_Z "X bytes, read 0x%X)";
error(format,
saved_code ? errmsg : "read past end of file",
pos,
size,
bytes_read);
}
}
#include "../../ldr/pe/common.cpp"
#define GetMappedFileName_Name "GetMappedFileNameW"
#define GetModuleFileNameEx_Name "GetModuleFileNameExW"
// function prototypes
typedef DWORD (WINAPI *GetMappedFileName_t)(HANDLE hProcess, LPVOID lpv, LPWSTR lpFilename, DWORD nSize);
typedef DWORD (WINAPI *GetModuleFileNameEx_t)(HANDLE hProcess, HMODULE hModule, LPWSTR lpFilename, DWORD nSize);
// functions pointers
//lint -esym(843,_GetMappedFileName,_GetModuleFileNameEx) could be const
static GetMappedFileName_t _GetMappedFileName = NULL;
static GetModuleFileNameEx_t _GetModuleFileNameEx = NULL;
// dynamic linking information for PSAPI functions
//lint -esym(843,hPSAPI) -esym(844,hPSAPI) could be const
static HMODULE hPSAPI = NULL;
// dw32 support
//lint -esym(843,system_teb_size) could be const
static DWORD system_teb_size = MEMORY_PAGE_SIZE;
//--------------------------------------------------------------------------
LPVOID win32_debmod_t::correct_exe_image_base(LPVOID base)
{
return base;
}
//--------------------------------------------------------------------------
eanat_t win32_debmod_t::s0tops(eanat_t ea)
{
return ea;
}
//--------------------------------------------------------------------------
eanat_t win32_debmod_t::pstos0(eanat_t ea)
{
return ea;
}
//--------------------------------------------------------------------------
bool win32_debmod_t::prepare_to_stop_process(debug_event_t *, const threads_t &)
{
return true;
}
//--------------------------------------------------------------------------
bool win32_debmod_t::disable_hwbpts()
{
for ( page_bpts_t::iterator p = page_bpts.begin(); p != page_bpts.end(); ++p )
dbg_enable_page_bpt(p, false);
return true;
}
//--------------------------------------------------------------------------
bool win32_debmod_t::enable_hwbpts()
{
for ( page_bpts_t::iterator p = page_bpts.begin(); p != page_bpts.end(); ++p )
dbg_enable_page_bpt(p, true);
return true;
}
//--------------------------------------------------------------------------
bool win32_debmod_t::may_write(ea_t /*ea*/)
{
return true;
}
//--------------------------------------------------------------------------
int win32_debmod_t::describe_stack_segment(
thid_t tid,
images_t &thr_ranges,
images_t &cls_ranges,
const _NT_TIB &tib,
const char *pref) // "x64" for x64 part of wow64 processes
{
int cnt = 1;
char name[MAXSTR];
asize_t size = EA_T(tib.StackBase) - EA_T(tib.StackLimit);
qsnprintf(name, sizeof(name), "%sStack[%08X]", pref, tid);
image_info_t ii_stack(this, EA_T(tib.StackLimit), size, name);
thr_ranges.insert(std::make_pair(ii_stack.base, ii_stack));
ii_stack.name = "STACK";
cls_ranges.insert(std::make_pair(ii_stack.base, ii_stack));
// verify a Stack PAGE_GUARD page exists
ea_t ea_guard = ii_stack.base - MEMORY_PAGE_SIZE;
MEMORY_BASIC_INFORMATION MemoryBasicInformation;
if ( VirtualQueryEx(process_handle, (LPCVOID)(size_t)ea_guard,
&MemoryBasicInformation, sizeof(MemoryBasicInformation)) )
{
if ( MemoryBasicInformation.Protect & PAGE_GUARD ) // a Stack PAGE_GUARD exists
{
qsnprintf(name, sizeof(name), "%sStack_PAGE_GUARD[%08X]", pref, tid);
image_info_t ii_guard(this, ea_guard, MEMORY_PAGE_SIZE, name);
thr_ranges.insert(std::make_pair(ii_guard.base, ii_guard));
ii_guard.name = "STACK";
cls_ranges.insert(std::make_pair(ii_guard.base, ii_guard));
cnt++;
}
}
return cnt;
}
//--------------------------------------------------------------------------
int win32_debmod_t::add_thread_ranges(
thid_t tid,
images_t &thr_ranges,
images_t &cls_ranges)
{
thread_info_t *ti = threads.get(tid);
if ( ti == NULL )
return 0;
// This structure is specific to NT, but stack related records are Win9X compatible
_NT_TIB tib;
ea_t ea_tib = EA_T(ti->lpThreadLocalBase);
if ( _read_memory(ea_tib, &tib, sizeof(tib)) != sizeof(tib) ) // read the TIB
return 0;
// additional test: we verify that TIB->Self contains the TIB's linear address
if ( EA_T(tib.Self) != ea_tib )
return false;
// add TIB range
char name[MAXSTR];
qsnprintf(name, sizeof(name), "TIB[%08X]", tid);
// we suppose the whole page is reserved for the TIB
image_info_t ii_tib(this, ea_tib, system_teb_size, name);
thr_ranges.insert(std::make_pair(ii_tib.base, ii_tib));
int cnt = 0;
const char *pref = "";
if ( check_wow64_process() == WOW64_YES )
{
// Note: This works for Windows versions <= 8.1
// The offset of the 32-bit TEB address within the 64-bit TEB is 0.
// This can be used to directly access the 32-bit TEB of a WOW64 thread
ea_t wow64_tib_ea = *(uint32*)&tib;
struct _NT_TIB32
{
DWORD ExceptionList;
DWORD StackBase;
DWORD StackLimit;
DWORD SubSystemTib;
DWORD FiberData;
DWORD ArbitraryUserPointer;
DWORD Self;
};
_NT_TIB32 tib32;
if ( _read_memory(wow64_tib_ea, &tib32, sizeof(tib32)) == sizeof(tib32) )
{
_NT_TIB tib2;
tib2.StackBase = (PVOID)(eanat_t)tib32.StackBase;
tib2.StackLimit = (PVOID)(eanat_t)tib32.StackLimit;
cnt += describe_stack_segment(tid, thr_ranges, cls_ranges, tib2, pref);
}
pref = "x64";
}
// add stack range
cnt += describe_stack_segment(tid, thr_ranges, cls_ranges, tib, pref);
return cnt;
}
//--------------------------------------------------------------------------
// Get PE header
// In: ea=DLL imagebase, nh=buffer to keep the answer
// child==true:ea is an address in the child process
// child==false:ea is an address in the the debugger itself
// Returns: offset to the headers, BADADDR means failure
ea_t win32_debmod_t::get_pe_header(eanat_t ea, peheader_t *nh)
{
uint32 offset = 0;
uint32 magic;
if ( _read_memory(ea, &magic, sizeof(magic)) != sizeof(magic) )
return BADADDR;
if ( ushort(magic) == MC2('M','Z') )
{
if ( _read_memory(ea+PE_PTROFF, &offset, sizeof(offset)) != sizeof(offset) )
return BADADDR;
}
peheader64_t pe64;
if ( _read_memory(ea+offset, &pe64, sizeof(pe64)) != sizeof(pe64) )
return BADADDR;
if ( !pe64_to_pe(*nh, pe64, true, true) )
return BADADDR;
if ( nh->signature != PEEXE_ID )
return BADADDR;
return offset;
}
//--------------------------------------------------------------------------
// calculate dll image size
// since we could not find anything nice, we just look
// at the beginning of the DLL module in the memory and extract
// correct value from the file header
uint32 win32_debmod_t::calc_imagesize(eanat_t base)
{
peheader_t nh;
ea_t peoff = get_pe_header(base, &nh);
if ( peoff == BADADDR )
return 0;
return nh.imagesize;
}
//--------------------------------------------------------------------------
bool win32_debmod_t::create_process(
const char *path,
const char *args,
const char *startdir,
bool is_gui,
bool hide_window,
PROCESS_INFORMATION *ProcessInformation)
{
linput_t *li = open_linput(path, false);
if ( li == NULL )
return false;
pe_loader_t pl;
pl.read_header(li, true);
close_linput(li);
#ifndef __EA64__
if ( pl.pe.is_pe_plus() )
{
dwarning("AUTOHIDE NONE\nPlease use ida64 to debug 64-bit applications");
SetLastError(ERROR_NOT_SUPPORTED);
return false;
}
#endif
#ifdef __X86__
if ( pl.pe.is_pe_plus() )
{
static const char server_name[] = "win64_remote64.exe";
if ( ask_yn(ASKBTN_YES,
"AUTOHIDE REGISTRY\nHIDECANCEL\n"
"Debugging 64-bit applications is only possible with the %s server.\n"
"Launch it now?",
server_name) == ASKBTN_YES )
{
do
{
// Switch to the remote win32 debugger
if ( !load_debugger("win32_stub", true) )
{
warning("Failed to switch to the remote windows debugger!");
break;
}
// Form the server path
char server_exe[QMAXPATH];
qmakepath(server_exe, sizeof(server_exe), idadir(NULL), server_name, NULL);
// Try to launch the server
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi;
if ( hide_window )
{
si.dwFlags |= STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE; /* SW_FORCEMINIMIZE ? */
}
if ( !::CreateProcess(server_exe, NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi) )
{
warning("Failed to run the 64-bit remote server!");
break;
}
// Set the remote debugging options: localhost
set_remote_debugger("localhost", "", -1);
// Notify the user
info("Debugging server has been started, please try debugging the program again.");
} while ( false );
}
SetLastError(ERROR_NOT_SUPPORTED);
return false;
}
#endif // __X86__
// Empty directory means our directory
if ( startdir != NULL && startdir[0] == '\0' )
startdir = NULL;
// Args passed as empty string?
if ( args != NULL && args[0] == '\0' )
args = NULL;
launch_process_params_t lpp;
lpp.flags |= LP_TRACE | LP_PATH_WITH_ARGS;
if ( !is_gui )
lpp.flags |= LP_NEW_CONSOLE;
if ( hide_window )
lpp.flags |= LP_HIDE_WINDOW;
lpp.path = path;
lpp.args = args;
lpp.startdir = startdir;
lpp.info = ProcessInformation;
qstring errbuf;
if ( launch_process(lpp, &errbuf) == NULL )
{
dwarning("AUTOHIDE NONE\n%s", errbuf.c_str());
return false;
}
return true;
}
//--------------------------------------------------------------------------
void term_win32_subsystem(void)
{
if ( hPSAPI != NULL )
{
FreeLibrary(hPSAPI);
hPSAPI = NULL;
}
if ( DriverHandle != NULL )
{
CloseHandle(DriverHandle);
DriverHandle = NULL;
NtUnloadDriver(&DriverPath);
}
}
//--------------------------------------------------------------------------
void init_win32_subsystem(void)
{
ntdll = GetModuleHandle(TEXT("ntdll.dll"));
if ( ntdll != NULL )
{
for ( int i=0; i < qnumber(ntfuncs); i++ )
*(FARPROC*)ntfuncs[i].fptr = GetProcAddress(ntdll, ntfuncs[i].name);
}
// load the library
hPSAPI = LoadLibrary(TEXT("psapi.dll"));
if ( hPSAPI != NULL )
{
// find the needed functions
*(FARPROC*)&_GetMappedFileName = GetProcAddress(hPSAPI, TEXT(GetMappedFileName_Name));
*(FARPROC*)&_GetModuleFileNameEx = GetProcAddress(hPSAPI, TEXT(GetModuleFileNameEx_Name));
if ( _GetMappedFileName == NULL )
{
FreeLibrary(hPSAPI);
hPSAPI = NULL;
}
}
}
//--------------------------------------------------------------------------
bool win32_debmod_t::can_access(ea_t addr)
{
char dummy;
return access_memory(addr, &dummy, 1, false, false) == 1;
}
//--------------------------------------------------------------------------
// return the address of all names exported by a DLL in 'ni'
// if 'exported_name' is given, only the address of this exported name will be returned in 'ni'
bool win32_debmod_t::get_pe_exports_from_path(
const char *path,
linput_t *li,
ea_t imagebase,
name_info_t &ni,
const char *exported_name) const
{
// prepare nice name prefix for exported functions names
char prefix[MAXSTR];
qstrncpy(prefix, qbasename(path), sizeof(prefix));
char *ptr = strrchr(prefix, '.');
if ( ptr != NULL )
*ptr = '\0';
qstrlwr(prefix);
pe_loader_t pl;
if ( !pl.read_header(li) )
return false;
struct export_reader_t : public pe_export_visitor_t
{
const char *prefix;
ea_t imagebase;
name_info_t &ni;
const char *exported_name;
export_reader_t(const char *pfx, ea_t base, name_info_t &_ni, const char *exname)
: prefix(pfx), imagebase(base), ni(_ni), exported_name(exname) {}
int idaapi visit_export(uint32 rva, uint32 ord, const char *name, const char *)
{
ea_t fulladdr = imagebase + rva;
if ( exported_name != NULL )
{
if ( strcmp(exported_name, name) == 0 )
{
ni.addrs.push_back(fulladdr);
return 1;
}
}
else
{
qstring n2;
if ( name[0] == '\0' )
n2.sprnt("%s_%u", prefix, ord);
else
n2.sprnt("%s_%s", prefix, name);
ni.addrs.push_back(fulladdr);
ni.names.push_back(n2.extract());
}
return 0;
}
};
export_reader_t er(prefix, imagebase, ni, exported_name);
return pl.process_exports(li, er) >= 0;
}
//--------------------------------------------------------------------------
// return the address of all names exported by a DLL in 'ni'
// if 'exported_name' is given, only the address of this exported name will be returned in 'ni'
bool win32_debmod_t::get_dll_exports(
const images_t &loaded_dlls,
ea_t imagebase,
name_info_t &ni,
const char *exported_name)
{
char prefix[MAXSTR];
images_t::const_iterator p = loaded_dlls.find(imagebase);
if ( p == loaded_dlls.end() )
{
dwarning("get_dll_exports: can't find dll name for imagebase %a", imagebase);
return false;
}
char dname[MAXSTR];
const char *dllname = p->second.name.c_str();
qstrncpy(dname, dllname, sizeof(dname));
if ( debapp_attrs.addrsize == 4 )
replace_system32(dname, MAXSTR);
linput_t *li = open_linput(dname, false);
if ( li == NULL )
{
// sysWOW64: ntdll32.dll does not exist but there is a file called ntdll.dll
if ( stricmp(qbasename(dllname), "ntdll32.dll") != 0 )
return false;
if ( qisabspath(dllname) )
{
qstrncpy(prefix, dllname, sizeof(prefix));
char *fname = qbasename(prefix);
qstrncpy(fname, "ntdll.dll", sizeof(prefix)-(fname-prefix));
dllname = prefix;
}
else
{
#ifdef __X86__
// TODO: On X86 there might by file redirection active on a X64 host
// Therefore we will load on such system always 32 bit DLL, as we can
// access 64 bit one without disabling the redirection
dllname = "C:\\Windows\\System32\\ntdll.dll";
#else
#ifndef __EA64__
dllname = "C:\\Windows\\SysWOW64\\ntdll.dll";
#else
dllname = debapp_attrs.addrsize != 4 ? "C:\\Windows\\System32\\ntdll.dll" : "C:\\Windows\\SysWOW64\\ntdll.dll";
#endif
#endif
}
li = open_linput(dllname, false);
if ( li == NULL )
return false;
}
bool ok = get_pe_exports_from_path(dllname, li, imagebase, ni, exported_name);
close_linput(li);
return ok;
}
//--------------------------------------------------------------------------
// get name from export directory in PE image in debugged process
bool win32_debmod_t::get_pe_export_name_from_process(
eanat_t imagebase,
char *name,
size_t namesize)
{
peheader_t pe;
ea_t peoff = get_pe_header(imagebase, &pe);
if ( peoff != BADADDR && pe.expdir.rva != 0 )
{
eanat_t ea = imagebase + pe.expdir.rva;
peexpdir_t expdir;
if ( _read_memory(ea, &expdir, sizeof(expdir)) == sizeof(expdir) )
{
ea = imagebase + expdir.dllname;
name[0] = '\0';
_read_memory(ea, name, namesize); // don't check the return code because
// we might have read more than necessary
if ( name[0] != '\0' )
return true;
}
}
return false;
}
//--------------------------------------------------------------------------
// Read/write a model specific register using the driver provided by WinDbg.
// The following requirements are imposed by this code:
// - debugger module should be run with admin privileges
// - System must be loaded with /debug switch (use bcdedit.exe to turn it on)
// - Windbg local kernel debugging should be used at least once
// This code is based on a sample kindly provided by Alex Ionescu.
int win32_debmod_t::kldbgdrv_access_msr(SYSDBG_MSR *msr, bool write)
{
NTSTATUS code;
IO_STATUS_BLOCK IoStatusBlock;
if ( DriverHandle == NULL )
{
//
// Acquire 'load driver' privilege
//
BOOLEAN Old;
code = RtlAdjustPrivilege(SE_LOAD_DRIVER_PRIVILEGE, TRUE, FALSE, &Old);
if ( FAILED(code) )
{
dwarning("AUTOHIDE NONE\n"
"Failed to acquire 'load driver' privilege, please run as admin!\n"
"Error: %s\n", winerr(code));
return code;
}
//
// And need this for the driver to accept our commands
// Additionally, system must be booted in /DEBUG mode
//
code = RtlAdjustPrivilege(SE_DEBUG_PRIVILEGE, TRUE, FALSE, &Old);
if ( FAILED(code) )
{
dwarning("AUTOHIDE NONE\n"
"Failed to acquire 'debug' privilege, is system booted in /debug mode?\n"
"Error: %s\n", winerr(code));
return code;
}
//
// Now load the driver
//
code = NtLoadDriver(&DriverPath);
if ( FAILED(code) && code != STATUS_IMAGE_ALREADY_LOADED )
{
dwarning("AUTOHIDE NONE\n"
"Failed to load 'kldbgdrv', please use local kernel debugging at least once!\n"
"Error: %s\n", winerr(code));
return code;
}
//
// Open a handle to it
//
OBJECT_ATTRIBUTES ObjectAttributes;
InitializeObjectAttributes(&ObjectAttributes, &DriverName, OBJ_CASE_INSENSITIVE, NULL, NULL);
code = NtCreateFile(&DriverHandle,
GENERIC_READ | GENERIC_WRITE,
&ObjectAttributes,
&IoStatusBlock,
NULL,
FILE_ATTRIBUTE_NORMAL,
0,
FILE_CREATE,
FILE_NON_DIRECTORY_FILE,
NULL,
0);
if ( FAILED(code) )
{
dwarning("AUTOHIDE NONE\n"
"Failed to open 'kldbgdrv'\n"
"Error: %s\n", winerr(code));
return code;
}
}
//
// Package the input parameters into the private structure
//
KLDD_DATA_DEBUG_CONTROL KldDebugCommand;
KldDebugCommand.Command = write ? SysDbgWriteMsr : SysDbgReadMsr;
KldDebugCommand.InputBuffer = msr;
KldDebugCommand.InputBufferLength = sizeof(*msr);
//
// Send the request -- output isn't packaged, just specify directly the buffer
//
code = NtDeviceIoControlFile(DriverHandle,
NULL,
NULL,
NULL,
&IoStatusBlock,
KLDD_CODE_DEBUG_CONTROL,
&KldDebugCommand,
sizeof(KldDebugCommand),
msr,
sizeof(*msr));
if ( FAILED(code) )
{
dwarning("AUTOHIDE NONE\n"
"Failed to access model specific register, is system booted in /debug mode?\n"
"Error: %s\n", winerr(code));
return code;
}
// all ok!
return code;
}
//--------------------------------------------------------------------------
int win32_debmod_t::rdmsr(int reg, uint64 *value)
{
SYSDBG_MSR msr;
msr.reg = reg;
msr.value = 0; // shut up the compiler
NTSTATUS code;
if ( NtSystemDebugControl == NULL )
code = STATUS_NOT_IMPLEMENTED;
else
code = NtSystemDebugControl(SysDbgReadMsr, &msr, sizeof(msr), &msr, sizeof(msr), 0);
// if failed to read it with SystemDebugControl, try the driver
if ( FAILED(code) )
code = kldbgdrv_access_msr(&msr, false);
if ( SUCCEEDED(code) )
*value = msr.value;
return code;
}
//--------------------------------------------------------------------------
int win32_debmod_t::wrmsr(int reg, uint64 value)
{
SYSDBG_MSR msr;
msr.reg = reg;
msr.value = value;
NTSTATUS code;
if ( NtSystemDebugControl == NULL )
code = STATUS_NOT_IMPLEMENTED;
else
code = NtSystemDebugControl(SysDbgWriteMsr, &msr, sizeof(msr), NULL, 0, 0);
// if failed to write it with SystemDebugControl, try the driver
if ( FAILED(code) )
code = kldbgdrv_access_msr(&msr, true);
return code;
}

View File

@@ -0,0 +1,208 @@
#ifndef __NT__
#define EXCEPTION_ACCESS_VIOLATION STATUS_ACCESS_VIOLATION
#define EXCEPTION_DATATYPE_MISALIGNMENT STATUS_DATATYPE_MISALIGNMENT
#define EXCEPTION_BREAKPOINT STATUS_BREAKPOINT
#define EXCEPTION_SINGLE_STEP STATUS_SINGLE_STEP
#define EXCEPTION_ARRAY_BOUNDS_EXCEEDED STATUS_ARRAY_BOUNDS_EXCEEDED
#define EXCEPTION_FLT_DENORMAL_OPERAND STATUS_FLOAT_DENORMAL_OPERAND
#define EXCEPTION_FLT_DIVIDE_BY_ZERO STATUS_FLOAT_DIVIDE_BY_ZERO
#define EXCEPTION_FLT_INEXACT_RESULT STATUS_FLOAT_INEXACT_RESULT
#define EXCEPTION_FLT_INVALID_OPERATION STATUS_FLOAT_INVALID_OPERATION
#define EXCEPTION_FLT_OVERFLOW STATUS_FLOAT_OVERFLOW
#define EXCEPTION_FLT_STACK_CHECK STATUS_FLOAT_STACK_CHECK
#define EXCEPTION_FLT_UNDERFLOW STATUS_FLOAT_UNDERFLOW
#define EXCEPTION_INT_DIVIDE_BY_ZERO STATUS_INTEGER_DIVIDE_BY_ZERO
#define EXCEPTION_INT_OVERFLOW STATUS_INTEGER_OVERFLOW
#define EXCEPTION_PRIV_INSTRUCTION STATUS_PRIVILEGED_INSTRUCTION
#define EXCEPTION_IN_PAGE_ERROR STATUS_IN_PAGE_ERROR
#define EXCEPTION_ILLEGAL_INSTRUCTION STATUS_ILLEGAL_INSTRUCTION
#define EXCEPTION_NONCONTINUABLE_EXCEPTION STATUS_NONCONTINUABLE_EXCEPTION
#define EXCEPTION_STACK_OVERFLOW STATUS_STACK_OVERFLOW
#define EXCEPTION_INVALID_DISPOSITION STATUS_INVALID_DISPOSITION
#define EXCEPTION_GUARD_PAGE STATUS_GUARD_PAGE_VIOLATION
#define EXCEPTION_INVALID_HANDLE STATUS_INVALID_HANDLE
#define CONTROL_C_EXIT STATUS_CONTROL_C_EXIT
#define DBG_CONTROL_C 0x40010005L
#define DBG_CONTROL_BREAK 0x40010008L
#define STATUS_GUARD_PAGE_VIOLATION 0x80000001L
#define STATUS_DATATYPE_MISALIGNMENT 0x80000002L
#define STATUS_BREAKPOINT 0x80000003L
#define STATUS_SINGLE_STEP 0x80000004L
#define STATUS_ACCESS_VIOLATION 0xC0000005L
#define STATUS_IN_PAGE_ERROR 0xC0000006L
#define STATUS_INVALID_HANDLE 0xC0000008L
#define STATUS_NO_MEMORY 0xC0000017L
#define STATUS_ILLEGAL_INSTRUCTION 0xC000001DL
#define STATUS_NONCONTINUABLE_EXCEPTION 0xC0000025L
#define STATUS_INVALID_DISPOSITION 0xC0000026L
#define STATUS_ARRAY_BOUNDS_EXCEEDED 0xC000008CL
#define STATUS_FLOAT_DENORMAL_OPERAND 0xC000008DL
#define STATUS_FLOAT_DIVIDE_BY_ZERO 0xC000008EL
#define STATUS_FLOAT_INEXACT_RESULT 0xC000008FL
#define STATUS_FLOAT_INVALID_OPERATION 0xC0000090L
#define STATUS_FLOAT_OVERFLOW 0xC0000091L
#define STATUS_FLOAT_STACK_CHECK 0xC0000092L
#define STATUS_FLOAT_UNDERFLOW 0xC0000093L
#define STATUS_INTEGER_DIVIDE_BY_ZERO 0xC0000094L
#define STATUS_INTEGER_OVERFLOW 0xC0000095L
#define STATUS_PRIVILEGED_INSTRUCTION 0xC0000096L
#define STATUS_STACK_OVERFLOW 0xC00000FDL
#define STATUS_CONTROL_C_EXIT 0xC000013AL
#define STATUS_FLOAT_MULTIPLE_FAULTS 0xC00002B4L
#define STATUS_FLOAT_MULTIPLE_TRAPS 0xC00002B5L
#define STATUS_REG_NAT_CONSUMPTION 0xC00002C9L
#define SUCCEEDED(x) (x >= 0)
#define FAILED(x) (x < 0)
#endif
#include <expr.hpp>
#include <loader.hpp>
#include "../ldr/pe/pe.h"
#include "../plugins/pdb/pdb.hpp"
#include "win32_rpc.h"
#include "dbg_rpc_hlp.h"
//--------------------------------------------------------------------------
static const char idc_win32_rdmsr_args[] = { VT_LONG, 0 };
static error_t idaapi idc_win32_rdmsr(idc_value_t *argv, idc_value_t *res)
{
uint64 value = 0; // shut up the compiler
uval_t reg = argv[0].num;
#ifdef RPC_CLIENT
void *out = NULL;
ssize_t outsize;
int code = g_dbgmod.send_ioctl(WIN32_IOCTL_RDMSR, &reg, sizeof(reg), &out, &outsize);
if ( SUCCEEDED(code) && outsize == sizeof(value) )
value = *(uint64*)out;
qfree(out);
#else
int code = g_dbgmod.rdmsr(reg, &value);
#endif
if ( FAILED(code) )
{
res->num = code;
return set_qerrno(eExecThrow); // read error, raise exception
}
res->set_int64(value);
return eOk;
}
//--------------------------------------------------------------------------
static const char idc_win32_wrmsr_args[] = { VT_LONG, VT_INT64, 0 };
static error_t idaapi idc_win32_wrmsr(idc_value_t *argv, idc_value_t *res)
{
win32_wrmsr_t msr;
msr.reg = argv[0].num;
msr.value = argv[1].i64;
#ifdef RPC_CLIENT
res->num = g_dbgmod.send_ioctl(WIN32_IOCTL_WRMSR, &msr, sizeof(msr), NULL, NULL);
#else
res->num = g_dbgmod.wrmsr(msr.reg, msr.value);
#endif
return eOk;
}
//--------------------------------------------------------------------------
// Installs or uninstalls debugger specific idc functions
static bool register_idc_funcs(bool reg)
{
static const ext_idcfunc_t idcfuncs[] =
{
{ IDC_READ_MSR, idc_win32_rdmsr, idc_win32_rdmsr_args, NULL, 0, 0 },
{ IDC_WRITE_MSR, idc_win32_wrmsr, idc_win32_wrmsr_args, NULL, 0, 0 },
};
return add_idc_funcs(idcfuncs, qnumber(idcfuncs), reg);
}
//--------------------------------------------------------------------------
void idaapi rebase_if_required_to(ea_t new_base)
{
netnode penode(PE_NODE);
ea_t currentbase = new_base;
ea_t imagebase = ea_t(penode.altval(PE_ALT_IMAGEBASE)); // loading address (usually pe.imagebase)
if ( imagebase == 0 )
{
if ( !is_miniidb() )
warning("AUTOHIDE DATABASE\n"
"IDA could not automatically determine if the program should be\n"
"rebased in the database because the database format is too old and\n"
"doesn't contain enough information.\n"
"Create a new database if you want automated rebasing to work properly.\n"
"Note you can always manually rebase the program by using the\n"
"Edit, Segments, Rebase program command.");
}
else if ( imagebase != currentbase )
{
rebase_or_warn(imagebase, currentbase);
}
}
//--------------------------------------------------------------------------
bool read_pe_header(peheader_t *pe)
{
netnode penode(PE_NODE);
return penode.valobj(pe, sizeof(peheader_t)) > 0;
}
//--------------------------------------------------------------------------
// Initialize Win32 debugger plugin
static bool win32_init_plugin(void)
{
// Remote debugger? Then nothing to initialize locally
#ifndef RPC_CLIENT
if ( !init_subsystem() )
return false;
#endif
if ( !netnode::inited() || is_miniidb() || inf_is_snapshot() )
{
#ifndef __NT__
// local debugger is available if we are running under Windows
// for other systems only the remote debugger is available
if ( !debugger.is_remote() )
return false;
#endif
}
else
{
if ( inf_get_filetype() != f_PE )
return false; // only PE files
processor_t &ph = PH;
if ( ph.id != TARGET_PROCESSOR && ph.id != -1 )
return false;
// find out the pe header
peheader_t pe;
if ( !read_pe_header(&pe) )
return false;
// debug only gui, console, or unknown applications
if ( pe.subsys != PES_WINGUI // Windows GUI
&& pe.subsys != PES_WINCHAR // Windows Character
&& pe.subsys != PES_UNKNOWN ) // Unknown
{
return false;
}
}
return true;
}
//--------------------------------------------------------------------------
inline void win32_term_plugin(void)
{
#ifndef RPC_CLIENT
term_subsystem();
#endif
}
//----------------------------------------------------------------------------
struct pdb_remote_session_t;
void close_pdb_remote_session(pdb_remote_session_t *)
{
}
#ifndef HAVE_PLUGIN_COMMENTS
//--------------------------------------------------------------------------
static const char comment[] = "Userland win32 debugger plugin";
#endif

View File

@@ -0,0 +1,203 @@
#ifndef WIN32_RPC_H
#define WIN32_RPC_H
// IOCTL codes for the win32 debugger
#define WIN32_IOCTL_RDMSR 0 // read model specific register
#define WIN32_IOCTL_WRMSR 1 // write model specific register
#define WIN32_IOCTL_READFILE 2 // server->client: read bytes from the input file
// uint64 offset;
// uint32 length;
// returns: 1 - ok
// -2 - error (text in output buffer)
// Open file for PDB retrieval.
//
// This operation will *typically* require that executable data
// be provided to the underlying MS PDB "DIA" dll. Therefore,
// there is _no_ way (currently) that this operation will
// immediately return something relevant. The client must
// poll for _OPERATION_COMPLETE-ness.
//
// client->server
// (unpacked) compiler_info_t: compiler_info
// (packed) uint64 : base_address
// (unpacked) char * : input_file
// (unpacked) char * : user symbols path
// server->client
// (packed) uint32 : session handle
#define WIN32_IOCTL_PDB_OPEN 3
// Close PDB 'session', previously opened with _PDB_OPEN.
//
// client->server
// (packed) uint32 : session handle
// server->client
// void
#define WIN32_IOCTL_PDB_CLOSE 4
// Fetch the data for one symbol.
//
// Synchronous operation.
//
// client->server
// (packed) uint32 : session handle
// (packed) uint64 : symbol ID
// server->client
// (unpacked) uint32: The integer value 1.
// (serialized) data: Packed symbol data (once).
#define WIN32_IOCTL_PDB_FETCH_SYMBOL 5
// Fetch the data for the children of a symbol.
//
// Synchronous operation.
//
// client->server
// (packed) uint32 : session handle
// (packed) uint64 : symbol ID
// (packed) uint32 : children type (a SymTagEnum)
// server->client
// (unpacked) uint32: Number of symbols whose data
// has been fetched.
// (serialized) data: Packed symbol data (N times).
#define WIN32_IOCTL_PDB_FETCH_CHILDREN 6
// Is the current operation complete?
//
// Depending on the type of the operation, the contents
// of the results will differ:
// - _OPEN
// (packed) uint64 : Global symbol ID.
// (packed) uint32 : machine type.
// (packed) uint32 : DIA version.
//
// NOTE: Currently, this IOCTL only makes sense to check
// for completeness of operation _OPEN, but this
// might change in the future.
//
// client->server
// (packed) uint32 : session handle
// server->client
// (packed) uint32 : See pdb_op_completion_t
// Depending on this first byte, the following will be come:
// pdb_op_not_complete:
// nothing
// pdb_op_complete:
// (packed) uint32 : global symbol ID
// (packed) uint32 : machine type
// (packed) uint32 : DIA version
// (unpacked) str : used file name
// pdb_op_failure:
// (unpacked) str : error message
#define WIN32_IOCTL_PDB_OPERATION_COMPLETE 7
// Get lines by VA
//
// client->server
// (packed) uint32 : session handle
// (packed) ea_t : VA
// (packed) uint64 : length
// server->client
// (packed) uint32 : the number of line-number objects
// (packed) data : the line-number objects (N times)
//
// Each of the line-number objects is transmitted like so:
// (packed) ea_t : VA
// (packed) uint32 : length
// (packed) uint32 : columnNumber
// (packed) uint32 : columnNumberEnd
// (packed) uint32 : lineNumber
// (packed) uint32 : lineNumberEnd
// (packed) uint32 : file_id
// (unpacked) byte : statement
#define WIN32_IOCTL_PDB_SIP_FETCH_LINES_BY_VA 8
// Get lines by coordinates
//
// client->server
// (packed) uint32 : session handle
// (packed) uint32 : file ID
// (packed) uint32 : lnnum
// (packed) uint32 : colnum
// server->client
// same as WIN32_IOCTL_PDB_SIP_FETCH_LINES_BY_VA
#define WIN32_IOCTL_PDB_SIP_FETCH_LINES_BY_COORDS 9
// Get symbols at EA
//
// client->server
// (packed) uint32 : session handle
// (packed) ea_t : VA
// (packed) uint64 : length
// (packed) uint32 : children type (a SymTagEnum)
// server->client
// (unpacked) uint32: Number of symbols whose data
// has been fetched.
// (serialized) data: Packed symbol data (N times).
#define WIN32_IOCTL_PDB_SIP_FETCH_SYMBOLS_AT_VA 10
// Get compilands for file
//
// client->server
// (packed) uint32 : session handle
// (packed) uint32 : file ID
// server->client
// (unpacked) uint32: Number of symbols whose data
// has been fetched.
// (serialized) data: Packed symbol data (N times).
#define WIN32_IOCTL_PDB_SIP_FETCH_FILE_COMPILANDS 11
// Get path for file ID
//
// client->server
// (packed) uint32 : session handle
// (packed) uint32 : file ID
// server->client
// (unpacked) str : the path
#define WIN32_IOCTL_PDB_SIP_FETCH_FILE_PATH 12
// Get files IDs for files corresponding to symbol
//
// client->server
// (packed) uint32 : session handle
// (packed) uint64 : symbol ID
// server->client
// (packed) uint32 : the number of IDs
// (packed) uint32 : file ID (N times).
#define WIN32_IOCTL_PDB_SIP_FETCH_SYMBOL_FILES 13
// Get files IDs for files whose name matches
//
// client->server
// (packed) uint32 : session handle
// (unpacked) str : the file name
// server->client
// (packed) uint32 : the number of IDs
// (packed) uint32 : file ID (N times).
#define WIN32_IOCTL_PDB_SIP_FIND_FILES 14
enum ioctl_pdb_code_t
{
pdb_ok = 1,
pdb_error = -2,
};
enum pdb_op_completion_t
{
pdb_op_not_complete = 0,
pdb_op_complete = 1,
pdb_op_failure = -1,
};
// WIN32_IOCTL_WRMSR uses this structure:
struct win32_wrmsr_t
{
uint32 reg;
uint64 value;
};
#endif // WIN32_RPC_H

View File

@@ -0,0 +1,406 @@
//
//
// This file contains win32 specific implementations of win32_debugger_module class
// server-side functionality only
//
//
#include <pro.h>
#include "win32_rpc.h"
#include "win32_debmod.h"
#include "dbg_rpc_hlp.h"
bool ida_export idb_utf8(qstring *, const char *, int, int) { return false; }
#include "tilfuncs.hpp"
//---------------------------------------------------------- main thread ---
static AS_PRINTF(3, 4) int pdb_ioctl_error(void **poutbuf, ssize_t *poutsize, const char *format, ...)
{
char buf[MAXSTR];
va_list va;
va_start(va, format);
int len = qvsnprintf(buf, sizeof(buf), format, va);
va_end(va);
msg("%s", buf);
*poutsize = len + 1;
*poutbuf = qstrdup(buf);
return pdb_error;
}
//---------------------------------------------------------- main thread ---
void win32_debmod_t::handle_pdb_thread_request(void *_pdb_rsess)
{
pdb_remote_session_t *pdb_rsess = (pdb_remote_session_t *) _pdb_rsess;
pdb_remote_session_t::client_read_request_t &rr = pdb_rsess->client_read_request;
if ( rr.kind == READ_INPUT_FILE )
{
// read input file
bytevec_t req;
req.pack_dq(rr.off_ea);
req.pack_dd(rr.size);
void *outbuf = NULL;
ssize_t outsize = 0;
// send request to IDA
int rc = send_ioctl(WIN32_IOCTL_READFILE, req.begin(), req.size(), &outbuf, &outsize);
if ( rc == 1 && outbuf != NULL )
{
// OK
size_t copylen = qmin(rr.size, outsize);
memcpy(rr.buffer, outbuf, copylen);
rr.size = copylen;
rr.result = true;
}
else
{
rr.result = false;
}
if ( outbuf != NULL )
qfree(outbuf);
}
else if ( rr.kind == READ_MEMORY )
{
// read memory
ea_t ea = ea_t(rr.off_ea);
void *buf = rr.buffer;
size_t size = rr.size;
ssize_t rc = _read_memory(ea, buf, size);
if ( rc >= 0 )
rr.size = rc;
rr.result = rc >= 0;
}
else
{
// unknown request
rr.result = false;
}
rr.read_complete();
}
//-------------------------------------------------------------------------
pdb_remote_session_t *win32_debmod_t::get_pdb_session(int id)
{
for ( size_t i = 0; i < pdb_remote_sessions.size(); ++i )
if ( pdb_remote_sessions[i]->get_id() == id )
return pdb_remote_sessions[i];
return NULL;
}
//-------------------------------------------------------------------------
void win32_debmod_t::delete_pdb_session(int id)
{
for ( size_t i = 0; i < pdb_remote_sessions.size(); ++i )
{
if ( pdb_remote_sessions[i]->get_id() == id )
{
pdb_remote_sessions[i]->stop();
delete pdb_remote_sessions[i];
pdb_remote_sessions.erase(pdb_remote_sessions.begin() + i);
break;
}
}
}
//----------------------------------------------------------------------------
void close_pdb_remote_session(pdb_remote_session_t *session)
{
session->stop();
delete session;
}
//---------------------------------------------------------- main thread ---
int idaapi win32_debmod_t::handle_ioctl(
int fn,
const void *buf,
size_t size,
void **poutbuf,
ssize_t *poutsize)
{
qnotused(size);
int sid = 0; // pdb_remote_session_t ID
pdb_remote_session_t *pdb_rsess = NULL;
switch ( fn )
{
case WIN32_IOCTL_RDMSR:
QASSERT(30119, size == sizeof(uval_t));
{
uint64 value;
uval_t reg = *(uval_t *)buf;
int code = rdmsr(reg, &value);
if ( SUCCEEDED(code) )
{
*poutbuf = qalloc(sizeof(value));
if ( *poutbuf != NULL )
{
memcpy(*poutbuf, &value, sizeof(value));
*poutsize = sizeof(value);
}
}
return code;
}
case WIN32_IOCTL_WRMSR:
QASSERT(30120, size == sizeof(win32_wrmsr_t));
{
win32_wrmsr_t &msr = *(win32_wrmsr_t *)buf;
return wrmsr(msr.reg, msr.value);
}
#define ENSURE_PDB_THREAD() \
do \
{ \
if ( !pdb_thread.is_running() ) \
return pdb_ioctl_error( \
poutbuf, poutsize, "PDB thread not running?!?\n"); \
} while ( false )
#define GET_OPENED_SESSION() \
do \
{ \
if ( mmdsr.empty() ) \
return pdb_error; \
sid = mmdsr.unpack_dd(); \
pdb_rsess = get_pdb_session(sid); \
} while ( false )
#define ENSURE_SESSION_OPENED() \
do \
{ \
GET_OPENED_SESSION(); \
if ( pdb_rsess == NULL ) \
return pdb_ioctl_error( \
poutbuf, poutsize, "Unknown PDB session #%d\n", sid); \
} while ( false )
#define FLUSH_PDB_REQUEST_STORAGE() \
do \
{ \
size_t sz = pdb_rsess->storage.size(); \
uint8 *raw = (uint8 *) qalloc(sz); \
if ( raw == NULL ) \
return pdb_error; \
memcpy(raw, pdb_rsess->storage.begin(), sz); \
*poutbuf = raw; \
*poutsize = sz; \
} while ( false )
case WIN32_IOCTL_PDB_OPEN:
{
pdb_thread.start_if_needed();
memory_deserializer_t mmdsr(buf, size);
pdb_rsess = new pdb_remote_session_t();
pdb_remote_sessions.push_back(pdb_rsess);
compiler_info_t cci;
mmdsr.unpack_obj(&cci, sizeof(cci));
pdbargs_t args;
args.pdb_path = mmdsr.unpack_str();
args.input_path = mmdsr.unpack_str();
mmdsr.unpack_obj(&args.pdb_sign, sizeof(args.pdb_sign));
args.spath = mmdsr.unpack_str();
args.loaded_base = mmdsr.unpack_ea64();
args.flags = mmdsr.unpack_dd();
pdb_rsess->open(cci, args);
bytevec_t storage;
storage.pack_dd(pdb_rsess->get_id());
*poutsize = storage.size();
*poutbuf = storage.extract();
pdb_rsess->is_opening = true;
}
return pdb_ok;
case WIN32_IOCTL_PDB_OPERATION_COMPLETE:
// This is used, in a polling fashion, by the the client,
// to check on completeness of the fetch. At the same time,
// this is our wake-up call for looking whether the
// fetch thread requires more information.
{
ENSURE_PDB_THREAD();
memory_deserializer_t mmdsr(buf, size);
ENSURE_SESSION_OPENED();
// If we've got a read request, handle it now
if ( pdb_rsess->client_read_request.pending() )
handle_pdb_thread_request(pdb_rsess);
bytevec_t storage;
bool done = pdb_rsess->is_done();
if ( done )
{
pdb_rsess->is_opening = false;
const char *fname = pdb_rsess->session_ref.session->get_used_fname();
local_pdb_access_t *acc = pdb_rsess->session_ref.session->pdb_access;
HRESULT hr = acc != NULL ? S_OK : E_FAIL;
if ( SUCCEEDED(hr) )
{
storage.pack_dd(uint32(pdb_op_complete));
storage.pack_dd(acc->get_global_symbol_id());
storage.pack_dd(acc->get_machine_type());
storage.pack_dd(acc->get_dia_version());
storage.pack_str(fname);
}
else
{
storage.pack_dd(uint32(pdb_op_failure));
qstring errmsg;
errmsg.sprnt("%s: %s\n", fname, pdberr(hr));
storage.pack_str(errmsg.c_str());
delete_pdb_session(sid);
}
}
else
{
storage.pack_dd(uint32(pdb_op_not_complete));
}
*poutsize = storage.size();
*poutbuf = storage.extract();
return pdb_ok;
}
case WIN32_IOCTL_PDB_FETCH_SYMBOL:
case WIN32_IOCTL_PDB_FETCH_CHILDREN:
{
ENSURE_PDB_THREAD();
memory_deserializer_t mmdsr(buf, size);
ENSURE_SESSION_OPENED();
DWORD sym_id = mmdsr.unpack_dd();
// msg("Fetch%s 0x%x\n",
// (fn == WIN32_IOCTL_PDB_FETCH_CHILDREN ? " children for" : ""),
// (uint32) sym);
bool ok;
if ( fn == WIN32_IOCTL_PDB_FETCH_SYMBOL )
{
// Symbol
ok = pdb_rsess->fetch_symbol(sym_id);
}
else
{
// Children
enum SymTagEnum children_type = (enum SymTagEnum) mmdsr.unpack_dd();
ok = pdb_rsess->fetch_children(sym_id, children_type);
}
if ( ok )
FLUSH_PDB_REQUEST_STORAGE();
return ok ? pdb_ok : pdb_error;
}
case WIN32_IOCTL_PDB_CLOSE:
{
ENSURE_PDB_THREAD();
memory_deserializer_t mmdsr(buf, size);
GET_OPENED_SESSION();
if ( pdb_rsess != NULL )
delete_pdb_session(sid);
return pdb_ok;
}
case WIN32_IOCTL_PDB_SIP_FETCH_LINES_BY_VA:
{
ENSURE_PDB_THREAD();
memory_deserializer_t mmdsr(buf, size);
ENSURE_SESSION_OPENED();
ea_t va = mmdsr.unpack_ea64();
uint64 length = mmdsr.unpack_dq();
bool ok = pdb_rsess->fetch_lines_by_va(
va, length);
if ( ok )
FLUSH_PDB_REQUEST_STORAGE();
return ok ? pdb_ok : pdb_error;
}
case WIN32_IOCTL_PDB_SIP_FETCH_LINES_BY_COORDS:
{
ENSURE_PDB_THREAD();
memory_deserializer_t mmdsr(buf, size);
ENSURE_SESSION_OPENED();
DWORD file_id = mmdsr.unpack_dd();
DWORD lnnum = mmdsr.unpack_dd();
DWORD colnum = mmdsr.unpack_dd();
bool ok = pdb_rsess->fetch_lines_by_coords(file_id, lnnum, colnum);
if ( ok )
FLUSH_PDB_REQUEST_STORAGE();
return ok ? pdb_ok : pdb_error;
}
case WIN32_IOCTL_PDB_SIP_FETCH_SYMBOLS_AT_VA:
{
ENSURE_PDB_THREAD();
memory_deserializer_t mmdsr(buf, size);
ENSURE_SESSION_OPENED();
ea_t va = mmdsr.unpack_ea64();
uint64 length = mmdsr.unpack_dq();
enum SymTagEnum type = (enum SymTagEnum) mmdsr.unpack_dd();
bool ok = pdb_rsess->fetch_symbols_at_va(va, length, type);
if ( ok )
FLUSH_PDB_REQUEST_STORAGE();
return ok ? pdb_ok : pdb_error;
}
case WIN32_IOCTL_PDB_SIP_FETCH_FILE_COMPILANDS:
{
ENSURE_PDB_THREAD();
memory_deserializer_t mmdsr(buf, size);
ENSURE_SESSION_OPENED();
uint32 file_id = mmdsr.unpack_dd();
bool ok = pdb_rsess->fetch_file_compilands(file_id);
if ( ok )
FLUSH_PDB_REQUEST_STORAGE();
return ok ? pdb_ok : pdb_error;
}
case WIN32_IOCTL_PDB_SIP_FETCH_FILE_PATH:
{
ENSURE_PDB_THREAD();
memory_deserializer_t mmdsr(buf, size);
ENSURE_SESSION_OPENED();
uint32 file_id = mmdsr.unpack_dd();
bool ok = pdb_rsess->fetch_file_path(file_id);
if ( ok )
FLUSH_PDB_REQUEST_STORAGE();
return ok ? pdb_ok : pdb_error;
}
case WIN32_IOCTL_PDB_SIP_FETCH_SYMBOL_FILES:
{
ENSURE_PDB_THREAD();
memory_deserializer_t mmdsr(buf, size);
ENSURE_SESSION_OPENED();
DWORD sym_id = mmdsr.unpack_dd();
bool ok = pdb_rsess->fetch_symbol_files(sym_id);
if ( ok )
FLUSH_PDB_REQUEST_STORAGE();
return ok ? pdb_ok : pdb_error;
}
case WIN32_IOCTL_PDB_SIP_FIND_FILES:
{
ENSURE_PDB_THREAD();
memory_deserializer_t mmdsr(buf, size);
ENSURE_SESSION_OPENED();
qstring fname = mmdsr.unpack_str();
bool ok = pdb_rsess->fetch_files(fname.c_str());
if ( ok )
FLUSH_PDB_REQUEST_STORAGE();
return ok ? pdb_ok : pdb_error;
}
default:
break;
}
return 0;
}

View File

@@ -0,0 +1,15 @@
//
//
// This file contains win32 specific implementations of win32_debugger_module class
// IDA-side functionality only (for local debugger)
//
//
#include <pro.h>
#include "win32_debmod.h"
//--------------------------------------------------------------------------
int idaapi win32_debmod_t::handle_ioctl(int /*fn*/, const void * /*buf*/, size_t /*size*/, void ** /*poutbuf*/, ssize_t * /*poutsize*/)
{
return 0;
}

View File

@@ -0,0 +1,128 @@
#define REMOTE_DEBUGGER
#define RPC_CLIENT
static const char wanted_name[] = "Remote Windows debugger";
#define DEBUGGER_NAME "win32"
#define PROCESSOR_NAME "metapc"
#define DEFAULT_PLATFORM_NAME "win32"
#define TARGET_PROCESSOR PLFM_386
#define DEBUGGER_ID DEBUGGER_ID_X86_IA32_WIN32_USER
#define DEBUGGER_FLAGS (DBG_FLAG_REMOTE \
| DBG_FLAG_EXITSHOTOK \
| DBG_FLAG_LOWCNDS \
| DBG_FLAG_DEBTHREAD \
| DBG_FLAG_ANYSIZE_HWBPT)
#define DEBUGGER_RESMOD (DBG_RESMOD_STEP_INTO)
#define HAVE_APPCALL
#define S_FILETYPE f_PE
#define win32_term_plugin term_plugin
#include <pro.h>
#include <idp.hpp>
#include <idd.hpp>
#include <ua.hpp>
#include <range.hpp>
#include <loader.hpp>
#include <kernwin.hpp>
#include <network.hpp>
#include "w32sehch.h"
#include "dbg_rpc_client.h"
#include "rpc_debmod.h"
class win32_rpc_debmod_t : public rpc_debmod_t
{
typedef rpc_debmod_t inherited;
public:
win32_rpc_debmod_t(const char *default_platform)
: rpc_debmod_t(default_platform) {}
virtual bool idaapi open_remote(
const char *hostname,
int port_number,
const char *password,
qstring *errbuf) override
{
char path[QMAXPATH];
get_input_file_path(path, sizeof(path));
pdb_file_path = path;
return inherited::open_remote(hostname, port_number, password, errbuf);
}
qstring pdb_file_path;
};
win32_rpc_debmod_t g_dbgmod(DEFAULT_PLATFORM_NAME);
#include "common_stub_impl.cpp"
#include "pc_local_impl.cpp"
#include "win32_local_impl.cpp"
//--------------------------------------------------------------------------
// handler on IDA: Server -> IDA
static int ioctl_handler(
rpc_engine_t * /*rpc*/,
int fn,
const void *buf,
size_t size,
void **poutbuf,
ssize_t *poutsize)
{
qnotused(size);
switch ( fn )
{
case WIN32_IOCTL_READFILE:
{
user_cancelled();
const uchar *ptr = (const uchar *)buf;
const uchar *end = ptr + size;
uint64 offset = unpack_dq(&ptr, end);
uint32 length = unpack_dd(&ptr, end);
*poutbuf = NULL;
*poutsize = 0;
if ( length != 0 )
{
FILE *infile = qfopen(g_dbgmod.pdb_file_path.c_str(), "rb");
if ( infile == NULL )
return -2;
void *outbuf = qalloc(length);
if ( outbuf == NULL )
return -2;
qfseek(infile, offset, SEEK_SET);
int readlen = qfread(infile, outbuf, length);
qfclose(infile);
if ( readlen < 0 || readlen > length )
{
qfree(outbuf);
return -2;
}
*poutbuf = outbuf;
*poutsize = readlen;
}
return 1;
}
}
return 0;
}
//lint -esym(528,init_plugin) static symbol '' not referenced
//--------------------------------------------------------------------------
// Initialize Win32 debugger stub
static bool init_plugin(void)
{
// There is no need to call win32_init_plugin() (which checks the PE
// file parameters) if the debugger is only being used to fetch PDBs.
bool should_init = !netnode(PDB_NODE_NAME).altval(PDB_LOADING_WIN32_DBG);
if ( should_init && !win32_init_plugin() )
return false;
g_dbgmod.set_ioctl_handler(ioctl_handler);
return true;
}
#include "common_local_impl.cpp"

View File

@@ -0,0 +1,282 @@
#ifndef WIN32_UNDOC_H
#define WIN32_UNDOC_H
// Definitions for Windows DDK and other undocumented MS Windows types.
// Instead of requiring a DDK and NDK, we just use this file.
//---------------------------------------------------------------------------
// WinDDK types
struct UNICODE_STRING
{
USHORT Length;
USHORT MaximumLength;
PWCH Buffer;
};
typedef UNICODE_STRING *PUNICODE_STRING;
extern "C" char _RTL_CONSTANT_STRING_type_check(const WCHAR *s);
// __typeof would be desirable here instead of sizeof.
template <size_t N> class _RTL_CONSTANT_STRING_remove_const_template_class;
template <> class _RTL_CONSTANT_STRING_remove_const_template_class<sizeof(char)>
{
public:
typedef char T;
};
template <> class _RTL_CONSTANT_STRING_remove_const_template_class<sizeof(WCHAR)>
{
public:
typedef WCHAR T;
};
#define _RTL_CONSTANT_STRING_remove_const_macro(s) \
(const_cast<_RTL_CONSTANT_STRING_remove_const_template_class<sizeof((s)[0])>::T*>(s))
#define RTL_CONSTANT_STRING(s) \
{ \
sizeof(s) - sizeof((s)[0]), \
sizeof(s) / sizeof(_RTL_CONSTANT_STRING_type_check(s)), \
_RTL_CONSTANT_STRING_remove_const_macro(s) \
}
typedef struct _OBJECT_ATTRIBUTES64
{
ULONG Length;
ULONG64 RootDirectory;
ULONG64 ObjectName;
ULONG Attributes;
ULONG64 SecurityDescriptor;
ULONG64 SecurityQualityOfService;
} OBJECT_ATTRIBUTES64;
typedef OBJECT_ATTRIBUTES64 *POBJECT_ATTRIBUTES64;
typedef CONST OBJECT_ATTRIBUTES64 *PCOBJECT_ATTRIBUTES64;
typedef struct _OBJECT_ATTRIBUTES32
{
ULONG Length;
ULONG RootDirectory;
ULONG ObjectName;
ULONG Attributes;
ULONG SecurityDescriptor;
ULONG SecurityQualityOfService;
} OBJECT_ATTRIBUTES32;
typedef OBJECT_ATTRIBUTES32 *POBJECT_ATTRIBUTES32;
typedef CONST OBJECT_ATTRIBUTES32 *PCOBJECT_ATTRIBUTES32;
typedef struct _OBJECT_ATTRIBUTES
{
ULONG Length;
HANDLE RootDirectory;
PUNICODE_STRING ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor; // Points to type SECURITY_DESCRIPTOR
PVOID SecurityQualityOfService; // Points to type SECURITY_QUALITY_OF_SERVICE
} OBJECT_ATTRIBUTES;
typedef OBJECT_ATTRIBUTES *POBJECT_ATTRIBUTES;
typedef CONST OBJECT_ATTRIBUTES *PCOBJECT_ATTRIBUTES;
#define InitializeObjectAttributes(p,n,a,r,s) \
{ \
(p)->Length = sizeof(OBJECT_ATTRIBUTES); \
(p)->RootDirectory = (r); \
(p)->Attributes = (a); \
(p)->ObjectName = (n); \
(p)->SecurityDescriptor = (s); \
(p)->SecurityQualityOfService = NULL; \
}
//
// Definitions for Object Creation
//
#define OBJ_INHERIT 0x00000002L
#define OBJ_PERMANENT 0x00000010L
#define OBJ_EXCLUSIVE 0x00000020L
#define OBJ_CASE_INSENSITIVE 0x00000040L
#define OBJ_OPENIF 0x00000080L
#define OBJ_OPENLINK 0x00000100L
#define OBJ_KERNEL_HANDLE 0x00000200L
#define OBJ_FORCE_ACCESS_CHECK 0x00000400L
#define OBJ_VALID_ATTRIBUTES 0x000007F2L
//---------------------------------------------------------------------------
// Undocumented types - pulled out of MS Windows NDK by Alex Ionecsu
//
// Privilege constants
//
#define SE_MIN_WELL_KNOWN_PRIVILEGE (2L)
#define SE_CREATE_TOKEN_PRIVILEGE (2L)
#define SE_ASSIGNPRIMARYTOKEN_PRIVILEGE (3L)
#define SE_LOCK_MEMORY_PRIVILEGE (4L)
#define SE_INCREASE_QUOTA_PRIVILEGE (5L)
#define SE_UNSOLICITED_INPUT_PRIVILEGE (6L)
#define SE_MACHINE_ACCOUNT_PRIVILEGE (6L)
#define SE_TCB_PRIVILEGE (7L)
#define SE_SECURITY_PRIVILEGE (8L)
#define SE_TAKE_OWNERSHIP_PRIVILEGE (9L)
#define SE_LOAD_DRIVER_PRIVILEGE (10L)
#define SE_SYSTEM_PROFILE_PRIVILEGE (11L)
#define SE_SYSTEMTIME_PRIVILEGE (12L)
#define SE_PROF_SINGLE_PROCESS_PRIVILEGE (13L)
#define SE_INC_BASE_PRIORITY_PRIVILEGE (14L)
#define SE_CREATE_PAGEFILE_PRIVILEGE (15L)
#define SE_CREATE_PERMANENT_PRIVILEGE (16L)
#define SE_BACKUP_PRIVILEGE (17L)
#define SE_RESTORE_PRIVILEGE (18L)
#define SE_SHUTDOWN_PRIVILEGE (19L)
#define SE_DEBUG_PRIVILEGE (20L)
#define SE_AUDIT_PRIVILEGE (21L)
#define SE_SYSTEM_ENVIRONMENT_PRIVILEGE (22L)
#define SE_CHANGE_NOTIFY_PRIVILEGE (23L)
#define SE_REMOTE_SHUTDOWN_PRIVILEGE (24L)
#define SE_MAX_WELL_KNOWN_PRIVILEGE (SE_REMOTE_SHUTDOWN_PRIVILEGE)
// Undocumented NTSystemDebugControl function (to read/write MSRs)
enum SYSDBG_COMMAND
{
SysDbgQueryModuleInformation = 1,
SysDbgQueryTraceInformation = 2,
SysDbgSetTracepoint = 3,
SysDbgSetSpecialCall = 4,
SysDbgClearSpecialCalls = 5,
SysDbgQuerySpecialCalls = 6,
SysDbgReadMsr = 16,
SysDbgWriteMsr = 17,
};
struct SYSDBG_MSR
{
uint32 reg;
uint32 padding;
uint64 value;
};
#define NTSTATUS int
#ifndef NTAPI
#define NTAPI WINAPI
#endif
#ifndef STATUS_NOT_IMPLEMENTED
#define STATUS_NOT_IMPLEMENTED 0xC0000002
#endif
#ifndef STATUS_IMAGE_ALREADY_LOADED
#define STATUS_IMAGE_ALREADY_LOADED 0xC000010E
#endif
//
// NtCreateFile OpenType Flags
//
#define FILE_SUPERSEDE 0x00000000
#define FILE_OPEN 0x00000001
#define FILE_CREATE 0x00000002
#define FILE_OPEN_IF 0x00000003
#define FILE_OVERWRITE 0x00000004
#define FILE_OVERWRITE_IF 0x00000005
#define FILE_MAXIMUM_DISPOSITION 0x00000005
//
// NtCreateFile Flags
//
#define FILE_DIRECTORY_FILE 0x00000001
#define FILE_WRITE_THROUGH 0x00000002
#define FILE_SEQUENTIAL_ONLY 0x00000004
#define FILE_NO_INTERMEDIATE_BUFFERING 0x00000008
#define FILE_SYNCHRONOUS_IO_ALERT 0x00000010
#define FILE_SYNCHRONOUS_IO_NONALERT 0x00000020
#define FILE_NON_DIRECTORY_FILE 0x00000040
#define FILE_CREATE_TREE_CONNECTION 0x00000080
#define FILE_COMPLETE_IF_OPLOCKED 0x00000100
#define FILE_NO_EA_KNOWLEDGE 0x00000200
#define FILE_OPEN_FOR_RECOVERY 0x00000400
#define FILE_RANDOM_ACCESS 0x00000800
#define FILE_DELETE_ON_CLOSE 0x00001000
#define FILE_OPEN_BY_FILE_ID 0x00002000
#define FILE_OPEN_FOR_BACKUP_INTENT 0x00004000
#define FILE_NO_COMPRESSION 0x00008000
#define FILE_RESERVE_OPFILTER 0x00100000
#define FILE_OPEN_REPARSE_POINT 0x00200000
#define FILE_OPEN_NO_RECALL 0x00400000
#define FILE_OPEN_FOR_FREE_SPACE_QUERY 0x00800000
//
// Interface for communicating with private WinDBG interface
//
#define KLDD_CODE_DEBUG_CONTROL \
CTL_CODE(FILE_DEVICE_UNKNOWN, 1, METHOD_NEITHER, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
typedef struct _KLDD_DATA_DEBUG_CONTROL
{
SYSDBG_COMMAND Command;
PVOID InputBuffer;
SIZE_T InputBufferLength;
} KLDD_DATA_DEBUG_CONTROL, *PKLDD_DATA_DEBUG_CONTROL;
//
// I/O Status Block
//
typedef struct _IO_STATUS_BLOCK
{
union
{
NTSTATUS Status;
PVOID Pointer;
};
ULONG_PTR Information;
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
//
// APC Callback for NtDeviceIoControlFile
//
typedef VOID
(NTAPI *PIO_APC_ROUTINE)(
IN PVOID ApcContext,
IN PIO_STATUS_BLOCK IoStatusBlock,
IN ULONG Reserved);
typedef NTSTATUS NTAPI NtSystemDebugControl_t(
IN SYSDBG_COMMAND Command,
IN PVOID InputBuffer OPTIONAL,
IN ULONG InputBufferLength,
OUT PVOID OutputBuffer OPTIONAL,
IN ULONG OutputBufferLength,
OUT PULONG ReturnLength OPTIONAL);
typedef NTSTATUS NTAPI NtLoadDriver_t(
IN PUNICODE_STRING DriverServiceName);
typedef NTSTATUS NTAPI NtUnloadDriver_t(
IN PUNICODE_STRING DriverServiceName);
typedef NTSTATUS NTAPI RtlAdjustPrivilege_t(
IN ULONG Privilege,
IN BOOLEAN NewValue,
IN BOOLEAN ForThread,
OUT PBOOLEAN OldValue);
typedef NTSTATUS NTAPI NtCreateFile_t(
OUT PHANDLE FileHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN PLARGE_INTEGER AllocationSize OPTIONAL,
IN ULONG FileAttributes,
IN ULONG ShareAccess,
IN ULONG CreateDisposition,
IN ULONG CreateOptions,
IN PVOID EaBuffer OPTIONAL,
IN ULONG EaLength);
typedef NTSTATUS NTAPI NtDeviceIoControlFile_t(
IN HANDLE DeviceHandle,
IN HANDLE Event OPTIONAL,
IN PIO_APC_ROUTINE UserApcRoutine OPTIONAL,
IN PVOID UserApcContext OPTIONAL,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN ULONG IoControlCode,
IN PVOID InputBuffer,
IN ULONG InputBufferSize,
OUT PVOID OutputBuffer,
IN ULONG OutputBufferSize);
#endif // define WIN32_UNDOC_H

View File

@@ -0,0 +1,46 @@
/*
This is main source code for the local win32 debugger module
*/
static const char wanted_name[] = "Local Windows debugger";
#define DEBUGGER_NAME "win32"
#define PROCESSOR_NAME "metapc"
#define TARGET_PROCESSOR PLFM_386
#define DEBUGGER_ID DEBUGGER_ID_X86_IA32_WIN32_USER
#define DEBUGGER_FLAGS (DBG_FLAG_EXITSHOTOK \
| DBG_FLAG_LOWCNDS \
| DBG_FLAG_DEBTHREAD \
| DBG_FLAG_ANYSIZE_HWBPT)
#define DEBUGGER_RESMOD (DBG_RESMOD_STEP_INTO)
#define HAVE_APPCALL
#define S_FILETYPE f_PE
// We must rename those method because common files
// refer to them as init_plugin/term_plugin
// Some other debugger modules compatible with win32
// have their own init/term and still call win32_init/term
// (since no renaming takes place)
#define win32_init_plugin init_plugin
#define win32_term_plugin term_plugin
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <objidl.h>
#include <fpro.h>
#include <ua.hpp>
#include <idd.hpp>
#include <loader.hpp>
#include "win32_debmod.h"
#include "w32sehch.h"
win32_debmod_t g_dbgmod;
#include "common_stub_impl.cpp"
#include "pc_local_impl.cpp"
#include "win32_local_impl.cpp"
#include "common_local_impl.cpp"
#include "win32_server_stub.cpp"

View File

@@ -0,0 +1,87 @@
#include <windows.h>
#include <pro.h>
#include "winbase_debmod.h"
#include "win32_util.hpp"
#ifndef __X86__
typedef BOOL WINAPI IsWow64Process_t(HANDLE, PBOOL);
static IsWow64Process_t *_IsWow64Process = NULL;
#endif
//--------------------------------------------------------------------------
//lint -esym(818,handle) could be pointer to const
wow64_state_t check_wow64_handle(HANDLE handle)
{
#ifdef __X86__
qnotused(handle);
#else
if ( _IsWow64Process == NULL )
{
HMODULE k32 = GetModuleHandle(kernel32_dll);
*(FARPROC*)&_IsWow64Process = GetProcAddress(k32, TEXT("IsWow64Process"));
if ( _IsWow64Process == NULL )
return WOW64_NONE; // unknown
}
BOOL bIsWow64 = FALSE;
if ( _IsWow64Process(handle, &bIsWow64) && bIsWow64 != 0 )
return WOW64_YES;
#endif
return WOW64_NO;
}
//--------------------------------------------------------------------------
wow64_state_t check_wow64_pid(int pid)
{
wow64_state_t r = WOW64_BAD; // assume pid is bad
HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
if ( h != NULL )
{
r = check_wow64_handle(h);
CloseHandle(h);
}
return r;
}
//--------------------------------------------------------------------------
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable: 4996) // GetVersionEx was declared deprecated
#endif
win_version_t::win_version_t()
{
OSVersionInfo.dwOSVersionInfoSize = sizeof(OSVersionInfo);
ver_ok = GetVersionEx(&OSVersionInfo) != 0; //lint !e2586 is deprecated
#ifdef __X86__
is_64bit_os = false;
if ( OSVersionInfo.dwMajorVersion > 5
|| OSVersionInfo.dwMajorVersion == 5 && OSVersionInfo.dwMinorVersion >= 1 )
{
is_64bit_os = check_wow64_handle(GetCurrentProcess()) > 0;
}
#endif
}
#ifdef _MSC_VER
# pragma warning(pop)
#endif
//--------------------------------------------------------------------------
win_tool_help_t::win_tool_help_t()
{
use_debug_break = qgetenv("IDA_DEBUGBREAKPROCESS");
HMODULE kern_handle = GetModuleHandle(kernel32_dll);
*(FARPROC*)&_DebugActiveProcessStop = GetProcAddress(kern_handle, TEXT("DebugActiveProcessStop"));
*(FARPROC*)&_DebugBreakProcess = GetProcAddress(kern_handle, TEXT("DebugBreakProcess"));
// find the needed functions
*(FARPROC*)&_CreateToolhelp32Snapshot = GetProcAddress(kern_handle, TEXT("CreateToolhelp32Snapshot"));
*(FARPROC*)&_Process32First = GetProcAddress(kern_handle, TEXT("Process32First"));
*(FARPROC*)&_Process32Next = GetProcAddress(kern_handle, TEXT("Process32Next"));
*(FARPROC*)&_Module32First = GetProcAddress(kern_handle, TEXT("Module32First"));
*(FARPROC*)&_Module32Next = GetProcAddress(kern_handle, TEXT("Module32Next"));
inited = _CreateToolhelp32Snapshot != NULL
&& _Process32First != NULL
&& _Process32Next != NULL
&& _Module32First != NULL
&& _Module32Next != NULL;
}

View File

@@ -0,0 +1,347 @@
//
// Wrapper for Windows ToolHelp library: enumerate processes/modules
//
// PSAPI.DLL: NT, 2K, XP/2K3
// KERNEL32.DLL (ToolHelp functions): 9X, ME, 2K, XP/2K3
//? add NT support
#ifndef __TOOLHELP_HPP__
#define __TOOLHELP_HPP__
#ifdef __NT__
#include <windows.h>
#include <Tlhelp32.h>
#include <dbghelp.h>
#include <segment.hpp>
#ifdef UNICODE
#define LookupPrivilegeValue_Name "LookupPrivilegeValueW"
#else
#define LookupPrivilegeValue_Name "LookupPrivilegeValueA"
#endif
#ifndef TH32CS_SNAPNOHEAPS
# define TH32CS_SNAPNOHEAPS 0x0
#endif
//--------------------------------------------------------------------------
enum wow64_state_t
{
WOW64_NONE = -2, // unknown yet
WOW64_BAD = -1,
WOW64_NO = 0,
WOW64_YES = 1,
};
wow64_state_t check_wow64_handle(HANDLE handle);
wow64_state_t check_wow64_pid(int pid);
//--------------------------------------------------------------------------
class win_version_t
{
public:
win_version_t();
const OSVERSIONINFO &get_info() const { return OSVersionInfo; }
bool ok() { return ver_ok; }
bool is_NT()
{
return ok() && OSVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT;
}
bool is_strictly_xp() // Is strictly XP (32bit)?
{
return ok()
&& is_NT()
&& OSVersionInfo.dwMajorVersion == 5
&& OSVersionInfo.dwMinorVersion == 1;
}
bool is_DW32()
{
return ok() && OSVersionInfo.dwPlatformId == 3;
}
bool is_2K() // Is at least Win2K?
{
return ok() && OSVersionInfo.dwMajorVersion >= 5;
}
bool is_64bitOS()
{
#ifndef __X86__
return true;
#else
return is_64bit_os;
#endif
}
//--------------------------------------------------------------------------
// GetProcessDEPPolicy() is broken for Win8, Win8.1, Win10:
// always set *lpPermanent == CL register, if not permanently policy.
// https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/05683d76-3c8a-49de-91e3-9d7ab8492f39/getprocessdeppolicy-does-not-set-the-correct-value-to-lppermanent?forum=windowscompatibility
bool is_GetProcessDEPPolicy_broken()
{
return ok()
&& ((OSVersionInfo.dwMajorVersion == 10 && OSVersionInfo.dwMinorVersion == 0) // Windows 10
|| (OSVersionInfo.dwMajorVersion == 6 && OSVersionInfo.dwMinorVersion == 3) // Windows 8.1
|| (OSVersionInfo.dwMajorVersion == 6 && OSVersionInfo.dwMinorVersion == 2)); // Windows 8
}
private:
OSVERSIONINFO OSVersionInfo;
bool ver_ok;
#ifdef __X86__
bool is_64bit_os;
#endif
};
//--------------------------------------------------------------------------
//-V:win_tool_help_t:730 Not all members of a class are initialized inside the constructor
class win_tool_help_t
{
public:
win_tool_help_t();
bool ok() { return inited; }
bool use_debug_break_process();
bool debug_break_process(HANDLE process_handle);
bool use_debug_detach_process();
bool debug_detach_process(pid_t pid);
private:
// function prototypes
typedef HANDLE WINAPI CreateToolhelp32Snapshot_t(DWORD dwFlags, DWORD th32ProcessID);
typedef BOOL WINAPI Process32First_t(HANDLE hSnapshot, LPPROCESSENTRY32 lppe);
typedef BOOL WINAPI Process32Next_t(HANDLE hSnapshot, LPPROCESSENTRY32 lppe);
typedef BOOL WINAPI Module32First_t(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
typedef BOOL WINAPI Module32Next_t(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
typedef BOOL WINAPI DebugActiveProcessStop_t(DWORD dwProcessID);
typedef BOOL WINAPI DebugBreakProcess_t(HANDLE Process);
typedef BOOL WINAPI CloseToolhelp32Snapshot_t(HANDLE hSnapshot);
// functions pointers
CreateToolhelp32Snapshot_t *_CreateToolhelp32Snapshot;
Process32First_t *_Process32First;
Process32Next_t *_Process32Next;
Module32First_t *_Module32First;
Module32Next_t *_Module32Next;
DebugActiveProcessStop_t *_DebugActiveProcessStop;
DebugBreakProcess_t *_DebugBreakProcess;
bool inited;
bool use_debug_break;
friend class toolhelp_snapshot_t;
friend class process_snapshot_t;
friend class module_snapshot_t;
};
//--------------------------------------------------------------------------
class toolhelp_snapshot_t
{
public:
inline toolhelp_snapshot_t(win_tool_help_t *tool);
inline ~toolhelp_snapshot_t();
inline bool ok();
inline bool open(uint32 flags, pid_t pid);
inline void close();
inline uint32 last_err();
protected:
bool seterr(); // always returns 'false' for convenience
win_tool_help_t *t;
HANDLE h;
uint32 last_error;
};
//--------------------------------------------------------------------------
class process_snapshot_t: public toolhelp_snapshot_t
{
public:
inline process_snapshot_t(win_tool_help_t *tool);
inline bool first(uint32 flags, LPPROCESSENTRY32 lppe);
inline bool next(LPPROCESSENTRY32 lppe);
};
//--------------------------------------------------------------------------
class module_snapshot_t: public toolhelp_snapshot_t
{
public:
inline module_snapshot_t(win_tool_help_t *tool);
inline bool first(uint32 flags, pid_t pid, LPMODULEENTRY32 lpme);
inline bool next(LPMODULEENTRY32 lpme);
};
//--------------------------------------------------------------------------
inline bool win_tool_help_t::use_debug_break_process()
{
return use_debug_break && _DebugBreakProcess != NULL;
}
//--------------------------------------------------------------------------
inline bool win_tool_help_t::debug_break_process(HANDLE process_handle)
{
return process_handle != INVALID_HANDLE_VALUE && _DebugBreakProcess(process_handle);
}
//--------------------------------------------------------------------------
inline bool win_tool_help_t::use_debug_detach_process()
{
return _DebugActiveProcessStop != NULL;
}
//--------------------------------------------------------------------------
inline bool win_tool_help_t::debug_detach_process(pid_t pid)
{
return _DebugActiveProcessStop != NULL && _DebugActiveProcessStop(pid);
}
//--------------------------------------------------------------------------
inline process_snapshot_t::process_snapshot_t(win_tool_help_t *tool)
: toolhelp_snapshot_t(tool)
{
}
//--------------------------------------------------------------------------
inline bool process_snapshot_t::first(uint32 flags, LPPROCESSENTRY32 lppe)
{
open(TH32CS_SNAPPROCESS | flags, 0);
lppe->dwSize = sizeof(PROCESSENTRY32);
if ( ok() && t->_Process32First(h, lppe) )
{
// ignore "System Process" (ID==0)
return lppe->th32ProcessID != 0 || next(lppe);
}
return seterr();
}
//--------------------------------------------------------------------------
inline bool process_snapshot_t::next(LPPROCESSENTRY32 lppe)
{
while ( ok() )
{
if ( !t->_Process32Next(h, lppe) )
break;
// ignore "System Process" (ID==0)
if ( lppe->th32ProcessID != 0 )
return true;
}
return seterr();
}
//--------------------------------------------------------------------------
inline module_snapshot_t::module_snapshot_t(win_tool_help_t *tool)
: toolhelp_snapshot_t(tool)
{
}
//--------------------------------------------------------------------------
inline bool module_snapshot_t::first(uint32 flags, pid_t pid, LPMODULEENTRY32 lpme)
{
wow64_state_t state = check_wow64_pid(pid);
flags |= state == WOW64_YES ? TH32CS_SNAPMODULE32 : 0;
if ( !open(TH32CS_SNAPMODULE | flags, pid) )
return false;
lpme->dwSize = sizeof(MODULEENTRY32);
if ( t->_Module32First(h, lpme) )
return true;
return seterr();
}
//--------------------------------------------------------------------------
inline bool module_snapshot_t::next(LPMODULEENTRY32 lpme)
{
if ( ok() )
return false;
if ( t->_Module32Next(h, lpme) )
return true;
seterr();
return false;
}
//--------------------------------------------------------------------------
inline toolhelp_snapshot_t::toolhelp_snapshot_t(win_tool_help_t *tool)
: t(tool), h(INVALID_HANDLE_VALUE), last_error(0)
{
}
//--------------------------------------------------------------------------
inline toolhelp_snapshot_t::~toolhelp_snapshot_t()
{
close();
}
//--------------------------------------------------------------------------
inline bool toolhelp_snapshot_t::ok()
{
return h != INVALID_HANDLE_VALUE;
}
//--------------------------------------------------------------------------
// // always returns 'false' for convenience
inline bool toolhelp_snapshot_t::seterr()
{
last_error = GetLastError();
return false;
}
//--------------------------------------------------------------------------
// // always returns 'false' for convenience
inline uint32 toolhelp_snapshot_t::last_err()
{
return last_error;
}
//--------------------------------------------------------------------------
inline bool toolhelp_snapshot_t::open(uint32 flags, pid_t pid)
{
if ( !t->ok() )
return false;
close();
for ( int cnt=0; cnt < 5; cnt++ )
{
h = t->_CreateToolhelp32Snapshot(flags, pid);
if ( h != INVALID_HANDLE_VALUE )
return true;
seterr();
// MSDN: If the function fails with ERROR_BAD_LENGTH, retry
// the function until it succeeds.
if ( last_err() != ERROR_BAD_LENGTH
|| (flags & (TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32)) == 0 )
{
break;
}
}
return false;
}
//--------------------------------------------------------------------------
inline void toolhelp_snapshot_t::close()
{
if ( t->ok() && h != INVALID_HANDLE_VALUE )
CloseHandle(h);
}
//--------------------------------------------------------------------------
// convert Windows protection modes to IDA protection modes
inline uchar win_prot_to_ida_perm(DWORD protection)
{
uchar perm = 0;
if ( protection & PAGE_READONLY )
perm |= SEGPERM_READ;
if ( protection & PAGE_READWRITE )
perm |= SEGPERM_READ | SEGPERM_WRITE;
if ( protection & PAGE_WRITECOPY )
perm |= SEGPERM_READ | SEGPERM_WRITE;
if ( protection & PAGE_EXECUTE )
perm |= SEGPERM_EXEC;
if ( protection & PAGE_EXECUTE_READ )
perm |= SEGPERM_READ | SEGPERM_EXEC;
if ( protection & PAGE_EXECUTE_READWRITE )
perm |= SEGPERM_READ | SEGPERM_WRITE | SEGPERM_EXEC;
if ( protection & PAGE_EXECUTE_WRITECOPY )
perm |= SEGPERM_READ | SEGPERM_WRITE | SEGPERM_EXEC;
return perm;
}
#endif // __NT__
#endif // __TOOLHELP_HPP__

View File

@@ -0,0 +1,633 @@
#include <windows.h>
#include <ida.hpp>
#include "winbase_debmod.h"
#ifndef __X86__
#define IDA_ADDRESS_SIZE 8
#else
#define IDA_ADDRESS_SIZE 4
#endif
const TCHAR kernel32_dll[] = TEXT("kernel32.dll");
typedef BOOL WINAPI GetProcessDEPPolicy_t(HANDLE hProcess, LPDWORD lpFlags, PBOOL lpPermanent);
static GetProcessDEPPolicy_t *_GetProcessDEPPolicy = NULL;
typedef dep_policy_t WINAPI GetSystemDEPPolicy_t(void);
static GetSystemDEPPolicy_t *_GetSystemDEPPolicy = NULL;
//--------------------------------------------------------------------------
winbase_debmod_t::winbase_debmod_t(void)
{
HMODULE k32 = GetModuleHandle(kernel32_dll);
if ( _GetProcessDEPPolicy == NULL )
*(FARPROC*)&_GetProcessDEPPolicy = GetProcAddress(k32, TEXT("GetProcessDEPPolicy"));
if ( _GetSystemDEPPolicy == NULL )
*(FARPROC*)&_GetSystemDEPPolicy = GetProcAddress(k32, TEXT("GetSystemDEPPolicy"));
if ( _GetSystemDEPPolicy != NULL )
dep_policy = _GetSystemDEPPolicy();
win_tool_help = NULL;
set_platform("win32");
}
//--------------------------------------------------------------------------
// Prepare new page protections for a breakpoint of BPTTYPE.
// Use INPUT as starting page protections.
// Return false in the case of failure.
bool winbase_debmod_t::remove_page_protections(
DWORD *p_input,
bpttype_t bpttype,
dep_policy_t dpolicy,
HANDLE proc_handle)
{
// If PAGE_GUARD is already set, do not change anything, it is already ok
DWORD input = *p_input;
if ( (input & PAGE_GUARD) != 0 )
return false;
// Convert between Unix permissions and Win32 page protections using this array:
static const uchar win32_page_protections[] =
{
PAGE_NOACCESS, // 000
PAGE_READONLY, // 001
0xFF, // 010 WRITE_ONLY does not exist on win32
PAGE_READWRITE, // 011
PAGE_EXECUTE, // 100
PAGE_EXECUTE_READ, // 101
0xFF, // 110 EXECUTE_WRITE does not exist on win32
PAGE_EXECUTE_READWRITE, // 111
};
uchar unix;
// convert ..COPY page protections into their non-copy counterparts
// this is the best thing we can do with them because they are automatically
// converted by the system upon a write access
if ( (input & PAGE_WRITECOPY) != 0 )
{
unix = 3; // rw
}
else if ( (input & PAGE_EXECUTE_WRITECOPY) != 0 )
{
unix = 7; // rwx
}
else
{
for ( unix=0; unix < 8; unix++ )
{
uchar p = win32_page_protections[unix];
if ( p != 0xFF && (input & p) != 0 )
break;
}
}
QASSERT(622, unix < 8);
// convert bpttype into unix permissions
int del = 0;
if ( (bpttype & BPT_READ) != 0 )
del |= 1;
if ( (bpttype & BPT_WRITE) != 0 )
del |= 2;
if ( (bpttype & BPT_EXEC) != 0 )
{
del |= 4;
// if DEP is disabled for this process then a program can
// happily execute code in a read only area so we need to
// remove *all* privileges, unfortunately
if ( dpolicy != dp_always_on )
{
// on XP, GetProcessDEPPolicy returns DEP policy for current process (i.e. the debugger)
// so we can't use it
// assume that DEP is disabled by default
DWORD flags = 0;
BOOL permanent = 0;
if ( _GetProcessDEPPolicy == NULL
|| winver.is_strictly_xp()
|| winver.is_GetProcessDEPPolicy_broken()
|| _GetProcessDEPPolicy(proc_handle, &flags, &permanent) )
{
// flags == 0: DEP is disabled for the specified process.
//
// Remarks: if permanent == 0 and global DEP policy is OptIn
// flags may be equal to 1 *but* having DEP disabled because,
// in case the process called SetProcessDEPPolicy the
// permanent argument would be 1, it seems to be a bug in the
// documentation
if ( (dpolicy == dp_opt_in && permanent == 0) || flags == 0 )
del |= 1;
}
}
}
// Remove the access types to trigger on
unix &= ~del;
// Handle WRITE_ONLY and EXECUTE_WRITE cases because win32 does not have them.
// We use stricter page permissions for them. This means that there will
// be more useless exceptions but we cannot do much about it.
if ( unix == 2 || unix == 6 )
unix = 0; // use PAGE_NOACCESS instead of WRITE_ONLY or EXECUTE_WRITE
uchar perm = win32_page_protections[unix];
*p_input = (input & ~0xFF) | perm;
return true;
}
//--------------------------------------------------------------------------
bool idaapi winbase_debmod_t::dbg_enable_page_bpt(
page_bpts_t::iterator p,
bool enable)
{
pagebpt_data_t &bpt = p->second;
if ( (bpt.old_prot != 0) == enable )
return false; // already the desired state
debdeb("dbg_enable_page_bpt(%s): page_ea=%a, old_prot=0x%x, new_prot=0x%x\n", enable ? "true" : "false", bpt.page_ea, bpt.old_prot, bpt.new_prot);
DWORD old;
DWORD prot = enable ? bpt.new_prot : bpt.old_prot;
if ( !VirtualProtectEx(process_handle, (void*)(size_t)bpt.page_ea,
bpt.real_len, prot, &old) )
{
deberr("VirtualProtectEx");
// if the page disappeared while disabling a bpt, do not complain,
// silently return success
if ( enable )
return false;
old = 0;
}
debdeb(" success! old=0x%x\n", old);
bpt.old_prot = enable ? old : 0;
return true;
}
//--------------------------------------------------------------------------
// Should we generate a BREAKPOINT event because of page bpt?
//lint -e{1746} could be made const reference
bool should_fire_page_bpt(
page_bpts_t::iterator p,
ea_t ea,
DWORD failed_access_type,
ea_t pc,
dep_policy_t dep_policy)
{
const pagebpt_data_t &bpt = p->second;
if ( !interval::contains(bpt.ea, bpt.user_len, ea) )
return false; // not in the user-defined interval
int bit;
switch ( failed_access_type )
{
default:
INTERR(623); //-V796 no break
case EXCEPTION_READ_FAULT: // failed READ access
// depending on the DEP policy we mark this access also
// to be triggered in case of EXEC breakpoints
bit = BPT_READ;
if ( dep_policy != dp_always_on && bpt.type == BPT_EXEC && pc == ea )
bit |= BPT_EXEC;
break;
case EXCEPTION_WRITE_FAULT: // failed WRITE access
bit = BPT_WRITE;
break;
case EXCEPTION_EXECUTE_FAULT: // failed EXECUTE access
bit = BPT_EXEC;
break;
}
return (bpt.type & bit) != 0;
}
//--------------------------------------------------------------------------
// returns 0-failure, 2-success
int idaapi winbase_debmod_t::dbg_add_page_bpt(
bpttype_t type,
ea_t ea,
int size)
{
// only one page breakpoint per page is permitted
page_bpts_t::iterator p = find_page_bpt(ea, size);
if ( p != page_bpts.end() )
return 0; // another page bpt exists
// Find out the current page protections
MEMORY_BASIC_INFORMATION meminfo;
ea_t page_ea = calc_page_base(ea);
if ( !VirtualQueryEx(process_handle, (void *)(size_t)page_ea,
&meminfo, sizeof(meminfo)) )
{
deberr("VirtualQueryEx");
return 0;
}
// Make sure the page is loaded
if ( (meminfo.State & MEM_FREE) != 0 )
{
deberr("%a: the page has not been allocated", page_ea);
return 0;
}
// According to MSDN documentation for VirtualQueryEx
// (...)
// AllocationProtect
// The memory protection option when the region was initially allocated. This member can be
// one of the memory protection constants or 0 if the caller does not have access.
//
// Unfortunately, there is no more information about why it my happen so, for now, I'm just
// returning an error.
if ( meminfo.Protect == 0 )
{
deberr("%a: the page cannot be accessed", page_ea);
return 0;
}
// Calculate new page protections
int aligned_len = align_up((ea-page_ea)+size, MEMORY_PAGE_SIZE);
int real_len = 0;
DWORD prot = meminfo.Protect;
if ( remove_page_protections(&prot, type, dep_policy, process_handle) )
{ // We have to set new protections
real_len = aligned_len;
}
// Remember the new breakpoint
p = page_bpts.insert(std::make_pair(page_ea, pagebpt_data_t())).first;
pagebpt_data_t &bpt = p->second;
bpt.ea = ea;
bpt.user_len = size;
bpt.page_ea = page_ea;
bpt.aligned_len = aligned_len;
bpt.real_len = real_len;
bpt.old_prot = 0;
bpt.new_prot = prot;
bpt.type = type;
// for PAGE_GUARD pages, no need to change the permissions, everything is fine already
if ( real_len == 0 )
{
bpt.old_prot = meminfo.Protect;
return 2;
}
return dbg_enable_page_bpt(p, true) ? 2 : 0;
}
//--------------------------------------------------------------------------
// returns true if changed *protect (in other words, if we have to mask
// the real page protections and return the original one)
bool winbase_debmod_t::mask_page_bpts(
ea_t startea,
ea_t endea,
uint32 *protect)
{
// if we have page breakpoints, what we return must be changed to show the
// real segment privileges, instead of the new ones we applied for the bpt
int newprot = 0;
page_bpts_t::iterator p = page_bpts.begin();
while ( p != page_bpts.end() )
{
pagebpt_data_t &pbd = p->second;
if ( pbd.page_ea + pbd.real_len > startea )
{
if ( pbd.page_ea >= endea )
break;
if ( pbd.old_prot != 0 )
{ // bpt has been written to the process memory
if ( *protect == pbd.new_prot )
{ // return the old protection, before setting the page bpt
newprot = pbd.old_prot;
}
else
{
debdeb("mask_page_bpts: app changed our page protection for %a (expected: 0x%x, actual: 0x%x)\n", pbd.page_ea, pbd.new_prot, *protect);
// page protection has been changed by the application
DWORD prot = *protect;
if ( prot == PAGE_WRITECOPY && pbd.new_prot == PAGE_READWRITE
|| prot == PAGE_EXECUTE_WRITECOPY && pbd.new_prot == PAGE_EXECUTE_READWRITE )
{
// in some cases OS may restore WRITECOPY protection; do nothing in such cases since it works the same way for breakpoint purposes
debdeb(" ignoring changes to WRITECOPY protection\n");
}
else if ( remove_page_protections(&prot, pbd.type, dep_policy, process_handle) )
{
pbd.new_prot = prot;
pbd.old_prot = 0; // mark our bpt as non-written
debdeb(" will re-set protection to 0x%x\n", pbd.new_prot);
}
}
}
}
++p;
}
if ( newprot != 0 )
{
*protect = newprot;
return true;
}
return false;
}
//--------------------------------------------------------------------------
// Page breakpoints modify the page protections to induce access violations.
// We must hide the modified page protections from IDA and report the original
// page protections.
// Second, the application may render a page bpt inactive by changing its page protections.
// In this case we must report to IDA the new page protections and also reactivate
// the page breakpoint.
void winbase_debmod_t::verify_page_protections(
meminfo_vec_t *areas,
const win32_prots_t &prots)
{
QASSERT(624, areas->size() == prots.size());
if ( page_bpts.empty() )
return;
for ( int i = 0; i < areas->size(); i++ )
{
uint32 prot = prots[i];
memory_info_t &a = areas->at(i);
if ( mask_page_bpts(a.start_ea, a.end_ea, &prot) )
a.perm = win_prot_to_ida_perm(prot);
}
// reactivate all disabled page bpts, if any
enable_page_bpts(true);
}
//--------------------------------------------------------------------------
#ifndef __X86__
wow64_state_t winbase_debmod_t::check_wow64_process()
{
if ( is_wow64 == WOW64_NONE )
{
is_wow64 = check_wow64_handle(process_handle);
if ( is_wow64 > 0 )
dmsg("WOW64 process has been detected (pid=%d)\n", pid);
}
return is_wow64;
}
#endif
//--------------------------------------------------------------------------
bool highdll_vec_t::has(eanat_t addr) const
{
for ( int i = 0; i < size(); ++i )
if ( (*this)[i].has(addr) )
return true;
return false;
}
//--------------------------------------------------------------------------
bool highdll_vec_t::add(eanat_t addr, size_t sz, HANDLE h)
{
if ( has(addr) )
return false;
// check removed: on new win10 we can have above 4GB:
// ntdll.dll, wow64.dll, wow64win.dll
// QASSERT(1491, size() < 2);
highdll_range_t &r = push_back();
r.start = addr;
r.end = addr + sz;
r.handle = h;
return true;
}
//--------------------------------------------------------------------------
bool highdll_vec_t::add_high_module(
eanat_t addr,
size_t sz,
HANDLE h)
{
if ( ea_t(addr) == addr )
return false;
add(addr, sz, h); //-V779 unreachable code
return true;
}
//--------------------------------------------------------------------------
bool highdll_vec_t::del_high_module(HANDLE *h, eanat_t addr)
{
for ( int i = 0; i < size(); ++i )
{
const highdll_range_t &r = (*this)[i];
if ( r.start == addr )
{
if ( h != NULL )
*h = r.handle;
erase(begin() + i);
return ea_t(addr) != addr;
}
}
return false;
}
//--------------------------------------------------------------------------
void idaapi winbase_debmod_t::dbg_term(void)
{
is_wow64 = WOW64_NONE;
delete win_tool_help;
win_tool_help = NULL;
}
//-------------------------------------------------------------------------
bool winbase_debmod_t::handle_process_start(pid_t _pid)
{
debapp_attrs.addrsize = get_process_addrsize(_pid);
is64 = debapp_attrs.addrsize == 8;
term_reg_ctx();
init_reg_ctx();
return true;
}
//-------------------------------------------------------------------------
void winbase_debmod_t::cleanup(void)
{
inherited::cleanup();
is64 = false;
}
//--------------------------------------------------------------------------
// Check if we need to install a temporary breakpoint to workaround the
// 'freely running after syscall' problem. Exactly, the problem is the
// following: after single stepping over a "jmp far ptr" instruction in
// wow64cpu.dll for a 32bits process under a 64bits OS (Win7), the trap flag
// is lost. Probably, it's a bug in wow64cpu!CpuReturnFromSimulatedCode.
//
// So, if we find an instruction like "call large dword fs:XX" we add a
// temporary breakpoint at the next instruction and re-enable tracing
// when the breakpoint is reached.
bool winbase_debmod_t::check_for_call_large(
const debug_event_t *event,
HANDLE handle)
{
if ( check_wow64_handle(handle) <= 0 )
return false;
uchar buf[3];
if ( dbg_read_memory(event->ea, buf, 3, NULL) == 3 )
{
// is it the call large instruction?
if ( memcmp(buf, "\x64\xFF\x15", 3) == 0 )
return true;
}
return false;
}
//--------------------------------------------------------------------------
// Get process bitness: 32bit - 4, 64bit - 8, 0 - unknown
int idaapi winbase_debmod_t::get_process_bitness(int _pid)
{
if ( _pid != -1 && _pid != GetCurrentProcessId() )
{
if ( !winver.is_64bitOS() )
return 4;
switch ( check_wow64_pid(_pid) )
{
case WOW64_BAD: return 0; // bad process id
case WOW64_YES: return 4; // wow64 process, 32bit
case WOW64_NO: return 8; // regular 64bit process
default: break;
}
}
return IDA_ADDRESS_SIZE;
}
//--------------------------------------------------------------------------
static const char *str_bitness(int addrsize)
{
switch ( addrsize )
{
case 8:
return "[64]";
case 4:
return "[32]";
default:
return "[x]";
}
}
//--------------------------------------------------------------------------
// this function may correct pinfo->addrsize
bool winbase_debmod_t::get_process_path(
ext_process_info_t *pinfo,
char *buf,
size_t bufsize)
{
module_snapshot_t msnap(get_tool_help());
MODULEENTRY32 me;
if ( !msnap.first(TH32CS_SNAPMODULE, pinfo->pid, &me) )
{
if ( msnap.last_err() == ERROR_PARTIAL_COPY && pinfo->addrsize == 0 )
{
// MSDN: If the specified process is a 64-bit process and the caller is a
// 32-bit process, error code is ERROR_PARTIAL_COPY
pinfo->addrsize = 8;
}
qstrncpy(buf, pinfo->name.c_str(), bufsize);
return false;
}
else
{
tchar_utf8(buf, me.szExePath, bufsize);
return true;
}
}
//--------------------------------------------------------------------------
win_tool_help_t *winbase_debmod_t::get_tool_help()
{
if ( win_tool_help == NULL )
win_tool_help = new win_tool_help_t;
return win_tool_help;
}
//-------------------------------------------------------------------------
int winbase_debmod_t::get_process_addrsize(pid_t _pid)
{
int addrsize = get_process_bitness(_pid);
return addrsize != 0 ? addrsize : IDA_ADDRESS_SIZE;
}
//--------------------------------------------------------------------------
//lint -e{1762} could be made const [in fact it cannot be made const in x64 mode]
bool winbase_debmod_t::is_ntdll_name(const char *path)
{
const char *base_name = qbasename(path);
const char *ntdll_name = winver.is_NT()
? "ntdll.dll" // NT
: "kernel32.dll"; // 9X/Me and KERNEL32.DLL
if ( strieq(base_name, ntdll_name) )
return true;
#ifndef __X86__
if ( winver.is_NT()
&& check_wow64_process() == WOW64_YES
&& strieq(base_name, "ntdll32.dll") )
{
return true;
}
#endif
return false;
}
//--------------------------------------------------------------------------
//lint -esym(1762,winbase_debmod_t::build_process_ext_name) could be made const
void winbase_debmod_t::build_process_ext_name(ext_process_info_t *pinfo)
{
char fullname[MAXSTR];
if ( get_process_path(pinfo, fullname, sizeof(fullname))
&& pinfo->addrsize == 0 )
{
// the WOW64 is optional on R2 x64 server
pinfo->addrsize = IDA_ADDRESS_SIZE;
}
pinfo->ext_name = str_bitness(pinfo->addrsize);
if ( !pinfo->ext_name.empty() )
pinfo->ext_name += ' ';
pinfo->ext_name += fullname;
}
//--------------------------------------------------------------------------
int idaapi winbase_debmod_t::get_process_list(procvec_t *list, qstring *)
{
int mypid = GetCurrentProcessId();
list->clear();
process_snapshot_t psnap(get_tool_help());
PROCESSENTRY32 pe32;
for ( bool ok = psnap.first(TH32CS_SNAPNOHEAPS, &pe32); ok; ok = psnap.next(&pe32) )
{
if ( pe32.th32ProcessID != mypid )
{
int addrsize = get_process_bitness(pe32.th32ProcessID);
#ifndef __EA64__
if ( addrsize > 4 )
continue; // skip 64bit processes, we cannot debug them because ea_t is 32bit
#endif
ext_process_info_t pinfo;
pinfo.pid = pe32.th32ProcessID;
pinfo.addrsize = addrsize;
tchar_utf8(&pinfo.name, pe32.szExeFile);
build_process_ext_name(&pinfo);
list->push_back(pinfo);
}
}
return list->size();
}
//--------------------------------------------------------------------------
// Returns the file name assciated with pid
bool idaapi winbase_debmod_t::get_exec_fname(int _pid, char *buf, size_t bufsize)
{
ext_process_info_t pinfo;
pinfo.pid = _pid;
pinfo.name.qclear();
return get_process_path(&pinfo, buf, bufsize);
}
//--------------------------------------------------------------------------
win_tool_help_t *winbase_debmod_t::win_tool_help = NULL;
win_version_t winbase_debmod_t::winver;

View File

@@ -0,0 +1,185 @@
#ifndef __WINBASE_HPP__
#define __WINBASE_HPP__
// Base class for win32 and windbg modules
using std::for_each;
using std::pair;
using std::make_pair;
//--------------------------------------------------------------------------
#define BASE_DEBUGGER_MODULE pc_debmod_t
#include "deb_pc.hpp"
#include "pc_debmod.h"
#define BPT_CODE_SIZE X86_BPT_SIZE
#include "win32_util.hpp"
extern const TCHAR kernel32_dll[];
//--------------------------------------------------------------------------
// DEP policies
enum dep_policy_t
{
dp_always_off,
dp_always_on,
dp_opt_in,
dp_opt_out
};
//--------------------------------------------------------------------------
enum attach_status_t
{
as_none, // no attach to process requested
as_attaching, // waiting for CREATE_PROCESS_DEBUG_EVENT, indicating the process is attached
as_breakpoint, // waiting for first breakpoint, indicating the process was properly initialized and suspended
as_attached, // process was successfully attached
as_detaching, // waiting for next get_debug_event() request, to return the process as detached
as_attach_kernel, // attaching to kernel
};
// vector of win32 page protections
// we need this type because meminfo_t does not contain the original win32 protections
// but we need them to verify page bpts
typedef qvector<uint32> win32_prots_t;
//--------------------------------------------------------------------------
// When debugging WOW64 processes with ida32 we have to take into account
// ntdll.dll (and wow64*.dll), which are x64 files
// that can be loaded into high addresses (above 4GB)
// Since ea_t cannot represent such addresses,
// we use our own type to remember the DLL boundaries
typedef size_t eanat_t;
struct highdll_range_t
{
eanat_t start;
eanat_t end;
HANDLE handle;
highdll_range_t() : start(0), end(0), handle(INVALID_HANDLE_VALUE) {}
bool has(eanat_t addr) const { return addr >= start && addr < end; }
};
DECLARE_TYPE_AS_MOVABLE(highdll_range_t);
struct highdll_vec_t : protected qvector<highdll_range_t>
{
private:
size_t num_ntdlls; // count of actual ntdll*.dll modules in the list
public:
typedef qvector<highdll_range_t> inherited;
highdll_vec_t() : num_ntdlls(0) {}
void clear() { inherited::clear(); num_ntdlls = 0; }
size_t size() const { return inherited::size(); }
size_t count_ntdlls() const { return num_ntdlls; }
bool empty() const { return inherited::empty(); }
// return false if there is already a dll with such an address
bool add(eanat_t addr, size_t size, HANDLE h = INVALID_HANDLE_VALUE);
bool add_ntdll(eanat_t addr, size_t size, HANDLE h = INVALID_HANDLE_VALUE)
{
bool ok = add(addr, size, h);
if ( ok )
num_ntdlls++;
return ok;
};
// it returns true if the dll address doesn't fit in `ea_t`
bool add_high_module(
eanat_t addr,
size_t size,
HANDLE h = INVALID_HANDLE_VALUE);
// it returns true if the dll address doesn't fit to `ea_t`
bool del_high_module(HANDLE *h, eanat_t addr);
bool has(eanat_t addr) const;
};
//--------------------------------------------------------------------------
class winbase_debmod_t: public BASE_DEBUGGER_MODULE
{
typedef BASE_DEBUGGER_MODULE inherited;
wow64_state_t is_wow64 = WOW64_NONE; // use check_wow64_process()
protected:
HANDLE process_handle = INVALID_HANDLE_VALUE;
dep_policy_t dep_policy = dp_always_off;
highdll_vec_t highdlls;
bool is64 = false;
// local functions
bool mask_page_bpts(ea_t startea, ea_t endea, uint32 *protect);
void verify_page_protections(meminfo_vec_t *areas, const win32_prots_t &prots);
winbase_debmod_t(void);
// overridden virtual functions
bool idaapi dbg_enable_page_bpt(page_bpts_t::iterator p, bool enable);
int idaapi dbg_add_page_bpt(bpttype_t type, ea_t ea, int size);
bool check_for_call_large(const debug_event_t *event, HANDLE process_handle);
#ifndef __X86__
wow64_state_t check_wow64_process();
#else
wow64_state_t check_wow64_process() { return WOW64_NO; }
#endif
int get_process_addrsize(pid_t pid);
bool is_ntdll_name(const char *path);
// return number of processes, -1 - not implemented
virtual int idaapi get_process_list(procvec_t *proclist, qstring *errbuf) override;
// return the file name assciated with pid
virtual bool idaapi get_exec_fname(int pid, char *buf, size_t bufsize) newapi;
// get process bitness: 32bit - 4, 64bit - 8, 0 - unknown
virtual int idaapi get_process_bitness(int pid) newapi;
virtual void init_reg_ctx(void) newapi {}
virtual void term_reg_ctx(void) newapi {}
public:
virtual void idaapi dbg_term(void) override;
static win_tool_help_t *get_tool_help();
static win_version_t winver;
protected:
bool handle_process_start(pid_t _pid);
void cleanup(void);
private:
void build_process_ext_name(ext_process_info_t *pinfo);
static bool get_process_path(
ext_process_info_t *pinfo,
char *buf,
size_t bufsize);
static bool remove_page_protections(
DWORD *p_input,
bpttype_t bpttype,
dep_policy_t dpolicy,
HANDLE proc_handle);
static win_tool_help_t *win_tool_help;
};
bool should_fire_page_bpt(page_bpts_t::iterator p, ea_t ea, DWORD failed_access_type, ea_t pc, dep_policy_t dep_policy);
#ifdef _PE_H_
bool read_pe_header(peheader_t *pe);
#endif
//-------------------------------------------------------------------------
inline void tchar_utf8(qstring *buf, TCHAR *tchar)
{
#ifdef UNICODE
utf16_utf8(buf, tchar);
#else
acp_utf8(buf, tchar);
#endif
}
//-------------------------------------------------------------------------
inline void tchar_utf8(char *buf, TCHAR *tchar, size_t bufsize)
{
qstring utf8;
tchar_utf8(&utf8, tchar);
qstrncpy(buf, utf8.c_str(), bufsize);
}
#endif