/* $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 #include #include #include #include #include #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; }