# HG changeset patch # User Matti Hamalainen # Date 1560524418 -10800 # Node ID 837c79747ea48aac15c5c9ac2263a016f19feca7 # Parent a36c81c3df85df1eb84e32fc0de6a86091641b8f Add functionality for grepping multiple consecutive values (8/16/32 le/be) and also wildcard values specified with '?'. diff -r a36c81c3df85 -r 837c79747ea4 tools/fanalyze.c --- a/tools/fanalyze.c Fri Jun 14 12:13:23 2019 +0300 +++ b/tools/fanalyze.c Fri Jun 14 18:00:18 2019 +0300 @@ -13,6 +13,7 @@ #define SET_MAX_FILES 64 #define SET_MAX_ELEMS 256 #define SET_MAX_VALUES 64 +#define SET_MAX_GREPLIST 64 /* Typedefs @@ -72,7 +73,9 @@ { int type; int disp; - Uint32 value; + int nvalues; + Uint32 values[SET_MAX_GREPLIST]; + BOOL vwildcards[SET_MAX_GREPLIST]; } DMGrepValue; @@ -131,8 +134,8 @@ { { 0, '?', "help", "Show this help", OPT_NONE }, { 1, 'v', "verbose", "Be more verbose", OPT_NONE }, - { 2, 'g', "grep", "Binary grep [,[8|16|32]]", OPT_ARGREQ }, - { 3, 'o', "offset", "Show data in offset ,[8|16|32][d|x]]", OPT_ARGREQ }, + { 2, 'g', "grep", "Binary grep [,...][:[8|16|32]]", OPT_ARGREQ }, + { 3, 'o', "offset", "Show data in offset :[8|16|32][d|x]]", OPT_ARGREQ }, }; static const int optListN = sizeof(optList) / sizeof(optList[0]); @@ -142,15 +145,96 @@ { dmPrintBanner(stdout, dmProgName, "[options] [...]"); dmArgsPrintHelp(stdout, optList, optListN, 0); + + fprintf(stdout, + "\n" + "Value lists for grep function can contain wildcard '?' (or '#') which\n" + "matches any value of the specified (or inferred) type. For example:\n" + "-g 0x0f,7,5,?,5,?,? will match sequence of bytes 0f 07 05 ?? 05 ?? ??\n" + "and -g 0xe,0x1001,?,2023:le16 will match le16 value 000e 1001 ???? 07e7\n" + ); +} + + +BOOL dmGetData(const int type, const DMSourceFile *file, const size_t offs, Uint32 *mval) +{ + Uint8 *data = file->data + offs; + if (offs + dmGrepTypes[type].bsize >= file->size) + { + *mval = 0; + return FALSE; + } + + switch (type) + { + case DMGV_UINT8: + *mval = *((Uint8 *) data); + break; + + case DMGV_UINT16_LE: + *mval = DM_LE16_TO_NATIVE(*((Uint16 *) data)); + break; + + case DMGV_UINT16_BE: + *mval = DM_BE16_TO_NATIVE(*((Uint16 *) data)); + break; + + case DMGV_UINT32_LE: + *mval = DM_LE32_TO_NATIVE(*((Uint32 *) data)); + break; + + case DMGV_UINT32_BE: + *mval = DM_BE32_TO_NATIVE(*((Uint32 *) data)); + break; + + default: + *mval = 0; + return FALSE; + } + return TRUE; +} + + +void dmPrintGrepValueList(const DMGrepValue *node, const BOOL match, DMSourceFile *file, const size_t offs) +{ + char vfmt[16]; + + snprintf(vfmt, sizeof(vfmt), "%%%s%d%s%%s", + node->disp == DMGS_HEX ? "0" : "", + dmGrepTypes[node->type].bsize * 2, + node->disp == DMGS_HEX ? "x" : "d"); + + for (int n = 0; n < node->nvalues; n++) + { + const char *veol = (n < node->nvalues - 1) ? " " : "\n"; + + if (match) + { + Uint32 mval; + dmGetData(node->type, file, offs + n, &mval); + dmPrint(1, vfmt, mval, veol); + } + else + { + if (node->vwildcards[n]) + dmPrint(1, "?%s", veol); + else + dmPrint(1, vfmt, node->values[n], veol); + } + } } int argParseGrepValue(const char *arg, const int mode) { - const char *sep = strchr(arg, ','); - char *vspec, *vstr; - int vdisp = DMGS_HEX, vtype = -1, ret = DMERR_OK; - Uint32 vval; + const char *specsep = strchr(arg, ':'); + char *vspec, *vstr, *vsep; + int vdisp = DMGS_HEX, vtype = -1, ret = DMERR_OK, nvalues; + BOOL more; + Uint32 vvalues[SET_MAX_GREPLIST]; + BOOL vwildcards[SET_MAX_GREPLIST]; + + memset(vwildcards, 0, sizeof(vwildcards)); if (setMode != FA_ANALYZE && setMode != mode) { @@ -160,10 +244,10 @@ setMode = mode; // Do we have spec? - if (sep != NULL) + if (specsep != NULL) { - vspec = dm_strdup_trim(sep + 1, DM_TRIM_BOTH); - vstr = dm_strndup_trim(arg, sep - arg, DM_TRIM_BOTH); + vspec = dm_strdup_trim(specsep + 1, DM_TRIM_BOTH); + vstr = dm_strndup_trim(arg, specsep - arg, DM_TRIM_BOTH); } else { @@ -237,12 +321,56 @@ } } - // Get value - if (!dmGetIntVal(vstr, &vval, NULL)) + // Get list of values + char *vtmp = vstr; + nvalues = 0; + do + { + if (nvalues >= SET_MAX_GREPLIST) + { + ret = dmError(DMERR_BOUNDS, + "Too many greplist values specified '%s'.\n", + vstr); + goto out; + } + + if ((vsep = strchr(vtmp, ',')) != NULL) + { + *vsep = 0; + more = TRUE; + } + else + more = FALSE; + + if (vtmp[0] == '#' || vtmp[0] == '?') + { + vwildcards[nvalues] = TRUE; + if (mode == FA_OFFSET) + { + ret = dmError(DMERR_INVALID_ARGS, + "Offset mode does not allow wildcard values.\n"); + goto out; + } + } + else + if (!dmGetIntVal(vtmp, &vvalues[nvalues], NULL)) + { + ret = dmError(DMERR_INVALID_ARGS, + "Not a valid integer value '%s'.\n", + vtmp); + goto out; + } + + nvalues++; + + if (more) + vtmp = vsep + 1; + } while (more); + + if (vwildcards[0]) { ret = dmError(DMERR_INVALID_ARGS, - "Not a valid integer value '%s'.\n", - vstr); + "First grep value can not be a wildcard.\n"); goto out; } @@ -254,7 +382,7 @@ for (int n = DMGV_last; n >= 0; n--) { const DMGrepType *def = &dmGrepTypes[n]; - if (vval <= def->nmax) + if (vvalues[0] <= def->nmax) vtype = n; } } @@ -268,11 +396,12 @@ } // Check range - if (vval > dmGrepTypes[vtype].nmax) + for (int n = 0; n < nvalues; n++) + if (!vwildcards[n] && vvalues[n] > dmGrepTypes[vtype].nmax) { ret = dmError(DMERR_INVALID_ARGS, "Integer value %d <= %d <= %d out of range for type %s.\n", - vval, 0, dmGrepTypes[vtype].nmax, dmGrepTypes[vtype].name); + vvalues[n], 0, dmGrepTypes[vtype].nmax, dmGrepTypes[vtype].name); goto out; } @@ -287,13 +416,17 @@ if (nsetGrepValues < SET_MAX_VALUES) { DMGrepValue *node = &setGrepValues[nsetGrepValues++]; + node->type = vtype; node->disp = vdisp; - node->value = vval; + node->nvalues = nvalues; + memcpy(node->values, vvalues, sizeof(vvalues[0]) * nvalues); + memcpy(node->vwildcards, vwildcards, sizeof(vwildcards[0]) * nvalues); - dmMsg(1, "Grep value %s : %d / 0x%x\n", - dmGrepTypes[vtype].name, - vval, vval); + dmMsg(1, "Grep %ss : ", + dmGrepTypes[vtype].name); + + dmPrintGrepValueList(node, FALSE, NULL, 0); } else { @@ -388,45 +521,6 @@ } -BOOL dmGetData(const int type, const DMSourceFile *file, const size_t offs, Uint32 *mval) -{ - Uint8 *data = file->data + offs; - if (offs + dmGrepTypes[type].bsize >= file->size) - { - *mval = 0; - return FALSE; - } - - switch (type) - { - case DMGV_UINT8: - *mval = *((Uint8 *) data); - break; - - case DMGV_UINT16_LE: - *mval = DM_LE16_TO_NATIVE(*((Uint16 *) data)); - break; - - case DMGV_UINT16_BE: - *mval = DM_BE16_TO_NATIVE(*((Uint16 *) data)); - break; - - case DMGV_UINT32_LE: - *mval = DM_LE32_TO_NATIVE(*((Uint32 *) data)); - break; - - case DMGV_UINT32_BE: - *mval = DM_BE32_TO_NATIVE(*((Uint32 *) data)); - break; - - default: - *mval = 0; - return FALSE; - } - return TRUE; -} - - int main(int argc, char *argv[]) { DMCompElem *compBuf = NULL; @@ -486,15 +580,28 @@ DMGrepValue *node = &setGrepValues[n]; const DMGrepType *def = &dmGrepTypes[node->type]; - for (size_t offs = 0; offs + def->bsize < file->size; offs++) + for (size_t offs = 0; offs + (def->bsize * node->nvalues) < file->size; offs++) { - Uint32 mval; - dmGetData(node->type, file, offs, &mval); + BOOL match = TRUE; + for (int n = 0; n < node->nvalues; n++) + if (!node->vwildcards[n]) + { + Uint32 mval; + dmGetData(node->type, file, offs + n, &mval); - if (mval == node->value) + if (mval != node->values[n]) + { + match = FALSE; + break; + } + } + + if (match) { - dmPrint(0, "%08x : %s match %d / 0x%x\n", - offs, def->name, mval, mval); + dmPrint(0, "%08x : %s match ", + offs, def->name); + + dmPrintGrepValueList(node, TRUE, file, offs); } } } @@ -523,7 +630,7 @@ { DMGrepValue *node = &setGrepValues[n]; const DMGrepType *def = &dmGrepTypes[node->type]; - printf("%08x : ", node->value); + printf("%08x : ", node->values[0]); for (int nfile = 0; nfile < nsrcFiles; nfile++) { @@ -532,7 +639,7 @@ char mstr[32]; int npad, nwidth; - if (dmGetData(node->type, file, node->value, &mval)) + if (dmGetData(node->type, file, node->values[0], &mval)) { char mfmt[16]; nwidth = def->bsize * 2;