620 lines
19 KiB
C++
620 lines
19 KiB
C++
#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)
|
|
: is_wow64(WOW64_NONE),
|
|
process_handle(INVALID_HANDLE_VALUE),
|
|
dep_policy(dp_always_off),
|
|
highdlls()
|
|
{
|
|
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;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// 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;
|