Files
sigmaker-ida/idasdk76/dbg/win32/win32_debmod_impl.cpp
2021-10-31 21:20:46 +02:00

718 lines
22 KiB
C++

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