198 lines
6.4 KiB
C++
198 lines
6.4 KiB
C++
|
|
#include "../ldr/mach-o/macho_node.h"
|
|
|
|
//--------------------------------------------------------------------------
|
|
static bool rebase_scattered_segments(ea_t base)
|
|
{
|
|
netnode node;
|
|
node.create(MACHO_NODE);
|
|
|
|
// detect if the code and data segments have moved relative to each other.
|
|
// if so, we cannot simply apply a uniform delta to the entire database.
|
|
// we must rebase one segment at a time.
|
|
ea_t ldr_data = 0;
|
|
ea_t ldr_text = 0;
|
|
|
|
if ( node.hashval("__DATA", &ldr_data, sizeof(ldr_data), SEGM_TAG) == -1
|
|
|| node.hashval("__TEXT", &ldr_text, sizeof(ldr_text), SEGM_TAG) == -1 )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ea_t dbg_data = g_dbgmod.dbg_get_segm_start(base, "__DATA");
|
|
ea_t dbg_text = g_dbgmod.dbg_get_segm_start(base, "__TEXT");
|
|
|
|
if ( dbg_data == BADADDR || dbg_text == BADADDR )
|
|
return false;
|
|
|
|
adiff_t slide = (dbg_data - dbg_text) - (ldr_data - ldr_text);
|
|
|
|
if ( slide == 0 )
|
|
return false;
|
|
|
|
scattered_image_t si;
|
|
// we have detected segment scattering.
|
|
// ensure that we can collect the vmaddr for each loader segment in IDA.
|
|
if ( g_dbgmod.dbg_get_scattered_image(si, base) <= 0 )
|
|
return false;
|
|
|
|
uint8 ubuf[16];
|
|
bytevec_t uuid;
|
|
// it is quite possible that the input file does not match the image in the cache.
|
|
// if there is a mismatch we warn the user.
|
|
if ( !g_dbgmod.dbg_get_image_uuid(&uuid, base)
|
|
|| uuid.size() != sizeof(ubuf)
|
|
|| node.supval(MACHO_ALT_UUID, ubuf, sizeof(ubuf)) <= 0
|
|
|| memcmp(uuid.begin(), ubuf, sizeof(ubuf)) != 0 )
|
|
{
|
|
warning("AUTOHIDE DATABASE\n"
|
|
"UUID mismatch between the input file and the image in memory.\n"
|
|
"\n"
|
|
"This could mean your dyld_shared_cache is out of sync with the library on disk,\n"
|
|
"and the database will likely not be rebased properly.\n"
|
|
"\n"
|
|
"To ensure proper rebasing, please confirm that the input file was included\n"
|
|
"the last time update_dyld_shared_cache was run.\n");
|
|
}
|
|
|
|
// adjust any pointers between the code and data segments
|
|
show_wait_box("Rebasing CODE -> DATA pointers");
|
|
// we want to patch pointers in the database without writing to debugger memory
|
|
lock_dbgmem_config();
|
|
|
|
for ( nodeidx_t nidx = node.charfirst(CODE_TAG);
|
|
nidx != BADNODE && !user_cancelled();
|
|
nidx = node.charnext(nidx, CODE_TAG) )
|
|
{
|
|
ea_t ea = node2ea(nidx);
|
|
uchar kind = node.charval_ea(ea, CODE_TAG);
|
|
switch ( kind )
|
|
{
|
|
case 1: // 32-bit pointer
|
|
add_dword(ea, slide);
|
|
break;
|
|
case 2: // 64-bit pointer
|
|
add_qword(ea, slide);
|
|
break;
|
|
default: // TODO: there are many more. we will deal with them later
|
|
break;
|
|
}
|
|
}
|
|
|
|
replace_wait_box("Rebasing DATA -> CODE pointers");
|
|
|
|
for ( nodeidx_t nidx = node.charfirst(DATA_TAG);
|
|
nidx != BADNODE && !user_cancelled();
|
|
nidx = node.charnext(nidx, DATA_TAG) )
|
|
{
|
|
ea_t ea = node2ea(nidx);
|
|
uchar kind = node.charval_ea(ea, DATA_TAG);
|
|
switch ( kind )
|
|
{
|
|
case 1: // pointer
|
|
if ( inf_is_64bit() )
|
|
add_qword(ea, -slide);
|
|
else
|
|
add_dword(ea, -slide);
|
|
break;
|
|
default: // TODO: there a few more. we will deal with them later
|
|
break;
|
|
}
|
|
}
|
|
|
|
hide_wait_box();
|
|
unlock_dbgmem_config();
|
|
|
|
qvector<segment_t *> ldrsegs;
|
|
// we must collect all the loader segments before we start calling move_segm().
|
|
// this is to avoid altering the areacb_t as we're iterating over it.
|
|
for ( segment_t *s=get_first_seg(); s != NULL; s=get_next_seg(s->start_ea) )
|
|
{
|
|
if ( s->is_loader_segm() )
|
|
ldrsegs.push_back(s);
|
|
}
|
|
|
|
show_wait_box("Rebasing scattered segments");
|
|
|
|
bool ok = true;
|
|
size_t ls_count = ldrsegs.size();
|
|
size_t ss_count = si.size();
|
|
|
|
// rebase each loader segment according to its matching segment in the scattered image.
|
|
// currently we require the list of scattered segments and the list of loader segments
|
|
// to have the exact same ordering. this is because we have no way to uniquely match a
|
|
// loader segment and a scattered segment without some context. the segment's name, start_ea,
|
|
// type, and selector are all not sufficient in this situation.
|
|
for ( size_t i = 0; i < ls_count && !user_cancelled(); i++ )
|
|
{
|
|
segment_t *s = ldrsegs[i];
|
|
|
|
qstring name;
|
|
get_segm_name(&name, s);
|
|
ea_t rebase_to = 0;
|
|
|
|
if ( i < ss_count && name == si[i].name )
|
|
{
|
|
// found the loader segment in memory. rebase it to this address.
|
|
rebase_to = si[i].start_ea;
|
|
}
|
|
else if ( name == "UNDEF" )
|
|
{
|
|
// UNDEF segment is not actually in memory. just rebase it along with the other data segments.
|
|
rebase_to = s->start_ea + (dbg_data - ldr_data);
|
|
}
|
|
else
|
|
{
|
|
msg("%a: Failed to find segment %s in process memory!\n", s->start_ea, name.c_str());
|
|
ok = false;
|
|
break;
|
|
}
|
|
|
|
if ( s->start_ea != rebase_to )
|
|
{
|
|
replace_wait_box("Moving segment %s to %#a", name.c_str(), rebase_to);
|
|
int code = move_segm(s, rebase_to, MSF_PRIORITY|MSF_SILENT);
|
|
if ( code != MOVE_SEGM_OK )
|
|
{
|
|
msg("%a: Failed to rebase segment %s to %a, code=%d\n", s->start_ea, name.c_str(), rebase_to, code);
|
|
ok = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
hide_wait_box();
|
|
set_imagebase(base);
|
|
node.altset(MACHO_ALT_IMAGEBASE, base);
|
|
|
|
// update segm eas in the database
|
|
qstring idx;
|
|
for ( ssize_t s = node.hashfirst(&idx, SEGM_TAG);
|
|
s >= 0;
|
|
s = node.hashnext(&idx, idx.c_str(), SEGM_TAG) )
|
|
{
|
|
ea_t start = g_dbgmod.dbg_get_segm_start(base, idx.c_str());
|
|
if ( start != BADADDR )
|
|
node.hashset(idx.c_str(), &start, sizeof(start), SEGM_TAG);
|
|
}
|
|
|
|
if ( !ok )
|
|
{
|
|
char buf[QMAXPATH];
|
|
dbg_get_input_path(buf, sizeof(buf));
|
|
warning("AUTOHIDE DATABASE\n"
|
|
"Some loader segments were not rebased properly.\n"
|
|
"\n"
|
|
"This error might occur if you are debugging a dylib from /System/Library or /usr/lib,\n"
|
|
"since these files have different segment info when they are loaded from dyld_shared_cache.\n"
|
|
"\n"
|
|
"To ensure more accurate segment info when debugging, you can:\n"
|
|
"\n"
|
|
" 1. open the dyld_shared_cache in IDA (usually found in /var/db/dyld/)\n"
|
|
" 2. select the 'Apple DYLD cache (single module)' option\n"
|
|
" 3. select %s from the list of modules\n"
|
|
" 4. use %s as the input file path in the Process Options dialog\n", qbasename(buf), buf);
|
|
}
|
|
|
|
return true;
|
|
}
|