update to ida 7.6, add builds

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

View File

@@ -0,0 +1,29 @@
PROC=uunp
O1=resext
O2=win9x
include ../plugin.mak
# MAKEDEP dependency list ------------------
$(F)resext$(O) : $(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp $(I)fpro.h \
$(I)funcs.hpp $(I)ida.hpp $(I)idp.hpp $(I)ieee.h \
$(I)kernwin.hpp $(I)lines.hpp $(I)llong.hpp \
$(I)loader.hpp $(I)nalt.hpp $(I)netnode.hpp $(I)pro.h \
$(I)prodir.h $(I)range.hpp $(I)segment.hpp $(I)ua.hpp \
$(I)xref.hpp resext.cpp uunp.hpp
$(F)uunp$(O) : $(I)auto.hpp $(I)bitrange.hpp $(I)bytes.hpp \
$(I)config.hpp $(I)dbg.hpp $(I)entry.hpp \
$(I)fpro.h $(I)funcs.hpp $(I)ida.hpp $(I)idd.hpp \
$(I)idp.hpp $(I)ieee.h $(I)kernwin.hpp $(I)lines.hpp \
$(I)llong.hpp $(I)loader.hpp \
$(I)nalt.hpp $(I)name.hpp \
$(I)netnode.hpp $(I)offset.hpp $(I)pro.h $(I)range.hpp \
$(I)segment.hpp $(I)ua.hpp $(I)xref.hpp uunp.cpp \
uunp.hpp
$(F)win9x$(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)ieee.h $(I)kernwin.hpp \
$(I)lines.hpp $(I)llong.hpp $(I)loader.hpp $(I)nalt.hpp \
$(I)netnode.hpp $(I)pro.h $(I)range.hpp $(I)segment.hpp \
$(I)ua.hpp $(I)xref.hpp uunp.hpp win9x.cpp

View File

@@ -0,0 +1,307 @@
// Written by Yury Haron yjh@styx.cabel.net
#include <windows.h>
#include <ida.hpp>
#include <prodir.h>
#include <idp.hpp>
#include <bytes.hpp>
#include "uunp.hpp"
//--------------------------------------------------------------------------
#pragma pack(push, 1)
struct rhdr_beg_t
{
uint32 DataSize;
uint32 HeaderSize;
};
#if 0
struct reshdr_t
{
rhdr_beg_t rb;
rhdr_name_t Type;
rhdr_name_t Name;
rhdr_end_t re;
};
#endif
#pragma pack()
// resources are always aligned to sizeof(uint32)
//---------------------------------------------------------------------------
void uunp_ctx_t::store(const void *Data, uint32 size)
{
static const uint32 zero4 = 0;
rhdr_beg_t rh;
size_t len = sizeof(rh) + sizeof(re);
if ( Names[0].len != 0 )
len += Names[0].len;
else
len += sizeof(zname);
if ( Names[1].len != 0 )
len += Names[1].len;
else
len += sizeof(zname);
rh.HeaderSize = (uint32)len;
rh.DataSize = size;
re.LanguageId = Names[2].Id;
qfwrite(fr, &rh, sizeof(rh));
if ( Names[0].len != 0 )
{
qfwrite(fr, Names[0].name, Names[0].len);
}
else
{
zname.Id = Names[0].Id;
qfwrite(fr, &zname, sizeof(zname));
}
if ( Names[1].len != 0 )
{
qfwrite(fr, Names[1].name, Names[1].len);
}
else
{
zname.Id = Names[1].Id;
qfwrite(fr, &zname, sizeof(zname));
}
qfwrite(fr, &re, sizeof(re));
if ( Data ) // for 'primary' header
{
qfwrite(fr, Data, size);
len += size;
}
if ( len & 3 )
qfwrite(fr, &zero4, 4 - (len & 3));
}
//---------------------------------------------------------------------------
static bool initPtrs(uunp_ctx_t &ctx, const char *fname)
{
IMAGE_DATA_DIRECTORY res;
ea_t nth;
nth = get_dword(ctx.curmod.start_ea + 0x3C) + ctx.curmod.start_ea;
size_t off;
if ( inf_is_64bit() )
{
off = offsetof(IMAGE_NT_HEADERS64,
OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress);
}
else
{
off = offsetof(IMAGE_NT_HEADERS32,
OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress);
}
if ( get_bytes(&res, sizeof(res), nth + off) != sizeof(res)
|| !res.VirtualAddress
|| !res.Size )
{
msg("There are no resources in the module\n");
return false;
}
ctx.ResBase = ctx.curmod.start_ea + res.VirtualAddress;
ctx.ResTop = res.Size;
ctx.ImgSize = ctx.curmod.end_ea - ctx.curmod.start_ea;
int minres = 2*sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY)+3*sizeof(IMAGE_RESOURCE_DIRECTORY);
if ( (res.Size & 3) != 0
|| res.Size <= minres
|| res.VirtualAddress >= ctx.ImgSize
|| res.Size >= ctx.ImgSize
|| res.Size + res.VirtualAddress > ctx.ImgSize )
{
msg("Invalid resource descriptor\n");
return false;
}
ctx.fr = qfopen(fname, "wb");
if ( ctx.fr == NULL )
{
msg("Cannot create the output file '%s' for the resources\n", fname);
return false;
}
return true;
}
//---------------------------------------------------------------------------
static bool extractData(uunp_ctx_t &ctx, uint32 off)
{
IMAGE_RESOURCE_DATA_ENTRY rd;
if ( off + sizeof(rd) > ctx.ResTop )
return false;
if ( get_bytes(&rd, sizeof(rd), ctx.ResBase + off) != sizeof(rd) )
return false;
if ( rd.OffsetToData >= ctx.ImgSize
|| rd.Size > ctx.ImgSize
|| rd.OffsetToData + rd.Size > ctx.ImgSize )
{
return false;
}
void *data = qalloc(rd.Size);
if ( data == NULL )
{
msg("Not enough memory for resources\n");
return false;
}
bool res = false;
if ( get_bytes(data, rd.Size, ctx.curmod.start_ea + rd.OffsetToData) == rd.Size )
{
ctx.store(data, rd.Size);
res = true;
}
qfree(data);
return res;
}
//---------------------------------------------------------------------------
static bool extractDirectory(uunp_ctx_t &ctx, uint32 off, int level);
static bool extractEntry(uunp_ctx_t &ctx, uint32 off, int level, bool named)
{
IMAGE_RESOURCE_DIRECTORY_ENTRY rde;
if ( off + sizeof(rde) >= ctx.ResTop )
return false;
if ( get_bytes(&rde, sizeof(rde), ctx.ResBase + off) != sizeof(rde) )
return false;
if ( (bool)rde.NameIsString != named )
return false;
if ( (bool)rde.DataIsDirectory != (level != 2) )
return false;
off += sizeof(rde);
if ( !named )
{
ctx.Names[level].Id = rde.Id;
}
else
{
ea_t npos = rde.NameOffset;
if ( npos < off || npos + 2 >= ctx.ResTop )
return false;
uint32 nlen = get_word(npos + ctx.ResBase)*sizeof(wchar_t);
if ( !nlen || npos + nlen > ctx.ResTop )
return false;
wchar_t *p = (wchar_t *)qalloc(nlen + sizeof(wchar_t));
if ( p == NULL )
{
msg("Not enough memory for resource names\n");
return false;
}
if ( get_bytes(p, nlen, npos + sizeof(uint16) + ctx.ResBase) != nlen )
{
bad_name:
qfree(p);
return false;
}
p[nlen/sizeof(wchar_t)] = 0;
size_t wlen = wcslen(p);
if ( !wlen || wlen < nlen/2-1 )
goto bad_name;
ctx.Names[level].name = p;
ctx.Names[level].len = uint32((wlen+1)*sizeof(wchar_t));
}
if ( level != 2 )
{
bool res = false;
if ( rde.OffsetToDirectory >= off )
res = extractDirectory(ctx, rde.OffsetToDirectory, level+1);
if ( ctx.Names[level].len )
qfree(ctx.Names[level].name);
ctx.Names[level].name = NULL;
ctx.Names[level].len = 0;
return res;
}
if ( rde.OffsetToData < off )
return false;
return extractData(ctx, rde.OffsetToData);
}
//---------------------------------------------------------------------------
static bool extractDirectory(uunp_ctx_t &ctx, uint32 off, int level)
{
IMAGE_RESOURCE_DIRECTORY rd;
if ( off + sizeof(rd) >= ctx.ResTop )
return false;
if ( get_bytes(&rd, sizeof(rd), ctx.ResBase + off) != sizeof(rd) )
return false;
off += sizeof(rd);
if ( rd.NumberOfNamedEntries != 0 )
{
if ( level == 2 ) // language must be ONLY numbered
return false;
do
{
if ( !extractEntry(ctx, off, level, true) )
return false;
off += sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY);
} while ( --rd.NumberOfNamedEntries );
}
if ( rd.NumberOfIdEntries != 0 )
{
do
{
if ( !extractEntry(ctx, off, level, false) )
return false;
off += sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY);
} while ( --rd.NumberOfIdEntries );
}
return true;
}
//---------------------------------------------------------------------------
void uunp_ctx_t::extract_resource(const char *fname)
{
if ( !initPtrs(*this, fname) )
return;
store(NULL, 0); // zero-resource header
bool wrerr = false;
bool res = extractDirectory(*this, 0, 0);
if ( !res )
{
msg("Can't extract resource (possible it is invalid)\n");
}
else
{
qflush(fr);
if ( ferror(fr) || feof(fr) )
wrerr = true;
}
if ( qfclose(fr) )
wrerr = true;
fr = nullptr; // just in case
if ( res && wrerr )
msg("Error writing resource file\n");
if ( !res || wrerr )
qunlink(fname);
else
msg("Resources have been extracted and stored in '%s'\n", fname);
}

