1024 lines
24 KiB
C++
1024 lines
24 KiB
C++
/*
|
|
* This Loader Module is written by Ilfak Guilfanov and
|
|
* rewriten by Yury Haron
|
|
*
|
|
*/
|
|
/*
|
|
L O A D E R for Netware Loadable Module (NLM)
|
|
*/
|
|
|
|
#include <stddef.h>
|
|
#include "../idaldr.h"
|
|
#include "nlm.h"
|
|
#include <typeinf.hpp>
|
|
|
|
#pragma pack(push, 1)
|
|
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
// check input file format. if recognized, then return 1
|
|
// and fill 'fileformatname'.
|
|
// otherwise return 0
|
|
//
|
|
static int idaapi accept_file(
|
|
qstring *fileformatname,
|
|
qstring *processor,
|
|
linput_t *li,
|
|
const char *)
|
|
{
|
|
char magic[NLM_MAGIC_SIZE];
|
|
|
|
if ( qlread(li, magic, sizeof(magic)) != sizeof(magic )
|
|
|| memcmp(magic, NLM_MAGIC, sizeof(magic)) != 0 )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
*fileformatname = "Netware Loadable Module (NLM)";
|
|
*processor = "metapc";
|
|
return f_NLM;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
NORETURN static void _errstruct(int lnnum)
|
|
{
|
|
loader_failure("Bad file structure or read error (line %d)", lnnum);
|
|
}
|
|
#define errstruct() _errstruct(__LINE__)
|
|
|
|
//--------------------------------------------------------------------------
|
|
//lint -e{958} padding needed
|
|
static struct local_data
|
|
{
|
|
struct unp_data
|
|
{
|
|
FILE *fo;
|
|
uint32 pos, size;
|
|
uchar *buff;
|
|
ushort b_pos, b_val;
|
|
};
|
|
|
|
linput_t *li;
|
|
nlmexe_t nlm;
|
|
union
|
|
{
|
|
char buf[MAXSTR];
|
|
unp_data unp;
|
|
}; //lint !e958 padding is required to align members
|
|
ea_t start, cbase, csize, dbase, dsize;
|
|
netnode impnode;
|
|
} lc;
|
|
|
|
//--------------------------------------------------------------------------
|
|
static int mread(void *buf, size_t size)
|
|
{
|
|
if ( qlread(lc.li, buf, size) == size )
|
|
return 0;
|
|
if ( ask_yn(ASKBTN_NO,
|
|
"HIDECANCEL\n"
|
|
"Read error or bad file structure. Continue loading?") <= ASKBTN_NO )
|
|
{
|
|
loader_failure();
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
static int getstr(void)
|
|
{
|
|
uchar len;
|
|
#if MAXSTR < 256
|
|
#error
|
|
#endif
|
|
|
|
if ( mread(&len, sizeof(len)) || (len && mread(lc.buf, len)) )
|
|
return 1;
|
|
lc.buf[len] = '\0';
|
|
return 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
static void set_reloc(ushort sel, ea_t fixaddr, ea_t toea, bool self_flag)
|
|
{
|
|
sval_t displ = int32(get_dword(fixaddr));
|
|
|
|
fixup_data_t fd(FIXUP_OFF32);
|
|
fd.off = toea;
|
|
fd.sel = sel;
|
|
if ( sel == 3 )
|
|
{
|
|
fd.set_extdef();
|
|
fd.displacement = displ;
|
|
}
|
|
if ( self_flag )
|
|
toea -= fixaddr;
|
|
put_dword(fixaddr, uint32(toea + displ));
|
|
fd.set(fixaddr);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
static void add_imports(void)
|
|
{
|
|
if ( lc.nlm.impoff == 0 || lc.nlm.impnum == 0 )
|
|
return;
|
|
uint32 i = lc.nlm.impnum;
|
|
msg("Creating imported names and relocating it...\n");
|
|
lc.impnode.create(LDR_NODE);
|
|
qlseek(lc.li, lc.nlm.impoff, SEEK_SET);
|
|
ea_t ebase = lc.start;
|
|
ea_t eend = ebase + (i * sizeof(int32));
|
|
uint64 fsize = qlsize(lc.li);
|
|
if ( lc.nlm.impoff >= fsize )
|
|
loader_failure("Bad import offset");
|
|
uint64 rest = fsize - lc.nlm.impoff;
|
|
if ( !is_mul_ok<uint32>(i, sizeof(int32))
|
|
|| eend <= ebase
|
|
|| qoff64_t(i)*2 > rest ) // each name requires at least 2 bytes
|
|
{
|
|
loader_failure("Bad import count");
|
|
}
|
|
|
|
set_selector(3, 0);
|
|
if ( add_segm(3, ebase, eend, NAME_EXTERN, NULL) )
|
|
{
|
|
segment_t *ps = getseg(ebase);
|
|
ps->type = SEG_XTRN;
|
|
ps->update();
|
|
}
|
|
else
|
|
{
|
|
loader_failure();
|
|
}
|
|
|
|
uchar ss = inf_get_specsegs();
|
|
for ( ; i; i--, ebase += ss )
|
|
{
|
|
if ( getstr() )
|
|
break;
|
|
show_addr(ebase);
|
|
set_name(ebase, lc.buf, SN_NOCHECK | SN_PUBLIC | SN_IDBENC);
|
|
if ( ss == 8 )
|
|
put_qword(ebase, 0);
|
|
else
|
|
put_dword(ebase, 0);
|
|
if ( lc.impnode )
|
|
netnode(lc.impnode).supset_ea(ebase, lc.buf);
|
|
uint32 fnum;
|
|
if ( mread(&fnum, sizeof(fnum)) )
|
|
{
|
|
FORCE_END:
|
|
if ( (ebase += ss) < eend )
|
|
set_segm_end(ebase, ebase, SEGMOD_KILL);
|
|
return;
|
|
}
|
|
int sff = 0;
|
|
while ( fnum-- )
|
|
{
|
|
uint32 ent;
|
|
if ( mread(&ent, sizeof(ent)) )
|
|
goto FORCE_END;
|
|
ea_t ea, sz;
|
|
bool self = (ent & 0x80000000) == 0;
|
|
if ( ent & 0x40000000 )
|
|
{
|
|
ea = lc.cbase;
|
|
sz = lc.csize;
|
|
}
|
|
else
|
|
{
|
|
ea = lc.dbase;
|
|
sz = lc.dsize;
|
|
}
|
|
ent &= ~0xC0000000;
|
|
if ( ent >= sz )
|
|
{
|
|
msg("Skip reference to extern '%s' with illegal offset 0x%X\n",
|
|
lc.buf, ent);
|
|
}
|
|
else
|
|
{
|
|
set_reloc(3, ea + ent, ebase, self);
|
|
if ( sff == 0 && self )
|
|
{
|
|
++sff;
|
|
add_func(ebase, ebase+ss-1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
static void add_fixups(void)
|
|
{
|
|
if ( lc.nlm.fixupoff == 0 || lc.nlm.fixupnum == 0 )
|
|
return;
|
|
uint32 i = lc.nlm.fixupnum;
|
|
msg("Applying internal fixup records...\n");
|
|
qlseek(lc.li, lc.nlm.fixupoff, SEEK_SET);
|
|
|
|
validate_array_count_or_die(lc.li, i, 4, "Number of fixups", lc.nlm.fixupoff);
|
|
while ( i-- != 0 )
|
|
{
|
|
uint32 ent;
|
|
if ( mread(&ent, sizeof(ent)) )
|
|
break;
|
|
ea_t base, toea;
|
|
asize_t size;
|
|
if ( ent & 0x40000000 )
|
|
{
|
|
base = lc.cbase;
|
|
size = lc.csize;
|
|
}
|
|
else
|
|
{
|
|
base = lc.dbase;
|
|
size = lc.dsize;
|
|
}
|
|
ushort sel;
|
|
if ( ent & 0x80000000 )
|
|
{
|
|
toea = lc.cbase;
|
|
sel = 1;
|
|
}
|
|
else
|
|
{
|
|
toea = lc.dbase;
|
|
sel = 2;
|
|
}
|
|
ent &= ~0xC0000000;
|
|
if ( ent >= size || !toea )
|
|
msg("Skip invalid relocation for offset 0x%X (to base %a)\n", ent, toea);
|
|
else
|
|
set_reloc(sel, ent + base, toea, 0);
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
static void add_exports_and_publics(void)
|
|
{
|
|
uint32 i, foff;
|
|
int pfl;
|
|
|
|
for ( pfl = 0, foff = lc.nlm.expoff, i = lc.nlm.expnum;
|
|
pfl < 2;
|
|
pfl++, foff = lc.nlm.puboff, i = lc.nlm.pubnum )
|
|
{
|
|
if ( foff == 0 || i == 0 )
|
|
continue;
|
|
msg("Applying %s names...\n", pfl ? "public" : "exported");
|
|
qlseek(lc.li, foff, SEEK_SET);
|
|
validate_array_count_or_die(lc.li, i, 4,
|
|
pfl ? "Number of publics" : "Number of exports",
|
|
foff);
|
|
while ( i-- != 0 )
|
|
{
|
|
bool flag;
|
|
ea_t ea;
|
|
CASSERT(sizeof(flag) == 1);
|
|
if ( !pfl )
|
|
{
|
|
if ( getstr() || mread(&ea, sizeof(uint32)) )
|
|
break;
|
|
flag = (ea & 0x80000000) != 0;
|
|
}
|
|
else if ( mread(&flag, sizeof(flag))
|
|
|| mread(&ea, sizeof(uint32))
|
|
|| getstr() )
|
|
{
|
|
break;
|
|
}
|
|
ea &= ~0xC0000000L;
|
|
ea_t base;
|
|
asize_t size;
|
|
if ( flag )
|
|
{
|
|
base = lc.cbase;
|
|
size = lc.csize;
|
|
}
|
|
else
|
|
{
|
|
base = lc.dbase;
|
|
size = lc.dsize;
|
|
}
|
|
if ( ea >= size )
|
|
{
|
|
msg("Skip name '%s' with illegal offset (0x%a)\n", lc.buf, ea);
|
|
}
|
|
else
|
|
{
|
|
ea += base;
|
|
add_entry(ea, ea, lc.buf, flag, AEF_IDBENC);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
static int LoadMsgString(void)
|
|
{
|
|
int c, i;
|
|
for ( i = 0; i < sizeof(lc.buf)-1; i++ )
|
|
{
|
|
if ( (c = qlgetc(lc.li)) == EOF )
|
|
return 0;
|
|
if ( !c )
|
|
break;
|
|
if ( c == 0xFF )
|
|
{
|
|
c = '.';
|
|
}
|
|
else if ( c < ' ' )
|
|
{
|
|
static const char rpts[] = "\t\n\r\a\v\f";
|
|
static const char rptc[] = "tnravf";
|
|
const char *p = strchr(rpts, c);
|
|
if ( p )
|
|
{
|
|
if ( i == sizeof(lc.buf)-2 )
|
|
break;
|
|
lc.buf[i++] = '\\';
|
|
c = (int)(uchar)rptc[(int)(p-rpts)];
|
|
}
|
|
}
|
|
lc.buf[i] = (char)c;
|
|
}
|
|
lc.buf[i] = '\0';
|
|
return 1;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
static uint32 addSpecialComment(void)
|
|
{
|
|
uchar vp = 0, cp = 0;
|
|
uint32 mp = 0;
|
|
qoff64_t pos = qltell(lc.li);
|
|
ea_t loadbase = lc.nlm.codeoff;
|
|
if ( lc.nlm.datalen && loadbase > lc.nlm.dataoff )
|
|
loadbase = lc.nlm.dataoff;
|
|
if ( loadbase > 0x1000 )
|
|
loadbase = 0x1000;
|
|
|
|
while ( pos + 12 < loadbase )
|
|
{
|
|
if ( mread(lc.buf, 12) )
|
|
break;
|
|
if ( !mp && !memcmp(lc.buf, "MeSsAgEs", 8) )
|
|
{
|
|
mp = pos + 8;
|
|
pos += 0x7C;
|
|
goto repos;
|
|
}
|
|
if ( !vp && !memcmp(lc.buf, "VeRsIoN#", 8) )
|
|
{
|
|
if ( mread(&lc.buf[12], 32-12) )
|
|
break;
|
|
++vp;
|
|
pos += 32 - 1;
|
|
qsnprintf(&lc.buf[32], sizeof(lc.buf)-32, "%u.%02u%c (%02u.%02u.%04u)",
|
|
*(uint32 *)&lc.buf[8], (uchar)lc.buf[8+4],
|
|
*(uint32 *)&lc.buf[8+8] ? (char)(lc.buf[8+8] + ('A'-1)) : ' ',
|
|
(uchar)lc.buf[8+20], (uchar)lc.buf[8+16],
|
|
*(ushort *)&lc.buf[8+12]);
|
|
add_pgm_cmt("Version : %s", &lc.buf[32]);
|
|
goto check;
|
|
}
|
|
if ( !cp && !memcmp(lc.buf, "CoPyRiGhT=", 10) )
|
|
{
|
|
pos += 10;
|
|
pos += (uchar)lc.buf[10];
|
|
qlseek(lc.li, -2L, SEEK_CUR);
|
|
if ( getstr() )
|
|
break;
|
|
add_pgm_cmt("Copyright : %s", lc.buf);
|
|
++cp;
|
|
goto check;
|
|
}
|
|
if ( !memcmp(lc.buf, "CuStHeAd", 8) )
|
|
{
|
|
pos += 8;
|
|
pos += *(uint32 *)&lc.buf[8];
|
|
goto repos;
|
|
}
|
|
check:
|
|
if ( mp && vp && cp )
|
|
break;
|
|
++pos;
|
|
repos:
|
|
qlseek(lc.li, pos, SEEK_SET);
|
|
}
|
|
return mp;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
static void addComments(int flg)
|
|
{
|
|
uint32 clst = (uint32)-1, msgpos = 0;
|
|
char *cl1 = NULL;
|
|
char *cl2 = NULL;
|
|
|
|
create_filename_cmt();
|
|
qlseek(lc.li, NLM_MODNAMOFF, SEEK_SET);
|
|
if ( lc.nlm.fname[0] )
|
|
add_pgm_cmt("Module Name : %.12s", lc.nlm.fname);
|
|
if ( !getstr() )
|
|
{
|
|
add_pgm_cmt("Description : %s", lc.buf);
|
|
qlseek(lc.li, 1, SEEK_CUR);
|
|
if ( mread(&clst, sizeof(clst)) )
|
|
{
|
|
clst = (uint32)-1;
|
|
}
|
|
else
|
|
{
|
|
qlseek(lc.li, 0xD-sizeof(clst), SEEK_CUR);
|
|
if ( !getstr() )
|
|
{
|
|
cl1 = qstrdup(lc.buf);
|
|
qlseek(lc.li, 1, SEEK_CUR);
|
|
if ( !getstr() )
|
|
{
|
|
cl2 = qstrdup(lc.buf);
|
|
msgpos = addSpecialComment();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
add_pgm_cmt("File Format Version : %08Xh%s", lc.nlm.version,
|
|
(lc.nlm.version & NLM_COMPRESSED) ? " (compressed)" : "");
|
|
{
|
|
static const char *const tt[15] =
|
|
{
|
|
"Generic",
|
|
"LAN Driver",
|
|
"Disk Driver",
|
|
"Name Space",
|
|
"Patch/Utility",
|
|
"Mirrored Server Link",
|
|
"OS",
|
|
"High OS",
|
|
"Host Adapter",
|
|
"Custom Device",
|
|
"FS Engine",
|
|
"Real Mode",
|
|
"OS", // duplicate 6
|
|
"Platform Support",
|
|
"Unknown"
|
|
};
|
|
int type = (int)lc.nlm.modType;
|
|
if ( lc.nlm.modType > 14 )
|
|
type = 14;
|
|
add_pgm_cmt("Module type : %s (%d)", tt[type], type);
|
|
}
|
|
if ( lc.nlm.bssSize )
|
|
add_pgm_cmt("Unitialized data size : %08Xh", lc.nlm.bssSize);
|
|
if ( lc.nlm.custoff && lc.nlm.custlen )
|
|
add_pgm_cmt("Custom data : off=%08Xh len=%08Xh",
|
|
lc.nlm.custoff, lc.nlm.custlen);
|
|
lc.buf[0]= '\0';
|
|
if ( lc.nlm.flags )
|
|
{
|
|
static const char *const ff[4] =
|
|
{
|
|
"reentrant",
|
|
"multiply loadable",
|
|
"synchronize",
|
|
"pseudo-preemptable"
|
|
};
|
|
char *ptr = lc.buf;
|
|
char *end = lc.buf + sizeof(lc.buf);
|
|
int cn = 0;
|
|
APPCHAR(ptr, end, ' ');
|
|
APPCHAR(ptr, end, '(');
|
|
for ( int i = 0; i < 4; i++ )
|
|
{
|
|
if ( lc.nlm.flags & (1 << i) )
|
|
{
|
|
if ( cn )
|
|
{
|
|
APPCHAR(ptr, end, ',');
|
|
APPCHAR(ptr, end, ' ');
|
|
}
|
|
++cn;
|
|
APPEND(ptr, end, ff[i]);
|
|
}
|
|
}
|
|
if ( cn )
|
|
APPCHAR(ptr, end, ')');
|
|
else
|
|
ptr = lc.buf;
|
|
APPZERO(ptr, end);
|
|
}
|
|
add_pgm_cmt("Flags : %08Xh%s", lc.nlm.flags, lc.buf);
|
|
if ( clst != (uint32)-1 )
|
|
add_pgm_cmt("CLIB Stack Size : %08Xh", clst);
|
|
if ( cl1 )
|
|
{
|
|
if ( *cl1 )
|
|
add_pgm_cmt("CLIB Screen Name : %s", cl1);
|
|
qfree(cl1);
|
|
}
|
|
if ( cl2 )
|
|
{
|
|
if ( *cl2 )
|
|
add_pgm_cmt("CLIB Thread Name : %s", cl2);
|
|
qfree(cl2);
|
|
}
|
|
if ( lc.nlm.autoliboff && lc.nlm.autolibnum )
|
|
{
|
|
qlseek(lc.li, lc.nlm.autoliboff, SEEK_SET);
|
|
for ( uint i = 0; i < lc.nlm.autolibnum; i++ )
|
|
{
|
|
if ( getstr() )
|
|
break;
|
|
add_pgm_cmt("Referenced Modules : '%s'", lc.buf);
|
|
if ( lc.impnode )
|
|
{
|
|
char *p1, *p2;
|
|
for ( p1 = lc.buf; p1 && *trim(p1); p1 = p2 )
|
|
{
|
|
if ( (p2 = strchr(p1, '|')) != NULL )
|
|
{
|
|
*p2++ = '\0';
|
|
if ( !*trim(p1) )
|
|
continue;
|
|
}
|
|
import_module(p1, NULL, lc.impnode, NULL, "netware");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !msgpos )
|
|
return;
|
|
|
|
qlseek(lc.li, msgpos, SEEK_SET);
|
|
if ( mread(lc.buf, 0x7C-8) )
|
|
return;
|
|
|
|
uint32 id = *(uint32 *)&lc.buf[0x60-8];
|
|
if ( id )
|
|
add_pgm_cmt("Product ID : %08Xh", id);
|
|
id = *(uint32 *)&lc.buf[0];
|
|
if ( id )
|
|
add_pgm_cmt("Language ID : %u", id);
|
|
id = *(uint32 *)&lc.buf[0x34-8];
|
|
if ( id )
|
|
add_pgm_cmt("Shared Data : off=%08Xh, size=%08Xh",
|
|
*(uint32 *)&lc.buf[0x30-8], id);
|
|
id = *(uint32 *)&lc.buf[0x2C-8];
|
|
if ( id )
|
|
{
|
|
add_pgm_cmt("Shared Code : off=%08Xh, size=%08Xh",
|
|
*(uint32 *)&lc.buf[0x28-8], id);
|
|
add_pgm_cmt("Shared Start Procedure : off=%08Xh",
|
|
*(uint32 *)&lc.buf[0x58-8]);
|
|
add_pgm_cmt("Shared Exit Procedure : off=%08Xh",
|
|
*(uint32 *)&lc.buf[0x5C-8]);
|
|
}
|
|
id = *(uint32 *)&lc.buf[0x1C-8];
|
|
if ( id )
|
|
add_pgm_cmt("Help : off=%08Xh, size=%08Xh",
|
|
*(uint32 *)&lc.buf[0x18-8], id);
|
|
id = *(uint32 *)&lc.buf[0x24-8];
|
|
if ( id )
|
|
add_pgm_cmt("RPC & BAG : off=%08Xh, size=%08Xh",
|
|
*(uint32 *)&lc.buf[0x20-8], id);
|
|
id = *(uint32 *)&lc.buf[0x10-8];
|
|
|
|
if ( id )
|
|
{
|
|
add_pgm_cmt("Message : off=%08Xh, size=%08Xh",
|
|
msgpos = *(uint32 *)&lc.buf[0xC-8], id);
|
|
|
|
if ( !flg )
|
|
return;
|
|
|
|
uint32 mlng, mcnt;
|
|
qlseek(lc.li, msgpos + 0x6A, SEEK_SET);
|
|
if ( mread(&mlng, sizeof(mlng)) || mread(&mcnt, sizeof(mcnt)) )
|
|
return;
|
|
|
|
add_pgm_cmt("Message Language : %08Xh", mlng);
|
|
msgpos += 0x76;
|
|
for ( clst = 0; clst < mcnt; clst++ )
|
|
{
|
|
qlseek(lc.li, msgpos + qoff64_t(clst)*4, SEEK_SET);
|
|
if ( mread(&mlng, sizeof(mlng)) )
|
|
break;
|
|
qlseek(lc.li, msgpos + mlng, SEEK_SET);
|
|
if ( !LoadMsgString() )
|
|
break;
|
|
add_pgm_cmt(" %08X : %s", clst, lc.buf);
|
|
}
|
|
// 4C - Shared Exported Symbols
|
|
// 44 - Shared Imported Symbols
|
|
// 3C - Shared Fixups
|
|
// 54 - Shared Debug Records
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
static void load_image(void)
|
|
{
|
|
uint32 lend = 0;
|
|
uint32 addbss = 0;
|
|
|
|
qoff64_t off = lc.nlm.codeoff;
|
|
if ( off != 0 )
|
|
{
|
|
lend = lc.nlm.codelen;
|
|
if ( lend != 0 )
|
|
{
|
|
lc.csize = lend;
|
|
lc.cbase = lc.start;
|
|
}
|
|
}
|
|
for ( int i = 1; ; )
|
|
{
|
|
const char *pn, *pc;
|
|
segment_t s;
|
|
s.start_ea = lc.start;
|
|
s.end_ea = lc.start;
|
|
s.align = saRel4K;
|
|
s.bitness = 1;
|
|
s.comb = scPub;
|
|
s.sel = i;
|
|
if ( i == 1 )
|
|
{
|
|
s.type = SEG_CODE;
|
|
pn = NAME_BSS;
|
|
pc = CLASS_CODE;
|
|
}
|
|
else
|
|
{
|
|
if ( lend == 0 )
|
|
{
|
|
pn = NAME_BSS;
|
|
pc = CLASS_BSS;
|
|
}
|
|
else
|
|
{
|
|
pn = NAME_DATA;
|
|
pc = CLASS_DATA;
|
|
}
|
|
if ( lend != 0 || addbss )
|
|
{
|
|
s.type = SEG_DATA;
|
|
lc.dbase = lc.start;
|
|
lc.dsize = lend;
|
|
}
|
|
}
|
|
if ( lend == 0 && !addbss )
|
|
{
|
|
s.type = SEG_NULL;
|
|
lc.start += 0x1000;
|
|
}
|
|
else
|
|
{
|
|
if ( lend != 0 )
|
|
{
|
|
s.end_ea += lend;
|
|
uint64 fsize = qlsize(lc.li);
|
|
if ( s.end_ea < s.start_ea || off > fsize || s.size() > fsize-off )
|
|
loader_failure("Truncated input file");
|
|
file2base(lc.li, off, s.start_ea, s.end_ea, FILEREG_PATCHABLE);
|
|
}
|
|
s.end_ea += addbss;
|
|
lc.start = (s.end_ea + 0xFFF) & ~0xFFF;
|
|
}
|
|
set_selector(s.sel, 0);
|
|
if ( !add_segm_ex(&s, pn, pc, ADDSEG_NOSREG | ADDSEG_SPARSE) )
|
|
loader_failure();
|
|
//-
|
|
if ( ++i > 2 )
|
|
break;
|
|
addbss = lc.nlm.bssSize;
|
|
lend = ((off = lc.nlm.dataoff) == 0) ? 0 : lc.nlm.datalen;
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
static void Unpack(void);
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
// load file into the database.
|
|
//
|
|
void idaapi load_file(linput_t *li, ushort neflag, const char * /*fileformatname*/)
|
|
{
|
|
set_processor_type("metapc", SETPROC_LOADER);
|
|
add_til("nlm", ADDTIL_DEFAULT);
|
|
inf_set_lflags(inf_get_lflags() | LFLG_PC_FLAT);
|
|
|
|
if ( qlread(lc.li = li, &lc.nlm, sizeof(lc.nlm)) != sizeof(lc.nlm) )
|
|
errstruct();
|
|
if ( lc.nlm.version & NLM_COMPRESSED )
|
|
Unpack();
|
|
|
|
lc.start = to_ea(inf_get_baseaddr(), 0);
|
|
memset(&lc.cbase, 0, sizeof(local_data) - qoffsetof(local_data, cbase));
|
|
|
|
load_image();
|
|
set_default_dataseg(2);
|
|
|
|
if ( lc.nlm.startIP < lc.csize )
|
|
{
|
|
uval_t bip = lc.cbase + lc.nlm.startIP;
|
|
inf_set_start_cs(1);
|
|
inf_set_start_ip(bip);
|
|
add_entry(bip, bip, "nlm_start", 1);
|
|
}
|
|
if ( (lc.nlm.endIP || lc.nlm.startIP) && lc.nlm.endIP < lc.csize )
|
|
{
|
|
uval_t eip = lc.cbase + lc.nlm.endIP;
|
|
add_entry(eip, eip, "nlm_terminate", 1);
|
|
}
|
|
if ( lc.nlm.auxIP && lc.nlm.auxIP < lc.csize )
|
|
{
|
|
uval_t aip = lc.cbase + lc.nlm.auxIP;
|
|
add_entry(aip, aip, "nlm_check_unload", 1);
|
|
}
|
|
|
|
// inf.nametype = NM_EA;
|
|
// inf.s_prefflag &= ~PREF_SEGADR;
|
|
// inf.start_ss = BADSEL;
|
|
// inf.start_sp = BADADDR;
|
|
inf_set_specsegs(inf_is_64bit() ? 8 : 4);
|
|
|
|
add_imports();
|
|
add_exports_and_publics();
|
|
add_fixups();
|
|
addComments(neflag & NEF_RSCS);
|
|
if ( lc.impnode )
|
|
import_module("nlm_root", NULL, lc.impnode, NULL, "netware");
|
|
inf_set_lowoff(inf_get_min_ea());
|
|
|
|
if ( lc.li != li )
|
|
close_linput(lc.li); // close & delete tmp (unpack) file
|
|
|
|
split_sreg_range(inf_get_start_ea(), PH.reg_data_sreg, 2, SR_autostart, true);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
//
|
|
// LOADER DESCRIPTION BLOCK
|
|
//
|
|
//----------------------------------------------------------------------
|
|
loader_t LDSC =
|
|
{
|
|
IDP_INTERFACE_VERSION,
|
|
0, // loader flags
|
|
//
|
|
// check input file format. if recognized, then return 1
|
|
// and fill 'fileformatname'.
|
|
// otherwise return 0
|
|
//
|
|
accept_file,
|
|
//
|
|
// load file into the database.
|
|
//
|
|
load_file,
|
|
//
|
|
// create output file from the database.
|
|
// this function may be absent.
|
|
//
|
|
NULL,
|
|
// take care of a moved segment (fix up relocations, for example)
|
|
NULL,
|
|
NULL,
|
|
};
|
|
|
|
//===============unpack=====================================================
|
|
NORETURN static void memerr(void)
|
|
{
|
|
nomem("NLM-loader (for unpacking)");
|
|
}
|
|
|
|
//==========================================================================
|
|
#define OUTSIZE 8192 // 2**13
|
|
|
|
static void unp_process(void);
|
|
|
|
static void Unpack(void)
|
|
{
|
|
uint32 fsize;
|
|
FILE *fo;
|
|
|
|
CASSERT(sizeof(lc) - qoffsetof(local_data, nlm) > 0x196); //-V658 value is being subtracted from the unsigned
|
|
CASSERT(qoffsetof(local_data,buf) == qoffsetof(local_data,nlm)+sizeof(lc.nlm));
|
|
|
|
if ( qlread(lc.li, lc.buf, 0x196-sizeof(lc.nlm)) != (0x196-sizeof(lc.nlm)) )
|
|
errstruct();
|
|
if ( lc.buf[0x190 - sizeof(lc.nlm )] != 1
|
|
|| lc.buf[0x191 - sizeof(lc.nlm)] != 10 )
|
|
{
|
|
loader_failure("Unknown compressing method");
|
|
}
|
|
if ( (fsize = *((uint32 *)&lc.buf[0x192 - sizeof(lc.nlm)])) <= 0x190 )
|
|
errstruct();
|
|
|
|
if ( (fo = qtmpfile()) == NULL )
|
|
loader_failure("Cannot create temporary file for decompressing");
|
|
qfwrite(fo, &lc.nlm, 0x190); //lint !e426
|
|
|
|
lc.unp.buff = (uchar *)qalloc(OUTSIZE);
|
|
if ( lc.unp.buff == NULL )
|
|
memerr();
|
|
lc.unp.b_pos = 0x190;
|
|
lc.unp.pos = 0x190;
|
|
lc.unp.b_val = 0;
|
|
lc.unp.fo = fo;
|
|
lc.unp.size = fsize;
|
|
|
|
msg("Decompressing module...");
|
|
unp_process();
|
|
|
|
qfree(lc.unp.buff);
|
|
if ( qflush(lc.unp.fo) || ferror(lc.unp.fo) || feof(lc.unp.fo) )
|
|
loader_failure("disk full or cannot write temporary file");
|
|
// qlseek(fo, sizeof(nlm), SEEK_SET);
|
|
msg("Ok\n");
|
|
lc.li = make_linput(lc.unp.fo);
|
|
}
|
|
|
|
//=========================================================================
|
|
struct record
|
|
{
|
|
record *left;
|
|
union
|
|
{
|
|
record *right;
|
|
uchar data;
|
|
};
|
|
};
|
|
|
|
//=====================================================================
|
|
static void putUnpByte(uchar data)
|
|
{
|
|
qfputc(data, lc.unp.fo);
|
|
++lc.unp.pos;
|
|
lc.unp.buff[lc.unp.b_pos] = data;
|
|
if ( ++lc.unp.b_pos == OUTSIZE )
|
|
lc.unp.b_pos = 0;
|
|
}
|
|
|
|
//=====================================================================
|
|
static void putUnpRepeatBlk(ushort off, ushort sizeBlk)
|
|
{
|
|
if ( !off || !sizeBlk || (uint32)off >= lc.unp.pos )
|
|
errstruct();
|
|
int pos = lc.unp.b_pos - off;
|
|
if ( pos < 0 )
|
|
pos += OUTSIZE;
|
|
while ( true )
|
|
{
|
|
putUnpByte(lc.unp.buff[pos]);
|
|
if ( --sizeBlk == 0 )
|
|
break;
|
|
if ( ++pos == OUTSIZE )
|
|
pos = 0;
|
|
}
|
|
}
|
|
|
|
//=====================================================================
|
|
inline ushort nextbyte(void)
|
|
{
|
|
uchar i;
|
|
if ( qlread(lc.li, &i, sizeof(i)) != sizeof(i) )
|
|
errstruct();
|
|
return i;
|
|
}
|
|
|
|
//=====================================================================
|
|
ushort getNbit(int cnt)
|
|
{
|
|
ushort res = 0;
|
|
ushort val = lc.unp.b_val;
|
|
int i = cnt;
|
|
do
|
|
{
|
|
if ( val <= 0xFF )
|
|
val = nextbyte() | 0x8000;
|
|
res >>= 1;
|
|
if ( val & 1 )
|
|
res |= 0x8000;
|
|
val >>= 1;
|
|
} while ( --i != 0 );
|
|
lc.unp.b_val = val;
|
|
return res >> (16-cnt);
|
|
}
|
|
|
|
//=====================================================================
|
|
static int getbit(void)
|
|
{
|
|
if ( lc.unp.b_val <= 0xFF )
|
|
lc.unp.b_val = nextbyte() | 0x8000;
|
|
int i = lc.unp.b_val & 1;
|
|
lc.unp.b_val >>= 1;
|
|
return i;
|
|
}
|
|
|
|
//=====================================================================
|
|
inline uchar getbyte(void)
|
|
{
|
|
if ( lc.unp.b_val <= 0xFF )
|
|
return (uchar)nextbyte();
|
|
return (uchar)getNbit(8);
|
|
}
|
|
|
|
//=====================================================================
|
|
static uchar extractByte(const record *p)
|
|
{
|
|
while ( p->left )
|
|
p = getbit() ? p->right : p->left;
|
|
return p->data;
|
|
}
|
|
|
|
//=====================================================================
|
|
static void free_record(record *p)
|
|
{
|
|
if ( p->left )
|
|
{
|
|
free_record(p->left);
|
|
free_record(p->right);
|
|
}
|
|
qfree(p);
|
|
}
|
|
|
|
//=====================================================================
|
|
static record *load_record(void)
|
|
{
|
|
record *p = (record *)qalloc(sizeof(record));
|
|
if ( p == NULL )
|
|
memerr();
|
|
if ( getbit() )
|
|
{
|
|
p->left = NULL;
|
|
p->data = getbyte();
|
|
}
|
|
else
|
|
{
|
|
p->left = load_record();
|
|
p->right = load_record();
|
|
}
|
|
return p;
|
|
}
|
|
|
|
//=====================================================================
|
|
static void unp_process(void)
|
|
{
|
|
record *rOne = load_record();
|
|
record *rKey = load_record();
|
|
record *rPos = load_record();
|
|
|
|
while ( lc.unp.pos < lc.unp.size )
|
|
{
|
|
if ( getbit() )
|
|
{
|
|
putUnpByte(extractByte(rOne));
|
|
continue;
|
|
}
|
|
ushort data = extractByte(rKey);
|
|
switch ( data )
|
|
{
|
|
case 255:
|
|
{
|
|
uint32 cnt = 0;
|
|
lc.unp.b_val = 0;
|
|
int i = 8;
|
|
do
|
|
putUnpByte((uchar)nextbyte());
|
|
while ( --i != 0 );
|
|
do
|
|
{
|
|
uchar c;
|
|
putUnpByte(c = (uchar)nextbyte());
|
|
cnt |= ((uint32)c << (i*8));
|
|
} while ( ++i < 4 );
|
|
while ( cnt-- )
|
|
putUnpByte((uchar)nextbyte());
|
|
}
|
|
break;
|
|
|
|
case 254:
|
|
data = getNbit(13);
|
|
// fallthrough
|
|
default:
|
|
{
|
|
ushort off = getNbit(5);
|
|
off |=((ushort)extractByte(rPos) << 5);
|
|
putUnpRepeatBlk(off, data);
|
|
}
|
|
break;
|
|
}
|
|
} // while
|
|
if ( lc.unp.pos != lc.unp.size )
|
|
errstruct();
|
|
|
|
free_record(rOne);
|
|
free_record(rKey);
|
|
free_record(rPos);
|
|
}
|
|
|
|
#pragma pack(pop)
|