2022-11-23 08:58:03 +08:00
|
|
|
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 =[[
|
|
|
|
|
2023-01-07 06:53:25 +08:00
|
|
|
This script can be used to read blocks, write blocks, dump sectors,
|
2022-11-23 08:58:03 +08:00
|
|
|
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:
|
2022-11-23 09:09:37 +08:00
|
|
|
script run hf_i2c_plus_2k_utils -m r -s 1 -b 04
|
2023-01-07 06:53:25 +08:00
|
|
|
|
2022-11-23 08:58:03 +08:00
|
|
|
Write FFFFFFFF to block A0 sector 1:
|
2022-11-23 09:09:37 +08:00
|
|
|
script run hf_i2c_plus_2k_utils -m w -s 1 -b A0 -d FFFFFFFF
|
2022-11-23 08:58:03 +08:00
|
|
|
|
|
|
|
Dump sector 1 user memory to console and file:
|
2022-11-23 09:09:37 +08:00
|
|
|
script run hf_i2c_plus_2k_utils -m d -s 1
|
2022-11-23 08:58:03 +08:00
|
|
|
|
|
|
|
Write a files hex bytes to sector 1 starting at block 04:
|
2022-11-23 09:09:37 +08:00
|
|
|
script run hf_i2c_plus_2k_utils -m f -s 1 -f data.txt
|
2022-11-23 08:58:03 +08:00
|
|
|
|
|
|
|
]]
|
|
|
|
usage = [[
|
|
|
|
|
2023-01-07 06:53:25 +08:00
|
|
|
Read mode:
|
2022-11-23 09:09:37 +08:00
|
|
|
script run hf_i2c_plus_2k_utils -m r -s <sector> -b <block (hex)>
|
2023-01-07 06:53:25 +08:00
|
|
|
|
|
|
|
Write mode:
|
2022-11-23 09:09:37 +08:00
|
|
|
script run hf_i2c_plus_2k_utils -m w -s <sector> -b <block (hex)> -d <data (hex)>
|
2023-01-07 06:53:25 +08:00
|
|
|
|
2022-11-23 08:58:03 +08:00
|
|
|
Dump mode:
|
2022-11-23 09:09:37 +08:00
|
|
|
script run hf_i2c_plus_2k_utils -m d -s <sector>
|
2023-01-07 06:53:25 +08:00
|
|
|
|
2022-11-23 08:58:03 +08:00
|
|
|
File mode:
|
2022-11-23 09:09:37 +08:00
|
|
|
script run hf_i2c_plus_2k_utils -m f -s <sector> -f <file>
|
2022-11-23 08:58:03 +08:00
|
|
|
|
|
|
|
]]
|
|
|
|
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
|
|
|
|
---
|
|
|
|
|
|
|
|
|
2023-01-07 06:53:25 +08:00
|
|
|
--- Function to connect
|
2022-11-23 08:58:03 +08:00
|
|
|
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
|
2023-01-07 06:53:25 +08:00
|
|
|
end_block = 225
|
2022-11-23 08:58:03 +08:00
|
|
|
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
|
2023-01-07 06:53:25 +08:00
|
|
|
|
2022-11-23 08:58:03 +08:00
|
|
|
else
|
|
|
|
return print(usage)
|
|
|
|
end
|
|
|
|
|
|
|
|
if command == '' then return print(usage) end
|
|
|
|
|
|
|
|
end
|
|
|
|
---
|
|
|
|
|
|
|
|
|
2022-11-23 09:09:37 +08:00
|
|
|
main(args)
|