Merge pull request #3168 from kormax/aidlist-response-contains

Add support for matching responses with `--aidsearch`
This commit is contained in:
Iceman
2026-03-25 07:34:43 +07:00
committed by GitHub
6 changed files with 199 additions and 42 deletions
+31 -4
View File
@@ -2217,11 +2217,30 @@
},
{
"AID": "4F53452E5641532E3031",
"Vendor": "Apple, Google",
"Vendor": "Apple",
"Country": "",
"Name": "Value-Added Services (OSE.VAS.01)",
"Description": "Used by Apple VAS and Google SmartTap",
"Type": "loyalty"
"Name": "Apple VAS (OSE.VAS.01)",
"Description": "Apple VAS",
"Type": "loyalty",
"ResponseContains": "50084170706c65506179"
},
{
"AID": "4F53452E5641532E3031",
"Vendor": "Google",
"Country": "",
"Name": "Google Smart Tap (OSE.VAS.01)",
"Description": "Google Smart Tap",
"Type": "loyalty",
"ResponseContains": "500a416e64726f6964506179"
},
{
"AID": "4F53452E5641532E3031",
"Vendor": "Samsung",
"Country": "",
"Name": "Samsung VAS (OSE.VAS.01)",
"Description": "Samsung VAS",
"Type": "loyalty",
"ResponseContains": "500d53616d73756e6757616c6c6574"
},
{
"AID": "A000000476D0000101",
@@ -2239,6 +2258,14 @@
"Description": "",
"Type": "loyalty"
},
{
"AID": "A000000870FC000000000034",
"Vendor": "Samsung",
"Country": "",
"Name": "Samsung VAS",
"Description": "",
"Type": "loyalty"
},
{
"AID": "A0000002480400",
"Vendor": "ISO/IEC JTC1/SC17",
+134 -36
View File
@@ -20,6 +20,7 @@
#include <string.h>
#include "fileutils.h"
#include "pm3_cmd.h"
#include "util.h"
static int openAIDFile(json_t **root, bool verbose) {
json_error_t error;
@@ -111,6 +112,27 @@ static bool aidCompare(const char *aidlarge, const char *aidsmall) {
return false;
}
static bool ResponseContainsMatch(const char *needle, const char *text) {
if (needle == NULL || text == NULL || needle[0] == '\0' || text[0] == '\0') {
return false;
}
char *needle_lc = str_dup(needle);
char *text_lc = str_dup(text);
if (needle_lc == NULL || text_lc == NULL) {
free(needle_lc);
free(text_lc);
return false;
}
str_lower(needle_lc);
str_lower(text_lc);
bool matched = (strstr(text_lc, needle_lc) != NULL);
free(needle_lc);
free(text_lc);
return matched;
}
bool AIDGetFromElm(json_t *data, uint8_t *aid, size_t aidmaxlen, int *aidlen) {
*aidlen = 0;
const char *hexaid = jsonStrGet(data, "AID");
@@ -124,7 +146,49 @@ bool AIDGetFromElm(json_t *data, uint8_t *aid, size_t aidmaxlen, int *aidlen) {
return true;
}
bool AIDSeenBefore(json_t *root, const uint8_t *aid, size_t aidlen, size_t before_index) {
if (root == NULL || aid == NULL || aidlen == 0) {
return false;
}
size_t limit = before_index;
if (limit > json_array_size(root)) {
limit = json_array_size(root);
}
for (size_t i = 0; i < limit; i++) {
json_t *data = AIDSearchGetElm(root, i);
if (data == NULL) {
continue;
}
uint8_t prev_aid[200] = {0};
int prev_aid_len = 0;
if ((AIDGetFromElm(data, prev_aid, sizeof(prev_aid), &prev_aid_len) == false) || (prev_aid_len <= 0)) {
continue;
}
if ((size_t)prev_aid_len == aidlen && memcmp(prev_aid, aid, aidlen) == 0) {
return true;
}
}
return false;
}
int PrintAIDDescription(json_t *xroot, char *aid, bool verbose) {
return PrintAIDDescriptionEx(xroot, aid, NULL, 0, verbose);
}
int PrintAIDDescriptionBuf(json_t *root, uint8_t *aid, size_t aidlen, bool verbose) {
return PrintAIDDescription(root, sprint_hex_inrow(aid, aidlen), verbose);
}
int PrintAIDDescriptionEx(json_t *xroot, char *aid, const uint8_t *response, size_t response_len, bool verbose) {
if (aid == NULL || aid[0] == '\0') {
return PM3_SUCCESS;
}
int retval = PM3_SUCCESS;
json_t *root = xroot;
@@ -135,50 +199,89 @@ int PrintAIDDescription(json_t *xroot, char *aid, bool verbose) {
goto out;
}
json_t *elm = NULL;
char *response_hex = NULL;
if (response != NULL && response_len > 0) {
if (response_len > ((SIZE_MAX - 1) / 2)) {
goto out;
}
size_t response_hexlen = (response_len * 2) + 1;
response_hex = calloc(response_hexlen, sizeof(char));
if (response_hex == NULL) {
goto out;
}
hex_to_buffer((uint8_t *)response_hex, response, response_len, response_hexlen - 1, 0, 0, true);
}
json_t *fallback_elm = NULL;
json_t *contains_elm = NULL;
size_t maxaidlen = 0;
for (size_t elmindx = 0; elmindx < json_array_size(root); elmindx++) {
json_t *data = AIDSearchGetElm(root, elmindx);
if (data == NULL) {
continue;
}
const char *dictaid = jsonStrGet(data, "AID");
if (aidCompare(aid, dictaid)) { // dictaid may be less length than requested aid
if (maxaidlen < strlen(dictaid) && strlen(dictaid) <= strlen(aid)) {
maxaidlen = strlen(dictaid);
elm = data;
if (dictaid == NULL) {
continue;
}
if (!aidCompare(aid, dictaid)) { // dictaid may be less length than requested aid
continue;
}
size_t dictaidlen = strlen(dictaid);
if (dictaidlen > strlen(aid)) {
continue;
}
if (dictaidlen > maxaidlen) {
maxaidlen = dictaidlen;
fallback_elm = data;
contains_elm = NULL;
} else if (dictaidlen < maxaidlen) {
continue;
}
if (response_hex != NULL) {
const char *response_contains = jsonStrGet(data, "ResponseContains");
if (response_contains && ResponseContainsMatch(response_contains, response_hex)) {
contains_elm = data;
}
}
}
if (elm == NULL) {
goto out;
json_t *elm = contains_elm ? contains_elm : fallback_elm;
if (elm != NULL) {
const char *vaid = jsonStrGet(elm, "AID");
const char *vendor = jsonStrGet(elm, "Vendor");
const char *name = jsonStrGet(elm, "Name");
const char *country = jsonStrGet(elm, "Country");
const char *description = jsonStrGet(elm, "Description");
const char *type = jsonStrGet(elm, "Type");
if (verbose == false) {
PrintAndLogEx(SUCCESS, "AID : " _YELLOW_("%s") " | %s | %s", vaid, vendor, name);
} else {
PrintAndLogEx(SUCCESS, "Input AID..... " _YELLOW_("%s"), aid);
if (aid)
PrintAndLogEx(SUCCESS, "Found AID..... " _YELLOW_("%s"), vaid);
if (vendor)
PrintAndLogEx(SUCCESS, "Vendor........ " _YELLOW_("%s"), vendor);
if (type)
PrintAndLogEx(SUCCESS, "Type.......... " _YELLOW_("%s"), type);
if (name)
PrintAndLogEx(SUCCESS, "Name.......... " _YELLOW_("%s"), name);
if (country)
PrintAndLogEx(SUCCESS, "Country....... %s", country);
if (description)
PrintAndLogEx(SUCCESS, "Description... %s", description);
}
}
// print here
const char *vaid = jsonStrGet(elm, "AID");
const char *vendor = jsonStrGet(elm, "Vendor");
const char *name = jsonStrGet(elm, "Name");
const char *country = jsonStrGet(elm, "Country");
const char *description = jsonStrGet(elm, "Description");
const char *type = jsonStrGet(elm, "Type");
if (verbose == false) {
PrintAndLogEx(SUCCESS, "AID : " _YELLOW_("%s") " | %s | %s", vaid, vendor, name);
} else {
PrintAndLogEx(SUCCESS, "Input AID..... " _YELLOW_("%s"), aid);
if (aid)
PrintAndLogEx(SUCCESS, "Found AID..... " _YELLOW_("%s"), vaid);
if (vendor)
PrintAndLogEx(SUCCESS, "Vendor........ " _YELLOW_("%s"), vendor);
if (type)
PrintAndLogEx(SUCCESS, "Type.......... " _YELLOW_("%s"), type);
if (name)
PrintAndLogEx(SUCCESS, "Name.......... " _YELLOW_("%s"), name);
if (country)
PrintAndLogEx(SUCCESS, "Country....... %s", country);
if (description)
PrintAndLogEx(SUCCESS, "Description... %s", description);
if (response_hex != NULL) {
free(response_hex);
}
out:
@@ -187,8 +290,3 @@ out:
}
return retval;
}
int PrintAIDDescriptionBuf(json_t *root, uint8_t *aid, size_t aidlen, bool verbose) {
return PrintAIDDescription(root, sprint_hex_inrow(aid, aidlen), verbose);
}
+2
View File
@@ -26,10 +26,12 @@
#include "jansson.h"
int PrintAIDDescription(json_t *xroot, char *aid, bool verbose);
int PrintAIDDescriptionEx(json_t *xroot, char *aid, const uint8_t *response, size_t response_len, bool verbose);
int PrintAIDDescriptionBuf(json_t *root, uint8_t *aid, size_t aidlen, bool verbose);
json_t *AIDSearchInit(bool verbose);
json_t *AIDSearchGetElm(json_t *root, size_t elmindx);
bool AIDGetFromElm(json_t *data, uint8_t *aid, size_t aidmaxlen, int *aidlen);
bool AIDSeenBefore(json_t *root, const uint8_t *aid, size_t aidlen, size_t before_index);
int AIDSearchFree(json_t *root);
#endif
+12 -1
View File
@@ -3182,6 +3182,9 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
if ((AIDGetFromElm(data, vaid, sizeof(vaid), &vaidlen) == false) || (vaidlen == 0)) {
continue;
}
if (AIDSeenBefore(root, vaid, (size_t)vaidlen, elmindx)) {
continue;
}
uint16_t sw = 0;
uint8_t result[1024] = {0};
@@ -3214,7 +3217,15 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
if (verbose) PrintAndLogEx(WARNING, "Application ( " _RED_("blocked") " )");
}
PrintAIDDescriptionBuf(root, vaid, vaidlen, verbose);
uint8_t response_for_match[1026] = {0};
size_t response_for_match_len = resultlen;
memcpy(response_for_match, result, MIN(resultlen, sizeof(response_for_match)));
if ((response_for_match_len + 2) <= sizeof(response_for_match)) {
response_for_match[response_for_match_len] = sw >> 8;
response_for_match[response_for_match_len + 1] = sw & 0xff;
response_for_match_len += 2;
}
PrintAIDDescriptionEx(root, sprint_hex_inrow(vaid, vaidlen), response_for_match, response_for_match_len, verbose);
if (dfnamelen) {
if (dfnamelen == vaidlen) {
+4 -1
View File
@@ -122,6 +122,9 @@ static void hf14b_aid_search(bool verbose) {
if ((AIDGetFromElm(data, vaid, sizeof(vaid), &vaidlen) == false) || (vaidlen == 0)) {
continue;
}
if (AIDSeenBefore(root, vaid, (size_t)vaidlen, elmindx)) {
continue;
}
// COMPUTE APDU
@@ -172,7 +175,7 @@ static void hf14b_aid_search(bool verbose) {
}
}
PrintAIDDescriptionBuf(root, vaid, vaidlen, verbose);
PrintAIDDescriptionEx(root, sprint_hex_inrow(vaid, vaidlen), result, (size_t)resultlen, verbose);
if (dfnamelen) {
if (dfnamelen == vaidlen) {
+16
View File
@@ -15,6 +15,9 @@ Each entry in `client/resources/aidlist.json` must contain all of the fields bel
- `Description`: Extra context, disambiguation, references, legacy naming, known usage notes, or deployment-specific remarks.
- `Type`: High-level category tag (for example `transport`, `emv`, `gp`, `pacs`, `ndef`).
## Optional fields
- `ResponseContains`: Case-insensitive hex substring matched against the APDU SELECT response (encoded as hex without separators). Use this field when multiple protocols share the same AID and can be distinguished by response content.
Example:
```json
{
@@ -26,3 +29,16 @@ Example:
"Type": "transport"
}
```
Response-disambiguation example:
```json
{
"AID": "4F53452E5641532E3031",
"Vendor": "Google",
"Country": "",
"Name": "Google Smart Tap (OSE.VAS.01)",
"Description": "Google Smart Tap",
"Type": "loyalty",
"ResponseContains": "500a416e64726f6964506179"
}
```