mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2026-05-12 11:18:11 -07:00
Merge pull request #3168 from kormax/aidlist-response-contains
Add support for matching responses with `--aidsearch`
This commit is contained in:
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user