mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-02-27 17:43:07 +08:00
ok
Merge branch 'master' of https://github.com/RfidResearchGroup/proxmark3
This commit is contained in:
commit
23b6756ac7
3 changed files with 314 additions and 3 deletions
309
client/luascripts/luxeodump.lua
Normal file
309
client/luascripts/luxeodump.lua
Normal file
|
@ -0,0 +1,309 @@
|
||||||
|
---
|
||||||
|
-- This Lua script is designed to run with Iceman/RRG Proxmark3 fork
|
||||||
|
-- Just copy luxeodump.lua to client/luascripts/
|
||||||
|
-- and run "script run luxeodump"
|
||||||
|
|
||||||
|
-- requirements
|
||||||
|
local cmds = require('commands')
|
||||||
|
local getopt = require('getopt')
|
||||||
|
local utils = require('utils')
|
||||||
|
local lib14a = require('read14a')
|
||||||
|
local ansicolors = require('ansicolors')
|
||||||
|
|
||||||
|
copyright = ''
|
||||||
|
author = '0xdrrb'
|
||||||
|
version = 'v0.1.0'
|
||||||
|
desc = [[
|
||||||
|
This is a script to dump and decrypt the data of a specific type of Mifare laundromat token.
|
||||||
|
]]
|
||||||
|
example = [[
|
||||||
|
script run luxeodump
|
||||||
|
]]
|
||||||
|
usage = [[
|
||||||
|
script run luxeodump
|
||||||
|
]]
|
||||||
|
|
||||||
|
local PM3_SUCCESS = 0
|
||||||
|
|
||||||
|
-- Some shortcuts
|
||||||
|
local band = bit32.band
|
||||||
|
local bor = bit32.bor
|
||||||
|
local bnot = bit32.bnot
|
||||||
|
local bxor = bit32.bxor
|
||||||
|
local lsh = bit32.lshift
|
||||||
|
local rsh = bit32.rshift
|
||||||
|
|
||||||
|
local acgreen = ansicolors.bright..ansicolors.green
|
||||||
|
local accyan = ansicolors.bright..ansicolors.cyan
|
||||||
|
local acred = ansicolors.red
|
||||||
|
local acyellow = ansicolors.bright..ansicolors.yellow
|
||||||
|
local acblue = ansicolors.bright..ansicolors.blue
|
||||||
|
local acmagenta = ansicolors.bright..ansicolors.magenta
|
||||||
|
local acoff = ansicolors.reset
|
||||||
|
|
||||||
|
|
||||||
|
-- This is only meant to be used when errors occur
|
||||||
|
local function oops(err)
|
||||||
|
print('ERROR: ', err)
|
||||||
|
core.clearCommandBuffer()
|
||||||
|
return nil, err
|
||||||
|
end
|
||||||
|
|
||||||
|
local function setdevicedebug( status )
|
||||||
|
local c = 'hw dbg '
|
||||||
|
if status then
|
||||||
|
c = c..'1'
|
||||||
|
else
|
||||||
|
c = c..'0'
|
||||||
|
end
|
||||||
|
core.console(c)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function xteaCrypt(num_rounds, v, key)
|
||||||
|
local v0 = v[0]
|
||||||
|
local v1 = v[1]
|
||||||
|
local delta = 0x9E3779B9
|
||||||
|
local sum = 0
|
||||||
|
|
||||||
|
for i = 0, num_rounds-1 do
|
||||||
|
-- v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
|
||||||
|
v0 = band(bxor(bxor(lsh(v1,4), rsh(v1,5)) + v1, sum + key[band(sum,3)]) + v0, 0xFFFFFFFF)
|
||||||
|
sum = band(sum + delta, 0xFFFFFFFF)
|
||||||
|
-- v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);
|
||||||
|
v1 = band(bxor(bxor(lsh(v0,4), rsh(v0,5)) + v0, sum + key[band(rsh(sum,11),3)]) + v1, 0xFFFFFFFF)
|
||||||
|
end
|
||||||
|
v[0]=v0
|
||||||
|
v[1]=v1
|
||||||
|
end
|
||||||
|
|
||||||
|
local function xteaDecrypt(num_rounds, v, key)
|
||||||
|
local v0 = v[0]
|
||||||
|
local v1 = v[1]
|
||||||
|
local delta = 0x9E3779B9
|
||||||
|
local sum = band(delta * num_rounds, 0xFFFFFFFF)
|
||||||
|
|
||||||
|
for i = 0, num_rounds-1 do
|
||||||
|
-- v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);
|
||||||
|
v1 = band(v1 - bxor(bxor(lsh(v0,4), rsh(v0,5)) + v0, sum + key[band(rsh(sum,11),3)]), 0xFFFFFFFF)
|
||||||
|
sum = band(sum - delta, 0xFFFFFFFF)
|
||||||
|
-- v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
|
||||||
|
v0 = band(v0 - bxor(bxor(lsh(v1,4), rsh(v1,5)) + v1, sum + key[band(sum,3)]), 0xFFFFFFFF)
|
||||||
|
end
|
||||||
|
v[0]=v0
|
||||||
|
v[1]=v1
|
||||||
|
end
|
||||||
|
|
||||||
|
local function createxteakey(mfuid)
|
||||||
|
local xteakey = {}
|
||||||
|
local buid = {}
|
||||||
|
local tmpkey = {}
|
||||||
|
local uid = {}
|
||||||
|
|
||||||
|
-- Warning ! "it is customary in Lua to START ARRAYS WITH ONE"
|
||||||
|
buid = utils.ConvertHexToBytes(mfuid)
|
||||||
|
uid[0] = bor(buid[1], lsh(buid[2], 8))
|
||||||
|
uid[1] = bor(buid[3], lsh(buid[4], 8))
|
||||||
|
|
||||||
|
tmpkey[0] = 0x198B
|
||||||
|
tmpkey[1] = uid[0]
|
||||||
|
tmpkey[2] = 0x46D8
|
||||||
|
tmpkey[3] = uid[1]
|
||||||
|
tmpkey[4] = 0x5310
|
||||||
|
tmpkey[5] = bxor(uid[0], 0xA312)
|
||||||
|
tmpkey[6] = 0xFFCB
|
||||||
|
tmpkey[7] = bxor(uid[1], 0x55AA)
|
||||||
|
|
||||||
|
xteakey[0] = bor(lsh(tmpkey[1], 16), tmpkey[0])
|
||||||
|
xteakey[1] = bor(lsh(tmpkey[3], 16), tmpkey[2])
|
||||||
|
xteakey[2] = bor(lsh(tmpkey[5], 16), tmpkey[4])
|
||||||
|
xteakey[3] = bor(lsh(tmpkey[7], 16), tmpkey[6])
|
||||||
|
|
||||||
|
return xteakey
|
||||||
|
end
|
||||||
|
|
||||||
|
-- CRC16/ARC from core.reveng_runmodel() does not seem to return the right values.
|
||||||
|
-- So here is an implementation in Lua.
|
||||||
|
local function bitreflect(data, nbits)
|
||||||
|
local output = 0
|
||||||
|
for i = 0, nbits-1 do
|
||||||
|
if bit.band(data,1) ~= 0 then
|
||||||
|
output = bit32.bor(output, bit32.lshift(1,((nbits - 1) - i)))
|
||||||
|
else
|
||||||
|
end
|
||||||
|
data = bit32.rshift(data,1)
|
||||||
|
end
|
||||||
|
return output
|
||||||
|
end
|
||||||
|
|
||||||
|
local function crc16arc(s)
|
||||||
|
assert(type(s) == 'string')
|
||||||
|
local crc = 0x0000
|
||||||
|
for i = 1, #s do
|
||||||
|
local c = s:byte(i)
|
||||||
|
dbyte = bitreflect(c, 8)
|
||||||
|
crc = bit32.bxor(crc, bit32.lshift(dbyte,8))
|
||||||
|
for j = 0, 7 do
|
||||||
|
local mix = bit32.band(crc, 0x8000)
|
||||||
|
crc = bit32.lshift(crc,1)
|
||||||
|
if mix ~= 0 then
|
||||||
|
crc = bit32.bxor(crc, 0x8005)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return bitreflect(crc, 16)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function getblockdata(response)
|
||||||
|
if not response then
|
||||||
|
return nil, 'No response from device'
|
||||||
|
end
|
||||||
|
if response.Status == PM3_SUCCESS then
|
||||||
|
return response.Data
|
||||||
|
else
|
||||||
|
return nil, "Couldn't read block.. ["..response.Status.."]"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function readblock(blockno, key)
|
||||||
|
-- Read block N
|
||||||
|
local keytype = '01' -- key B
|
||||||
|
local data = ('%02x%s%s'):format(blockno, keytype, key)
|
||||||
|
local c = Command:newNG{cmd = cmds.CMD_HF_MIFARE_READBL, data = data}
|
||||||
|
local b, err = getblockdata(c:sendNG(false))
|
||||||
|
if not b then return oops(err) end
|
||||||
|
return b
|
||||||
|
end
|
||||||
|
|
||||||
|
local function readtag(mfkey,xteakey)
|
||||||
|
local tagdata = {}
|
||||||
|
local cleardata = {}
|
||||||
|
local v = {}
|
||||||
|
local vv = {}
|
||||||
|
|
||||||
|
-- Read 4 sectors and build table
|
||||||
|
for sect = 8, 11 do
|
||||||
|
for blockn = sect*4, (sect*4)+2 do
|
||||||
|
local blockdata = readblock(blockn, mfkey)
|
||||||
|
if not blockdata then return oops('[!] failed reading block') end
|
||||||
|
table.insert(tagdata, blockdata)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Decrypt data and build clear table
|
||||||
|
for key,value in ipairs(tagdata) do
|
||||||
|
local clearblockdata
|
||||||
|
v[0]=utils.SwapEndianness(value:sub(1,8),32)
|
||||||
|
v[1]=utils.SwapEndianness(value:sub(9,16),32)
|
||||||
|
xteaDecrypt(16, v, xteakey)
|
||||||
|
vv[0]=utils.SwapEndianness(value:sub(17,24),32)
|
||||||
|
vv[1]=utils.SwapEndianness(value:sub(25,32),32)
|
||||||
|
xteaDecrypt(16, vv, xteakey)
|
||||||
|
clearblockdata=string.format("%08X%08X%08X%08X",
|
||||||
|
utils.SwapEndianness(string.format("%08X", v[0]),32),
|
||||||
|
utils.SwapEndianness(string.format("%08X", v[1]),32),
|
||||||
|
utils.SwapEndianness(string.format("%08X", vv[0]),32),
|
||||||
|
utils.SwapEndianness(string.format("%08X", vv[1]),32))
|
||||||
|
table.insert(cleardata, clearblockdata)
|
||||||
|
end
|
||||||
|
|
||||||
|
return tagdata,cleardata
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function main(args)
|
||||||
|
local xteakey = {}
|
||||||
|
-- local v = {}
|
||||||
|
local edata = {}
|
||||||
|
local cdata = {}
|
||||||
|
|
||||||
|
-- Turn off Debug
|
||||||
|
setdevicedebug(false)
|
||||||
|
|
||||||
|
-- GET TAG UID
|
||||||
|
tag, err = lib14a.read(false, true)
|
||||||
|
if err then
|
||||||
|
lib14a.disconnect()
|
||||||
|
return oops(err)
|
||||||
|
end
|
||||||
|
core.clearCommandBuffer()
|
||||||
|
|
||||||
|
-- simple tag check
|
||||||
|
if 0x08 ~= tag.sak then
|
||||||
|
if 0x0400 ~= tag.atqa then
|
||||||
|
return oops(('[fail] found tag %s :: looking for Mifare S50 1k'):format(tag.name))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
xteakey = createxteakey(tag.uid)
|
||||||
|
print(acblue.."UID: "..tag.uid..acoff)
|
||||||
|
print(acblue..string.format("XTEA key: %08X %08X %08X %08X", xteakey[0], xteakey[1], xteakey[2], xteakey[3])..acoff)
|
||||||
|
|
||||||
|
edata, cdata = readtag("415A54454B4D",xteakey)
|
||||||
|
|
||||||
|
if edata == nil or cdata == nil then
|
||||||
|
print("ERROR Reading tag!")
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
print("Ciphered data:")
|
||||||
|
for key,value in ipairs(edata) do
|
||||||
|
print(value)
|
||||||
|
if key % 3 == 0 then print("") end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- compute CRC for each segment
|
||||||
|
crcH = crc16arc(utils.ConvertHexToAscii(cdata[1]..cdata[2]..cdata[3]:sub(1,28)))
|
||||||
|
crcA = crc16arc(utils.ConvertHexToAscii(cdata[4]..cdata[5]..cdata[6]..cdata[7]:sub(1,28)))
|
||||||
|
crcB = crc16arc(utils.ConvertHexToAscii(cdata[8]..cdata[9]..cdata[10]..cdata[11]:sub(1,28)))
|
||||||
|
|
||||||
|
print("\nHeader:")
|
||||||
|
for key,value in ipairs(cdata) do
|
||||||
|
if key == 3 then
|
||||||
|
print(value:sub(1,28)..acmagenta..value:sub(29,32)..acoff)
|
||||||
|
if utils.SwapEndianness(value:sub(29,32),16) == crcH then strcrc = " OK" else strcrc = acred.." CRCERROR !!" end
|
||||||
|
print(acmagenta.."CRC16/ARC = "..string.format("0x%04X", crcH)..strcrc..acoff)
|
||||||
|
print("\nDataA:")
|
||||||
|
elseif key == 4 then
|
||||||
|
print(acgreen..value:sub(1,4)..acoff..value:sub(5,16)..accyan..value:sub(17,24)..acoff..value:sub(25,26)..accyan..value:sub(27,28)..acoff..value:sub(29,32))
|
||||||
|
versionA = utils.SwapEndianness(value:sub(1,4),16)
|
||||||
|
dateA = string.format("%d/%02d/%02d %02d:%02d", tonumber(value:sub(17,18),10)+2000, tonumber(value:sub(19,20),10),
|
||||||
|
tonumber(string.format("%02X", band(tonumber(value:sub(21,22),16),0x3f)),10),
|
||||||
|
tonumber(value:sub(23,24),10), tonumber(value:sub(27,28),10))
|
||||||
|
elseif key == 8 then
|
||||||
|
print(acgreen..value:sub(1,4)..acoff..value:sub(5,16)..accyan..value:sub(17,24)..acoff..value:sub(25,26)..accyan..value:sub(27,28)..acoff..value:sub(29,32))
|
||||||
|
versionB = utils.SwapEndianness(value:sub(1,4),16)
|
||||||
|
dateB = string.format("%d/%02d/%02d %02d:%02d", tonumber(value:sub(17,18),10)+2000, tonumber(value:sub(19,20),10),
|
||||||
|
tonumber(string.format("%02X", band(tonumber(value:sub(21,22),16),0x3f)),10),
|
||||||
|
tonumber(value:sub(23,24),10), tonumber(value:sub(27,28),10))
|
||||||
|
elseif key == 5 then
|
||||||
|
print(acyellow..value:sub(1,4)..acoff..value:sub(5,32))
|
||||||
|
creditA = utils.SwapEndianness(value:sub(1,4),16)/100
|
||||||
|
elseif key == 9 then
|
||||||
|
print(acyellow..value:sub(1,4)..acoff..value:sub(5,32))
|
||||||
|
creditB = utils.SwapEndianness(value:sub(1,4),16)/100
|
||||||
|
elseif key == 7 then
|
||||||
|
print(value:sub(1,28)..acmagenta..value:sub(29,32)..acoff)
|
||||||
|
print(acgreen.."Version "..string.format("0x%04X", versionA)..acoff)
|
||||||
|
print(acyellow.."Credit : "..creditA..acoff)
|
||||||
|
if utils.SwapEndianness(value:sub(29,32),16) == crcA then strcrc = " OK" else strcrc = acred.." CRCERROR !!" end
|
||||||
|
print(acmagenta.."CRC16/ARC = "..string.format("0x%04X", crcA)..strcrc..acoff)
|
||||||
|
print(accyan.."Date: "..dateA..acoff)
|
||||||
|
print("\nDataB:")
|
||||||
|
elseif key == 11 then
|
||||||
|
print(value:sub(1,28)..acmagenta..value:sub(29,32)..acoff)
|
||||||
|
print(acgreen.."Version "..string.format("0x%04X", versionB)..acoff)
|
||||||
|
print(acyellow.."Credit : "..creditB..acoff)
|
||||||
|
if utils.SwapEndianness(value:sub(29,32),16) == crcB then strcrc = " OK" else strcrc = acred.." CRCERROR !!" end
|
||||||
|
print(acmagenta.."CRC16/ARC = "..string.format("0x%04X", crcB)..strcrc..acoff)
|
||||||
|
print(accyan.."Date: "..dateB..acoff)
|
||||||
|
print("\nFooter:")
|
||||||
|
else
|
||||||
|
print(value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
main(args)
|
|
@ -6,7 +6,7 @@ set -e
|
||||||
pre_submit_hook
|
pre_submit_hook
|
||||||
|
|
||||||
## delete all previous tarballs
|
## delete all previous tarballs
|
||||||
rm proxmark3.all.*.tgz proxmark3.all.*.log
|
rm -f proxmark3.all.*.tgz proxmark3.all.*.log
|
||||||
|
|
||||||
TODAY="$(date --date now +%Y%m%d.%H%M)"
|
TODAY="$(date --date now +%Y%m%d.%H%M)"
|
||||||
VERSION="0.1.$TODAY"
|
VERSION="0.1.$TODAY"
|
||||||
|
|
6
pm3
6
pm3
|
@ -21,7 +21,7 @@ PM3LIST=()
|
||||||
function get_pm3_list_Linux {
|
function get_pm3_list_Linux {
|
||||||
PM3LIST=()
|
PM3LIST=()
|
||||||
for DEV in $(find /dev/ttyACM* 2>/dev/null); do
|
for DEV in $(find /dev/ttyACM* 2>/dev/null); do
|
||||||
if udevadm info -q property -n "$DEV" |grep -q "ID_MODEL=proxmark3"; then
|
if udevadm info -q property -n "$DEV" |grep -q "ID_VENDOR=proxmark.org"; then
|
||||||
PM3LIST+=("$DEV")
|
PM3LIST+=("$DEV")
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
@ -29,7 +29,9 @@ function get_pm3_list_Linux {
|
||||||
|
|
||||||
function get_pm3_list_macOS {
|
function get_pm3_list_macOS {
|
||||||
PM3LIST=()
|
PM3LIST=()
|
||||||
for DEV in $(ioreg -r -n proxmark3 -l|awk -F '"' '/IODialinDevice/{print $4}'); do
|
for DEV in $(ioreg -r -c "IOUSBHostDevice" -l|awk -F '"' '
|
||||||
|
$2=="USB Vendor Name"{b=($4=="proxmark.org")}
|
||||||
|
b==1 && $2=="IODialinDevice"{print $4}'); do
|
||||||
PM3LIST+=("$DEV")
|
PM3LIST+=("$DEV")
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue