ÜberWall

UWfirmforce

/* $Id: UWfirmforce.c,v 1.1 2009/02/21 21:25:17 khorben Exp $ */
/* Somewhat efficient automated firmware reverse-engineering tool
* UberWall security team \../. .\../ */
/* Copyright (c) 2006, 2007 khorben of Uberwall */
/* 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 version 2 of the License.
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include <unistd.h>
#include <dirent.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <dlfcn.h>
#include "plugins/plugin.h"
#include "UWfirmforce.h"
#include "config.h"
/* macros */
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
/* types */
typedef struct _Prefs
{
int flags;
int threshold;
int bufsiz;
} Prefs;
#define PREFS_h 0x1
#define PREFS_l 0x2
#define PREFS_v 0x4
#define PREFS_x 0x8
/* constants */
#ifndef PREFIX
# define PREFIX "/usr/local"
#endif
#ifndef LIBDIR
# define LIBDIR PREFIX "/lib"
#endif
#ifndef PLUGINDIR
# define PLUGINDIR LIBDIR "/UWfirmforce"
#endif
/* _UWfirmforce */
static PluginHandle * _plugins_load_next(void);
static int _UWfirmforce_do(Prefs * prefs, PluginHandle ** plugin,
unsigned int plugin_cnt, int filec, char * filev[]);
static int _UWfirmforce(Prefs * prefs, int filec, char * filev[])
{
int ret = 0;
PluginHandle ** plugin = NULL;
unsigned int plugin_cnt = 0;
PluginHandle * p;
PluginHandle ** q;
unsigned int i;
while((p = _plugins_load_next()) != NULL)
{
if((q = realloc(plugin, sizeof(*plugin) * (plugin_cnt+1)))
== NULL)
break;
plugin = q;
plugin[plugin_cnt++] = p;
}
if(plugin == NULL)
{
fprintf(stderr, "%s", "UWfirmforce: No plug-in available\n");
return 1;
}
ret = p == NULL ? _UWfirmforce_do(prefs, plugin, plugin_cnt, filec,
filev) : 1;
for(i = 0; i < plugin_cnt; i++)
plugin_delete(plugin[i]);
return ret;
}
/* _UWfirmforce_do */
static int _UWfirmforce_do_file(Prefs * prefs, PluginHandle ** plugin,
unsigned int plugin_cnt, char * buf, size_t buf_cnt,
size_t overhead, char const * filename);
static int _UWfirmforce_do(Prefs * prefs, PluginHandle ** plugin,
unsigned int plugin_cnt, int filec, char * filev[])
{
int ret = 0;
unsigned int i;
int j;
char * buf;
size_t overhead = 0;
for(i = 0; i < plugin_cnt; i++)
overhead = max(overhead, plugin_get_buffer_size(plugin[i]));
if((buf = malloc(overhead + prefs->bufsiz)) == NULL)
return UWfirmforce_error("malloc", 1);
for(j = 0; j < filec; j++)
ret |= _UWfirmforce_do_file(prefs, plugin, plugin_cnt, buf,
overhead + prefs->bufsiz, overhead, filev[j]);
free(buf);
return ret;
}
/* _UWfirmforce_do_file */
static int _do_file_do(Prefs * prefs, char const * filename, FILE * fp,
PluginHandle ** plugin, unsigned int plugin_cnt, long offset,
char * buf, size_t buf_cnt);
static int _UWfirmforce_do_file(Prefs * prefs, PluginHandle ** plugin,
unsigned int plugin_cnt, char * buf, size_t buf_cnt,
size_t overhead, char const * filename)
{
int ret = 0;
FILE * fp;
size_t cnt;
size_t pos = 0;
size_t i;
size_t len;
long int offset = 0;
fpos_t fpos;
printf("%s%s\n", "\nAnalyzing file: ", filename);
if((fp = fopen(filename, "r")) == NULL)
return UWfirmforce_error(filename, 1);
while((cnt = fread(&buf[pos], sizeof(char), buf_cnt-pos, fp)) > 0)
{
if(cnt < buf_cnt-pos) /* short read: try until the end */
{
len = (pos == overhead) ? cnt + overhead : cnt;
pos = 0;
}
else /* read until just the overhead is left */
{
len = (pos == overhead) ? cnt : cnt - overhead;
pos = overhead;
}
if(fgetpos(fp, &fpos) != 0)
return UWfirmforce_error(filename, 1);
for(i = 0; i < len; i++)
if(_do_file_do(prefs, filename, fp, plugin, plugin_cnt,
offset + i, &buf[i], len - i) != 0)
break;
if(fsetpos(fp, &fpos) != 0)
return UWfirmforce_error(filename, 1);
memmove(buf, &buf[i], pos);
offset+=i;
}
if(cnt == 0 && !feof(fp))
ret = UWfirmforce_error(filename, 1);
fclose(fp);
return ret;
}
/* _do_file_do */
static void _do_matched(Prefs * prefs, PluginHandle * plugin, int signature,
long offset);
static void _do_score(int score);
static int _do_file_do(Prefs * prefs, char const * filename, FILE * fp,
PluginHandle ** plugin, unsigned int plugin_cnt, long offset,
char * buf, size_t buf_cnt)
{
unsigned int i;
int score;
int signature;
for(i = 0; i < plugin_cnt; i++)
{
if((signature = plugin_match(plugin[i], buf, buf_cnt)) < 0)
continue;
if(fseek(fp, offset, SEEK_SET) != 0)
{
UWfirmforce_error(filename, 0);
continue;
}
if((score = plugin_do(plugin[i], signature, 0, 0, filename, fp))
< prefs->threshold)
continue;
_do_matched(prefs, plugin[i], signature, offset);
if(prefs->flags & PREFS_v)
{
if(fseek(fp, offset, SEEK_SET) == 0)
{
plugin_do(plugin[i], signature, 1,
prefs->flags & PREFS_x ? 1 : 0,
filename, fp);
fflush(stdout);
}
else
UWfirmforce_error(filename, 0);
}
_do_score(score);
}
return 0;
}
static void _do_matched(Prefs * prefs, PluginHandle * plugin, int signature,
long offset)
{
char * format;
format = prefs->flags & PREFS_h ? "%s%s%s%d%s0x%08x\n"
: "%s%s%s%d%s%ld\n";
printf(format, "Matching ", plugin_get_name(plugin), " signature #",
signature, " at offset ", offset);
}
static void _do_score(int score)
{
if(score < 0)
return;
printf("%s%d%%\n\n", "Score: ", min(100, score));
}
/* _UWfirmforce_plugins */
static int _UWfirmforce_plugins(void)
{
PluginHandle * p;
printf("%s", "Plugins available:\n");
while((p = _plugins_load_next()) != NULL)
{
plugin_info(p);
plugin_delete(p);
}
return 0;
}
static PluginHandle * _plugins_load_next(void)
{
static DIR * dir = NULL;
static char * filename = NULL;
static size_t filename_len = 0;
struct dirent * de;
size_t len;
char * p;
PluginHandle * plugin;
if(dir == NULL && (dir = opendir(PLUGINDIR)) == NULL)
{
UWfirmforce_error(PLUGINDIR, 0);
return NULL;
}
for(de = readdir(dir); de != NULL; de = readdir(dir))
{
len = strlen(de->d_name);
if(len < 4 || strcmp(".so", &de->d_name[len-3]) != 0)
continue;
if(sizeof(PLUGINDIR) + len + 2 > filename_len)
{
if((p = realloc(filename, sizeof(PLUGINDIR) + len + 5))
== NULL)
break;
filename = p;
filename_len = sizeof(PLUGINDIR) + len + 5;
}
snprintf(filename, filename_len, "%s/%s", PLUGINDIR,
de->d_name);
if((plugin = plugin_new(filename)) != NULL)
return plugin;
}
free(filename);
filename = NULL;
filename_len = 0;
closedir(dir);
dir = NULL;
if(de != NULL)
UWfirmforce_error(PLUGINDIR, 0);
return NULL;
}
/* UWfirmforce_error */
int UWfirmforce_error(char const * message, int ret)
{
fprintf(stderr, "%s", "UWfirmforce: ");
perror(message);
return ret;
}
/* UWfirmforce_dlerror */
int UWfirmforce_dlerror(char const * message, int ret)
{
fprintf(stderr, "%s%s%s%s\n", "UWfirmforce: ", message, ": ",
dlerror());
return ret;
}
/* usage */
static int _usage(void)
{
fprintf(stderr, "%s%u%s", "Usage: UWfirmforce [-hv][-b size][-t score]"
"file...\n"
" UWfirmforce -l\n"
" -b Specify buffer size (default ", BUFSIZ, ")\n"
" -h Use hexadecimal offsets and sizes\n"
" -l List available plug-ins\n"
" -t Score threshold (percentage)\n"
" -v Verbose mode\n"
" -x Attempt to extract firmware contents\n");
return 1;
}
/* main */
int main(int argc, char * argv[])
{
Prefs prefs;
int o;
char * p;
memset(&prefs, 0, sizeof(prefs));
prefs.bufsiz = BUFSIZ;
while((o = getopt(argc, argv, "b:hlt:vx")) != -1)
switch(o)
{
case 'b':
o = strtol(optarg, &p, 10);
if(*optarg == '\0' || *p != '\0' || o < 0)
return _usage();
prefs.bufsiz = o;
break;
case 'h':
prefs.flags |= PREFS_h;
break;
case 'l':
prefs.flags |= PREFS_l;
break;
case 't':
o = strtol(optarg, &p, 10);
if(*optarg == '\0' || *p != '\0'
|| o < 0 || o > 100)
return _usage();
prefs.threshold = o;
break;
case 'v':
prefs.flags |= PREFS_v;
break;
case 'x':
prefs.flags |= PREFS_x;
break;
default:
return _usage();
}
if((prefs.flags & PREFS_l) == PREFS_l)
return _UWfirmforce_plugins();
if(optind == argc)
return _usage();
return _UWfirmforce(&prefs, argc - optind, &argv[optind]) == 0 ? 0 : 2;
}