View File

@@ -0,0 +1,825 @@
// Universal Unpacker based on IDA debugger 1.2
// Unpacks PE files
// The algorithm of this plugin is:
// 1. start the process until the entry point of the packed program
// 2. add a breakpoint at kernel32.GetProcAddress
// 3. resume the execution and wait until the packer calls GetProcAddress
// if the function name passed to GetProcAddress is not in the ignore-list,
// then switch to the trace mode
// A call to GetProcAddress() most likely means that the program has been
// unpacked in the memory and now it setting up its import table
// 4. trace the program in the single step mode until we jump to
// the range with the original entry point.
// 5. as soon as the current ip belongs OEP range, suspend the execution and
// inform the user
//
// So, in short, we allow the unpacker to do its job full speed until
// it starts to setup the import table. At this moment we switch to the single
// step mode and try to find the original entry point.
//
// While this algorithm works with UPX, aspack, and several other packers,
// it might fail and execution of the packed program might go out of control.
// So please use this plugin with precaution.
//
// Ilfak Guilfanov, Yury Haron
#include <windows.h>
#ifdef _MSC_VER
# pragma warning(disable: 4996) // GetVersion was declared deprecated
#endif
#include <ida.hpp>
#include <dbg.hpp>
#include <loader.hpp>
#include <kernwin.hpp>
#include <bytes.hpp>
#include <offset.hpp>
#include <auto.hpp>
#include <entry.hpp>
#include <name.hpp>
#include "uunp.hpp"
//--------------------------------------------------------------------------
#define REGNAME_EAX (inf_is_64bit() ? "rax" : "eax")
#define REGNAME_ECX (inf_is_64bit() ? "rcx" : "ecx")
#define REGVALUE_MASK (inf_is_64bit() ? ea_t(-1) : ea_t(0xffffffffu))
//--------------------------------------------------------------------------
static size_t get_ptrsize(void)
{
#ifndef __EA64__
return sizeof(ea_t);
#else
static size_t ptr_sz = 0;
if ( ptr_sz == 0 )
ptr_sz = inf_is_64bit() ? 8 : 4;
return ptr_sz;
#endif
}
//--------------------------------------------------------------------------
bool doPtr(ea_t ea)
{
bool ok = get_ptrsize() == 4 ? create_dword(ea, 4) : create_qword(ea, 8);
return ok && op_plain_offset(ea, 0, 0);
}
//--------------------------------------------------------------------------
ea_t getPtr(ea_t ea)
{
return get_ptrsize() == 4 ? get_dword(ea) : get_qword(ea);
}
//--------------------------------------------------------------------------
inline bool my_add_bpt(uunp_ctx_t &ctx, ea_t ea)
{
ctx.bpt_ea = ea;
return add_bpt(ea);
}
//--------------------------------------------------------------------------
inline bool my_del_bpt(uunp_ctx_t &ctx, ea_t ea)
{
ctx.bpt_ea = BADADDR;
return del_bpt(ea);
}
//---------------------------------------------------------------------------
inline void uunp_ctx_t::_hide_wait_box()
{
if ( wait_box_visible )
{
wait_box_visible = false;
hide_wait_box();
}
}
//--------------------------------------------------------------------------
inline void uunp_ctx_t::set_wait_box(const char *mesg)
{
if ( wait_box_visible )
{
replace_wait_box("HIDECANCEL\n%s", mesg);
}
else
{
wait_box_visible = true;
show_wait_box("HIDECANCEL\n%s", mesg);
}
}
//--------------------------------------------------------------------------
static void move_entry(uunp_ctx_t &ctx, ea_t rstart)
{
// remove old start
set_name(inf_get_start_ea(), "");
// patch inf struct
inf_set_start_ea(rstart);
inf_set_start_ip(rstart);
// add new entry point
add_entry(rstart, rstart, "start", true);
ctx.success = true;
segment_t *ps = getseg(rstart);
if ( ps != NULL )
{
ps->set_loader_segm(true);
ps->update();
}
}
//--------------------------------------------------------------------------
// Unpacker might use some Win32 functions to perform their function
// This function verifies whether we must switch to the trace mode
// or continue to wait for GetProcAddress() of some other interesting function
static bool ignore_win32_api(const char *name)
{
static const char *const ignore_names[] = { "VirtualAlloc", "VirtualFree" };
for ( size_t i=0; i < qnumber(ignore_names); i++ )
{
if ( strcmp(name, ignore_names[i]) == 0 )
return true;
}
return false;
}
//--------------------------------------------------------------------------
inline bool is_library_entry(const uunp_ctx_t &ctx, ea_t ea)
{
return !ctx.curmod.contains(ea);
}
//--------------------------------------------------------------------------
static bool find_module(ea_t ea, modinfo_t *mi)
{
bool ok;
for ( ok=get_first_module(mi); ok; ok=get_next_module(mi) )
{
if ( range_t(mi->base, mi->base+mi->size).contains(ea) )
break;
}
return ok;
}
//--------------------------------------------------------------------------
static bool create_idata_segm(const range_t &impdir)
{
segment_t ns;
segment_t *s = getseg(impdir.start_ea);
if ( s != NULL )
ns = *s;
else
ns.sel = setup_selector(0);
ns.start_ea = impdir.start_ea;
ns.end_ea = impdir.end_ea;
ns.type = SEG_XTRN;
ns.set_loader_segm(true);
bool ok = add_segm_ex(&ns, ".idata", "XTRN", ADDSEG_NOSREG) != 0;
if ( !ok )
ok = ask_yn(ASKBTN_NO,
"HIDECANCEL\n"
"Cannot create the import segment. Continue anyway?") > ASKBTN_NO;
return ok;
}
//--------------------------------------------------------------------------
static bool find_impdir(uunp_ctx_t &ctx, range_t *impdir)
{
impdir->start_ea = impdir->end_ea = 0;
uint32 ea32 = uint32(ctx.an_imported_func);
for ( ea_t pos = ctx.curmod.start_ea;
pos <= ctx.curmod.end_ea;
pos += sizeof(DWORD) )
{
pos = bin_search2(pos, ctx.curmod.end_ea, (uchar *)&ea32, NULL, 4,
BIN_SEARCH_NOBREAK|BIN_SEARCH_CASE|BIN_SEARCH_FORWARD);
if ( pos == BADADDR )
break;
// skip unaligned matches
if ( (pos & 3) != 0 )
continue;
// cool, we found a pointer to an imported function
// now try to determine the impdir bounds
ea_t bounds[2] = { pos, pos };
for ( int k=0; k < 2; k++ )
{
ea_t ea = pos;
while ( true )
{
if ( k == 1 )
ea += get_ptrsize();
else
ea -= get_ptrsize();
ea_t func = ctx.is_9x ? ctx.win9x_find_thunk(ea) : getPtr(ea);
if ( func == 0 )
continue;
if ( !is_mapped(func) )
break;
if ( ctx.curmod.contains(func) )
break;
modinfo_t mi;
if ( !find_module(func, &mi) )
break;
bounds[k] = ea;
}
}
bounds[1] += get_ptrsize();
asize_t bsize = bounds[1] - bounds[0];
if ( bsize > impdir->size() )
*impdir = range_t(bounds[0], bounds[1]);
}
return impdir->start_ea != 0;
}
//--------------------------------------------------------------------------
static bool create_impdir(uunp_ctx_t &ctx, const range_t &impdir)
{
// now rename all entries in impdir
del_items(impdir.start_ea, DELIT_EXPAND, impdir.size());
if ( !create_idata_segm(impdir) )
return false;
char dll[MAXSTR];
qstring buf;
dll[0] = '\0';
modinfo_t mi;
mi.base = BADADDR;
mi.size = 0;
size_t len = 0;
for ( ea_t ea=impdir.start_ea; ea < impdir.end_ea; ea += get_ptrsize() )
{
doPtr(ea);
ea_t func = ctx.is_9x ? ctx.win9x_find_thunk(ea) : getPtr(ea);
if ( get_name(&buf, func) <= 0 )
continue;
if ( !range_t(mi.base, mi.base+mi.size).contains(func) )
{
find_module(func, &mi);
qstrncpy(dll, qbasename(mi.name.c_str()), sizeof(dll));
char *ptr = strrchr(dll, '.');
if ( ptr != NULL )
*ptr = '\0';
if ( streq(dll, "ntdll32") ) // ntdll32 -> ntdll
dll[5] = '\0';
len = strlen(dll);
}
const char *name = buf.begin();
if ( strnicmp(dll, name, len) == 0 && name[len] == '_' )
name += len + 1;
if ( !force_name(ea, name, SN_IDBENC) )
msg("%a: cannot rename to imported name '%s'\n", ea, name);
}
return true;
}
//--------------------------------------------------------------------------
static void create_impdir(uunp_ctx_t &ctx)
{
// refresh dll entry point names
dbg->suspended(true);
// refresh memory configuration
invalidate_dbgmem_config();
// found impdir?
range_t impdir;
if ( !find_impdir(ctx, &impdir) )
return;
msg("Uunp: Import directory bounds %a..%a\n", impdir.start_ea, impdir.end_ea);
create_impdir(ctx, impdir);
}
//--------------------------------------------------------------------------
static void tell_about_failure(void)
{
warning("The plugin failed to unpack the program, sorry.\n"
"If you want to improve it, the source code is in the SDK!");
}
//--------------------------------------------------------------------------
ssize_t idaapi dbg_listener_t::on_event(ssize_t code, va_list va)
{
return ctx.on_dbg_event(code, va);
}
ssize_t idaapi uunp_ctx_t::on_dbg_event(ssize_t code, va_list va)
{
switch ( code )
{
case dbg_process_start:
case dbg_process_attach:
get_input_file_path(needed_file, sizeof(needed_file));
// no break
case dbg_library_load:
if ( stage == 0 )
{
const debug_event_t *pev = va_arg(va, const debug_event_t *);
const char *modname = pev->modinfo().name.c_str();
const char *myname = needed_file;
if ( !inf_is_dll() )
{ // ignore the full path for exe names (to handle subst drives)
modname = qbasename(modname);
myname = qbasename(myname);
}
if ( !strieq(modname, myname) )
break;
if ( code == dbg_library_load )
is_dll = true;
// remember the current module bounds
if ( pev->modinfo().rebase_to != BADADDR )
curmod.start_ea = pev->modinfo().rebase_to;
else
curmod.start_ea = pev->modinfo().base;
curmod.end_ea = curmod.start_ea + pev->modinfo().size;
deb(IDA_DEBUG_DBGINFO, "UUNP: module space %a-%a\n", curmod.start_ea, curmod.end_ea);
++stage;
}
break;
case dbg_library_unload:
if ( stage != 0 && is_dll )
{
const debug_event_t *pev = va_arg(va, const debug_event_t *);
if ( curmod.start_ea == pev->modinfo().base
|| curmod.start_ea == pev->modinfo().rebase_to )
{
deb(IDA_DEBUG_DBGINFO, "UUNP: unload unpacked module\n");
if ( stage > 2 )
enable_step_trace(false);
stage = 0;
curmod.start_ea = 0;
curmod.end_ea = 0;
_hide_wait_box();
}
}
break;
case dbg_run_to: // Parameters: const debug_event_t *event
dbg->suspended(true);
bp_gpa = get_name_ea(BADADDR, "kernel32_GetProcAddress");
if ( (LONG)GetVersion() < 0 ) // win9x mode -- use thunk's
{
is_9x = true;
win9x_resolve_gpa_thunk();
}
if ( bp_gpa == BADADDR )
{
bring_debugger_to_front();
warning("Sorry, could not find kernel32.GetProcAddress");
FORCE_STOP:
stage = 4; // last stage
clear_requests_queue();
request_exit_process();
run_requests();
break;
}
else if ( !my_add_bpt(*this, bp_gpa) )
{
bring_debugger_to_front();
warning("Sorry, cannot set bpt to kernel32.GetProcAddress");
goto FORCE_STOP;
}
else
{
++stage;
set_wait_box("Waiting for a call to GetProcAddress()");
}
continue_process();
break;
case dbg_bpt: // A user defined breakpoint was reached.
// Parameters: thid_t tid
// ea_t breakpoint_ea
// int *warn = -1
// Return (in *warn):
// -1 - to display a breakpoint warning dialog
// if the process is suspended.
// 0 - to never display a breakpoint warning dialog.
// 1 - to always display a breakpoint warning dialog.
{
thid_t tid = va_arg(va, thid_t); qnotused(tid);
ea_t ea = va_arg(va, ea_t);
ea &= REGVALUE_MASK;
//int *warn = va_arg(va, int*);
if ( stage == 2 )
{
if ( ea == bp_gpa )
{
ea_t esp;
if ( get_sp_val(&esp) )
{
invalidate_dbgmem_contents(esp, 1024);
ea_t gpa_caller = getPtr(esp);
if ( !is_library_entry(*this, gpa_caller) )
{
ea_t nameaddr;
if ( get_ptrsize() == 4 )
{
nameaddr = get_dword(esp+8);
}
else
{
regval_t rv;
get_reg_val(REGNAME_ECX, &rv);
nameaddr = ea_t(rv.ival) & REGVALUE_MASK;
}
invalidate_dbgmem_contents(nameaddr, 1024);
qstring name;
size_t len = get_max_strlit_length(nameaddr, STRTYPE_C, ALOPT_IGNHEADS);
get_strlit_contents(&name, nameaddr, len, STRTYPE_C);
if ( !ignore_win32_api(name.c_str()) )
{
deb(IDA_DEBUG_DBGINFO, "%a: found a call to GetProcAddress(%s)\n", gpa_caller, name.c_str());
if ( !my_del_bpt(*this, bp_gpa) || !my_add_bpt(*this, gpa_caller) )
error("Cannot modify breakpoint");
}
}
}
}
else if ( ea == bpt_ea )
{
my_del_bpt(*this, ea);
if ( !is_library_entry(*this, ea) )
{
msg("Uunp: reached unpacker code at %a, switching to trace mode\n", ea);
enable_step_trace(true);
++stage;
uint64 eax = 0;
if ( get_reg_val(REGNAME_EAX, &eax) )
an_imported_func = ea_t(eax) & REGVALUE_MASK;
set_wait_box("Waiting for the unpacker to finish");
}
else
{
warning("%a: bpt in library code", ea); // how can it be?
my_add_bpt(*this, bp_gpa);
}
}
// not our bpt? skip it
else
{
// hide the wait box to allow others plugins to properly stop
_hide_wait_box();
break;
}
}
}
// while continue_process() would work here too, request+run is more universal
// because they do not ignore the request queue
request_continue_process();
run_requests();
break;
case dbg_trace: // A step occurred (one instruction was executed). This event
// notification is only generated if step tracing is enabled.
// Parameter: none
if ( stage == 3 )
{
thid_t tid = va_arg(va, thid_t); qnotused(tid);
ea_t ip = va_arg(va, ea_t);
ip &= REGVALUE_MASK;
// ip reached the OEP range?
if ( oep_range.contains(ip) )
{
// stop the trace mode
enable_step_trace(false);
msg("Uunp: reached OEP %a\n", ip);
set_wait_box("Reanalyzing the unpacked code");
// reanalyze the unpacked code
del_items(oep_range.start_ea, DELIT_EXPAND, oep_range.size());
auto_make_code(ip); // plan to make code
plan_range(oep_range.start_ea, oep_range.end_ea); // plan to reanalyze
auto_mark_range(oep_range.start_ea, oep_range.end_ea, AU_FINAL); // plan to analyze
move_entry(*this, ip); // mark the program's entry point
_hide_wait_box();
// inform the user
bring_debugger_to_front();
if ( ask_yn(ASKBTN_YES,
"HIDECANCEL\n"
"The universal unpacker has finished its work.\n"
"Do you want to take a memory snapshot and stop now?\n"
"(you can do it yourself if you want)\n") > ASKBTN_NO )
{
set_wait_box("Recreating the import table");
invalidate_dbgmem_config();
if ( is_9x )
find_thunked_imports();
create_impdir(*this);
set_wait_box("Extracting resources");
if ( !resfile.empty() )
extract_resource(resfile.c_str());
_hide_wait_box();
if ( take_memory_snapshot(true) )
goto FORCE_STOP;
}
suspend_process();
unhook_event_listener(HT_DBG, &dbg_listener);
}
}
break;
case dbg_process_exit:
{
stage = 0;
// stop the tracing
_hide_wait_box();
unhook_event_listener(HT_DBG, &dbg_listener);
if ( success )
jumpto(inf_get_start_ea(), -1);
else
tell_about_failure();
}
break;
case dbg_exception:// Parameters: const debug_event_t *event
// int *warn = -1
// Return (in *warn):
// -1 - to display an exception warning dialog
// if the process is suspended.
// 0 - to never display an exception warning dialog.
// 1 - to always display an exception warning dialog.
{
// const debug_event_t *event = va_arg(va, const debug_event_t *);
// int *warn = va_arg(va, int *);
// FIXME: handle code which uses SEH to unpack itself
if ( ask_yn(ASKBTN_YES,
"AUTOHIDE DATABASE\n"
"HIDECANCEL\n"
"An exception occurred in the program.\n"
"UUNP does not support exceptions yet.\n"
"The execution has been suspended.\n"
"Do you want to continue the unpacking?") <= ASKBTN_NO )
{
_hide_wait_box();
stage = 0;
enable_step_trace(false); // stop the trace mode
suspend_process();
}
else
{
continue_process();
}
}
break;
case dbg_request_error:
// An error occurred during the processing of a request.
// Parameters: ui_notification_t failed_command
// dbg_notification_t failed_dbg_notification
{
ui_notification_t failed_cmd = va_arg(va, ui_notification_t);
dbg_notification_t failed_dbg_notification = va_arg(va, dbg_notification_t);
_hide_wait_box();
stage = 0;
warning("dbg request error: command: %d notification: %d",
failed_cmd, failed_dbg_notification);
}
break;
}
return 0;
}
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
// 0 - run uunp interactively
// 1 - run without questions
// 2 - run manual reconstruction
bool idaapi uunp_ctx_t::run(size_t arg)
{
if ( arg == 2 )
{
range_t impdir = range_t(0, 0);
ea_t oep;
netnode n;
// Settings never stored before?
if ( n.create(UUNP_NODE_NAME) )
{
// Populate default values
oep = get_screen_ea();
segment_t *s = getseg(oep);
if ( s != NULL )
{
oep_range.start_ea = s->start_ea;
oep_range.end_ea = s->end_ea;
}
}
else
{
// Restore previous settings
oep = n.altval(0);
oep_range.start_ea = n.altval(1);
oep_range.end_ea = n.altval(2);
impdir.start_ea = n.altval(3);
impdir.end_ea = n.altval(4);
}
CASSERT(sizeof(oep_range.start_ea) == sizeof(ea_t));
CASSERT(sizeof(oep_range.end_ea) == sizeof(ea_t));
if ( !ask_form("Reconstruction parameters\n"
"\n"
" <~O~riginal entrypoint:N::32::>\n"
" <Code ~s~tart address:N::32::>\n"
" <Code ~e~nd address :N::32::>\n"
"\n"
" <IAT s~t~art address:N::32::>\n"
" <IAT e~n~d address:N::32::>\n"
"\n",
&oep,
&oep_range.start_ea, &oep_range.end_ea,
&impdir.start_ea, &impdir.end_ea) )
{
// Cancelled?
return true;
}
// Invalid settings?
if ( impdir.start_ea == 0 || impdir.end_ea == 0 )
{
msg("Invalid import address table boundaries\n");
return true;
}
// Store settings
n.altset(0, oep);
n.altset(1, oep_range.start_ea);
n.altset(2, oep_range.end_ea);
n.altset(3, impdir.start_ea);
n.altset(4, impdir.end_ea);
if ( !create_impdir(*this, impdir) )
return false;
// reanalyze the unpacked code
del_items(oep_range.start_ea, DELIT_EXPAND, oep_range.size());
auto_make_code(oep);
plan_range(oep_range.start_ea, oep_range.end_ea);
auto_mark_range(oep_range.start_ea, oep_range.end_ea, AU_FINAL);
// mark the program's entry point
move_entry(*this, oep);
take_memory_snapshot(true);
arg = 0;
goto oep_setted;
}
// Determine the original entry point range
for ( segment_t *s = get_first_seg(); s != NULL; s=get_next_seg(s->start_ea) )
{
if ( s->type != SEG_GRP )
{
oep_range = *s;
break;
}
}
oep_setted:
if ( arg == 0
&& ask_yn(ASKBTN_NO,
"HIDECANCEL\n"
"AUTOHIDE REGISTRY\n"
"Universal PE unpacker\n"
"\n"
"IMPORTANT INFORMATION, PLEASE READ CAREFULLY!\n"
"\n"
"This plugin will start the program execution and try to suspend it\n"
"as soon as the packer finishes its work. Since there might be many\n"
"variations in packers and packing methods, the execution might go out\n"
"of control. There are many ways how things can go wrong, but since you\n"
"have the source code of this plugin, you can modify it as you wish.\n"
"\n"
"Do you really want to launch the program?\n") <= 0 )
{
return true;
}
success = false;
char resfile_[QMAXPATH];
set_file_ext(resfile_, sizeof(resfile_), get_path(PATH_TYPE_IDB), "res");
if ( arg == 0
&& !ask_form("Uunp parameters\n"
"IDA will suspend the program when the execution reaches\n"
"the original entry point range. The default values are in\n"
"this dialog box. Please verify them and correct if you wish.\n"
"\n"
"ORIGINAL ENTRY POINT AREA\n"
" <~S~tart address:N::32::>\n"
" <~E~nd address :N::32::>\n"
"\n"
"OUTPUT RESOURCE FILE NAME\n"
" <~R~esource file:f:1:32::>\n"
"\n",
&oep_range.start_ea,
&oep_range.end_ea,
resfile_) )
{
return true;
}
resfile = resfile_;
if ( !hook_event_listener(HT_DBG, &dbg_listener) )
{
warning("Could not hook to notification point");
return true;
}
if ( dbg == NULL )
load_debugger("win32", false);
// Let's start the debugger
if ( !run_to(inf_get_start_ea()) )
{
warning("Sorry, could not start the process");
unhook_event_listener(HT_DBG, &dbg_listener);
}
return true;
}
//--------------------------------------------------------------------------
static plugmod_t *idaapi init()
{
// Our plugin works only for x86 PE executables
processor_t &ph = PH;
if ( ph.id != PLFM_386 || inf_get_filetype() != f_PE )
return nullptr;
return new uunp_ctx_t;
}
//--------------------------------------------------------------------------
uunp_ctx_t::uunp_ctx_t()
{
}
uunp_ctx_t::~uunp_ctx_t()
{
// listeners are uninstalled automatically
// when the owner module is unloaded
// just to be safe
_hide_wait_box();
fr = nullptr;
}
//--------------------------------------------------------------------------
static const char wanted_name[] = "Universal PE unpacker";
//--------------------------------------------------------------------------
//
// PLUGIN DESCRIPTION BLOCK
//
//--------------------------------------------------------------------------
plugin_t PLUGIN =
{
IDP_INTERFACE_VERSION,
PLUGIN_MULTI, // The plugin can work with multiple idbs in parallel
init, // initialize
nullptr,
nullptr,
wanted_name, // long comment about the plugin
// it could appear in the status line
// or as a hint
wanted_name, // multiline help about the plugin
wanted_name, // the preferred short name of the plugin
"" // the preferred hotkey to run the plugin
};

View File

@@ -0,0 +1,89 @@
#include <idp.hpp>
#include <loader.hpp>
#define UUNP_NODE_NAME "$ uunp"
//----------------------------------------------------------------------
struct uunp_ctx_t;
DECLARE_LISTENER(dbg_listener_t, uunp_ctx_t, ctx);
struct uunp_ctx_t : public plugmod_t
{
dbg_listener_t dbg_listener = dbg_listener_t(*this);
ea_t bp_gpa = BADADDR; // address of GetProcAddress()
range_t curmod; // current module range
bool wait_box_visible = false;
range_t oep_range; // original entry point range
qstring resfile; // resource file name
ea_t an_imported_func = BADADDR; // an imported function
bool success = false;
bool is_9x = false;
ea_t bpt_ea = BADADDR; // our bpt address
// win9x.cpp
typedef std::map<ea_t, ea_t> thunks_t;
thunks_t thunks;
// resext.cpp
FILE *fr = NULL;
ea_t ResBase = 0;
uint32 ResTop = 0;
asize_t ImgSize = 0;
struct
{
union
{
wchar_t *name = nullptr;
uint16 Id;
};
uint32 len = 0;
} Names[3];
#pragma pack(push, 1)
struct rhdr_end_t
{
uint32 DataVersion;
uint16 MemoryFlags;
uint16 LanguageId;
uint32 Version;
uint32 Characteristrics;
};
union rhdr_name_t
{
struct
{
uint16 prefix; // = 0xFFFF if number entry
uint16 Id; // for number entry
};
wchar_t Name[1]; // zero terminated
};
#pragma pack()
rhdr_end_t re = { 0 };
rhdr_name_t zname = { { 0xFFFF } };
// on_event()
int stage = 0;
bool is_dll = false;
char needed_file[QMAXPATH] = "";
uunp_ctx_t();
~uunp_ctx_t();
virtual bool idaapi run(size_t) override;
ssize_t idaapi on_dbg_event(ssize_t code, va_list va);
inline void set_wait_box(const char *mesg);
inline void _hide_wait_box();
// Windows9x specific functions
void win9x_resolve_gpa_thunk();
ea_t win9x_find_thunk(ea_t ea);
void find_thunked_imports();
// Resource extractor function
void extract_resource(const char *fname);
void store(const void *Data, uint32 size);
};
extern int data_id;

