update to ida 7.6, add builds
This commit is contained in:
176
idasdk76/dbg/win32/makefile
Normal file
176
idasdk76/dbg/win32/makefile
Normal 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
|
||||
338
idasdk76/dbg/win32/w32sehch.cpp
Normal file
338
idasdk76/dbg/win32/w32sehch.cpp
Normal 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);
|
||||
}
|
||||
7
idasdk76/dbg/win32/w32sehch.h
Normal file
7
idasdk76/dbg/win32/w32sehch.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#ifndef __W32SEHCH__
|
||||
#define __W32SEHCH__
|
||||
|
||||
void install_x86seh_menu();
|
||||
void remove_x86seh_menu();
|
||||
|
||||
#endif
|
||||
3519
idasdk76/dbg/win32/win32_debmod.cpp
Normal file
3519
idasdk76/dbg/win32/win32_debmod.cpp
Normal file
File diff suppressed because it is too large
Load Diff
427
idasdk76/dbg/win32/win32_debmod.h
Normal file
427
idasdk76/dbg/win32/win32_debmod.h
Normal 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 ®s,
|
||||
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
|
||||
|
||||
717
idasdk76/dbg/win32/win32_debmod_impl.cpp
Normal file
717
idasdk76/dbg/win32/win32_debmod_impl.cpp
Normal 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 ∋
|
||||
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;
|
||||
}
|
||||
208
idasdk76/dbg/win32/win32_local_impl.cpp
Normal file
208
idasdk76/dbg/win32/win32_local_impl.cpp
Normal 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, ®, 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
|
||||
203
idasdk76/dbg/win32/win32_rpc.h
Normal file
203
idasdk76/dbg/win32/win32_rpc.h
Normal 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
|
||||
406
idasdk76/dbg/win32/win32_server.cpp
Normal file
406
idasdk76/dbg/win32/win32_server.cpp
Normal 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;
|
||||
}
|
||||
15
idasdk76/dbg/win32/win32_server_stub.cpp
Normal file
15
idasdk76/dbg/win32/win32_server_stub.cpp
Normal 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;
|
||||
}
|
||||
128
idasdk76/dbg/win32/win32_stub.cpp
Normal file
128
idasdk76/dbg/win32/win32_stub.cpp
Normal 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"
|
||||
282
idasdk76/dbg/win32/win32_undoc.h
Normal file
282
idasdk76/dbg/win32/win32_undoc.h
Normal 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
|
||||
46
idasdk76/dbg/win32/win32_user.cpp
Normal file
46
idasdk76/dbg/win32/win32_user.cpp
Normal 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"
|
||||
87
idasdk76/dbg/win32/win32_util.cpp
Normal file
87
idasdk76/dbg/win32/win32_util.cpp
Normal 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;
|
||||
}
|
||||
347
idasdk76/dbg/win32/win32_util.hpp
Normal file
347
idasdk76/dbg/win32/win32_util.hpp
Normal 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__
|
||||
633
idasdk76/dbg/win32/winbase_debmod.cpp
Normal file
633
idasdk76/dbg/win32/winbase_debmod.cpp
Normal 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;
|
||||
185
idasdk76/dbg/win32/winbase_debmod.h
Normal file
185
idasdk76/dbg/win32/winbase_debmod.h
Normal 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
|
||||
Reference in New Issue
Block a user