1 local getopt
= require('getopt')
2 local ansicolors
= require('ansicolors')
11 This is scripts loops though a tear attack and reads expected value.
14 Full automatic, with password:
15 script run lf_em_tearoff_protect -p 50524F58
17 Manual fix increment over specified range:
18 script run lf_em_tearoff_protect -n 2 -s 200 -e 400
20 Trying repeatedly for a fixed timing, forever or till success:
21 script run lf_em_tearoff_protect -s 400 -e 400
25 Move card somehow away from the antenna to a position where it still works
28 script run lf_em_tearoff_protect [-h] [-n <steps us>] [-p <pwd>] [-s <start us>] [-e <end us>]
32 -n <steps us> steps in milliseconds for each tear-off
33 -p <pwd> (optional) use a password
34 -s <delay us> initial start delay
35 -e <delay us> end delay, must be larger or equal to start delay
39 local set_tearoff_delay
= 'hw tearoff --on --delay %d'
40 local wr_template
= 'lf em 4x05 write --po -d %s -p %s'
43 -- This is only meant to be used when errors occur
44 local function oops(err
)
46 core
.clearCommandBuffer()
56 print(ansicolors
.cyan
..'Usage'..ansicolors
.reset
)
58 print(ansicolors
.cyan
..'Arguments'..ansicolors
.reset
)
60 print(ansicolors
.cyan
..'Example usage'..ansicolors
.reset
)
64 local function exit_msg()
66 print('================= '..ansicolors
.green
..'verify with'..ansicolors
.reset
..' =================')
67 print(' lf em 4x05_dump')
68 print('===============================================')
72 local function reset(wr_value
, password
)
73 print('[=] '..ansicolors
.red
..'resetting the active lock block'..ansicolors
.reset
)
74 core
.console(wr_template
:format(wr_value
, password
))
77 local function main(args
)
80 Basically it does the following,
86 The first two commands don't need a feedback from the system, so going with core.console commands.
87 Since the read needs demodulation of signal I opted to add that function from cmdlfem4x.c to the core lua scripting
88 core.em4x05_read(addr, password)
91 local n
, password
, sd
, ed
93 for o
, a
in getopt
.getopt(args
, 'he:s:p:n:') do
94 if o
== 'h' then return help() end
95 if o
== 'n' then n
= tonumber(a
) end
96 if o
== 'p' then password
= a
end
97 if o
== 'e' then ed
= tonumber(a
) end
98 if o
== 's' then sd
= tonumber(a
) end
101 password
= password
or ''
102 if #password
~= 8 then
106 local word14
, err14
= core
.em4x05_read(14, password
)
110 local word15
, err15
= core
.em4x05_read(15, password
)
114 local bit15
= bit
.band(0x00008000, word15
)
115 if bit15
== 0x00008000 then
116 rd_value
= ('%08X'):format(word15
)
117 reset(wr_value
, password
)
119 rd_value
= ('%08X'):format(word14
)
121 if rd_value
== '00008000' then
122 print('Tag already fully unlocked, nothing to do')
125 local wr_value
= '00000000'
133 if sd
== nil or ed
== nil then
134 return oops('start and stop delays need to be defined')
137 return oops('start delay can\'t be larger than end delay', sd
, ed
)
141 print('==========================================')
142 print('Starting EM4x05 tear-off : target PROTECT')
144 if password
~= '' then
145 print('target pwd', password
)
148 print('automatic mode', 'enabled')
150 print('target stepping', n
)
151 print('target delay', sd
,ed
)
152 print('read value', rd_value
)
153 print('write value', wr_value
)
154 print('==========================================')
157 local res_nowrite
= 0
159 -- fix at one specific delay
169 if auto
and n
< 1 then -- n is a float
170 print('[!] Reached n < 1 => '..ansicolors
.yellow
..'disabling automatic mode'..ansicolors
.reset
)
178 if (tries
>= 5) and (n
== 0) and (soon
~= late
) then
180 print(('[!] Tried %d times, soon:%i late:%i => '):format(tries
, soon
, late
)..ansicolors
.yellow
..'adjusting delay by +1 us'..ansicolors
.reset
)
184 print(('[!] Tried %d times, soon:%i late:%i => '):format(tries
, soon
, late
)..ansicolors
.yellow
..'adjusting delay by -1 us'..ansicolors
.reset
)
194 if core
.kbd_enter_pressed() then
195 print("aborted by user")
199 core
.clearCommandBuffer()
201 local c
= set_tearoff_delay
:format(sd
)
204 c
= wr_template
:format(wr_value
, password
)
207 word14
, err14
= core
.em4x05_read(14, password
)
212 local wordstr14
= ('%08X'):format(word14
)
214 word15
, err15
= core
.em4x05_read(15, password
)
219 local wordstr15
= ('%08X'):format(word15
)
221 print(('[=] ref:'..rd_value
..' 14:%08X 15:%08X '):format(word14
, word15
))
224 if wordstr14
== rd_value
and wordstr15
== '00000000' then
225 print('[=] Status: Nothing happened => '..ansicolors
.green
..'tearing too soon'..ansicolors
.reset
)
229 print(('[+] Adjusting params: n=%i sd=%i ed=%i'):format(n
, sd
, ed
))
234 if wordstr15
== rd_value
then
235 if wordstr14
== '00000000' then
236 print('[=] Status: Protect succeeded => '..ansicolors
.green
..'tearing too late'..ansicolors
.reset
)
238 if wordstr14
== rd_value
then
239 print('[=] Status: 15 ok, 14 not yet erased => '..ansicolors
.green
..'tearing too late'..ansicolors
.reset
)
241 print('[=] Status: 15 ok, 14 partially erased => '..ansicolors
.green
..'tearing too late'..ansicolors
.reset
)
244 reset(wr_value
, password
)
245 -- it could still happen that a bitflip got committed, let's check...
246 local word14b
, err14b
= core
.em4x05_read(14, password
)
250 local wordstr14b
= ('%08X'):format(word14b
)
251 if (wordstr14b
== '00000000') then
252 reset(wr_value
, password
)
253 word14b
, err14b
= core
.em4x05_read(14, password
)
258 if (wordstr14b
~= rd_value
) then
259 local word15b
, err15b
= core
.em4x05_read(15, password
)
263 print(('[=] Status: new definitive value! => '..ansicolors
.red
..'SUCCESS: '..ansicolors
.reset
..'14: '..ansicolors
.cyan
..'%08X'..ansicolors
.reset
..' 15: %08X'):format(word14b
, word15b
))
270 print(('[+] Adjusting params: n=%i sd=%i ed=%i'):format(n
, sd
, ed
))
275 bit15
= bit
.band(0x00008000, word15
)
276 if bit15
== 0x00008000 then
277 print(('[=] Status: 15 bitflipped and active => '..ansicolors
.red
..'SUCCESS?: '..ansicolors
.reset
..'14: %08X 15: '..ansicolors
.cyan
..'%08X'..ansicolors
.reset
):format(word14
, word15
))
278 print('[+] Committing results...')
279 reset(wr_value
, password
)
280 local word14b
, err14b
= core
.em4x05_read(14, password
)
284 local wordstr14b
= ('%08X'):format(word14b
)
285 local word15b
, err15b
= core
.em4x05_read(15, password
)
289 local wordstr15b
= ('%08X'):format(word15b
)
290 print(('[=] ref:'..rd_value
..' 14:%08X 15:%08X '):format(word14b
, word15b
))
292 bit15
= bit
.band(0x00008000, word14b
)
293 if bit15
== 0x00008000 then
294 if (wordstr14b
== wordstr15
) then
295 print(('[=] Status: confirmed => '..ansicolors
.red
..'SUCCESS: '..ansicolors
.reset
..'14: '..ansicolors
.cyan
..'%08X'..ansicolors
.reset
..' 15: %08X'):format(word14b
, word15b
))
298 if (wordstr14b
~= rd_value
) then
299 print(('[=] Status: new definitive value! => '..ansicolors
.red
..'SUCCESS: '..ansicolors
.reset
..'14: '..ansicolors
.cyan
..'%08X'..ansicolors
.reset
..' 15: %08X'):format(word14b
, word15b
))
302 print(('[=] Status: failed to commit bitflip => '..ansicolors
.red
..'FAIL: '..ansicolors
.reset
..'14: %08X 15: %08X'):format(word14b
, word15b
))
304 print(('[=] Status: failed to commit => '..ansicolors
.red
..'FAIL: '..ansicolors
.reset
..'14: %08X 15: %08X'):format(word14b
, word15b
))
315 print(('[=] Status: 15 bitflipped but inactive => '..ansicolors
.yellow
..'PROMISING: '..ansicolors
.reset
..'14: %08X 15: '..ansicolors
.cyan
..'%08X'..ansicolors
.reset
):format(word14
, word15
))
326 In the future, we may implement so that scripts are invoked directly
327 into a 'main' function, instead of being executed blindly. For future
328 compatibility, I have done so, but I invoke my main from here.