From 54586180537abbf17fe86ba87c51279d5d5da4f2 Mon Sep 17 00:00:00 2001 From: Shain Lakin Date: Wed, 23 Nov 2022 08:58:03 +0800 Subject: [PATCH] Added new script for modifying user memory in sector 0 and 1 on the NTAG I2C PLUS 2K tag. Can also be used to dump a sector to disk, and write a file to a sector. --- CHANGELOG.md | 1 + client/luascripts/hf_i2c_plus_2k_utils.lua | 310 +++++++++++++++++++++ 2 files changed, 311 insertions(+) create mode 100644 client/luascripts/hf_i2c_plus_2k_utils.lua diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d57d2b09..d06091bab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 luascript `hf_i2c_plus_2k_utils` - Script for dumping/modifying user memory of sectors 0 and 1 (@flamebarke) - Added `hf mfu esave` - saves emulator memory to mfu dump file (@DidierA) - Added luascript `hf_mfu_ntag` - Script for configuring NTAG216 configuration pages (@flamebarke) - Changed `hf mf hardnested` - a detection for static encrypted nonces (@iceman1001) diff --git a/client/luascripts/hf_i2c_plus_2k_utils.lua b/client/luascripts/hf_i2c_plus_2k_utils.lua new file mode 100644 index 000000000..1816feb46 --- /dev/null +++ b/client/luascripts/hf_i2c_plus_2k_utils.lua @@ -0,0 +1,310 @@ +local getopt = require('getopt') +local lib14a = require('read14a') +local cmds = require('commands') +local utils = require('utils') +local ansicolors = require('ansicolors') + +--- Commands +NTAG_I2C_PLUS_2K = '0004040502021503C859' +GET_VERSION = '60' +SELECT_SECTOR_PKT1 = 'C2FF' +SELECT_SECTOR0_PKT2 = '00000000' +SELECT_SECTOR1_PKT2 = '01000000' +READ_BLOCK = '30' +WRITE_BLOCK = 'A2' +ACK = '0A' +NAK = '00' +--- + + +--- Arguments +copyright = '' +author = 'Shain Lakin' +version = 'v1.0.0' +desc =[[ + +This script can be used to read blocks, write blocks, dump sectors, +or write a files hex bytes to sector 0 or 1 on the NTAG I2C PLUS 2K tag. + +]] + +example =[[ + + Read block 04 from sector 1: + script run hf_ntagi2c_plus2k -m r -s 1 -b 04 + + Write FFFFFFFF to block A0 sector 1: + script run hf_ntagi2c_plus2k -m w -s 1 -b A0 -d FFFFFFFF + + Dump sector 1 user memory to console and file: + script run hf_ntag12c_plus2k -m d -s 1 + + Write a files hex bytes to sector 1 starting at block 04: + script run hf_ntagi2c_plus2k -m f -s 1 -f data.txt + +]] +usage = [[ + + Read mode: + script run hf_ntagi2c_plus2k -m r -s -b + + Write mode: + script run hf_ntagi2c_plus2k -m w -s -b -d + + Dump mode: + script run hf_ntagi2c_plus2k -m d -s + + File mode: + script run hf_ntagi2c_plus2k -m f -s -f + +]] +arguments = [[ + -h this help + -m mode (r/w/f) + -b block (hex) + -f file + -s sector (0/1) + -d data (hex) +]] +--- + + +--- Help function +local function help() + + print(copyright) + print(author) + print(version) + print(desc) + print(ansicolors.cyan..'Usage'..ansicolors.reset) + print(usage) + print(ansicolors.cyan..'Arguments'..ansicolors.reset) + print(arguments) + print(ansicolors.cyan..'Example usage'..ansicolors.reset) + print(example) + +end +--- + + +--- Message function +local function msg(string) + print(ansicolors.magenta..string.rep('-',29)..ansicolors.reset) + print(ansicolors.cyan..string..ansicolors.reset) + print(ansicolors.magenta..string.rep('-',29)..ansicolors.reset) +end +--- + + +--- Error handling +local function warn(err) + + print(ansicolors.magenta.."ERROR:"..ansicolors.reset,err) + core.clearCommandBuffer() + return nil, err + +end +--- + + +--- Setup tx/rx +local function sendRaw(rawdata, options) + + local flags = lib14a.ISO14A_COMMAND.ISO14A_NO_DISCONNECT + + lib14a.ISO14A_COMMAND.ISO14A_RAW + + lib14a.ISO14A_COMMAND.ISO14A_APPEND_CRC + + local c = Command:newMIX{cmd = cmds.CMD_HF_ISO14443A_READER, + arg1 = flags, + arg2 = string.len(rawdata)/2, + data = rawdata} + + return c:sendMIX(options.ignore_response) +end +--- + + +--- Function to connect +local function connect() + core.clearCommandBuffer() + + info, err = lib14a.read(true, true) + if err then + lib14a.disconnect() + return error(err) + else + return info.uid + end + core.clearCommandBuffer() + +end +--- + + +--- Function to disconnect +local function disconnect() + core.clearCommandBuffer() + lib14a.disconnect() +end +--- + + +--- Function to get response data +local function getResponseData(usbpacket) + + local resp = Command.parse(usbpacket) + local len = tonumber(resp.arg1) * 2 + return string.sub(tostring(resp.data), 0, len); + +end +--- + + +--- Function to send raw bytes +local function send(payload) + + local usb, err = sendRaw(payload,{ignore_response = false}) + if err then return warn(err) end + return getResponseData(usb) + +end +--- + +--- Function to select sector +local function select_sector(sector) + send(SELECT_SECTOR_PKT1) + if sector == '0' then + send(SELECT_SECTOR0_PKT2) + elseif sector == '1' then + send(SELECT_SECTOR1_PKT2) + end +end +--- + +--- Function to write file to sector +local function filewriter(file,sector) + file_bytes = utils.ReadDumpFile(file) + len = string.len(file_bytes) / 8 + start_char = 1 + end_char = 8 + block_counter = 4 + -- NTAG_I2C_PLUS_2K:SECTOR_0:225,SECTOR_1:255 + end_block = 225 + connect() + select_sector(sector) + for count = 1, len do + block = file_bytes:sub(start_char, end_char) + data = send(WRITE_BLOCK..string.format("%02x",block_counter)..block) + print('[*] Writing bytes '..block..' to page '..string.format("%02x", block_counter)) + if data == ACK then + print(ansicolors.cyan..'[*] Received ACK, write successful'..ansicolors.reset) + else + print(ansicolors.magenta..'[!] Write failed'..ansicolors.reset) + end + start_char = start_char + 8 + end_char = end_char + 8 + block_counter = block_counter + 1 + if block_counter == end_block then + print(ansicolors.magenta..'[!] Not enough memory space!'..ansicolors.reset) + break + end + end + disconnect() +end +--- + +--- Function to dump user memory to console and disk +local function dump(sector,uid) + connect() + select_sector(sector) + counter = 0 + dest = uid..'.hex' + file = io.open(dest, 'a') + io.output(file) + print("\n[+] Dumping sector "..sector.."\n") + print(ansicolors.magenta..string.rep('--',16)..ansicolors.reset) + for count = 1, 64 do + result = send(READ_BLOCK..string.format("%02x", counter)) + print(ansicolors.cyan..result:sub(1,32)..ansicolors.reset) + io.write(result:sub(1,32)) + counter = counter + 4 + end + io.close(file) + print(ansicolors.magenta..string.rep('--',16)..ansicolors.reset) + print("\n[+] Memory dump saved to "..uid..".hex") + disconnect() +end +--- + + +--- Function to read and write blocks +local function exec(cmd, sector, block, bytes) + connect() + select_sector(sector) + if cmd == READ_BLOCK then + data = send(cmd..block) + msg(data:sub(1,8)) + elseif cmd == WRITE_BLOCK then + if bytes == 'NOP' then + err = '[!] You need to pass some data' + warn(err) + print(usage) + do return end + else + data = send(cmd..block..bytes) + if data == ACK then + print(ansicolors.cyan..'[+] Received ACK, write succesful'..ansicolors.reset) + elseif data ~= ACK then + print(ansicolors.magenta..'[!] Write failed'..ansicolors.reset) + end + end + end + disconnect() + return(data) +end +--- + + +--- Main +local function main(args) + + for o, a in getopt.getopt(args, 'm:b:s:d:f:h') do + if o == 'm' then mode = a end + if o == 'b' then block = a end + if o == 's' then sector = a end + if o == 'd' then bytes = a end + if o == 'f' then file = a end + if o == 'h' then return help() end + end + + uid = connect() + + connect() + version = send(GET_VERSION) + disconnect() + + if version == NTAG_I2C_PLUS_2K then + + if mode == 'r' then + print('\n[+] Reading sector '..sector..' block '..block) + exec(READ_BLOCK,sector,block,bytes) + elseif mode == 'w' then + print('\n[+] Writing '..bytes..' to sector '..sector..' block '..block) + exec(WRITE_BLOCK,sector,block,bytes) + elseif mode == 'f' then + filewriter(file,sector) + elseif mode == 'd' then + dump(sector,uid) + end + + else + return print(usage) + end + + if command == '' then return print(usage) end + +end +--- + + +main(args) \ No newline at end of file