View File

@@ -0,0 +1,113 @@
/*
This is a reimplementation of the uunp universal unpacker in IDC.
It illustrates the use of the new debugger functions in IDA v5.2
*/
#include <idc.idc>
//--------------------------------------------------------------------------
static main()
{
auto ea, bptea, tea1, tea2, code, minea, maxea, r_esp, r_eip, caller, funcname;
// Calculate the target IP range. It is the first segment.
// As soon as the EIP register points to this range, we assume that
// the unpacker has finished its work.
tea1 = get_first_seg();
tea2 = get_segm_end(tea1);
// Calculate the current module boundaries. Any calls to GetProcAddress
// outside of these boundaries will be ignored.
minea = get_inf_attr(INF_MIN_EA);
maxea = get_inf_attr(INF_MAX_EA);
// Use win32 local debugger
load_debugger("win32", 0);
// Launch the debugger and run until the entry point
if ( !run_to(get_inf_attr(INF_START_EA)) )
return Failed(-10);
// Wait for the process to stop at the entry point
code = wait_for_next_event(WFNE_SUSP, -1);
if ( code <= 0 )
return Failed(code);
// Set a breakpoint at GetProcAddress
bptea = get_name_ea_simple("kernel32_GetProcAddress");
if ( bptea == BADADDR )
return warning("Could not locate GetProcAddress");
add_bpt(bptea);
while ( 1 )
{
// resume the execution and wait until the unpacker calls GetProcAddress
code = wait_for_next_event(WFNE_SUSP|WFNE_CONT, -1); // CONT means resume
if ( code <= 0 )
return Failed(code);
// check the caller, it must be from our module
r_esp = get_reg_value("ESP");
caller = get_wide_dword(r_esp);
if ( caller < minea || caller >= maxea )
continue;
// if the function name passed to GetProcAddress is not in the ignore-list,
// then switch to the trace mode
funcname = get_strlit_contents(get_wide_dword(r_esp+8), -1, STRTYPE_C);
// ignore some api calls because they might be used by the unpacker
if ( funcname == "VirtualAlloc" )
continue;
if ( funcname == "VirtualFree" )
continue;
// A call to GetProcAddress() probably means that the program has been
// unpacked in the memory and now is setting up its import table
break;
}
// trace the program in the single step mode until we jump to
// the area with the original entry point.
del_bpt(bptea);
enable_tracing(TRACE_STEP, 1);
for ( code = wait_for_next_event(WFNE_ANY|WFNE_CONT, -1); // resume
code > 0;
code = wait_for_next_event(WFNE_ANY, -1) )
{
r_eip = get_event_ea();
if ( r_eip >= tea1 && r_eip < tea2 )
break;
}
if ( code <= 0 )
return Failed(code);
// as soon as the current ip belongs OEP area, suspend the execution and
// inform the user
suspend_process();
code = wait_for_next_event(WFNE_SUSP, -1);
if ( code <= 0 )
return Failed(code);
enable_tracing(TRACE_STEP, 0);
// Clean up the disassembly so it looks nicer
del_items(tea1, DELIT_EXPAND|DELIT_DELNAMES, tea2-tea1);
create_insn(r_eip);
auto_mark_range(tea1, tea2, AU_USED);
auto_mark_range(tea1, tea2, AU_FINAL);
take_memory_snapshot(1);
set_name(r_eip, "real_start");
warning("Successfully traced to the completion of the unpacker code\n"
"Please rebuild the import table using renimp.idc\n"
"before stopping the debugger");
}
//--------------------------------------------------------------------------
// Print an failure message
static Failed(code)
{
warning("Failed to unpack the file, sorry (code %d)", code);
return 0;
}

View File

@@ -0,0 +1,172 @@
/*
This file contains Win9x (95, 98) specific stuff.
It can be safely ignored if you are only interested in XP systems.
*/
#include <windows.h>
#include <ida.hpp>
#include <idp.hpp>
#include <dbg.hpp>
#include "uunp.hpp"
#pragma pack(push, 1)
//lint --e{958} Padding required
struct push_insn_t
{
BYTE push; // must be 0x68
DWORD ea;
};
//lint -estring(958,member) padding is required to align members
struct push_jump_insns_t
{
BYTE push; // must be 0x68
DWORD ea;
BYTE jmp; // must be 0xE9
DWORD reloff; //lint !e754 not referenced
};
#pragma pack(pop)
//--------------------------------------------------------------------------
// find the address of the thunk for GetProcessAddress() under Windows 9x
void uunp_ctx_t::win9x_resolve_gpa_thunk()
{
DWORD off;
ea_t ea = curmod.start_ea + offsetof(IMAGE_DOS_HEADER, e_lfanew);
if ( read_dbg_memory(ea, &off, sizeof(off)) != sizeof(off) )
return;
#define _OI offsetof(IMAGE_NT_HEADERS32, OptionalHeader.DataDirectory[ \
IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress)
if ( read_dbg_memory((DWORD)curmod.start_ea + off + _OI, &off, sizeof(off)) != sizeof(off) )
return;
#undef _OI
IMAGE_IMPORT_DESCRIPTOR imp;
DWORD hK32 = DWORD(size_t(GetModuleHandle("kernel32")));
bool found = false;
for ( off += (DWORD)curmod.start_ea;
read_dbg_memory(off, &imp, sizeof(imp)) == sizeof(imp) && imp.Name;
off += sizeof(imp) )
{
if ( imp.ForwarderChain == hK32 )
{
found = true;
break;
}
}
if ( found )
{
DWORD tmp;
for ( off = imp.FirstThunk + (DWORD)curmod.start_ea;
read_dbg_memory(off, &tmp, sizeof(tmp)) == sizeof(tmp) && tmp != 0;
off += sizeof(DWORD) )
{
if ( tmp >= hK32 )
continue; // for TH_xxx entries
push_insn_t thunk;
if ( read_dbg_memory(tmp, &thunk, sizeof(thunk)) != sizeof(thunk)
|| thunk.push != 0x68 )
{
break;
}
if ( thunk.ea == bp_gpa )
{
bp_gpa = tmp;
break;
}
}
}
}
//--------------------------------------------------------------------------
// find all dwords equal to 'ea' and remember their translations
// search in the current module
static bool calc_thunk_target(uunp_ctx_t &ctx, uint32 ea32, uint32 imp32)
{
bool matched = false;
for ( ea_t pos = ctx.curmod.start_ea;
pos <= ctx.curmod.end_ea;
pos += sizeof(DWORD) )
{
pos = bin_search2(pos, ctx.curmod.end_ea, (uchar *)&ea32, NULL,
4, BIN_SEARCH_NOBREAK|BIN_SEARCH_CASE|BIN_SEARCH_FORWARD);
if ( pos == BADADDR )
break;
if ( pos & 3 )
continue;
flags_t F = get_flags(pos);
if ( is_tail(F) )
continue;
matched = true;
ctx.thunks[pos] = imp32;
}
return matched;
}
//--------------------------------------------------------------------------
// find Windows 9x import thunk
static bool resolve_thunk(uunp_ctx_t &ctx, ea_t ea)
{
push_jump_insns_t thunk;
if ( get_bytes(&thunk, sizeof(thunk), ea) != sizeof(thunk)
|| thunk.push != 0x68 || thunk.jmp != 0xE9
|| thunk.ea < 0x80000000 || thunk.ea >= 0xC0000000 )
{
return false;
}
if ( !calc_thunk_target(ctx, uint32(ea), thunk.ea) )
msg("%a: Thunked import (%08a) without references\n", ea, ea_t(thunk.ea));
return true;
}
//--------------------------------------------------------------------------
// Windows 9x: find thunked imports and their targets
void uunp_ctx_t::find_thunked_imports()
{
if ( (DWORD)bp_gpa & 0xF )
{
warning("Non-standard thunk address");
return;
}
// find the thunk area for our module
invalidate_dbgmem_contents(curmod.start_ea, curmod.end_ea); // for bin-search
invalidate_dbgmem_contents(0x80000000, 0xC0000000);
for ( ea_t ea = bp_gpa; ea > 0x80000000; ea -= 0x10 )
{
if ( !resolve_thunk(*this, ea) )
break;
}
for ( ea_t ea = bp_gpa + 0x10; ea < 0xC0000000; ea += 0x10 )
{
if ( !resolve_thunk(*this, ea) )
break;
}
if ( thunks.empty() )
warning("Could not find thunk area");
}
//--------------------------------------------------------------------------
ea_t uunp_ctx_t::win9x_find_thunk(ea_t ea)
{
thunks_t::iterator p = thunks.find(ea);
ea_t func = p != thunks.end() ? p->second : get_dword(ea);
return func;
}