blob: 4aa9a42eda3e39680c3d0320c537b9b5d5d90ede [file] [log] [blame] [edit]
/*
gendef - Generate list of exported symbols from a Portable Executable.
Copyright (C) 2009-2016 mingw-w64 project
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <string.h>
#include "compat_string.h"
#include "gendef.h"
#include "fsredir.h"
#ifdef HAVE_LIBMANGLE
#include <libmangle.h>
#endif
#define ENABLE_DEBUG 0
#if ENABLE_DEBUG == 1
#define PRDEBUG(ARG...) fprintf(stderr,ARG)
#else
#define PRDEBUG(ARG...) do { } while(0)
#endif
static void decode_mangle (FILE *fp, const char *n);
static int load_pep (void);
static void do_pedef (void);
static void do_pepdef (void);
static void do_import_read32 (uint32_t va_imp, uint32_t sz_imp);
static void do_export_read (uint32_t va_exp,uint32_t sz_exp,int be64);
static void add_export_list (uint32_t ord,uint32_t func,const char *name, const char *forward,int be64,int beData);
static void dump_def (void);
static int disassembleRet (uint32_t func,uint32_t *retpop,const char *name, sImportname **ppimpname, int *seen_ret);
static size_t getMemonic (int *aCode,uint32_t pc,volatile uint32_t *jmp_pc,const char *name, sImportname **ppimpname);
static sImportname *imp32_add (const char *dll, const char *name, uint32_t addr, uint16_t ord);
static void imp32_free (void);
static sImportname *imp32_findbyaddress (uint32_t addr);
static sImportname *theImports = NULL;
static void *map_va (uint32_t va);
static int is_data (uint32_t va);
static int is_reloc (uint32_t va);
static int has_atdecoration (void);
static int disassembleRetIntern (uint32_t pc, uint32_t *retpop, sAddresses *seen, sAddresses *stack,
int *hasret, int *atleast_one, const char *name, sImportname **ppimpname);
static sAddresses*init_addr (void);
static void dest_addr (sAddresses *ad);
static int push_addr (sAddresses *ad,uint32_t val);
static int pop_addr (sAddresses *ad,uint32_t *val);
static sExportName *gExp = NULL;
static sExportName *gExpTail = NULL;
char *fninput;
char *fnoutput;
char *fndllname;
size_t gDta_size;
unsigned char *gDta;
PIMAGE_DOS_HEADER gMZDta;
PIMAGE_NT_HEADERS32 gPEDta;
PIMAGE_NT_HEADERS64 gPEPDta;
#ifdef REDIRECTOR
static int use_redirector = 0; /* Use/Disable FS redirector */
#endif
static int std_output = 0;
static int assume_stdcall = 0; /* Set to one, if function symbols should be assumed to have stdcall. */
static int no_forward_output = 0; /* Set to one, if in .def files forwarders shouldn't be displayed. */
static Gendefopts *chain_ptr = NULL;
__attribute__((noreturn)) static void show_usage (void);
static int opt_chain (const char *, const char *);
static int
opt_chain (const char *opts, const char *next)
{
static Gendefopts *prev, *current;
char *r1, *r2;
if (!strncmp (opts, "-", 2))
{
std_output = 1;
return 0;
}
if (!strcmp (opts, "--help") || !strcmp (opts, "-h"))
{
show_usage();
return 0;
}
if (!strcmp (opts, "--assume-stdcall") || !strcmp (opts, "-a"))
{
assume_stdcall = 1;
return 0;
}
if (!strcmp (opts, "--include-def-path") || !strcmp (opts, "-I"))
{
if (!next)
{
fprintf (stderr, "Error: %s expects path as next arguement.", opts);
return 0;
}
gendef_addpath_def (next);
return 1;
}
if (!strcmp (opts, "--no-forward-output") || !strcmp (opts, "-f"))
{
no_forward_output = 1;
return 0;
}
#ifdef REDIRECTOR
if (!strcmp (opts, "--disable-fs-redirector") || !strcmp (opts, "-r"))
{
use_redirector = 1;
return 0;
}
#endif
current = malloc (sizeof(Gendefopts));
if (current)
{
memset (current, 0, sizeof (Gendefopts));
current->next = NULL;
if (!prev)
chain_ptr = current;
else
prev->next = current;
r1 = strrchr (opts,'/');
r2 = strrchr (opts,'\\');
current->fninput = strdup (opts);
if (!r1 && r2 != NULL)
r1 = r2 + 1;
else if(r1 == NULL && r2 == NULL)
r1 = current->fninput;
else if (r2 != NULL && r1 != NULL && r1 < r2)
r1 = r2 + 1;
else
r1++;
current->fnoutput = (char *) malloc (strlen (current->fninput) + 5);
strcpy (current->fnoutput,r1);
r1 = strrchr (current->fnoutput,'.');
if (r1)
strcpy (r1,".def");
else
strcat (current->fnoutput,".def");
prev = current;
}
return 0;
}
void
show_usage (void)
{
fprintf (stderr, "Usage: gendef [OPTION]... [DLL]...\n");
fprintf (stderr, "Dumps DLL exports information from PE32/PE32+ executables\n");
fprintf (stderr, "\n");
fprintf (stderr, "Options:\n"
" - Dump to stdout\n"
" -h, --help Show this help.\n"
" -a, --assume-stdcall Assume functions with ambiguous call\n"
" convention as stdcall.\n"
" -I, --include-def-path <path>\n"
" Add additional search paths to find\n"
" hint .def files.\n"
" -f, --no-forward-output Don't output forwarders in .def file\n"
#ifdef REDIRECTOR
" -r, --disable-fs-redirector\n"
" Disable Win64 FS redirection, for 32-bit\n"
" gendef on 64-bit Windows\n"
#endif
);
fprintf (stderr, "\n");
fprintf (stderr, "Usage example: \n"
" By default, the output files are named after their DLL counterparts\n"
" gendef MYDLL.DLL Produces MYDLL.def\n"
" gendef - MYDLL.DLL Prints the exports to stdout\n");
fprintf (stderr, "\nBuilt on %s\n", __DATE__);
fprintf (stderr, "\nReport bugs to <mingw-w64-public@lists.sourceforge.net>\n");
exit (0);
}
int main(int argc,char **argv)
{
int i;
Gendefopts *opt, *next;
if (argc < 2)
{
show_usage ();
return 0;
}
for (i = 1; i < argc; i++)
i += opt_chain (argv[i], ((i+1) < argc ? argv[i+1] : NULL));
#ifdef REDIRECTOR
doredirect(use_redirector);
#endif
opt = chain_ptr;
while (opt)
{
fninput = opt->fninput;
fnoutput = opt->fnoutput;
if (load_pep ())
{
if (gPEDta || gPEPDta)
{
if (gPEDta)
do_pedef ();
else
do_pepdef ();
dump_def ();
}
}
if (fndllname)
free (fndllname);
fndllname = NULL;
if (gDta)
{
free (gDta);
gDta = NULL;
}
next = opt->next;
free(opt->fninput);
free(opt->fnoutput);
free(opt);
opt = next;
}
imp32_free ();
return 0;
}
static int
load_pep (void)
{
FILE *fp = fopen (fninput, "rb");
if (!fp)
{
fprintf (stderr, "*** [%s] failed to open()\n", fninput);
return 0;
}
fseek (fp, 0, SEEK_END);
gDta_size = (size_t) ftell (fp);
if (gDta_size > 0) {
fseek (fp,0,SEEK_SET);
gDta = (unsigned char *) malloc (gDta_size + 1);
if (gDta)
{
if (fread (gDta, gDta_size, 1, fp) != 1)
{
free (gDta);
gDta = NULL;
}
else
gDta[gDta_size] = 0;
}
}
fclose (fp);
if (!gDta)
{
fprintf (stderr, "*** [%s] unable to allocate %lu bytes\n", fninput,
(unsigned long) gDta_size);
return 0;
}
gMZDta = (PIMAGE_DOS_HEADER) gDta;
if (gDta_size < sizeof(IMAGE_DOS_HEADER) || gDta[0]!='M' || gDta[1]!='Z'
|| gMZDta->e_lfanew <= 0
|| gMZDta->e_lfanew >= (int32_t) gDta_size)
{
fprintf(stderr,"*** [%s] not a PE(+) file\n", fninput);
free(gDta);
gDta = NULL;
return 0;
}
gPEDta = (PIMAGE_NT_HEADERS32) &gDta[gMZDta->e_lfanew];
gPEPDta = (PIMAGE_NT_HEADERS64) gPEDta;
if (gPEDta->Signature != IMAGE_NT_SIGNATURE)
{
fprintf (stderr, "*** [%s] no PE(+) signature\n", fninput);
free (gDta);
gDta = NULL;
gPEPDta = NULL;
gPEDta = NULL;
return 0;
}
if (gPEDta->FileHeader.SizeOfOptionalHeader == IMAGE_SIZEOF_NT_OPTIONAL32_HEADER
&& gPEDta->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC)
{
gPEPDta = NULL;
fprintf (stderr, " * [%s] Found PE image\n", fninput);
}
else if (gPEPDta->FileHeader.SizeOfOptionalHeader == IMAGE_SIZEOF_NT_OPTIONAL64_HEADER
&& gPEPDta->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)
{
gPEDta = NULL;
fprintf (stderr, " * [%s] Found PE+ image\n", fninput);
}
else
{
free (gDta);
gDta = NULL;
fprintf (stderr, "*** [%s] no PE(+) optional header\n", fninput);
gPEPDta = NULL;
gPEDta = NULL;
return 0;
}
return 1;
}
static int
is_data (uint32_t va)
{
PIMAGE_SECTION_HEADER sec;
uint32_t sec_cnt,i;
/* If export va is directly relocated, so it must be data. */
if (is_reloc (va))
return 1;
if (gPEDta)
{
sec_cnt = gPEDta->FileHeader.NumberOfSections;
sec = IMAGE_FIRST_SECTION(gPEDta);
}
else
{
sec_cnt = gPEPDta->FileHeader.NumberOfSections;
sec = IMAGE_FIRST_SECTION(gPEPDta);
}
if (!sec)
return 0;
for (i = 0;i < sec_cnt;i++)
{
if (va >= sec[i].VirtualAddress && va < (sec[i].VirtualAddress+sec[i].Misc.VirtualSize))
break;
}
if (i == sec_cnt)
return 0;
if ((sec[i].Characteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_CNT_CODE)) != 0)
return 0;
if ((sec[i].Characteristics & IMAGE_SCN_MEM_DISCARDABLE) != 0)
return 1;
if ((sec[i].Characteristics & IMAGE_SCN_MEM_READ) ==0)
return 0;
if ((sec[i].Characteristics & (IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_LNK_COMDAT)) != 0)
return 1;
return 1;
}
static int
is_reloc (uint32_t va)
{
uint32_t va_rel, sz_rel, pos;
unsigned char *p;
PIMAGE_BASE_RELOCATION brel;
if (gPEDta)
{
va_rel = gPEDta->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress;
sz_rel = gPEDta->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;
}
else
{
va_rel = gPEPDta->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress;
sz_rel = gPEPDta->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;
}
if (va_rel == 0 || sz_rel < IMAGE_SIZEOF_BASE_RELOCATION)
return 0;
p = (unsigned char *) map_va (va_rel);
for (pos = 0; pos < sz_rel;)
{
uint16_t *r;
uint32_t nums, j;
if ((sz_rel - pos) < IMAGE_SIZEOF_BASE_RELOCATION)
break;
brel = (PIMAGE_BASE_RELOCATION) &p[pos];
if (brel->SizeOfBlock == 0)
break;
pos += IMAGE_SIZEOF_BASE_RELOCATION;
nums = (brel->SizeOfBlock - IMAGE_SIZEOF_BASE_RELOCATION) / 2;
r = (uint16_t *) &p[pos];
if (va >= brel->VirtualAddress && va < (brel->VirtualAddress + 0x1008))
{
for (j = 0; j < nums; j++)
{
uint32_t relsz = 0;
uint32_t offs = (uint32_t) (r[j] & 0xfff) + brel->VirtualAddress;
uint16_t typ = (r[j] >> 12) & 0xf;
if (typ == IMAGE_REL_BASED_HIGHADJ)
j++;
switch (typ)
{
case IMAGE_REL_BASED_HIGHLOW:
relsz = 4;
break;
case IMAGE_REL_BASED_DIR64:
relsz = 8;
break;
}
if (relsz != 0 && va >= offs && va < (offs + relsz))
return 1;
}
}
pos += (brel->SizeOfBlock - IMAGE_SIZEOF_BASE_RELOCATION);
}
return 0;
}
static void *
map_va (uint32_t va)
{
PIMAGE_SECTION_HEADER sec;
uint32_t sec_cnt,sz,i;
char *dptr;
if (gPEDta)
{
sec_cnt = gPEDta->FileHeader.NumberOfSections;
sec = IMAGE_FIRST_SECTION(gPEDta);
}
else
{
sec_cnt = gPEPDta->FileHeader.NumberOfSections;
sec = IMAGE_FIRST_SECTION(gPEPDta);
}
for (i = 0;i < sec_cnt;i++)
{
sz = sec[i].Misc.VirtualSize;
if (!sz) sz = sec[i].SizeOfRawData;
if (va >= sec[i].VirtualAddress && va < (sec[i].VirtualAddress+sz))
{
dptr = (char *) &gDta[va-sec[i].VirtualAddress+sec[i].PointerToRawData];
return (void *)dptr;
}
}
return NULL;
}
/* For pep we can take the exports itself, there is no additional decoration necessary. */
static void
do_pepdef (void)
{
uint32_t va_exp = gPEPDta->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
uint32_t sz_exp = gPEPDta->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;
do_export_read (va_exp, sz_exp, 1);
}
static void
do_pedef (void)
{
uint32_t va_exp = gPEDta->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
uint32_t sz_exp = gPEDta->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;
uint32_t va_imp = gPEDta->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
uint32_t sz_imp = gPEDta->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size;
imp32_free ();
do_import_read32 (va_imp, sz_imp);
do_export_read (va_exp, sz_exp, 0);
}
static void
do_import_read32 (uint32_t va_imp, uint32_t sz_imp)
{
IMAGE_IMPORT_DESCRIPTOR *pid;
if (!sz_imp || !va_imp)
return;
pid = (IMAGE_IMPORT_DESCRIPTOR *) map_va (va_imp);
while (pid != NULL && sz_imp >= 20 && pid->Name != 0 && pid->OriginalFirstThunk != 0)
{
uint32_t index = 0;
PIMAGE_THUNK_DATA32 pIAT;
PIMAGE_THUNK_DATA32 pFT;
char *imp_name = (char *) map_va (pid->Name);
for (;;) {
char *fctname;
pIAT = (PIMAGE_THUNK_DATA32) map_va (pid->OriginalFirstThunk + index);
pFT = (PIMAGE_THUNK_DATA32) map_va (pid->FirstThunk + index);
if (pIAT->u1.Ordinal == 0 || pFT->u1.Ordinal == 0)
break;
if (IMAGE_SNAP_BY_ORDINAL32 (pIAT->u1.Ordinal))
fctname = NULL;
else
fctname = (char *) map_va (pIAT->u1.Function + 2);
if (fctname)
imp32_add (imp_name, fctname,
//pid->OriginalFirstThunk + index + gPEDta->OptionalHeader.ImageBase,
pid->FirstThunk + index + gPEDta->OptionalHeader.ImageBase,
*((uint16_t *) map_va (pIAT->u1.Function)));
index += 4;
}
sz_imp -= 20;
va_imp += 20;
if (sz_imp >= 20)
pid = (IMAGE_IMPORT_DESCRIPTOR *) map_va (va_imp);
}
}
static sImportname *
imp32_findbyaddress (uint32_t addr)
{
sImportname *h = theImports;
while (h != NULL && h->addr_iat != addr)
h = h->next;
return h;
}
static void
imp32_free (void)
{
while (theImports != NULL)
{
sImportname *h = theImports;
theImports = theImports->next;
if (h->dll)
free (h->dll);
if (h->name)
free (h->name);
free (h);
}
}
static sImportname *
imp32_add (const char *dll, const char *name, uint32_t addr, uint16_t ord)
{
sImportname *n = (sImportname *) malloc (sizeof (sImportname));
memset (n, 0, sizeof (sImportname));
n->dll = strdup (dll);
n->name = strdup (name);
n->ord = ord;
n->addr_iat = addr;
n->next = theImports;
theImports = n;
return n;
}
static void
do_export_read (uint32_t va_exp, uint32_t sz_exp, int be64)
{
uint32_t i;
PIMAGE_EXPORT_DIRECTORY exp_dir;
uint32_t *functions;
uint16_t *ordinals;
uint32_t *name;
if (va_exp == 0 || sz_exp == 0)
return;
exp_dir = (PIMAGE_EXPORT_DIRECTORY) map_va (va_exp);
PRDEBUG(" * export directory at VA = 0x%x size=0x%x\n", (unsigned int) va_exp, (unsigned int) sz_exp);
fndllname = strdup ((char *) map_va (exp_dir->Name));
PRDEBUG(" * Name: %s\n * Base: %u\n", fndllname, (unsigned int) exp_dir->Base);
functions = (uint32_t *) map_va (exp_dir->AddressOfFunctions);
ordinals = (uint16_t *) map_va (exp_dir->AddressOfNameOrdinals);
name = (uint32_t *) map_va (exp_dir->AddressOfNames);
for (i = 0;i < exp_dir->NumberOfFunctions;i++)
{
uint32_t entryPointRVA = functions[i];
uint32_t j;
char *fname;
uint32_t ord;
fname = NULL;
if (!entryPointRVA)
continue;
ord = i + exp_dir->Base;
for (j = 0;j < exp_dir->NumberOfNames;j++)
if (ordinals[j]==i)
fname = (char *) map_va (name[j]);
if (entryPointRVA >= va_exp && entryPointRVA <= (va_exp + sz_exp))
add_export_list (ord, 0, fname,(char *) map_va (entryPointRVA), be64, 0);
else
add_export_list(ord, entryPointRVA, fname, NULL, be64, is_data (entryPointRVA));
}
}
static void
add_export_list(uint32_t ord,uint32_t func,const char *name, const char *forward,int be64,int beData)
{
sExportName *exp = NULL;
if (!name)
name = "";
if (!forward)
forward = "";
exp = (sExportName *) malloc (sizeof (sExportName) + strlen (name) + strlen (forward) + 2);
if (!exp)
return;
exp->name = (char *) &exp[1];
exp->forward = exp->name + strlen (name) + 1;
strcpy (exp->name, name);
strcpy (exp->forward, forward);
exp->next = NULL;
exp->ord = ord;
exp->func = func;
exp->be64 = be64;
exp->beData = beData;
exp->retpop = (uint32_t)-1;
if (gExpTail)
gExpTail->next = exp;
else
gExp = exp;
gExpTail = exp;
}
static void
dump_def (void)
{
sExportName *exp;
FILE *fp;
if (!fndllname || gExp == NULL)
return;
if (!std_output)
fp = fopen(fnoutput,"wt");
else
fp = stdout;
if(!fp) {
fprintf(stderr," * failed to create %s ...\n",fnoutput);
return;
}
fprintf (fp,";\n; Definition file of %s\n; Automatic generated by gendef\n; written by Kai Tietz 2008\n;\n",
fndllname);
fprintf (fp,"LIBRARY \"%s\"\nEXPORTS\n",fndllname);
while ((exp = gExp) != NULL)
{
sImportname *pimpname;
int seen_ret;
seen_ret = 1;
gExp = exp->next;
if (exp->name[0] == '?')
{
decode_mangle (fp, exp->name);
}
if (exp->name[0] == 0)
fprintf (fp, "ord_%u", (unsigned int) exp->ord);
else
{
const char *quote = strchr (exp->name, '.') ? "\"" : "";
fprintf (fp, "%s%s%s", quote, exp->name, quote);
}
if (exp->name[0] == '?' && exp->name[1] == '?')
{
if (!strncmp (exp->name, "??_7", 4))
exp->beData = 1;
}
pimpname = NULL;
if (!exp->beData && !exp->be64 && exp->func != 0)
{
seen_ret = 0;
exp->beData = disassembleRet (exp->func, &exp->retpop, exp->name, &pimpname, &seen_ret);
}
if (!exp->be64 && exp->retpop == (uint32_t) -1 && pimpname)
{
int isD = 0;
uint32_t at = 0;
if (gendef_getsymbol_info (pimpname->dll, pimpname->name, &isD, &at))
{
exp->beData = isD;
if (!isD)
exp->retpop = at;
}
}
else if (exp->func == 0 && !exp->beData)
{
int isD = 0;
uint32_t at = 0;
if (gendef_getsymbol_info (exp->forward, NULL, &isD, &at))
{
exp->beData = isD;
if (!isD)
exp->retpop = at;
}
}
if (exp->be64)
exp->retpop = 0;
if (exp->retpop != (uint32_t) -1 && !exp->be64 && has_atdecoration())
{
if (exp->name[0]=='?')
fprintf(fp," ; has WINAPI (@%u)", (unsigned int) exp->retpop);
else
fprintf(fp,"@%u", (unsigned int) exp->retpop);
}
if (exp->func == 0 && no_forward_output == 0)
fprintf (fp, " = %s", exp->forward);
if (exp->name[0] == 0)
fprintf(fp," @%u", (unsigned int) exp->ord);
if (exp->beData)
fprintf(fp," DATA");
if (exp->retpop != (uint32_t) -1 || (exp->retpop == 0 && exp->be64) || !has_atdecoration ())
{
}
else if (pimpname)
{
fprintf (fp, " ; Check!!! forwards to %s in %s (ordinal %u)",
pimpname->name, pimpname->dll, pimpname->ord);
}
else if (exp->func == 0 && !exp->beData)
{
fprintf (fp, " ; Check!!! forwards to %s", exp->forward);
}
else if (seen_ret == 0 && !exp->beData)
{
fprintf (fp, " ; Check!!! Couldn't determine function argument count. Function doesn't return. ");
}
fprintf(fp,"\n");
free (exp);
}
gExpTail=NULL;
if (!std_output)
fclose(fp);
}
static sAddresses *
init_addr (void)
{
sAddresses *r = (sAddresses*) malloc (sizeof (sAddresses));
r->max = 8;
r->cnt = 0;
r->ptrs = (uint32_t *) malloc (sizeof (uint32_t) * 8);
r->idx = 0;
return r;
}
static void
dest_addr (sAddresses *ad)
{
free (ad->ptrs);
free (ad);
}
static int
push_addr (sAddresses *ad, uint32_t val)
{
uint32_t i;
for (i = 0;i < ad->cnt; i++)
{
if (ad->ptrs[i] == val)
return 0;
}
if (ad->max == ad->cnt)
{
uint32_t *p = (uint32_t *) malloc (sizeof (uint32_t) * (ad->max + 8));
memcpy (p, ad->ptrs, sizeof (uint32_t) * ad->max);
ad->max += 8;
free (ad->ptrs);
ad->ptrs = p;
}
ad->ptrs[ad->cnt++] = val;
return 1;
}
static int
pop_addr (sAddresses *ad, uint32_t *val)
{
if (!ad || ad->idx == ad->cnt)
return 0;
ad->idx++;
*val = ad->ptrs[ad->idx-1];
return 1;
}
static int
has_atdecoration (void)
{
return (gPEDta && gPEDta->FileHeader.Machine == 0x014c /* IMAGE_FILE_MACHINE_I386 */);
}
/* exp->beData */
static int
disassembleRet (uint32_t func, uint32_t *retpop, const char *name, sImportname **ppimpname, int *seen_ret)
{
sAddresses *seen = init_addr ();
sAddresses *stack = init_addr ();
uint32_t pc;
int hasret = 0;
int atleast_one = 0;
if (!has_atdecoration())
{
*retpop = 0;
return 0;
}
*retpop = (uint32_t) -1;
push_addr (stack, func);
while (!hasret && pop_addr (stack,&pc))
{
if (disassembleRetIntern (pc, retpop, seen, stack, &hasret, &atleast_one, name, ppimpname))
break;
}
*seen_ret = hasret;
dest_addr (seen);
dest_addr (stack);
return (atleast_one ? 0 : 1);
}
static int
disassembleRetIntern (uint32_t pc, uint32_t *retpop, sAddresses *seen, sAddresses *stack,
int *hasret, int *atleast_one, const char *name, sImportname **ppimpname)
{
size_t sz = 0;
int code = 0,break_it = 0;
volatile uint32_t tojmp = 0;
while(1)
{
if (!push_addr (seen, pc))
return 0;
sz = getMemonic (&code, pc, &tojmp, name, ppimpname) & 0xffffffff;
if (!sz || code == c_ill)
{
PRDEBUG(" %s = 0x08%x ILL (%u) at least one==%d\n",name,
(unsigned int) pc, (unsigned int) sz,atleast_one[0]);
#if ENABLE_DEBUG == 1
{
unsigned char *ppc = (unsigned char *) map_va (pc);
size_t i;
fprintf (stderr, "%s(0x%x): ",name, (unsigned int) pc);
for (i = 0;i < sz; i++)
{
fprintf (stderr, "%s0x%x", (i == 0 ? " ":","), ppc[i]);
}
fprintf (stderr, "\n");
}
exit (0);
#endif
break;
}
#if ENABLE_DEBUG == 1
{
unsigned char *ppc = (unsigned char *) map_va (pc);
size_t i;
fprintf (stderr, "%s(0x%x): ",name, (unsigned int) pc);
for (i = 0;i < sz; i++)
{
fprintf (stderr, "%s0x%x", (i == 0 ? " ":","), ppc[i]);
}
fprintf (stderr, "\n");
}
#endif
atleast_one[0] += 1;
break_it = 0;
pc += sz;
switch(code)
{
case c_jmpnjb: case c_jmpnjv:
pc = tojmp;
break;
case c_jxx:
push_addr (stack, tojmp);
break;
case c_jmpfap: case c_int3:
break_it = 1;
break;
case c_iret: case c_retf: case c_retn:
*hasret = 1;
if (assume_stdcall)
*retpop = 0;
return 1;
case c_retflw: case c_retnlw:
*hasret = 1;
*retpop = tojmp;
return 1;
}
if (break_it)
break;
}
return 0;
}
static int opMap2[256] = {
c_EG,c_EG,c_EG,c_EG,c_1,c_1,c_ill,c_ill, /* 0x00-0x07 */
c_1,c_1,c_ill,c_ill,c_ill,c_ill,c_ill,c_ill, /* 0x08-0x0f */
c_EG,c_EG,c_EG,c_EG,c_EG,c_EG,c_EG,c_EG, /* 0x10-0x17 */
c_EG,c_EG,c_EG,c_EG,c_EG,c_EG,c_EG,c_EG, /* 0x18-0x1f */
c_EG,c_EG,c_EG,c_EG,c_EG,c_ill,c_EG,c_ill, /* 0x20-0x27 */
c_EG,c_EG,c_EG,c_EG,c_EG,c_EG,c_EG,c_EG, /* 0x28-0x2f */
c_1,c_1,c_1,c_1,c_1,c_1,c_ill,c_1, /* 0x30-0x37 */
c_ill,c_ill,c_ill,c_ill,c_ill,c_ill,c_ill,c_ill, /* 0x38-0x3f */
c_EG,c_EG,c_EG,c_EG,c_EG,c_EG,c_EG,c_EG, /* 0x40-0x47 */
c_EG,c_EG,c_EG,c_EG,c_EG,c_EG,c_EG,c_EG, /* 0x48-0x4f */
c_EG,c_EG,c_EG,c_EG,c_EG,c_EG,c_EG,c_EG, /* 0x50-0x57 */
c_EG,c_EG,c_EG,c_EG,c_EG,c_EG,c_EG,c_EG, /* 0x58-0x5f */
c_EG,c_EG,c_EG,c_EG,c_EG,c_EG,c_EG,c_EG, /* 0x60-0x67 */
c_EG,c_EG,c_EG,c_EG,c_EG,c_EG,c_EG,c_EG, /* 0x68-0x6f */
c_EG,c_EG,c_EG,c_EG,c_EG,c_EG,c_EG,c_EG, /* 0x70-0x77 */
c_EG,c_EG,c_EG,c_EG,c_EG,c_EG,c_EG,c_EG, /* 0x78-0x7f */
c_jxxv,c_jxxv,c_jxxv,c_jxxv,c_jxxv,c_jxxv,c_jxxv,c_jxxv, /* 0x80-0x87 */
c_jxxv,c_jxxv,c_jxxv,c_jxxv,c_jxxv,c_jxxv,c_jxxv,c_jxxv, /* 0x88-0x8f */
c_EG,c_EG,c_EG,c_EG,c_EG,c_EG,c_EG,c_EG, /* 0x90-0x97 */
c_EG,c_EG,c_EG,c_EG,c_EG,c_EG,c_EG,c_EG, /* 0x98-0x9f */
c_1,c_1,c_1,c_EG,c_EGlb,c_EG,c_ill,c_ill, /* 0xa0-0xa7 */
c_1,c_1,c_1,c_EG,c_EGlb,c_EG,c_EG,c_EG, /* 0xa8-0xaf */
c_EG,c_EG,c_EG,c_EG,c_EG,c_EG,c_EG,c_EG, /* 0xb0-0xb7 */
c_EG,c_1,c_EGlb,c_EG, c_EG,c_EG,c_EG,c_EG, /* 0xb8-0xbf */
c_EG,c_EG,c_EGlb,c_EG,c_EGlb,c_EGlb,c_EGlb,c_EG, /* 0xc0-0xc7 */
c_1,c_1,c_1,c_1,c_1,c_1,c_1,c_1, /* 0xc8-0xcf */
c_EG,c_EG,c_EG,c_EG,c_EG,c_EG,c_EG,c_EG, /* 0xd0-0xd7 */
c_EG,c_EG,c_EG,c_EG,c_EG,c_EG,c_EG,c_EG, /* 0xd8-0xdf */
c_EG,c_EG,c_EG,c_EG,c_EG,c_EG,c_EG,c_EG, /* 0xe0-0xe7 */
c_EG,c_EG,c_EG,c_EG,c_EG,c_EG,c_EG,c_EG, /* 0xe8-0xef */
c_ill,c_EG,c_EG,c_EG,c_EG,c_EG,c_EG,c_EG, /* 0xf0-0xf7 */
c_EG,c_EG,c_EG,c_EG,c_EG,c_EG,c_EG,c_ill /* 0xf8-0xff */
};
static int opMap1[256] = {
c_EG,c_EG,c_EG,c_EG,c_lb,c_lv,c_1,c_1, /* 0x00-0x07 */
c_EG,c_EG,c_EG,c_EG,c_lb,c_lv,c_1,c_0f, /* 0x08-0x0f */
c_EG,c_EG,c_EG,c_EG,c_lb,c_lv,c_1,c_1, /* 0x10-0x17 */
c_EG,c_EG,c_EG,c_EG,c_lb,c_lv,c_1,c_1, /* 0x18-0x1f */
c_EG,c_EG,c_EG,c_EG,c_lb,c_lv,c_1,c_1, /* 0x20-0x27 */
c_EG,c_EG,c_EG,c_EG,c_lb,c_lv,c_1,c_1, /* 0x28-0x2f */
c_EG,c_EG,c_EG,c_EG,c_lb,c_lv,c_1,c_1, /* 0x30-0x37 */
c_EG,c_EG,c_EG,c_EG,c_lb,c_lv,c_1,c_1, /* 0x38-0x3f */
c_1,c_1,c_1,c_1,c_1,c_1,c_1,c_1, /* 0x40-0x47 */
c_1,c_1,c_1,c_1,c_1,c_1,c_1,c_1, /* 0x48-0x4f */
c_1,c_1,c_1,c_1,c_1,c_1,c_1,c_1, /* 0x50-0x57 */
c_1,c_1,c_1,c_1,c_1,c_1,c_1,c_1, /* 0x58-0x5f */
c_1,c_1,c_EG,c_EG,c_1,c_1,c_op,c_ad, /* 0x60-0x67 */
c_lv,c_EGlv,c_lb,c_EGlb,c_1,c_1,c_1,c_1, /* 0x68-0x6f */
c_jxx,c_jxx,c_jxx,c_jxx,c_jxx,c_jxx,c_jxx,c_jxx, /* 0x70-0x77 */
c_jxx,c_jxx,c_jxx,c_jxx,c_jxx,c_jxx,c_jxx,c_jxx, /* 0x78-0x7f */
c_EGlb,c_EGlv,c_EGlb,c_EGlb,c_EG,c_EG,c_EG,c_EG, /* 0x80-0x87 */
c_EG,c_EG,c_EG,c_EG,c_EG,c_EG,c_EG,c_EG, /* 0x88-0x8f */
c_1,c_1,c_1,c_1,c_1,c_1,c_1,c_1, /* 0x90-0x97 */
c_1,c_1,c_callfar,c_1,c_1,c_1,c_1,c_1, /* 0x98-0x9f */
c_O,c_O,c_O,c_O,c_1,c_1,c_1,c_1, /* 0xa0-0xa7 */
c_lb,c_lv,c_1,c_1,c_1,c_1,c_1,c_1, /* 0xa8- 0xaf */
c_2,c_2,c_2,c_2,c_2,c_2,c_2,c_2, /* 0xb0-0xb7 */
c_lv,c_lv,c_lv,c_lv,c_lv,c_lv,c_lv,c_lv, /* 0xb8-0xbf */
c_EGlb,c_EGlb,c_retnlw,c_retn,c_EG,c_EG,c_EGlb,c_EGlv, /* 0xc0-0xc7 */
c_4,c_1,c_retflw,c_retf,c_int3,c_2,c_1,c_iret, /* 0xc8-0xcf */
c_EG,c_EG,c_EG,c_EG,c_2,c_2,c_1,c_1, /* 0xd0-0xd7 */
c_EG,c_EG,c_EG,c_EG,c_EG,c_EG,c_EG,c_EG, /* 0xd8-0xdf */
c_jxx,c_jxx,c_jxx,c_jxx,c_2,c_2,c_2,c_2, /* 0xe0-0xe7 */
c_calljv,c_jmpnjv,c_jmpfap,c_jmpnjb,c_1,c_1,c_1,c_1, /* 0xe8-0xef */
c_1,c_1,c_1,c_1, c_1,c_1,c_EGg3b,c_EGg3v, /* 0xf0-0xf7 */
c_1,c_1,c_1,c_1,c_1,c_1,c_g4,c_g4 /* 0xf8-0xff */
};
#if ENABLE_DEBUG == 1
#define MAX_INSN_SAVE 20
static void
enter_save_insn (unsigned char *s, unsigned char b)
{
int i;
for (i=0;i<MAX_INSN_SAVE-1;i++)
s[i]=s[i+1];
s[MAX_INSN_SAVE-1]=b;
}
static void
print_save_insn (const char *name, unsigned char *s)
{
int i;
PRDEBUG("From %s: ",name);
for (i=0;i<MAX_INSN_SAVE;i++)
{
PRDEBUG("%s0x%x",(i!=0 ? "," : ""), (unsigned int) s[i]);
}
}
#endif
static size_t
getMemonic(int *aCode,uint32_t pc,volatile uint32_t *jmp_pc, __attribute__((unused)) const char *name, sImportname **ppimpname)
{
#if ENABLE_DEBUG == 1
static unsigned char lw[MAX_INSN_SAVE];
#endif
unsigned char *p;
int addr_mode = 1;
int oper_mode = 1;
size_t sz = 0;
unsigned char b;
int tb1;
for(;;) {
p = (unsigned char *) map_va (pc + sz);
if (!p) { *aCode=c_ill; return 0; }
b = p[0];
if (b==0x26 || b==0x2e || b==0x36 || b==0x3e || b==0x64 || b==0x65)
++sz;
else if (b==0x66) { oper_mode^=1; sz++; }
else if (b==0x67) { addr_mode^=1; sz++; }
else if (b==0xf2 || b==0xf3 || b==0xf0) sz++;
else break;
}
sz++;
tb1=opMap1[(unsigned int) b];
redo_switch:
#if ENABLE_DEBUG == 1
if (tb1!=c_ill) { enter_save_insn(lw,b); }
#endif
switch (tb1) {
case c_ill:
#if ENABLE_DEBUG == 1
print_save_insn (name, lw);
PRDEBUG(" 0x%x illegal ", (unsigned int) b);
#endif
*aCode=c_ill; return 0;
case c_4: sz++;/* fallthru */
case c_3: sz++;/* fallthru */
case c_lb:
case c_2: sz++;/* fallthru */
case c_retn: case c_retf:
case c_iret: case c_int3:
case c_ad: case c_op:
case c_1: *aCode=tb1; return sz;
case c_lv:
if (oper_mode) sz+=4;
else sz+=2;
*aCode=tb1; return sz;
case c_O: case c_calljv:
if (addr_mode) sz+=4;
else sz+=2;
*aCode=tb1; return sz;
case c_EG: case c_EGlv: case c_EGlb: case c_g4: case c_EGg3v: case c_EGg3b:
p = (unsigned char *) map_va (pc + sz);
sz++;
if (!p) { *aCode=c_ill; return 0; }
b = p[0];
#if ENABLE_DEBUG == 1
enter_save_insn(lw,b);
#endif
if (addr_mode) {
if((b&0xc0)!=0xc0 && (b&0x7)==4)
{
p = (unsigned char *) map_va (pc + sz);
if (!p) { *aCode=c_ill; return 0; }
#if ENABLE_DEBUG == 1
enter_save_insn(lw,p[0]);
#endif
b&=~0x7; b|=(p[0]&7);
sz+=1;
}
if((b&0xc0)==0 && (b&7)==5)
{
/* Here we check if for jmp instruction it points to an IAT entry. */
if(tb1==c_g4 && ((b&0x38)==0x20 || (b&0x38)==0x28))
{
uint32_t vaddr;
sImportname *inss;
vaddr = *((uint32_t *) map_va (pc + sz));
inss = imp32_findbyaddress (vaddr);
if (inss)
*ppimpname = inss;
}
sz+=4;
}
else if((b&0xc0)==0x40)
sz+=1;
else if((b&0xc0)==0x80)
sz+=4;
} else {
if((b&0xc0)==0) {
if((b&0x07)==6) sz+=2;
} else if((b&0xc0)==0x40)
sz+=1;
else if((b&0xc0)==0x80)
sz+=2;
}
if (tb1==c_EGlv) sz+=(oper_mode ? 4 : 2);
else if(tb1==c_EGlb) sz++;
else if(tb1==c_g4) {
if ((b&0x38)==0x20 || (b&0x38)==0x28)
tb1=c_int3;
else if((b&0x38)==0x38)
tb1=c_ill;
} else if (tb1==c_EGg3v || tb1==c_EGg3b) {
switch (((b&0x38)>>3)) {
case 1:
case 0: sz+= (tb1==c_EGg3v ? (oper_mode ? 4 : 2) : 1); break;
default: break;
}
}
*aCode=tb1; return sz;
case c_jxx: case c_jmpnjb:
p = (unsigned char *) map_va (pc + sz);
if (!p) { *aCode=c_ill; return 0; }
b = p[0];
sz++;
jmp_pc[0]=(uint32_t) pc + (uint32_t) sz;
if ((b&0x80)!=0)
jmp_pc[0] = jmp_pc[0] + (((uint32_t) b) | 0xffffff00);
else
jmp_pc[0] = jmp_pc[0] + (uint32_t) b;
*aCode=tb1; return sz;
case c_jmpnjv:
case c_jxxv:
p = (unsigned char *) map_va (pc + sz);
if (!p) { *aCode=c_ill; return 0; }
if (oper_mode) { jmp_pc[0]=*((uint32_t *)p); sz+=4; }
else {
jmp_pc[0]=(uint32_t) *((uint16_t *)p);
if ((jmp_pc[0]&0x8000)!=0) jmp_pc[0]|=0xffff0000;
sz+=2;
}
jmp_pc[0]+=(uint32_t) pc+(uint32_t) sz;
#if ENABLE_DEBUG == 1
if ((jmp_pc[0]&0xff000000)!=0) {
print_save_insn (name, lw);
PRDEBUG(" 0x%x illegal ", (unsigned int) b);
PRDEBUG("jmp(cond) 0x%x (sz=%x,pc=%x,off=%x) ",
jmp_pc[0], (unsigned int) sz,(unsigned int) pc,
(unsigned int) (jmp_pc[0]-(sz+pc)));
}
#endif
*aCode=(tb1==c_jxxv ? c_jxx : tb1);
return sz;
case c_0f:
p = (unsigned char *) map_va (pc + sz);
if (!p) { *aCode=c_ill; return 0; }
b = p[0];
sz++;
tb1=opMap2[b];
goto redo_switch;
case c_jmpfap:
sz+=4; if(oper_mode) sz+=2;
*aCode=tb1; return sz;
case c_callfar:
sz+=4; if(oper_mode) sz+=2;
*aCode=tb1; return sz;
case c_retflw: case c_retnlw:
p = (unsigned char *) map_va (pc + sz);
if (!p) { *aCode=c_ill; return 0; }
jmp_pc[0]=*((uint16_t*) p);
sz+=2;
*aCode=tb1;
#if ENABLE_DEBUG == 1
if (jmp_pc[0]>0x100 || jmp_pc[0]&3) {
print_save_insn (name, lw);
PRDEBUG(" 0x%x illegal ", (unsigned int) b);
PRDEBUG("ret dw 0x%x (sz=%x) ", (unsigned int) jmp_pc[0], (unsigned int) sz);
}
#endif
return sz;
default:
PRDEBUG(" * opcode 0x%x (tbl=%d) unknown\n", (unsigned int) b, tb1);
sz=0; *aCode=c_ill; break;
}
return sz;
}
static void
decode_mangle (FILE *fp, const char *n)
{
#ifdef HAVE_LIBMANGLE
libmangle_gc_context_t *gc = libmangle_generate_gc ();
libmangle_tokens_t ptok;
#endif
if (!fp || !n || *n == 0)
return;
#ifdef HAVE_LIBMANGLE
ptok = libmangle_decode_ms_name (gc, n);
if (ptok)
{
char *h = libmangle_sprint_decl (ptok);
if (h)
{
fprintf (fp, "; %s\n", h);
free (h);
}
}
libmangle_release_gc (gc);
#endif
}