update to ida 7.6, add builds
This commit is contained in:
29
idasdk76/plugins/uunp/makefile
Normal file
29
idasdk76/plugins/uunp/makefile
Normal 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
|
||||
307
idasdk76/plugins/uunp/resext.cpp
Normal file
307
idasdk76/plugins/uunp/resext.cpp
Normal 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);
|
||||
}
|
||||
|
||||
825
idasdk76/plugins/uunp/uunp.cpp
Normal file
825
idasdk76/plugins/uunp/uunp.cpp
Normal 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
|
||||
};
|
||||
89
idasdk76/plugins/uunp/uunp.hpp
Normal file
89
idasdk76/plugins/uunp/uunp.hpp
Normal 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;
|
||||
|
||||
113
idasdk76/plugins/uunp/uunp.idc
Normal file
113
idasdk76/plugins/uunp/uunp.idc
Normal 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;
|
||||
}
|
||||
172
idasdk76/plugins/uunp/win9x.cpp
Normal file
172
idasdk76/plugins/uunp/win9x.cpp
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user