mirror of
https://github.com/RfidResearchGroup/ChameleonUltra.git
synced 2026-05-12 11:22:59 -07:00
Implement new Mifare Classic emulator configuration commands, improve CLI
Slot Won't work like this More randomness on nested auth (for hardnested recovery) and change default GCC location Nobody will have GCC in this default location, /usr/bin/ will target way more users Get enabled slots command hw slot list show disabled slots Improve python code quality Show Mifare Classic emulator settings in hw slot list Implement hf mf settings to change Mifare Classic emulator settings Update
This commit is contained in:
@@ -463,7 +463,7 @@ data_frame_tx_t* cmd_processor_set_mf1_anti_collision_res(uint16_t cmd, uint16_t
|
||||
} else {
|
||||
uint8_t uid_length = length - 3;
|
||||
if (is_valid_uid_size(uid_length)) {
|
||||
nfc_tag_14a_coll_res_referen_t* info = get_miafre_coll_res();
|
||||
nfc_tag_14a_coll_res_referen_t* info = get_mifare_coll_res();
|
||||
// copy sak
|
||||
info->sak[0] = data[0];
|
||||
// copy atqa
|
||||
@@ -528,6 +528,124 @@ data_frame_tx_t* cmd_processor_get_slot_tag_nick_name(uint16_t cmd, uint16_t sta
|
||||
return data_frame_make(cmd, status, length, data);
|
||||
}
|
||||
|
||||
data_frame_tx_t* cmd_processor_get_mf1_info(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
||||
uint8_t mf1_info[5] = {};
|
||||
mf1_info[0] = nfc_tag_mf1_is_detection_enable();
|
||||
mf1_info[1] = nfc_tag_mf1_is_gen1a_magic_mode();
|
||||
mf1_info[2] = nfc_tag_mf1_is_gen2_magic_mode();
|
||||
mf1_info[3] = nfc_tag_mf1_is_use_mf1_coll_res();
|
||||
nfc_tag_mf1_write_mode_t write_mode = nfc_tag_mf1_get_write_mode();
|
||||
if (write_mode == NFC_TAG_MF1_WRITE_NORMAL) {
|
||||
mf1_info[4] = 0;
|
||||
} else if (write_mode == NFC_TAG_MF1_WRITE_DENIED) {
|
||||
mf1_info[4] = 1;
|
||||
} else if (write_mode == NFC_TAG_MF1_WRITE_DECEIVE) {
|
||||
mf1_info[4] = 2;
|
||||
} else if (write_mode == NFC_TAG_MF1_WRITE_SHADOW) {
|
||||
mf1_info[4] = 3;
|
||||
}
|
||||
return data_frame_make(cmd, STATUS_DEVICE_SUCCESS, 5, mf1_info);
|
||||
}
|
||||
|
||||
data_frame_tx_t* cmd_processor_get_mf1_gen1a_magic_mode(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
||||
if (nfc_tag_mf1_is_gen1a_magic_mode()) {
|
||||
status = 1;
|
||||
} else {
|
||||
status = 0;
|
||||
}
|
||||
return data_frame_make(cmd, STATUS_DEVICE_SUCCESS, 1, (uint8_t*)&status);
|
||||
}
|
||||
|
||||
data_frame_tx_t* cmd_processor_set_mf1_gen1a_magic_mode(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
||||
if (length == 1 && (data[0] == 0 || data[0] == 1)) {
|
||||
nfc_tag_mf1_set_gen1a_magic_mode(data[0]);
|
||||
status = STATUS_DEVICE_SUCCESS;
|
||||
} else {
|
||||
status = STATUS_PAR_ERR;
|
||||
}
|
||||
return data_frame_make(cmd, status, 0, NULL);
|
||||
}
|
||||
|
||||
data_frame_tx_t* cmd_processor_get_mf1_gen2_magic_mode(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
||||
if (nfc_tag_mf1_is_gen2_magic_mode()) {
|
||||
status = 1;
|
||||
} else {
|
||||
status = 0;
|
||||
}
|
||||
return data_frame_make(cmd, STATUS_DEVICE_SUCCESS, 1, (uint8_t*)&status);
|
||||
}
|
||||
|
||||
data_frame_tx_t* cmd_processor_set_mf1_gen2_magic_mode(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
||||
if (length == 1 && (data[0] == 0 || data[0] == 1)) {
|
||||
nfc_tag_mf1_set_gen2_magic_mode(data[0]);
|
||||
status = STATUS_DEVICE_SUCCESS;
|
||||
} else {
|
||||
status = STATUS_PAR_ERR;
|
||||
}
|
||||
return data_frame_make(cmd, status, 0, NULL);
|
||||
}
|
||||
|
||||
data_frame_tx_t* cmd_processor_get_mf1_use_coll_res(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
||||
if (nfc_tag_mf1_is_use_mf1_coll_res()) {
|
||||
status = 1;
|
||||
} else {
|
||||
status = 0;
|
||||
}
|
||||
return data_frame_make(cmd, STATUS_DEVICE_SUCCESS, 1, (uint8_t*)&status);
|
||||
}
|
||||
|
||||
data_frame_tx_t* cmd_processor_set_mf1_use_coll_res(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
||||
if (length == 1 && (data[0] == 0 || data[0] == 1)) {
|
||||
nfc_tag_mf1_set_use_mf1_coll_res(data[0]);
|
||||
status = STATUS_DEVICE_SUCCESS;
|
||||
} else {
|
||||
status = STATUS_PAR_ERR;
|
||||
}
|
||||
return data_frame_make(cmd, status, 0, NULL);
|
||||
}
|
||||
|
||||
data_frame_tx_t* cmd_processor_get_mf1_write_mode(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
||||
nfc_tag_mf1_write_mode_t write_mode = nfc_tag_mf1_get_write_mode();
|
||||
if (write_mode == NFC_TAG_MF1_WRITE_NORMAL) {
|
||||
status = 0;
|
||||
} else if (write_mode == NFC_TAG_MF1_WRITE_DENIED) {
|
||||
status = 1;
|
||||
} else if (write_mode == NFC_TAG_MF1_WRITE_DECEIVE) {
|
||||
status = 2;
|
||||
} else if (write_mode == NFC_TAG_MF1_WRITE_SHADOW) {
|
||||
status = 3;
|
||||
}
|
||||
return data_frame_make(cmd, STATUS_DEVICE_SUCCESS, 1, (uint8_t*)&status);
|
||||
}
|
||||
|
||||
data_frame_tx_t* cmd_processor_set_mf1_write_mode(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
||||
if (length == 1 && (data[0] >= 0 || data[0] <= 3)) {
|
||||
uint8_t mode = data[0];
|
||||
if (mode == 0) {
|
||||
nfc_tag_mf1_set_write_mode(NFC_TAG_MF1_WRITE_NORMAL);
|
||||
} else if (mode == 1) {
|
||||
nfc_tag_mf1_set_write_mode(NFC_TAG_MF1_WRITE_DENIED);
|
||||
} else if (mode == 2) {
|
||||
nfc_tag_mf1_set_write_mode(NFC_TAG_MF1_WRITE_DECEIVE);
|
||||
} else if (mode == 3) {
|
||||
nfc_tag_mf1_set_write_mode(NFC_TAG_MF1_WRITE_SHADOW);
|
||||
}
|
||||
status = STATUS_DEVICE_SUCCESS;
|
||||
} else {
|
||||
status = STATUS_PAR_ERR;
|
||||
}
|
||||
return data_frame_make(cmd, status, 0, NULL);
|
||||
}
|
||||
|
||||
data_frame_tx_t* cmd_processor_get_enabled_slots(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
||||
uint8_t slot_info[8] = {};
|
||||
for (uint8_t slot = 0; slot < 8; slot++) {
|
||||
slot_info[slot] = tag_emulation_slot_is_enable(slot);
|
||||
}
|
||||
|
||||
return data_frame_make(cmd, STATUS_DEVICE_SUCCESS, 8, slot_info);
|
||||
}
|
||||
|
||||
#if defined(PROJECT_CHAMELEON_ULTRA)
|
||||
|
||||
|
||||
@@ -614,6 +732,8 @@ static cmd_data_map_t m_data_cmd_map[] = {
|
||||
{ DATA_CMD_GET_ACTIVE_SLOT, NULL, cmd_processor_get_activated_slot, NULL },
|
||||
{ DATA_CMD_GET_SLOT_INFO, NULL, cmd_processor_get_slot_info, NULL },
|
||||
{ DATA_CMD_WIPE_FDS, NULL, cmd_processor_wipe_fds, NULL },
|
||||
{ DATA_CMD_GET_ENABLED_SLOTS, NULL, cmd_processor_get_enabled_slots, NULL },
|
||||
|
||||
|
||||
|
||||
{ DATA_CMD_SET_EM410X_EMU_ID, NULL, cmd_processor_set_em410x_emu_id, NULL },
|
||||
@@ -624,6 +744,15 @@ static cmd_data_map_t m_data_cmd_map[] = {
|
||||
{ DATA_CMD_GET_MF1_DETECTION_RESULT, NULL, cmd_processor_get_mf1_detection_log, NULL },
|
||||
{ DATA_CMD_LOAD_MF1_BLOCK_DATA, NULL, cmd_processor_set_mf1_emulator_block, NULL },
|
||||
{ DATA_CMD_SET_MF1_ANTI_COLLISION_RES, NULL, cmd_processor_set_mf1_anti_collision_res, NULL },
|
||||
{ DATA_CMD_GET_MF1_EMULATOR_CONFIG, NULL, cmd_processor_get_mf1_info, NULL },
|
||||
{ DATA_CMD_GET_MF1_GEN1A_MODE, NULL, cmd_processor_get_mf1_gen1a_magic_mode, NULL },
|
||||
{ DATA_CMD_SET_MF1_GEN1A_MODE, NULL, cmd_processor_set_mf1_gen1a_magic_mode, NULL },
|
||||
{ DATA_CMD_GET_MF1_GEN2_MODE, NULL, cmd_processor_get_mf1_gen2_magic_mode, NULL },
|
||||
{ DATA_CMD_SET_MF1_GEN2_MODE, NULL, cmd_processor_set_mf1_gen2_magic_mode, NULL },
|
||||
{ DATA_CMD_GET_MF1_USE_FIRST_BLOCK_COLL, NULL, cmd_processor_get_mf1_use_coll_res, NULL },
|
||||
{ DATA_CMD_SET_MF1_USE_FIRST_BLOCK_COLL, NULL, cmd_processor_set_mf1_use_coll_res, NULL },
|
||||
{ DATA_CMD_GET_MF1_WRITE_MODE, NULL, cmd_processor_get_mf1_write_mode, NULL },
|
||||
{ DATA_CMD_SET_MF1_WRITE_MODE, NULL, cmd_processor_set_mf1_write_mode, NULL },
|
||||
|
||||
{ DATA_CMD_SET_SLOT_TAG_NICK, NULL, cmd_processor_set_slot_tag_nick_name, NULL },
|
||||
{ DATA_CMD_GET_SLOT_TAG_NICK, NULL, cmd_processor_get_slot_tag_nick_name, NULL },
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#define DATA_CMD_GET_ACTIVE_SLOT (1018)
|
||||
#define DATA_CMD_GET_SLOT_INFO (1019)
|
||||
#define DATA_CMD_WIPE_FDS (1020)
|
||||
#define DATA_CMD_GET_ENABLED_SLOTS (1023)
|
||||
//
|
||||
// ******************************************************************
|
||||
|
||||
@@ -69,6 +70,17 @@
|
||||
//
|
||||
#define DATA_CMD_LOAD_MF1_BLOCK_DATA (4000)
|
||||
#define DATA_CMD_SET_MF1_ANTI_COLLISION_RES (4001)
|
||||
|
||||
#define DATA_CMD_GET_MF1_EMULATOR_CONFIG (4009)
|
||||
#define DATA_CMD_GET_MF1_GEN1A_MODE (4010)
|
||||
#define DATA_CMD_SET_MF1_GEN1A_MODE (4011)
|
||||
#define DATA_CMD_GET_MF1_GEN2_MODE (4012)
|
||||
#define DATA_CMD_SET_MF1_GEN2_MODE (4013)
|
||||
#define DATA_CMD_GET_MF1_USE_FIRST_BLOCK_COLL (4014)
|
||||
#define DATA_CMD_SET_MF1_USE_FIRST_BLOCK_COLL (4015)
|
||||
#define DATA_CMD_GET_MF1_WRITE_MODE (4016)
|
||||
#define DATA_CMD_SET_MF1_WRITE_MODE (4017)
|
||||
|
||||
//
|
||||
// ******************************************************************
|
||||
|
||||
|
||||
@@ -38,14 +38,14 @@ NRF_LOG_MODULE_REGISTER();
|
||||
#define CMD_AUTH_A 0x60
|
||||
#define CMD_AUTH_B 0x61
|
||||
#define CMD_AUTH_FRAME_SIZE 2 /* Bytes without CRCA */
|
||||
#define CMD_AUTH_RB_FRAME_SIZE 4 /* Bytes */
|
||||
#define CMD_AUTH_AB_FRAME_SIZE 8 /* Bytes */
|
||||
#define CMD_AUTH_BA_FRAME_SIZE 4 /* Bytes */
|
||||
#define CMD_AUTH_RB_FRAME_SIZE 4 /* Bytes */
|
||||
#define CMD_AUTH_AB_FRAME_SIZE 8 /* Bytes */
|
||||
#define CMD_AUTH_BA_FRAME_SIZE 4 /* Bytes */
|
||||
#define CMD_HALT 0x50
|
||||
#define CMD_HALT_FRAME_SIZE 2 /* Bytes without CRCA */
|
||||
#define CMD_HALT_FRAME_SIZE 2 /* Bytes without CRCA */
|
||||
#define CMD_READ 0x30
|
||||
#define CMD_READ_FRAME_SIZE 2 /* Bytes without CRCA */
|
||||
#define CMD_READ_RESPONSE_FRAME_SIZE 16 /* Bytes without CRCA */
|
||||
#define CMD_READ_RESPONSE_FRAME_SIZE 16 /* Bytes without CRCA */
|
||||
#define CMD_WRITE 0xA0
|
||||
#define CMD_WRITE_FRAME_SIZE 2 /* Bytes without CRCA */
|
||||
#define CMD_DECREMENT 0xC0
|
||||
@@ -54,8 +54,6 @@ NRF_LOG_MODULE_REGISTER();
|
||||
#define CMD_INCREMENT_FRAME_SIZE 2 /* Bytes without CRCA */
|
||||
#define CMD_RESTORE 0xC2
|
||||
#define CMD_RESTORE_FRAME_SIZE 2 /* Bytes without CRCA */
|
||||
#define CMD_SIG_READ 0xC2
|
||||
#define CMD_SIG_READ_FRAME_SIZE 1 /* Bytes without CRCA */
|
||||
#define CMD_TRANSFER 0xB0
|
||||
#define CMD_TRANSFER_FRAME_SIZE 2 /* Bytes without CRCA */
|
||||
|
||||
@@ -311,9 +309,18 @@ void ValueToBlock(uint8_t *Block, uint32_t Value) {
|
||||
/** @brief mf1获取一个随机数
|
||||
* @param nonce 随机数的Buffer
|
||||
*/
|
||||
void nfc_tag_mf1_random_nonce(uint8_t nonce[4]) {
|
||||
void nfc_tag_mf1_random_nonce(uint8_t nonce[4], bool isNested) {
|
||||
// 使用rand进行快速产生随机数,性能损耗较小
|
||||
num_to_bytes(rand(), 4, nonce);
|
||||
// isNested provides more randomness for hardnested attack
|
||||
if (isNested) {
|
||||
nonce[0] = rand() & 0xff;
|
||||
nonce[1] = rand() & 0xff;
|
||||
nonce[2] = rand() & 0xff;
|
||||
nonce[3] = rand() & 0xff;
|
||||
} else {
|
||||
// fast for most readers
|
||||
num_to_bytes(rand(), 4, nonce);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -494,7 +501,7 @@ void nfc_tag_mf1_state_handler(uint8_t* p_data, uint16_t szDataBits) {
|
||||
m_tag_trailer_info = (nfc_tag_mf1_trailer_info_t*)m_tag_information->memory[BlockEnd];
|
||||
|
||||
// 生成随机数
|
||||
nfc_tag_mf1_random_nonce(CardNonce);
|
||||
nfc_tag_mf1_random_nonce(CardNonce, false);
|
||||
|
||||
// 根据卡随机数预先计算读卡器应答
|
||||
for (uint8_t i = 0; i < sizeof(ReaderResponse); i++) {
|
||||
@@ -674,10 +681,10 @@ void nfc_tag_mf1_state_handler(uint8_t* p_data, uint16_t szDataBits) {
|
||||
case CMD_READ: {
|
||||
// 保存当前操作的块地址
|
||||
CurrentAddress = p_data[1];
|
||||
// 生成访问控制,用于下面的数据访问控制
|
||||
uint8_t Acc = abTrailorAccessConditions[ GetAccessCondition(CurrentAddress) ][ KeyInUse ];
|
||||
// 读取命令。从内存中读取数据并附加CRCA。注意:读取操作受到控制位的限制,但是目前我们只限制控制位的读取
|
||||
if ((p_data[1] < 128 && (p_data[1] & 3) == 3) || ((p_data[1] & 15) == 15)) {
|
||||
// 生成访问控制,用于下面的数据访问控制
|
||||
uint8_t Acc = abTrailorAccessConditions[ GetAccessCondition(CurrentAddress) ][ KeyInUse ];
|
||||
if ((CurrentAddress < 128 && (CurrentAddress & 3) == 3) || ((CurrentAddress & 15) == 15)) {
|
||||
// 清空一下buffer,避免缓存的数据影响到后续操作
|
||||
memset(m_tag_tx_buffer.tx_raw_buffer, 0x00, sizeof(m_tag_tx_buffer.tx_raw_buffer));
|
||||
// 让这块数据区域变成我们需要的尾部块类型
|
||||
@@ -717,7 +724,7 @@ void nfc_tag_mf1_state_handler(uint8_t* p_data, uint16_t szDataBits) {
|
||||
}
|
||||
case CMD_WRITE: {
|
||||
// 正常的卡不允许写block0,不然会被CUID防火墙识别到
|
||||
if (p_data[1] == 0x00) {
|
||||
if (p_data[1] == 0x00 && !m_tag_information->config.mode_gen2_magic) {
|
||||
// 直接重置14a的状态机,让标签休眠
|
||||
nfc_tag_14a_set_state(NFC_TAG_STATE_14A_HALTED);
|
||||
// 告知一下读头此操作不被允许
|
||||
@@ -823,7 +830,7 @@ void nfc_tag_mf1_state_handler(uint8_t* p_data, uint16_t szDataBits) {
|
||||
m_tag_trailer_info = (nfc_tag_mf1_trailer_info_t*)m_tag_information->memory[BlockEnd];
|
||||
|
||||
// 生成随机数
|
||||
nfc_tag_mf1_random_nonce(CardNonce);
|
||||
nfc_tag_mf1_random_nonce(CardNonce, true);
|
||||
|
||||
// 根据卡随机数预先计算读卡器响应
|
||||
for (uint8_t i = 0; i < sizeof(ReaderResponse); i++) {
|
||||
@@ -1041,7 +1048,7 @@ void nfc_tag_mf1_state_handler(uint8_t* p_data, uint16_t szDataBits) {
|
||||
/**
|
||||
* @brief 提供mifare标签必要的防冲突资源(仅提供指针)
|
||||
*/
|
||||
nfc_tag_14a_coll_res_referen_t* get_miafre_coll_res() {
|
||||
nfc_tag_14a_coll_res_referen_t* get_mifare_coll_res() {
|
||||
// 根据当前的互通配置,选择性的返回其中配置的数据,假设开启了数据互通,那么我们还需要确保当前模拟的卡是4BYTE的
|
||||
if (m_tag_information->config.use_mf1_coll_res && m_tag_information->res_coll.size == NFC_TAG_14A_UID_SINGLE_SIZE) {
|
||||
// 获得数据区域的厂商信息
|
||||
@@ -1119,7 +1126,7 @@ int nfc_tag_mf1_data_loadcb(tag_specific_type_t type, tag_data_buffer_t* buffer)
|
||||
m_tag_type = type;
|
||||
// 注册14a通信管理接口
|
||||
nfc_tag_14a_handler_t handler_for_14a = {
|
||||
.get_coll_res = get_miafre_coll_res,
|
||||
.get_coll_res = get_mifare_coll_res,
|
||||
.cb_state = nfc_tag_mf1_state_handler,
|
||||
.cb_reset = nfc_tag_mf1_reset_handler,
|
||||
};
|
||||
@@ -1166,6 +1173,7 @@ bool nfc_tag_mf1_data_factory(uint8_t slot, tag_specific_type_t tag_type) {
|
||||
|
||||
// default mf1 config
|
||||
p_mf1_information->config.mode_gen1a_magic = false;
|
||||
p_mf1_information->config.mode_gen2_magic = false;
|
||||
p_mf1_information->config.use_mf1_coll_res = false;
|
||||
p_mf1_information->config.mode_block_write = NFC_TAG_MF1_WRITE_NORMAL;
|
||||
p_mf1_information->config.detection_enable = false;
|
||||
@@ -1204,3 +1212,44 @@ void nfc_tag_mf1_detection_log_clear(void) {
|
||||
uint32_t nfc_tag_mf1_detection_log_count(void) {
|
||||
return m_auth_log.count;
|
||||
}
|
||||
|
||||
// Set gen1a magic mode
|
||||
void nfc_tag_mf1_set_gen1a_magic_mode(bool enable) {
|
||||
m_tag_information->config.mode_gen1a_magic = enable;
|
||||
}
|
||||
|
||||
// Is in gen1a magic mode?
|
||||
bool nfc_tag_mf1_is_gen1a_magic_mode(void) {
|
||||
return m_tag_information->config.mode_gen1a_magic;
|
||||
}
|
||||
|
||||
// Set gen2 magic mode
|
||||
void nfc_tag_mf1_set_gen2_magic_mode(bool enable) {
|
||||
m_tag_information->config.mode_gen2_magic = enable;
|
||||
}
|
||||
|
||||
// Is in gen2 magic mode?
|
||||
bool nfc_tag_mf1_is_gen2_magic_mode(void) {
|
||||
return m_tag_information->config.mode_gen2_magic;
|
||||
}
|
||||
|
||||
// Set anti collision data from block 0
|
||||
void nfc_tag_mf1_set_use_mf1_coll_res(bool enable) {
|
||||
m_tag_information->config.use_mf1_coll_res = enable;
|
||||
}
|
||||
|
||||
// Get is anti collision data from block 0
|
||||
bool nfc_tag_mf1_is_use_mf1_coll_res(void) {
|
||||
return m_tag_information->config.use_mf1_coll_res;
|
||||
}
|
||||
|
||||
// Set write mode
|
||||
void nfc_tag_mf1_set_write_mode(nfc_tag_mf1_write_mode_t write_mode) {
|
||||
m_tag_information->config.mode_block_write = write_mode;
|
||||
}
|
||||
|
||||
// Get write mode
|
||||
nfc_tag_mf1_write_mode_t nfc_tag_mf1_get_write_mode(void) {
|
||||
return m_tag_information->config.mode_block_write;
|
||||
}
|
||||
|
||||
|
||||
@@ -44,10 +44,10 @@ typedef enum {
|
||||
// mf1配置
|
||||
typedef struct {
|
||||
/**
|
||||
* 正常写入模式(根据当前的状态去正常写入,受控制位和后门卡影响)
|
||||
* 拒绝写入模式(类似控制位锁死,直接拒绝任何写入,返回nack)
|
||||
* 欺诈写入模式(表面上返回ack表示写入成功,其实连RAM都不写入)
|
||||
* 影子写入模式(写入到RAM里面,并且返回ack表示成功,但是不保存到flash里面)
|
||||
* Normal write mode (write normally according to the current state, affected by the control bit and the back door card)
|
||||
* Deny write mode (similar to control bit lock, directly reject any write, return nack)
|
||||
* Fraudulent writing mode (on the surface, returning ack indicates that the writing is successful, but in fact, even RAM is not written)
|
||||
* Shadow write mode (write to RAM, and return ack to indicate success, but not save to flash)
|
||||
* @see nfc_tag_mf1_write_mode_t
|
||||
*/
|
||||
nfc_tag_mf1_write_mode_t mode_block_write;
|
||||
@@ -66,8 +66,10 @@ typedef struct {
|
||||
* 使能侦测,将自动记录mf1的验证日志
|
||||
*/
|
||||
uint8_t detection_enable: 1;
|
||||
// Allow to write block 0 (CUID/gen2 mode)
|
||||
uint8_t mode_gen2_magic: 1;
|
||||
// 保留
|
||||
uint8_t reserved1: 5;
|
||||
uint8_t reserved1: 4;
|
||||
uint8_t reserved2;
|
||||
uint8_t reserved3;
|
||||
} nfc_tag_mf1_configure_t;
|
||||
@@ -140,6 +142,15 @@ void nfc_tag_mf1_set_detection_enable(bool enable);
|
||||
bool nfc_tag_mf1_is_detection_enable(void);
|
||||
void nfc_tag_mf1_detection_log_clear(void);
|
||||
uint32_t nfc_tag_mf1_detection_log_count(void);
|
||||
nfc_tag_14a_coll_res_referen_t* get_miafre_coll_res(void);
|
||||
nfc_tag_14a_coll_res_referen_t* get_mifare_coll_res(void);
|
||||
void nfc_tag_mf1_set_gen1a_magic_mode(bool enable);
|
||||
bool nfc_tag_mf1_is_gen1a_magic_mode(void);
|
||||
void nfc_tag_mf1_set_gen2_magic_mode(bool enable);
|
||||
bool nfc_tag_mf1_is_gen2_magic_mode(void);
|
||||
void nfc_tag_mf1_set_use_mf1_coll_res(bool enable);
|
||||
bool nfc_tag_mf1_is_use_mf1_coll_res(void);
|
||||
void nfc_tag_mf1_set_write_mode(nfc_tag_mf1_write_mode_t write_mode);
|
||||
nfc_tag_mf1_write_mode_t nfc_tag_mf1_get_write_mode(void);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
GNU_INSTALL_ROOT ?= /usr/local/gcc-arm-none-eabi-10.3-2021.10/bin/
|
||||
GNU_INSTALL_ROOT ?= /usr/bin/
|
||||
GNU_VERSION ?= 9.3.1
|
||||
GNU_PREFIX ?= arm-none-eabi
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import argparse
|
||||
import os
|
||||
import platform
|
||||
import sys
|
||||
import traceback
|
||||
@@ -10,9 +9,6 @@ import colorama
|
||||
import chameleon_cli_unit
|
||||
import os
|
||||
|
||||
if os.name == 'posix':
|
||||
import readline
|
||||
|
||||
ULTRA = r"""
|
||||
╦ ╦╦ ╔╦╗╦═╗╔═╗
|
||||
███████ ║ ║║ ║ ╠╦╝╠═╣
|
||||
@@ -43,10 +39,7 @@ def new_uint(unit_clz, help_msg):
|
||||
:param help_msg: unit usage
|
||||
:return: a dict...
|
||||
"""
|
||||
return {
|
||||
'unit': unit_clz,
|
||||
'help': help_msg,
|
||||
}
|
||||
return {'unit': unit_clz, 'help': help_msg}
|
||||
|
||||
|
||||
class ChameleonCLI:
|
||||
@@ -60,7 +53,7 @@ class ChameleonCLI:
|
||||
'connect': new_uint(chameleon_cli_unit.HWConnect, "Connect to chameleon by serial port"),
|
||||
'chipid': {
|
||||
'get': new_uint(chameleon_cli_unit.HWChipIdGet, "Get device chipset ID"),
|
||||
'help': "Device chipsed ID get"
|
||||
'help': "Device chipset ID get"
|
||||
},
|
||||
'address': {
|
||||
'get': new_uint(chameleon_cli_unit.HWAddressGet, "Get device address (used with Bluetooth)"),
|
||||
@@ -72,7 +65,7 @@ class ChameleonCLI:
|
||||
'help': "Device mode get/set"
|
||||
},
|
||||
'slot': {
|
||||
'info': new_uint(chameleon_cli_unit.HWSlotInfo, "Get information about slots"),
|
||||
'list': new_uint(chameleon_cli_unit.HWSlotList, "Get information about slots"),
|
||||
'change': new_uint(chameleon_cli_unit.HWSlotSet, "Set emulation tag slot activated."),
|
||||
'type': new_uint(chameleon_cli_unit.HWSlotTagType, "Set emulation tag type"),
|
||||
'init': new_uint(chameleon_cli_unit.HWSlotDataDefault, "Set emulation tag data to default"),
|
||||
@@ -87,7 +80,8 @@ class ChameleonCLI:
|
||||
'help': "Emulation tag slot.",
|
||||
},
|
||||
'version': new_uint(chameleon_cli_unit.HWVersion, "Get current device firmware version"),
|
||||
'dfu': new_uint(chameleon_cli_unit.HWDFU, "Restart application to bootloader mode(Not yet implement dfu)."),
|
||||
'dfu': new_uint(chameleon_cli_unit.HWDFU, "Restart application to bootloader mode(Not yet implement "
|
||||
"dfu)."),
|
||||
'settings': {
|
||||
'animation': {
|
||||
'get': new_uint(chameleon_cli_unit.HWSettingsAnimationGet, "Get current animation mode value"),
|
||||
@@ -98,7 +92,8 @@ class ChameleonCLI:
|
||||
'reset': new_uint(chameleon_cli_unit.HWSettingsReset, "Reset settings to default values"),
|
||||
'help': "Chameleon settings management"
|
||||
},
|
||||
'factory_reset': new_uint(chameleon_cli_unit.HWFactoryReset, "Wipe all data and return to factory settings"),
|
||||
'factory_reset': new_uint(chameleon_cli_unit.HWFactoryReset, "Wipe all data and return to factory "
|
||||
"settings"),
|
||||
'help': "hardware controller",
|
||||
},
|
||||
'hf': {
|
||||
@@ -110,15 +105,16 @@ class ChameleonCLI:
|
||||
'mf': {
|
||||
'nested': new_uint(chameleon_cli_unit.HFMFNested, "Mifare Classic nested recover key"),
|
||||
'darkside': new_uint(chameleon_cli_unit.HFMFDarkside, "Mifare Classic darkside recover key"),
|
||||
'rdbl': new_uint(chameleon_cli_unit.HFMFRDBL, "MiFARE Classic read one block"),
|
||||
'wrbl': new_uint(chameleon_cli_unit.HFMFWRBL, "MiFARE Classic write one block"),
|
||||
'rdbl': new_uint(chameleon_cli_unit.HFMFRDBL, "Mifare Classic read one block"),
|
||||
'wrbl': new_uint(chameleon_cli_unit.HFMFWRBL, "Mifare Classic write one block"),
|
||||
'detection': {
|
||||
'enable': new_uint(chameleon_cli_unit.HFMFDetectionEnable, "Detection enable"),
|
||||
'count': new_uint(chameleon_cli_unit.HFMFDetectionLogCount, "Detection log count"),
|
||||
'decrypt': new_uint(chameleon_cli_unit.HFMFDetectionDecrypt, "Download log and decrypt keys"),
|
||||
'help': "Mifare Classic detection log"
|
||||
},
|
||||
'sim': new_uint(chameleon_cli_unit.HFMFSim, "Simulation a mifare classic card"),
|
||||
'settings': new_uint(chameleon_cli_unit.HFMFSettings, "Settings of Mifare Classic emulator"),
|
||||
'sim': new_uint(chameleon_cli_unit.HFMFSim, "Simulate a Mifare Classic card"),
|
||||
'eload': new_uint(chameleon_cli_unit.HFMFELoad, "Load data to emulator memory"),
|
||||
'help': "Mifare Classic mini/1/2/4, attack/read/write"
|
||||
},
|
||||
@@ -154,12 +150,10 @@ class ChameleonCLI:
|
||||
"""
|
||||
cmds = cmd_str.split(" ")
|
||||
cmd_maps: dict or types.FunctionType = self.cmd_maps
|
||||
cmd_end = ""
|
||||
cmd_end_position = 0
|
||||
for cmd in cmds:
|
||||
if cmd in cmd_maps: # CMD found in map, we can continue find next
|
||||
cmd_maps = cmd_maps[cmd]
|
||||
cmd_end = cmd
|
||||
cmd_end_position += len(cmd) + 1
|
||||
else: # CMD not found
|
||||
break
|
||||
@@ -175,13 +169,15 @@ class ChameleonCLI:
|
||||
while True:
|
||||
# wait user input
|
||||
status = f"{colorama.Fore.GREEN}USB" if self.device_com.isOpen() else f"{colorama.Fore.RED}Offline"
|
||||
cmd_str = ""
|
||||
try:
|
||||
cmd_str = input(f"[{status}{colorama.Style.RESET_ALL}] chameleon --> ").strip()
|
||||
except EOFError:
|
||||
print("")
|
||||
closing = True
|
||||
|
||||
if closing or cmd_str == "exit" or cmd_str == "quit" or cmd_str.startswith('q', 0) or cmd_str.startswith('e', 0):
|
||||
if closing or cmd_str == "exit" or cmd_str == "quit" or cmd_str.startswith('q', 0) or cmd_str.startswith(
|
||||
'e', 0):
|
||||
print("Bye, thank you. ^.^ ")
|
||||
self.device_com.close()
|
||||
sys.exit(996)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -32,6 +32,8 @@ DATA_CMD_GET_SLOT_INFO = 1019
|
||||
|
||||
DATA_CMD_WIPE_FDS = 1020
|
||||
|
||||
DATA_CMD_GET_ENABLED_SLOTS = 1023
|
||||
|
||||
DATA_CMD_SCAN_14A_TAG = 2000
|
||||
DATA_CMD_MF1_SUPPORT_DETECT = 2001
|
||||
DATA_CMD_MF1_NT_LEVEL_DETECT = 2002
|
||||
@@ -49,11 +51,22 @@ DATA_CMD_WRITE_EM410X_TO_T5577 = 3001
|
||||
DATA_CMD_LOAD_MF1_BLOCK_DATA = 4000
|
||||
DATA_CMD_SET_MF1_ANTI_COLLISION_RES = 4001
|
||||
|
||||
DATA_CMD_GET_MF1_EMULATOR_CONFIG = 4009
|
||||
DATA_CMD_GET_MF1_GEN1A_MODE = 4010
|
||||
DATA_CMD_SET_MF1_GEN1A_MODE = 4011
|
||||
DATA_CMD_GET_MF1_GEN2_MODE = 4012
|
||||
DATA_CMD_SET_MF1_GEN2_MODE = 4013
|
||||
DATA_CMD_GET_MF1_USE_FIRST_BLOCK_COLL = 4014
|
||||
DATA_CMD_SET_MF1_USE_FIRST_BLOCK_COLL = 4015
|
||||
DATA_CMD_GET_MF1_WRITE_MODE = 4016
|
||||
DATA_CMD_SET_MF1_WRITE_MODE = 4017
|
||||
|
||||
DATA_CMD_SET_EM410X_EMU_ID = 5000
|
||||
DATA_CMD_SET_MF1_DETECTION_ENABLE = 5003
|
||||
DATA_CMD_GET_MF1_DETECTION_COUNT = 5004
|
||||
DATA_CMD_GET_MF1_DETECTION_RESULT = 5005
|
||||
|
||||
|
||||
@enum.unique
|
||||
class SlotNumber(enum.IntEnum):
|
||||
SLOT_1 = 1,
|
||||
@@ -66,7 +79,7 @@ class SlotNumber(enum.IntEnum):
|
||||
SLOT_8 = 8,
|
||||
|
||||
@staticmethod
|
||||
def to_fw(index: int): # can be int or SlotNumber
|
||||
def to_fw(index: int): # can be int or SlotNumber
|
||||
# SlotNumber() will raise error for us if index not in slot range
|
||||
return SlotNumber(index).value - 1
|
||||
|
||||
@@ -78,13 +91,12 @@ class SlotNumber(enum.IntEnum):
|
||||
|
||||
@enum.unique
|
||||
class TagSenseType(enum.IntEnum):
|
||||
# 无场感应
|
||||
TAG_SENSE_NO = 0,
|
||||
# 低频125khz场感应
|
||||
TAG_SENSE_LF = 1,
|
||||
# 高频13.56mhz场感应
|
||||
TAG_SENSE_HF = 2,
|
||||
|
||||
# Unknown
|
||||
TAG_SENSE_NO = 0
|
||||
# 125 kHz
|
||||
TAG_SENSE_LF = 1
|
||||
# 13.56 MHz
|
||||
TAG_SENSE_HF = 2
|
||||
|
||||
@staticmethod
|
||||
def list(exclude_unknown=True):
|
||||
@@ -100,18 +112,19 @@ class TagSenseType(enum.IntEnum):
|
||||
return "HF"
|
||||
return "None"
|
||||
|
||||
|
||||
@enum.unique
|
||||
class TagSpecificType(enum.IntEnum):
|
||||
# 特定的且必须存在的标志不存在的类型
|
||||
# Empty slot
|
||||
TAG_TYPE_UNKNOWN = 0
|
||||
# 125khz(ID卡)系列
|
||||
# 125 kHz(ID)cards
|
||||
TAG_TYPE_EM410X = 1
|
||||
# Mifare系列
|
||||
# Mifare Classic
|
||||
TAG_TYPE_MIFARE_Mini = 2
|
||||
TAG_TYPE_MIFARE_1024 = 3
|
||||
TAG_TYPE_MIFARE_2048 = 4
|
||||
TAG_TYPE_MIFARE_4096 = 5
|
||||
# NTAG系列
|
||||
# NTAG
|
||||
TAG_TYPE_NTAG_213 = 6
|
||||
TAG_TYPE_NTAG_215 = 7
|
||||
TAG_TYPE_NTAG_216 = 8
|
||||
@@ -143,6 +156,32 @@ class TagSpecificType(enum.IntEnum):
|
||||
return "Unknown"
|
||||
|
||||
|
||||
@enum.unique
|
||||
class MifareClassicWriteMode(enum.IntEnum):
|
||||
# Normal write
|
||||
NORMAL = 0
|
||||
# Send NACK to write attempts
|
||||
DEINED = 1
|
||||
# Acknowledge writes, but don't remember contents
|
||||
DECEIVE = 2
|
||||
# Store data to RAM, but not to ROM
|
||||
SHADOW = 3
|
||||
|
||||
@staticmethod
|
||||
def list():
|
||||
return list(map(int, MifareClassicWriteMode))
|
||||
|
||||
def __str__(self):
|
||||
if self == MifareClassicWriteMode.NORMAL:
|
||||
return "Normal"
|
||||
elif self == MifareClassicWriteMode.DEINED:
|
||||
return "Deined"
|
||||
elif self == MifareClassicWriteMode.DECEIVE:
|
||||
return "Deceive"
|
||||
elif self == MifareClassicWriteMode.SHADOW:
|
||||
return "Shadow"
|
||||
return "None"
|
||||
|
||||
|
||||
class BaseChameleonCMD:
|
||||
"""
|
||||
@@ -159,25 +198,25 @@ class BaseChameleonCMD:
|
||||
"""
|
||||
Get firmware version number(application)
|
||||
"""
|
||||
resp = self.device.send_cmd_sync(DATA_CMD_GET_APP_VERSION, 0x00, None)
|
||||
resp = self.device.send_cmd_sync(DATA_CMD_GET_APP_VERSION, 0x00)
|
||||
return int.from_bytes(resp.data, 'little')
|
||||
|
||||
|
||||
def get_device_chip_id(self) -> str:
|
||||
"""
|
||||
Get device chip id
|
||||
"""
|
||||
resp = self.device.send_cmd_sync(DATA_CMD_GET_DEVICE_CHIP_ID, 0x00, None)
|
||||
resp = self.device.send_cmd_sync(DATA_CMD_GET_DEVICE_CHIP_ID, 0x00)
|
||||
return resp.data.hex()
|
||||
|
||||
|
||||
def get_device_address(self) -> str:
|
||||
"""
|
||||
Get device address
|
||||
"""
|
||||
resp = self.device.send_cmd_sync(DATA_CMD_GET_DEVICE_ADDRESS, 0x00, None)
|
||||
resp = self.device.send_cmd_sync(DATA_CMD_GET_DEVICE_ADDRESS, 0x00)
|
||||
return resp.data[::-1].hex()
|
||||
|
||||
def get_git_version(self) -> str:
|
||||
resp = self.device.send_cmd_sync(DATA_CMD_GET_GIT_VERSION, 0x00, None)
|
||||
resp = self.device.send_cmd_sync(DATA_CMD_GET_GIT_VERSION, 0x00)
|
||||
return resp.data.decode('utf-8')
|
||||
|
||||
def is_reader_device_mode(self) -> bool:
|
||||
@@ -185,7 +224,7 @@ class BaseChameleonCMD:
|
||||
Get device mode, reader or tag
|
||||
:return: True is reader mode, else tag mode
|
||||
"""
|
||||
resp = self.device.send_cmd_sync(DATA_CMD_GET_DEVICE_MODE, 0x00, None)
|
||||
resp = self.device.send_cmd_sync(DATA_CMD_GET_DEVICE_MODE, 0x00)
|
||||
return True if resp.data[0] == 1 else False
|
||||
|
||||
def set_reader_device_mode(self, reader_mode: bool = True):
|
||||
@@ -201,21 +240,21 @@ class BaseChameleonCMD:
|
||||
扫描场内的14a标签
|
||||
:return:
|
||||
"""
|
||||
return self.device.send_cmd_sync(DATA_CMD_SCAN_14A_TAG, 0x00, None)
|
||||
return self.device.send_cmd_sync(DATA_CMD_SCAN_14A_TAG, 0x00)
|
||||
|
||||
def detect_mf1_support(self):
|
||||
"""
|
||||
检测是否是mifare classic标签
|
||||
:return:
|
||||
"""
|
||||
return self.device.send_cmd_sync(DATA_CMD_MF1_SUPPORT_DETECT, 0x00, None)
|
||||
return self.device.send_cmd_sync(DATA_CMD_MF1_SUPPORT_DETECT, 0x00)
|
||||
|
||||
def detect_mf1_nt_level(self):
|
||||
"""
|
||||
检测mifare classic的nt漏洞的等级
|
||||
:return:
|
||||
"""
|
||||
return self.device.send_cmd_sync(DATA_CMD_MF1_NT_LEVEL_DETECT, 0x00, None)
|
||||
return self.device.send_cmd_sync(DATA_CMD_MF1_NT_LEVEL_DETECT, 0x00)
|
||||
|
||||
def detect_darkside_support(self):
|
||||
"""
|
||||
@@ -315,7 +354,7 @@ class BaseChameleonCMD:
|
||||
读取EM410X的卡号
|
||||
:return:
|
||||
"""
|
||||
return self.device.send_cmd_sync(DATA_CMD_SCAN_EM410X_TAG, 0x00, None)
|
||||
return self.device.send_cmd_sync(DATA_CMD_SCAN_EM410X_TAG, 0x00)
|
||||
|
||||
def write_em_410x_to_t55xx(self, id_bytes: bytearray):
|
||||
"""
|
||||
@@ -324,10 +363,7 @@ class BaseChameleonCMD:
|
||||
:return:
|
||||
"""
|
||||
new_key = [0x20, 0x20, 0x66, 0x66]
|
||||
old_keys = [
|
||||
[0x51, 0x24, 0x36, 0x48],
|
||||
[0x19, 0x92, 0x04, 0x27],
|
||||
]
|
||||
old_keys = [[0x51, 0x24, 0x36, 0x48], [0x19, 0x92, 0x04, 0x27]]
|
||||
if len(id_bytes) != 5:
|
||||
raise ValueError("The id bytes length must equal 5")
|
||||
data = bytearray()
|
||||
@@ -342,19 +378,19 @@ class BaseChameleonCMD:
|
||||
Get slots info
|
||||
:return:
|
||||
"""
|
||||
return self.device.send_cmd_sync(DATA_CMD_GET_SLOT_INFO, 0x00, None)
|
||||
return self.device.send_cmd_sync(DATA_CMD_GET_SLOT_INFO, 0x00)
|
||||
|
||||
def get_active_slot(self):
|
||||
"""
|
||||
Get selected slot
|
||||
:return:
|
||||
"""
|
||||
return self.device.send_cmd_sync(DATA_CMD_GET_ACTIVE_SLOT, 0x00, None)
|
||||
return self.device.send_cmd_sync(DATA_CMD_GET_ACTIVE_SLOT, 0x00)
|
||||
|
||||
def set_slot_activated(self, slot_index: SlotNumber):
|
||||
"""
|
||||
设置当前激活使用的卡槽
|
||||
:param slot_index: 卡槽索引,从 1 - 8(不是从0下标开始)
|
||||
Set the card slot currently active for use
|
||||
:param slot_index: Card slot index
|
||||
:return:
|
||||
"""
|
||||
# SlotNumber() will raise error for us if slot_index not in slot range
|
||||
@@ -428,7 +464,7 @@ class BaseChameleonCMD:
|
||||
获取当前侦测记录的统计个数
|
||||
:return:
|
||||
"""
|
||||
return self.device.send_cmd_sync(DATA_CMD_GET_MF1_DETECTION_COUNT, 0x00, None)
|
||||
return self.device.send_cmd_sync(DATA_CMD_GET_MF1_DETECTION_COUNT, 0x00)
|
||||
|
||||
def get_mf1_detection_log(self, index: int):
|
||||
"""
|
||||
@@ -465,7 +501,7 @@ class BaseChameleonCMD:
|
||||
data.extend(atqa)
|
||||
data.extend(uid)
|
||||
return self.device.send_cmd_sync(DATA_CMD_SET_MF1_ANTI_COLLISION_RES, 0X00, data)
|
||||
|
||||
|
||||
def set_slot_tag_nick_name(self, slot: SlotNumber, sense_type: TagSenseType, name: bytes):
|
||||
"""
|
||||
设置MF1的模拟卡的防冲撞资源信息
|
||||
@@ -479,26 +515,61 @@ class BaseChameleonCMD:
|
||||
data.extend([SlotNumber.to_fw(slot), sense_type])
|
||||
data.extend(name)
|
||||
return self.device.send_cmd_sync(DATA_CMD_SET_SLOT_TAG_NICK, 0x00, data)
|
||||
|
||||
|
||||
def get_slot_tag_nick_name(self, slot: SlotNumber, sense_type: TagSenseType):
|
||||
"""
|
||||
设置MF1的模拟卡的防冲撞资源信息
|
||||
:param slot: 卡槽号码
|
||||
:param sense_type: 场类型
|
||||
:param name: 卡槽昵称
|
||||
:return:
|
||||
"""
|
||||
# SlotNumber() will raise error for us if slot not in slot range
|
||||
data = bytearray()
|
||||
data.extend([SlotNumber.to_fw(slot), sense_type])
|
||||
return self.device.send_cmd_sync(DATA_CMD_GET_SLOT_TAG_NICK, 0x00, data)
|
||||
|
||||
|
||||
def get_mf1_emulator_settings(self):
|
||||
"""
|
||||
Get array of Mifare Classic emulators settings:
|
||||
[0] - mf1_is_detection_enable (mfkey32)
|
||||
[1] - mf1_is_gen1a_magic_mode
|
||||
[2] - mf1_is_gen2_magic_mode
|
||||
[3] - mf1_is_use_mf1_coll_res (use UID/BCC/SAK/ATQA from 0 block)
|
||||
[4] - mf1_get_write_mode
|
||||
:return:
|
||||
"""
|
||||
return self.device.send_cmd_sync(DATA_CMD_GET_MF1_EMULATOR_CONFIG, 0x00)
|
||||
|
||||
def set_mf1_gen1a_mode(self, enabled: bool):
|
||||
"""
|
||||
Set gen1a magic mode
|
||||
"""
|
||||
return self.device.send_cmd_sync(DATA_CMD_SET_MF1_GEN1A_MODE, 0x00, bytearray([1 if enabled else 0]))
|
||||
|
||||
def set_mf1_gen2_mode(self, enabled: bool):
|
||||
"""
|
||||
Set gen2 magic mode
|
||||
"""
|
||||
return self.device.send_cmd_sync(DATA_CMD_SET_MF1_GEN2_MODE, 0x00, bytearray([1 if enabled else 0]))
|
||||
|
||||
def set_mf1_block_anti_coll_mode(self, enabled: bool):
|
||||
"""
|
||||
Set 0 block anti-collision data
|
||||
"""
|
||||
return self.device.send_cmd_sync(DATA_CMD_SET_MF1_ANTI_COLLISION_RES, 0x00, bytearray([1 if enabled else 0]))
|
||||
|
||||
def set_mf1_write_mode(self, mode: int):
|
||||
"""
|
||||
Set write mode
|
||||
"""
|
||||
return self.device.send_cmd_sync(DATA_CMD_SET_MF1_WRITE_MODE, 0x00, bytearray([mode]))
|
||||
|
||||
def update_slot_data_config(self):
|
||||
"""
|
||||
更新卡槽的配置和数据到flash中。
|
||||
:return:
|
||||
"""
|
||||
return self.device.send_cmd_sync(DATA_CMD_SLOT_DATA_CONFIG_SAVE, 0x00, None)
|
||||
return self.device.send_cmd_sync(DATA_CMD_SLOT_DATA_CONFIG_SAVE, 0x00)
|
||||
|
||||
def enter_dfu_mode(self):
|
||||
"""
|
||||
@@ -506,19 +577,25 @@ class BaseChameleonCMD:
|
||||
:return:
|
||||
"""
|
||||
return self.device.send_cmd_auto(DATA_CMD_ENTER_BOOTLOADER, 0x00, close=True)
|
||||
|
||||
|
||||
def get_settings_animation(self):
|
||||
"""
|
||||
Get animation mode value
|
||||
"""
|
||||
return self.device.send_cmd_sync(DATA_CMD_GET_ANIMATION_MODE, 0x00, None)
|
||||
|
||||
return self.device.send_cmd_sync(DATA_CMD_GET_ANIMATION_MODE, 0x00)
|
||||
|
||||
def get_enabled_slots(self):
|
||||
"""
|
||||
Get animation mode value
|
||||
"""
|
||||
return self.device.send_cmd_sync(DATA_CMD_GET_ENABLED_SLOTS, 0x00)
|
||||
|
||||
def set_settings_animation(self, value: int):
|
||||
"""
|
||||
Set animation mode value
|
||||
"""
|
||||
return self.device.send_cmd_sync(DATA_CMD_SET_ANIMATION_MODE, 0x00, bytearray([value]))
|
||||
|
||||
|
||||
def reset_settings(self):
|
||||
"""
|
||||
Reset settings stored in flash memory
|
||||
@@ -530,7 +607,7 @@ class BaseChameleonCMD:
|
||||
Store settings to flash memory
|
||||
"""
|
||||
return self.device.send_cmd_sync(DATA_CMD_SAVE_SETTINGS, 0x00)
|
||||
|
||||
|
||||
def factory_reset(self):
|
||||
"""
|
||||
Reset to factory settings
|
||||
@@ -578,8 +655,8 @@ class PositiveChameleonCMD(BaseChameleonCMD):
|
||||
return ret
|
||||
|
||||
def acquire_nested(self, block_known, type_known, key_known, block_target, type_target):
|
||||
ret = super(PositiveChameleonCMD, self).acquire_nested(
|
||||
block_known, type_known, key_known, block_target, type_target)
|
||||
ret = super(PositiveChameleonCMD, self).acquire_nested(block_known, type_known, key_known, block_target,
|
||||
type_target)
|
||||
self.check_status(ret.status, chameleon_status.Device.HF_TAG_OK)
|
||||
return ret
|
||||
|
||||
@@ -590,10 +667,7 @@ class PositiveChameleonCMD(BaseChameleonCMD):
|
||||
|
||||
def auth_mf1_key(self, block, type_value, key):
|
||||
ret = super(PositiveChameleonCMD, self).auth_mf1_key(block, type_value, key)
|
||||
self.check_status(ret.status, [
|
||||
chameleon_status.Device.HF_TAG_OK,
|
||||
chameleon_status.Device.MF_ERRAUTH,
|
||||
])
|
||||
self.check_status(ret.status, [chameleon_status.Device.HF_TAG_OK, chameleon_status.Device.MF_ERRAUTH])
|
||||
return ret
|
||||
|
||||
def read_mf1_block(self, block, type_value, key):
|
||||
@@ -621,17 +695,17 @@ class PositiveChameleonCMD(BaseChameleonCMD):
|
||||
self.check_status(ret.status, chameleon_status.Device.STATUS_DEVICE_SUCCESS)
|
||||
return ret
|
||||
|
||||
def set_slot_tag_type(self, slot_index: int, tag_type: TagSpecificType):
|
||||
def set_slot_tag_type(self, slot_index: SlotNumber, tag_type: TagSpecificType):
|
||||
ret = super(PositiveChameleonCMD, self).set_slot_tag_type(slot_index, tag_type)
|
||||
self.check_status(ret.status, chameleon_status.Device.STATUS_DEVICE_SUCCESS)
|
||||
return ret
|
||||
|
||||
def set_slot_data_default(self, slot_index: int, tag_type: TagSpecificType):
|
||||
def set_slot_data_default(self, slot_index: SlotNumber, tag_type: TagSpecificType):
|
||||
ret = super(PositiveChameleonCMD, self).set_slot_data_default(slot_index, tag_type)
|
||||
self.check_status(ret.status, chameleon_status.Device.STATUS_DEVICE_SUCCESS)
|
||||
return ret
|
||||
|
||||
def set_slot_enable(self, slot_index: int, enable: bool):
|
||||
def set_slot_enable(self, slot_index: SlotNumber, enable: bool):
|
||||
ret = super(PositiveChameleonCMD, self).set_slot_enable(slot_index, enable)
|
||||
self.check_status(ret.status, chameleon_status.Device.STATUS_DEVICE_SUCCESS)
|
||||
return ret
|
||||
@@ -656,17 +730,17 @@ class PositiveChameleonCMD(BaseChameleonCMD):
|
||||
self.check_status(ret.status, chameleon_status.Device.STATUS_DEVICE_SUCCESS)
|
||||
return ret
|
||||
|
||||
def set_mf1_anti_collision_res(self, sak: int, atqa: bytearray, uid: bytearray):
|
||||
def set_mf1_anti_collision_res(self, sak: bytearray, atqa: bytearray, uid: bytearray):
|
||||
ret = super(PositiveChameleonCMD, self).set_mf1_anti_collision_res(sak, atqa, uid)
|
||||
self.check_status(ret.status, chameleon_status.Device.STATUS_DEVICE_SUCCESS)
|
||||
return ret
|
||||
|
||||
def set_slot_tag_nick_name(self, slot: int, sense_type: int, name: bytes):
|
||||
|
||||
def set_slot_tag_nick_name(self, slot: SlotNumber, sense_type: TagSenseType, name: bytes):
|
||||
ret = super(PositiveChameleonCMD, self).set_slot_tag_nick_name(slot, sense_type, name)
|
||||
self.check_status(ret.status, chameleon_status.Device.STATUS_DEVICE_SUCCESS)
|
||||
return ret
|
||||
|
||||
def get_slot_tag_nick_name(self, slot: int, sense_type: int):
|
||||
|
||||
def get_slot_tag_nick_name(self, slot: SlotNumber, sense_type: TagSenseType):
|
||||
ret = super(PositiveChameleonCMD, self).get_slot_tag_nick_name(slot, sense_type)
|
||||
self.check_status(ret.status, chameleon_status.Device.STATUS_DEVICE_SUCCESS)
|
||||
return ret
|
||||
@@ -679,12 +753,12 @@ if __name__ == '__main__':
|
||||
cml = BaseChameleonCMD(dev)
|
||||
ver = cml.get_firmware_version()
|
||||
print(f"Firmware number of application: {ver}")
|
||||
id = cml.get_device_chip_id()
|
||||
print(f"Device chip id: {id}")
|
||||
|
||||
chip = cml.get_device_chip_id()
|
||||
print(f"Device chip id: {chip}")
|
||||
|
||||
# disconnect
|
||||
dev.close()
|
||||
|
||||
# nerver exit
|
||||
while True: pass
|
||||
|
||||
# never exit
|
||||
while True:
|
||||
pass
|
||||
|
||||
@@ -40,8 +40,6 @@ class ChameleonCom:
|
||||
Chameleon device base class
|
||||
Communication and Data frame implemented
|
||||
"""
|
||||
|
||||
baudrate = 115200
|
||||
data_frame_sof = 0x11
|
||||
data_max_length = 512
|
||||
|
||||
@@ -49,7 +47,7 @@ class ChameleonCom:
|
||||
"""
|
||||
Create a chameleon device instance
|
||||
"""
|
||||
self.serial_instance: serial.Serial = None
|
||||
self.serial_instance: serial.Serial | None = None
|
||||
self.send_data_queue = queue.Queue()
|
||||
self.wait_response_map = {}
|
||||
self.event_closing = threading.Event()
|
||||
@@ -72,7 +70,7 @@ class ChameleonCom:
|
||||
error = None
|
||||
try:
|
||||
# open serial port
|
||||
self.serial_instance = serial.Serial(port=port, baudrate=self.baudrate)
|
||||
self.serial_instance = serial.Serial(port=port, baudrate=115200)
|
||||
except Exception as e:
|
||||
error = e
|
||||
finally:
|
||||
@@ -80,7 +78,7 @@ class ChameleonCom:
|
||||
raise OpenFailException(error)
|
||||
try:
|
||||
self.serial_instance.dtr = 1 # must make dtr enable
|
||||
except Exception as e:
|
||||
except:
|
||||
# not all serial support dtr, e.g. virtual serial over BLE
|
||||
pass
|
||||
self.serial_instance.timeout = 0 # noblock
|
||||
@@ -89,9 +87,9 @@ class ChameleonCom:
|
||||
self.wait_response_map.clear()
|
||||
# Start a sub thread to process data
|
||||
self.event_closing.clear()
|
||||
threading.Thread(target=self.thread_data_receive, ).start()
|
||||
threading.Thread(target=self.thread_data_transfer, ).start()
|
||||
threading.Thread(target=self.thread_check_timeout, ).start()
|
||||
threading.Thread(target=self.thread_data_receive).start()
|
||||
threading.Thread(target=self.thread_data_transfer).start()
|
||||
threading.Thread(target=self.thread_check_timeout).start()
|
||||
return self
|
||||
|
||||
def check_open(self):
|
||||
@@ -200,11 +198,8 @@ class ChameleonCom:
|
||||
del self.wait_response_map[data_cmd]
|
||||
fn_call(data_cmd, data_status, data_response)
|
||||
else:
|
||||
self.wait_response_map[data_cmd]['response'] = Response(
|
||||
data_cmd,
|
||||
data_status,
|
||||
data_response
|
||||
)
|
||||
self.wait_response_map[data_cmd]['response'] = Response(data_cmd, data_status,
|
||||
data_response)
|
||||
else:
|
||||
print(f"No task wait process: ${data_cmd}")
|
||||
else:
|
||||
@@ -232,13 +227,9 @@ class ChameleonCom:
|
||||
task_close = task['close']
|
||||
# register to wait map
|
||||
if 'callback' in task and callable(task['callback']):
|
||||
self.wait_response_map[task_cmd] = {
|
||||
'callback': task['callback'] # The callback for this task
|
||||
}
|
||||
self.wait_response_map[task_cmd] = {'callback': task['callback']} # The callback for this task
|
||||
else:
|
||||
self.wait_response_map[task_cmd] = {
|
||||
'response': None,
|
||||
}
|
||||
self.wait_response_map[task_cmd] = {'response': None}
|
||||
# set start time
|
||||
start_time = time.time()
|
||||
self.wait_response_map[task_cmd]['start_time'] = start_time
|
||||
@@ -279,7 +270,6 @@ class ChameleonCom:
|
||||
:return: frame
|
||||
"""
|
||||
frame = bytearray()
|
||||
lrc = 0x00
|
||||
# sof and sof lrc byte
|
||||
frame.append(self.data_frame_sof)
|
||||
frame.append(self.lrc_calc(frame[0:1]))
|
||||
@@ -295,7 +285,8 @@ class ChameleonCom:
|
||||
frame.append(self.lrc_calc(frame))
|
||||
return frame
|
||||
|
||||
def send_cmd_auto(self, cmd: int, status: int, data: bytearray = None, callback=None, timeout: int = 3, close: bool = False):
|
||||
def send_cmd_auto(self, cmd: int, status: int, data: bytearray = None, callback=None, timeout: int = 3,
|
||||
close: bool = False):
|
||||
"""
|
||||
Send cmd to device
|
||||
:param timeout: wait response timeout
|
||||
@@ -303,6 +294,7 @@ class ChameleonCom:
|
||||
:param status: status(optional)
|
||||
:param callback: call on response
|
||||
:param data: bytes data
|
||||
:param close: close connection after executing
|
||||
:return:
|
||||
"""
|
||||
self.check_open()
|
||||
@@ -311,12 +303,7 @@ class ChameleonCom:
|
||||
del self.wait_response_map[cmd]
|
||||
# make data frame
|
||||
data_frame = self.make_data_frame_bytes(cmd, status, data)
|
||||
task = {
|
||||
'cmd': cmd,
|
||||
'frame': data_frame,
|
||||
'timeout': timeout,
|
||||
'close': close,
|
||||
}
|
||||
task = {'cmd': cmd, 'frame': data_frame, 'timeout': timeout, 'close': close}
|
||||
if callable(callback):
|
||||
task['callback'] = callback
|
||||
self.send_data_queue.put(task)
|
||||
|
||||
@@ -22,7 +22,7 @@ def parse_14a_scan_tag_result(data: bytearray):
|
||||
'uid_size': data[10],
|
||||
'uid_hex': data[0:data[10]].hex(),
|
||||
'sak_hex': hex(data[12]).lstrip('0x').rjust(2, '0'),
|
||||
'atqa_hex': data[13:15].hex().upper(),
|
||||
'atqa_hex': data[13:15].hex().upper()
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ def parse_nested_nt_acquire_group(data: bytearray):
|
||||
group.append({
|
||||
'nt': bytes_to_u32(data[i: i + 4]),
|
||||
'nt_enc': bytes_to_u32(data[i + 4: i + 8]),
|
||||
'par': data[i + 8],
|
||||
'par': data[i + 8]
|
||||
})
|
||||
i += 9
|
||||
return group
|
||||
@@ -62,32 +62,15 @@ def parse_darkside_acquire_result(data: bytearray):
|
||||
:return:
|
||||
"""
|
||||
return {
|
||||
'uid': bytes_to_u32(data[0 : 4]),
|
||||
'nt1': bytes_to_u32(data[4 : 8]),
|
||||
'par': bytes_to_u32(data[8 : 16]),
|
||||
'uid': bytes_to_u32(data[0: 4]),
|
||||
'nt1': bytes_to_u32(data[4: 8]),
|
||||
'par': bytes_to_u32(data[8: 16]),
|
||||
'ks1': bytes_to_u32(data[16: 24]),
|
||||
'nr': bytes_to_u32(data[24: 28]),
|
||||
'ar': bytes_to_u32(data[28: 32]),
|
||||
'ar': bytes_to_u32(data[28: 32])
|
||||
}
|
||||
|
||||
|
||||
"""
|
||||
// 验证的基础信息
|
||||
struct {
|
||||
uint8_t block;
|
||||
uint8_t is_keyb: 1;
|
||||
uint8_t is_nested: 1;
|
||||
// 空域,占位置用的
|
||||
uint8_t : 6;
|
||||
} cmd;
|
||||
// mfkey32必要参数
|
||||
uint8_t uid[4];
|
||||
uint8_t nt[4];
|
||||
uint8_t nr[4];
|
||||
uint8_t ar[4];
|
||||
"""
|
||||
|
||||
|
||||
def parse_mf1_detection_result(data: bytearray):
|
||||
"""
|
||||
From bytes parse detection param
|
||||
@@ -105,7 +88,7 @@ def parse_mf1_detection_result(data: bytearray):
|
||||
'uid': data[2 + pos: 6 + pos].hex(),
|
||||
'nt': data[6 + pos: 10 + pos].hex(),
|
||||
'nr': data[10 + pos: 14 + pos].hex(),
|
||||
'ar': data[14 + pos: 18 + pos].hex(),
|
||||
'ar': data[14 + pos: 18 + pos].hex()
|
||||
})
|
||||
pos += 18
|
||||
|
||||
@@ -127,5 +110,3 @@ def parse_mf1_detection_result(data: bytearray):
|
||||
result_map[uid][block][type_chr].append(item)
|
||||
|
||||
return result_map
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
|
||||
class MetaDevice(type):
|
||||
|
||||
def __iter__(self):
|
||||
for attr in dir(self):
|
||||
if not attr.startswith("__"):
|
||||
@@ -16,60 +14,59 @@ class MetaDevice(type):
|
||||
|
||||
|
||||
class Device(metaclass=MetaDevice):
|
||||
HF_TAG_OK = 0x00 # IC卡操作成功
|
||||
HF_TAG_NO = 0x01 # 没有发现IC卡
|
||||
HF_ERRSTAT = 0x02 # IC卡通信异常
|
||||
HF_ERRCRC = 0x03 # IC卡通信校验异常
|
||||
HF_COLLISION = 0x04 # IC卡冲突
|
||||
HF_ERRBCC = 0x05 # IC卡BCC错误
|
||||
MF_ERRAUTH = 0x06 # MF卡验证失败
|
||||
HF_ERRPARITY = 0x07 # IC卡奇偶校验错误
|
||||
|
||||
HF_TAG_OK = 0x00 # IC卡操作成功
|
||||
HF_TAG_NO = 0x01 # 没有发现IC卡
|
||||
HF_ERRSTAT = 0x02 # IC卡通信异常
|
||||
HF_ERRCRC = 0x03 # IC卡通信校验异常
|
||||
HF_COLLISION = 0x04 # IC卡冲突
|
||||
HF_ERRBCC = 0x05 # IC卡BCC错误
|
||||
MF_ERRAUTH = 0x06 # MF卡验证失败
|
||||
HF_ERRPARITY = 0x07 # IC卡奇偶校验错误
|
||||
#
|
||||
DARKSIDE_CANT_FIXED_NT = 0x20 # Darkside,无法固定随机数,这个情况可能出现在UID卡上
|
||||
DARKSIDE_LUCK_AUTH_OK = 0x21 # Darkside,直接验证成功了,可能刚好密钥是空的
|
||||
DARKSIDE_NACK_NO_SNED = 0x22 # Darkside,卡片不响应nack,可能是一张修复了nack逻辑漏洞的卡片
|
||||
DARKSIDE_TAG_CHANGED = 0x23 # Darkside,在运行darkside的过程中出现了卡片切换,可能信号问题,或者真的是两张卡迅速切换了
|
||||
NESTED_TAG_IS_STATIC = 0x24 # Nested,检测到卡片应答的随机数是固定的
|
||||
NESTED_TAG_IS_HARD = 0x25 # Nested,检测到卡片应答的随机数是不可预测的
|
||||
#
|
||||
LF_TAG_OK = 0x40 # 低频卡的一些操作成功!
|
||||
EM410X_TAG_NO_FOUND = 0x41 # 无法搜索到有效的EM410X标签
|
||||
#
|
||||
STATUS_PAR_ERR = 0x60 # BLE指令传递的参数错误,或者是调用某些函数传递的参数错误
|
||||
STATUS_DEVIEC_MODE_ERROR = 0x66 # 当前设备所处的模式错误,无法调用对应的API
|
||||
STATUS_INVALID_CMD = 0x67 # 无效的指令
|
||||
STATUS_DEVICE_SUCCESS = 0x68 # 设备相关操作成功执行
|
||||
STATUS_NOT_IMPLEMENTED = 0x69 # 调用了某些未实现的操作,属于开发者遗漏的错误
|
||||
STATUS_FLASH_WRITE_FAIL = 0x70 # flash写入失败
|
||||
STATUS_FLASH_READ_FAIL = 0x71 # flash读取失败
|
||||
DARKSIDE_CANT_FIXED_NT = 0x20 # Darkside,无法固定随机数,这个情况可能出现在UID卡上
|
||||
DARKSIDE_LUCK_AUTH_OK = 0x21 # Darkside,直接验证成功了,可能刚好密钥是空的
|
||||
DARKSIDE_NACK_NO_SEND = 0x22 # Darkside,卡片不响应nack,可能是一张修复了nack逻辑漏洞的卡片
|
||||
DARKSIDE_TAG_CHANGED = 0x23 # Darkside,在运行darkside的过程中出现了卡片切换,可能信号问题,或者真的是两张卡迅速切换了
|
||||
NESTED_TAG_IS_STATIC = 0x24 # Nested,检测到卡片应答的随机数是固定的
|
||||
NESTED_TAG_IS_HARD = 0x25 # Nested,检测到卡片应答的随机数是不可预测的
|
||||
|
||||
LF_TAG_OK = 0x40 # 低频卡的一些操作成功!
|
||||
EM410X_TAG_NO_FOUND = 0x41 # 无法搜索到有效的EM410X标签
|
||||
|
||||
STATUS_PAR_ERR = 0x60 # BLE指令传递的参数错误,或者是调用某些函数传递的参数错误
|
||||
STATUS_DEVICE_MODE_ERROR = 0x66 # 当前设备所处的模式错误,无法调用对应的API
|
||||
STATUS_INVALID_CMD = 0x67 # 无效的指令
|
||||
STATUS_DEVICE_SUCCESS = 0x68 # 设备相关操作成功执行
|
||||
STATUS_NOT_IMPLEMENTED = 0x69 # 调用了某些未实现的操作,属于开发者遗漏的错误
|
||||
STATUS_FLASH_WRITE_FAIL = 0x70 # flash写入失败
|
||||
STATUS_FLASH_READ_FAIL = 0x71 # flash读取失败
|
||||
|
||||
|
||||
message = {
|
||||
Device.HF_TAG_OK : "HF tag operation succeeded",
|
||||
Device.HF_TAG_NO : "HF tag no found or lost",
|
||||
Device.HF_ERRSTAT : "HF tag status error",
|
||||
Device.HF_ERRCRC : "HF tag data crc error",
|
||||
Device.HF_COLLISION : "HF tag collision",
|
||||
Device.HF_ERRBCC : "HF tag uid bcc error",
|
||||
Device.MF_ERRAUTH : "HF tag auth fail",
|
||||
Device.HF_ERRPARITY : "HF tag data parity error",
|
||||
Device.HF_TAG_OK: "HF tag operation succeeded",
|
||||
Device.HF_TAG_NO: "HF tag no found or lost",
|
||||
Device.HF_ERRSTAT: "HF tag status error",
|
||||
Device.HF_ERRCRC: "HF tag data crc error",
|
||||
Device.HF_COLLISION: "HF tag collision",
|
||||
Device.HF_ERRBCC: "HF tag uid bcc error",
|
||||
Device.MF_ERRAUTH: "HF tag auth fail",
|
||||
Device.HF_ERRPARITY: "HF tag data parity error",
|
||||
|
||||
Device.DARKSIDE_CANT_FIXED_NT : "Darkside Can't select a nt(PRNG is unpredictable)",
|
||||
Device.DARKSIDE_LUCK_AUTH_OK : "Darkside try to recover a default key",
|
||||
Device.DARKSIDE_NACK_NO_SNED : "Darkside can't make tag response nack(enc)",
|
||||
Device.DARKSIDE_TAG_CHANGED : "Darkside running, can't change tag",
|
||||
Device.NESTED_TAG_IS_STATIC : "StaticNested tag, not weak nested",
|
||||
Device.NESTED_TAG_IS_HARD : "HardNested tag, not weak nested",
|
||||
Device.DARKSIDE_CANT_FIXED_NT: "Darkside Can't select a nt(PRNG is unpredictable)",
|
||||
Device.DARKSIDE_LUCK_AUTH_OK: "Darkside try to recover a default key",
|
||||
Device.DARKSIDE_NACK_NO_SEND: "Darkside can't make tag response nack(enc)",
|
||||
Device.DARKSIDE_TAG_CHANGED: "Darkside running, can't change tag",
|
||||
Device.NESTED_TAG_IS_STATIC: "StaticNested tag, not weak nested",
|
||||
Device.NESTED_TAG_IS_HARD: "HardNested tag, not weak nested",
|
||||
|
||||
Device.LF_TAG_OK : "LF tag operation succeeded",
|
||||
Device.EM410X_TAG_NO_FOUND : "EM410x tag no found",
|
||||
Device.LF_TAG_OK: "LF tag operation succeeded",
|
||||
Device.EM410X_TAG_NO_FOUND: "EM410x tag no found",
|
||||
|
||||
Device.STATUS_PAR_ERR : "API request fail, param error",
|
||||
Device.STATUS_DEVIEC_MODE_ERROR : "API request fail, device mode error",
|
||||
Device.STATUS_INVALID_CMD : "API request fail, cmd invalid",
|
||||
Device.STATUS_DEVICE_SUCCESS : "Device operation succeeded",
|
||||
Device.STATUS_NOT_IMPLEMENTED : "Some api not implemented",
|
||||
Device.STATUS_FLASH_WRITE_FAIL : "Flash write failed",
|
||||
Device.STATUS_FLASH_READ_FAIL : "Flash read failed"
|
||||
Device.STATUS_PAR_ERR: "API request fail, param error",
|
||||
Device.STATUS_DEVICE_MODE_ERROR: "API request fail, device mode error",
|
||||
Device.STATUS_INVALID_CMD: "API request fail, cmd invalid",
|
||||
Device.STATUS_DEVICE_SUCCESS: "Device operation succeeded",
|
||||
Device.STATUS_NOT_IMPLEMENTED: "Some api not implemented",
|
||||
Device.STATUS_FLASH_WRITE_FAIL: "Flash write failed",
|
||||
Device.STATUS_FLASH_READ_FAIL: "Flash read failed"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user