mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-21 05:43:48 -07:00
Merge branch 'RfidResearchGroup:master' into spi_flash_v2
This commit is contained in:
commit
3b6530cb92
13 changed files with 201 additions and 23 deletions
|
@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file.
|
|||
This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log...
|
||||
|
||||
## [unreleased][unreleased]
|
||||
- Added support for a new KDF (@iceman1001)
|
||||
- Added Inner range aid and mad entries (@iceman1001)
|
||||
- Changed `mem spiffs` - Use all available space in SPI flash (@ANTodorov)
|
||||
- Fixed wrong size check in MifareSim (@iceman1001)
|
||||
|
@ -69,6 +70,8 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac
|
|||
- Added `hf 14a aidsim` - simulates a PICC (like `14a sim`), and allows you to respond to specific AIDs and getData responses (@evildaemond)
|
||||
- Fixed arguments for `SimulateIso14443aTag` and `SimulateIso14443aInit` in `hf_young.c`, `hf_aveful.c`, `hf_msdsal.c`, `hf_cardhopper.c`, `hf_reblay.c`, `hf_tcprst.c` and `hf_craftbyte.c` (@archi)
|
||||
- Added `mf_backdoor_dump.py` script that dumps FM11RF08S and similar (Mifare Classic 1k) tag data that can be directly read by known backdoor keys. (@Aptimex)
|
||||
- Added keys for Metro Q transit cards in Huston, TX. (@Anarchothulhu)
|
||||
- Add new Mifare Classic keys from MifareClassicTool and Flipper projects. (@onovy)
|
||||
|
||||
## [Backdoor.4.18994][2024-09-10]
|
||||
- Changed flashing messages to be less scary (@iceman1001)
|
||||
|
|
|
@ -22,7 +22,8 @@ add_library(pm3rrg_rdv4_mbedtls STATIC
|
|||
../../common/mbedtls/des.c
|
||||
../../common/mbedtls/ecdsa.c
|
||||
../../common/mbedtls/md.c
|
||||
../../common/mbedtls/md5.c
|
||||
../../common/mbedtls/hkdf.c
|
||||
../../common/mbedtls/md5.c
|
||||
../../common/mbedtls/oid.c
|
||||
../../common/mbedtls/pem.c
|
||||
../../common/mbedtls/arc4.c
|
||||
|
|
|
@ -2698,3 +2698,60 @@ F14D329CBDBE
|
|||
# Hotel cards from Austria
|
||||
AB287B3B4903
|
||||
7B0DEDA7E162
|
||||
#
|
||||
#Metro Q transit cards from Huston, Texas
|
||||
373B72D34B80
|
||||
BF3FFC245C9b
|
||||
7D1D9E7CF8A7
|
||||
6A917BF357E6
|
||||
B1D461EC62CA
|
||||
C6BECABEBE8A
|
||||
66026782D435
|
||||
4547E34E40D9
|
||||
753897b99AAE
|
||||
1C36761E8ABD
|
||||
6D8FBD8CC524
|
||||
5A3274779706
|
||||
23F13602CC1A
|
||||
511C1C2C9804
|
||||
F8B2B926555E
|
||||
2593E37D9B2E
|
||||
41A1F17EE990
|
||||
64DD48AEDE88
|
||||
7915ED4D9903
|
||||
D139DD71DB92
|
||||
216D97D46E88
|
||||
D9D1C447E427
|
||||
911E789433CB
|
||||
93B43D689F85
|
||||
525A869053F1
|
||||
69B25667E0B4
|
||||
6AACA2D97645
|
||||
# UK London Office
|
||||
435DF6296EC4
|
||||
2338B4913222
|
||||
# Acces card of students, and more in Occitanie, France
|
||||
E9A553102EA5
|
||||
F982E971CFED
|
||||
1F42AB9159EE
|
||||
BBFB836A48B8
|
||||
B5D170B2E8F5
|
||||
E76978A05F10
|
||||
0B1A995DD007
|
||||
650DB9CEDB6B
|
||||
13E54B4448B7
|
||||
3E3540C2C273
|
||||
A76152840117
|
||||
066CCC7666BC
|
||||
3C0B3AC3AFA3
|
||||
CCB541598D72
|
||||
1988B5D48EC3
|
||||
892EEF0D30FB
|
||||
0FE5CE5CC640
|
||||
# Volgograd (Russia) Volna transport cards keys
|
||||
2B787A063D5D
|
||||
D37C8F1793F7
|
||||
# H World Hotel Chain Room Keys
|
||||
543071543071
|
||||
5F01015F0101
|
||||
200510241234
|
||||
|
|
|
@ -201,10 +201,10 @@
|
|||
},
|
||||
{
|
||||
"AID": "F47300",
|
||||
"Vendor": "Inner Range",
|
||||
"Vendor": "Inner Range Pty Ltd",
|
||||
"Country": "AU",
|
||||
"Name": "Sifer-P, Sifer-U Credential",
|
||||
"Description": "Inner Range access control",
|
||||
"Name": "SIFER-P / SIFER-U Credential",
|
||||
"Description": "Inner Range Access Control",
|
||||
"Type": "pacs"
|
||||
},
|
||||
{
|
||||
|
@ -699,7 +699,7 @@
|
|||
"AID": "F33480",
|
||||
"Vendor": "Besucherausweis",
|
||||
"Country": "DE",
|
||||
"Name": "Visitor's Pass",
|
||||
"Name": "Visitor Badge",
|
||||
"Description": "Besucherausweis",
|
||||
"Type": "student"
|
||||
},
|
||||
|
@ -943,6 +943,14 @@
|
|||
"Description": "DUB Leap Card // Transport for Ireland // FIDs: 01,1F: Backup Data; 02-0A: Standard Data",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "484000",
|
||||
"Vendor": "Sistema de Tren Elétrico Urbano (SITEUR)",
|
||||
"Country": "MX",
|
||||
"Name": "Mi Movilidad Card (GDL)",
|
||||
"Description": "GDL Mi Movilidad Card",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "4F5931",
|
||||
"Vendor": "Transport for London (TfL)",
|
||||
|
@ -1047,6 +1055,14 @@
|
|||
"Description": "SOF Sofia City Card",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "DD00DD",
|
||||
"Vendor": "Regional Transporation District (RTD) via masabi justride",
|
||||
"Country": "US",
|
||||
"Name": "MyRide Card (DEN)",
|
||||
"Description": "DEN MyRide Card",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "EF2011",
|
||||
"Vendor": "Helsinki Region Transport (HRT)",
|
||||
|
@ -1215,4 +1231,4 @@
|
|||
"Description": "Used by AKL AT HOP, DXB nol, and SEA ORCA",
|
||||
"Type": "transport"
|
||||
}
|
||||
]
|
||||
]
|
||||
|
|
|
@ -797,7 +797,7 @@ static int mf_load_keys(uint8_t **pkeyBlock, uint32_t *pkeycnt, uint8_t *userkey
|
|||
PrintAndLogEx(DEBUG, _YELLOW_("%2d") " - %s", *pkeycnt + i, sprint_hex(*pkeyBlock + (*pkeycnt + i) * MIFARE_KEY_SIZE, MIFARE_KEY_SIZE));
|
||||
}
|
||||
*pkeycnt += ARRAYLEN(g_mifare_default_keys);
|
||||
PrintAndLogEx(SUCCESS, "loaded " _GREEN_("%zu") " keys from hardcoded default array", ARRAYLEN(g_mifare_default_keys));
|
||||
PrintAndLogEx(SUCCESS, "loaded " _GREEN_("%zu") " hardcoded keys", ARRAYLEN(g_mifare_default_keys));
|
||||
}
|
||||
|
||||
// Handle user supplied dictionary file
|
||||
|
@ -883,7 +883,7 @@ static int CmdHF14AMfDarkside(const char *Cmd) {
|
|||
arg_param_begin,
|
||||
arg_int0(NULL, "blk", "<dec> ", "Target block"),
|
||||
arg_lit0("b", NULL, "Target key B instead of default key A"),
|
||||
arg_int0("c", NULL, "<dec>", "Target key type is key A + offset"),
|
||||
arg_int0("c", NULL, "<dec>", "Target Auth 6x"),
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||
|
@ -9697,7 +9697,8 @@ static int CmdHF14AMfInfo(const char *Cmd) {
|
|||
}
|
||||
|
||||
if (fKeyType != 0xFF) {
|
||||
PrintAndLogEx(SUCCESS, "Block 0.......... %s", sprint_hex_ascii(blockdata, MFBLOCK_SIZE));
|
||||
PrintAndLogEx(SUCCESS, "Block 0.... %s | " NOLF, sprint_hex_inrow(blockdata, MFBLOCK_SIZE));
|
||||
PrintAndLogEx(NORMAL, "%s", sprint_ascii(blockdata + 8, 8));
|
||||
}
|
||||
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
|
|
|
@ -4126,7 +4126,7 @@ static int CmdHF14AMfUCSetUid(const char *Cmd) {
|
|||
|
||||
// save old block2.
|
||||
uint8_t oldblock2[4] = {0x00};
|
||||
memcpy(resp.data.asBytes, oldblock2, 4);
|
||||
memcpy(oldblock2, resp.data.asBytes, 4);
|
||||
|
||||
// Enforce bad BCC handling temporarily as BCC will be wrong between
|
||||
// block 1 write and block2 write
|
||||
|
@ -4431,6 +4431,9 @@ static int CmdHF14AMfUPwdGen(const char *Cmd) {
|
|||
PrintAndLogEx(INFO, " Dorma Kaba algo");
|
||||
PrintAndLogEx(INFO, " STiD algo");
|
||||
PrintAndLogEx(INFO, "-------------------------------------");
|
||||
key = 0;
|
||||
mfc_algo_bambu_one(uid, 0, MF_KEY_A, &key);
|
||||
PrintAndLogEx(INFO, " Bambu........ %012" PRIX64, key);
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
|
|
|
@ -567,3 +567,35 @@ size_t concatbits(uint8_t *dest, int dest_offset, const uint8_t *src, int src_of
|
|||
|
||||
return dest_offset + nbits;
|
||||
}
|
||||
|
||||
int char2int(char c) {
|
||||
if (c >= '0' && c <= '9') return c - '0';
|
||||
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
|
||||
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
|
||||
return -1; // Invalid character for hex
|
||||
}
|
||||
|
||||
// returns the number of bytes written
|
||||
int hexstr2ByteArr(const char *hexstr, unsigned char *array, size_t asize) {
|
||||
size_t n = 0;
|
||||
while (hexstr[n] != '\0') {
|
||||
n++;
|
||||
}
|
||||
|
||||
// Check if the input is valid and fits in the output array
|
||||
if (n % 2 != 0 || asize < n >> 1) {
|
||||
return -1; // Error: invalid length or insufficient byte array size
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < n; i += 2) {
|
||||
int high = char2int(hexstr[i]);
|
||||
int low = char2int(hexstr[i + 1]);
|
||||
|
||||
if (high == -1 || low == -1) {
|
||||
return -1; // Error: invalid hex character
|
||||
}
|
||||
|
||||
array[i >> 1] = (high << 4) | low;
|
||||
}
|
||||
return n >> 1;
|
||||
}
|
|
@ -65,6 +65,20 @@
|
|||
#define REV64(x) (REV32(x) + ((uint64_t)(REV32((x) >> 32) << 32)))
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
int Year;
|
||||
int Month;
|
||||
int Day;
|
||||
int Hour;
|
||||
int Minute;
|
||||
} Date_t;
|
||||
|
||||
|
||||
int calculate_hours_between_dates(const Date_t s, Date_t *e);
|
||||
void add_hours(Date_t *d, int hours_to_add);
|
||||
void add_days(Date_t *d, int days_to_add);
|
||||
uint8_t days_in_month(int year, int month);
|
||||
|
||||
|
||||
extern struct version_information_t g_version_information;
|
||||
void FormatVersionInformation(char *dst, int len, const char *prefix, const void *version_info);
|
||||
|
@ -136,4 +150,6 @@ void reverse_arraybytes(uint8_t *arr, size_t len);
|
|||
void reverse_arraybytes_copy(uint8_t *arr, uint8_t *dest, size_t len);
|
||||
|
||||
size_t concatbits(uint8_t *dest, int dest_offset, const uint8_t *src, int src_offset, size_t nbits);
|
||||
int char2int(char c);
|
||||
int hexstr2ByteArr(const char *hexstr, unsigned char *array, size_t asize);
|
||||
#endif
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
#include <string.h>
|
||||
#include "commonutil.h" //BSWAP_16
|
||||
#include "common.h" //BSWAP_32/64
|
||||
#include "util.h"
|
||||
|
||||
#include "pm3_cmd.h"
|
||||
#include "crc16.h" // crc16 ccitt
|
||||
#include "mbedtls/sha1.h"
|
||||
|
@ -33,9 +33,11 @@
|
|||
#include "mbedtls/cmac.h"
|
||||
#include "mbedtls/cipher.h"
|
||||
#include "mbedtls/md.h"
|
||||
#include "mbedtls/hkdf.h"
|
||||
|
||||
#ifndef ON_DEVICE
|
||||
#include "ui.h"
|
||||
#include "util.h"
|
||||
# define prnt(args...) PrintAndLogEx(DEBUG, ## args );
|
||||
#else
|
||||
# include "dbprint.h"
|
||||
|
@ -351,12 +353,11 @@ int mfc_algo_saflok_one(uint8_t *uid, uint8_t sector, uint8_t keytype, uint64_t
|
|||
0xda3e3fd649ddULL, 0x58dded078e3eULL, 0x5cd005cfd907ULL, 0x118dd00187d0ULL
|
||||
};
|
||||
|
||||
uint8_t h = ((uid[3] >> 4) & 0xF);
|
||||
h += ((uid[2] >> 4) & 0xF);
|
||||
uint8_t h = (NIBBLE_HIGH(uid[3]) & 0xF);
|
||||
h += (NIBBLE_HIGH(uid[2]) & 0xF);
|
||||
h += uid[0] & 0xF;
|
||||
|
||||
uint64_t m = lut[h & 0xF];
|
||||
|
||||
uint64_t id = (bytes_to_num(uid, 4) << 8);
|
||||
|
||||
*key = (h + (id + m + ((uint64_t)h << 40ULL))) & 0xFFFFFFFFFFFFULL;
|
||||
|
@ -540,6 +541,41 @@ int mfc_algo_sky_all(uint8_t *uid, uint8_t *keys) {
|
|||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static const uint8_t bambu_salt[] = { 0x9a, 0x75, 0x9c, 0xf2, 0xc4, 0xf7, 0xca, 0xff, 0x22, 0x2c, 0xb9, 0x76, 0x9b, 0x41, 0xbc, 0x96 };
|
||||
static const uint8_t bambu_context_a[] = "RFID-A";
|
||||
static const uint8_t bambu_context_b[] = "RFID-B";
|
||||
|
||||
int mfc_algo_bambu_one(uint8_t *uid, uint8_t sector, uint8_t keytype, uint64_t *key) {
|
||||
if (uid == NULL) return PM3_EINVARG;
|
||||
if (key == NULL) return PM3_EINVARG;
|
||||
|
||||
uint8_t keys[16 * 6] = {0};
|
||||
|
||||
// prepare hmac context
|
||||
const mbedtls_md_info_t *info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
|
||||
|
||||
if (keytype == 0) {
|
||||
mbedtls_hkdf(info, bambu_salt, sizeof(bambu_salt), uid, 4, bambu_context_a, sizeof(bambu_context_a), keys, sizeof(keys) );
|
||||
} else {
|
||||
mbedtls_hkdf(info, bambu_salt, sizeof(bambu_salt), uid, 4, bambu_context_b, sizeof(bambu_context_b), keys, sizeof(keys));
|
||||
}
|
||||
|
||||
*key = bytes_to_num(keys + (sector * 6), 6);
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
int mfc_algo_bambu_all(uint8_t *uid, uint8_t *keys) {
|
||||
if (uid == NULL) return PM3_EINVARG;
|
||||
if (keys == NULL) return PM3_EINVARG;
|
||||
|
||||
// prepare hmac context
|
||||
const mbedtls_md_info_t *info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
|
||||
mbedtls_hkdf(info, bambu_salt, sizeof(bambu_salt), uid, 4, bambu_context_a, sizeof(bambu_context_a), keys, (16 * 6) );
|
||||
mbedtls_hkdf(info, bambu_salt, sizeof(bambu_salt), uid, 4, bambu_context_b, sizeof(bambu_context_b), keys + (16 * 6), (16 * 6));
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
// LF T55x7 White gun cloner algo
|
||||
uint32_t lf_t55xx_white_pwdgen(uint32_t id) {
|
||||
uint32_t r1 = rotl(id & 0x000000ec, 8);
|
||||
|
@ -617,10 +653,9 @@ int mfc_algo_touch_one(uint8_t *uid, uint8_t sector, uint8_t keytype, uint64_t *
|
|||
|
||||
int generator_selftest(void) {
|
||||
#ifndef ON_DEVICE
|
||||
#define NUM_OF_TEST 10
|
||||
#define NUM_OF_TEST 11
|
||||
|
||||
PrintAndLogEx(INFO, "PWD / KEY generator selftest");
|
||||
PrintAndLogEx(INFO, "----------------------------");
|
||||
PrintAndLogEx(INFO, "------- " _CYAN_("PWD / KEY generator self tests") " --------");
|
||||
|
||||
uint8_t testresult = 0;
|
||||
|
||||
|
@ -629,7 +664,6 @@ int generator_selftest(void) {
|
|||
bool success = (pwd1 == 0x8432EB17);
|
||||
if (success)
|
||||
testresult++;
|
||||
|
||||
PrintAndLogEx(success ? SUCCESS : WARNING, "UID | %s | %08X - %s", sprint_hex(uid1, 7), pwd1, success ? _GREEN_("ok") : "->8432EB17<-");
|
||||
|
||||
uint8_t uid2[] = {0x04, 0x1f, 0x98, 0xea, 0x1e, 0x3e, 0x81};
|
||||
|
@ -687,7 +721,7 @@ int generator_selftest(void) {
|
|||
success = (key8 == 0x82c7e64bc565);
|
||||
if (success)
|
||||
testresult++;
|
||||
PrintAndLogEx(success ? SUCCESS : WARNING, "UID | %s | %"PRIx64" - %s", sprint_hex(uid8, 4), key8, success ? _GREEN_("ok") : "->82C7E64BC565<--");
|
||||
PrintAndLogEx(success ? SUCCESS : WARNING, "UID | %s | %012"PRIx64" - %s", sprint_hex(uid8, 4), key8, success ? _GREEN_("ok") : "->82C7E64BC565<--");
|
||||
|
||||
// MFC SAFLOK
|
||||
uint8_t uid9[] = {0x11, 0x22, 0x33, 0x44};
|
||||
|
@ -696,13 +730,22 @@ int generator_selftest(void) {
|
|||
success = (key9 == 0xD1E2AA68E39A);
|
||||
if (success)
|
||||
testresult++;
|
||||
PrintAndLogEx(success ? SUCCESS : WARNING, "UID | %s | %"PRIX64" - %s", sprint_hex(uid9, 4), key9, success ? _GREEN_("ok") : _RED_(">> D1E2AA68E39A <<"));
|
||||
PrintAndLogEx(success ? SUCCESS : WARNING, "UID | %s | %012"PRIX64" - %s", sprint_hex(uid9, 4), key9, success ? _GREEN_("ok") : _RED_(">> D1E2AA68E39A <<"));
|
||||
|
||||
uint32_t lf_id = lf_t55xx_white_pwdgen(0x00000080);
|
||||
success = (lf_id == 0x00018383);
|
||||
if (success)
|
||||
testresult++;
|
||||
PrintAndLogEx(success ? SUCCESS : WARNING, "ID | 0x00000080 | %08"PRIx32 " - %s", lf_id, success ? _GREEN_("ok") : "->00018383<--");
|
||||
PrintAndLogEx(success ? SUCCESS : WARNING, "ID | 0x00000080 | %08"PRIx32 " - %s", lf_id, success ? _GREEN_("ok") : ">> 00018383 <<");
|
||||
|
||||
// MFC Bambu
|
||||
uint64_t key13 = 0;
|
||||
mfc_algo_bambu_one(uid9, 0, 0, &key13);
|
||||
success = (key13 == 0x0729F3B2D37A);
|
||||
if (success)
|
||||
testresult++;
|
||||
PrintAndLogEx(success ? SUCCESS : WARNING, "UID | %s | %012"PRIX64" - %s", sprint_hex(uid9, 4), key13, success ? _GREEN_("ok") : _RED_(">> 0729F3B2D37A <<"));
|
||||
|
||||
|
||||
PrintAndLogEx(SUCCESS, "------------------- Selftest %s", (testresult == NUM_OF_TEST) ? _GREEN_("ok") : _RED_("fail"));
|
||||
|
||||
|
|
|
@ -62,6 +62,8 @@ int mfc_generate4b_nuid(uint8_t *uid, uint8_t *nuid);
|
|||
|
||||
int mfc_algo_touch_one(uint8_t *uid, uint8_t sector, uint8_t keytype, uint64_t *key);
|
||||
|
||||
int mfc_algo_bambu_one(uint8_t *uid, uint8_t sector, uint8_t keytype, uint64_t *key);
|
||||
int mfc_algo_bambu_all(uint8_t *uid, uint8_t *keys);
|
||||
uint32_t lf_t55xx_white_pwdgen(uint32_t id);
|
||||
|
||||
int mfdes_kdf_input_gallagher(uint8_t *uid, uint8_t uidLen, uint8_t keyNo, uint32_t aid, uint8_t *kdfInputOut, uint8_t *kdfInputLen);
|
||||
|
|
|
@ -25,6 +25,7 @@ MYSRCS = \
|
|||
ecdh.c \
|
||||
ecdsa.c \
|
||||
gcm.c \
|
||||
hkdf.c \
|
||||
md.c \
|
||||
md5.c \
|
||||
oid.c \
|
||||
|
|
|
@ -2849,7 +2849,7 @@
|
|||
* This module adds support for the Hashed Message Authentication Code
|
||||
* (HMAC)-based key derivation function (HKDF).
|
||||
*/
|
||||
//#define MBEDTLS_HKDF_C
|
||||
#define MBEDTLS_HKDF_C
|
||||
|
||||
/**
|
||||
* \def MBEDTLS_HMAC_DRBG_C
|
||||
|
|
|
@ -17,7 +17,10 @@ all: $(BINS)
|
|||
echo "ERROR: Firmware image too large for your platform! $$FWSIZE > $(FWMAXSIZE)"; \
|
||||
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"; \
|
||||
exit 1; \
|
||||
fi
|
||||
fi; \
|
||||
echo "==================================================================="; \
|
||||
echo "Firmware size: $$FWSIZE bytes ($$((FWSIZE/1024))kb) = $$((FWSIZE*100/$(FWMAXSIZE)))% of $$(($(FWMAXSIZE)/1024))kb"; \
|
||||
echo "==================================================================="
|
||||
|
||||
bootrom.bin: ../bootrom/obj/bootrom.elf
|
||||
$(info [=] GEN $@)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue