Part 1, Topic 2: Clock Glitching to Bypass PasswordĀ¶
NOTE: This lab references some (commercial) training material on ChipWhisperer.io. You can freely execute and use the lab per the open-source license (including using it in your own courses if you distribute similarly), but you must maintain notice about this source location. Consider joining our training course to enjoy the full experience.
SUMMARY: In the previous lab, we learned how clock glitching can be used to cause a target to behave unexpectedly. In this lab, we'll look at a slightly more realistic example - glitching past a password check
LEARNING OUTCOMES:
- Applying previous glitch settings to new firmware
- Checking for success and failure when glitching
FirmwareĀ¶
We've already seen how we can insert clock gliches to mess up a calculation that a target is trying to make. While this has many applications, some which will be covered in Fault_201, let's take a look at something a little closer to our original example of glitch vulnerable code: a password check. No need to change out firmware here, we're still using the simpleserial-glitch project (though we'll go through all the build stuff again).
The code is as follows for the password check:
uint8_t password(uint8_t* pw)
{
char passwd[] = "touch";
char passok = 1;
int cnt;
trigger_high();
//Simple test - doesn't check for too-long password!
for(cnt = 0; cnt < 5; cnt++){
if (pw[cnt] != passwd[cnt]){
passok = 0;
}
}
trigger_low();
simpleserial_put('r', 1, (uint8_t*)&passok);
return passok;
}
There's really nothing out of the ordinary here - it's just a simple password check. We can communicate with it using the 'p'
command.
SCOPETYPE = 'OPENADC'
PLATFORM = 'CW308_STM32F4'
SS_VER = 'SS_VER_2_1'
allowable_exceptions = None
VERSION = 'HARDWARE'
CRYPTO_TARGET = 'TINYAES128C'
#!/usr/bin/env python
# coding: utf-8
# In[ ]:
import chipwhisperer as cw
try:
if not scope.connectStatus:
scope.con()
except NameError:
scope = cw.scope(hw_location=(5, 7))
try:
if SS_VER == "SS_VER_2_1":
target_type = cw.targets.SimpleSerial2
elif SS_VER == "SS_VER_2_0":
raise OSError("SS_VER_2_0 is deprecated. Use SS_VER_2_1")
else:
target_type = cw.targets.SimpleSerial
except:
SS_VER="SS_VER_1_1"
target_type = cw.targets.SimpleSerial
try:
target = cw.target(scope, target_type)
except:
print("INFO: Caught exception on reconnecting to target - attempting to reconnect to scope first.")
print("INFO: This is a work-around when USB has died without Python knowing. Ignore errors above this line.")
scope = cw.scope(hw_location=(5, 7))
target = cw.target(scope, target_type)
print("INFO: Found ChipWhispererš")
# In[ ]:
if "STM" in PLATFORM or PLATFORM == "CWLITEARM" or PLATFORM == "CWNANO":
prog = cw.programmers.STM32FProgrammer
elif PLATFORM == "CW303" or PLATFORM == "CWLITEXMEGA":
prog = cw.programmers.XMEGAProgrammer
elif "neorv32" in PLATFORM.lower():
prog = cw.programmers.NEORV32Programmer
elif PLATFORM == "CW308_SAM4S" or PLATFORM == "CWHUSKY":
prog = cw.programmers.SAM4SProgrammer
else:
prog = None
# In[ ]:
import time
time.sleep(0.05)
scope.default_setup()
def reset_target(scope):
if PLATFORM == "CW303" or PLATFORM == "CWLITEXMEGA":
scope.io.pdic = 'low'
time.sleep(0.1)
scope.io.pdic = 'high_z' #XMEGA doesn't like pdic driven high
time.sleep(0.1) #xmega needs more startup time
elif "neorv32" in PLATFORM.lower():
raise IOError("Default iCE40 neorv32 build does not have external reset - reprogram device to reset")
elif PLATFORM == "CW308_SAM4S" or PLATFORM == "CWHUSKY":
scope.io.nrst = 'low'
time.sleep(0.25)
scope.io.nrst = 'high_z'
time.sleep(0.25)
else:
scope.io.nrst = 'low'
time.sleep(0.05)
scope.io.nrst = 'high_z'
time.sleep(0.05)
INFO: Found ChipWhispererš
scope.gain.mode changed from low to high scope.gain.gain changed from 0 to 30 scope.gain.db changed from 5.5 to 24.8359375 scope.adc.basic\_mode changed from low to rising\_edge scope.adc.samples changed from 98134 to 5000 scope.adc.trig\_count changed from 11243949 to 22691022 scope.clock.adc\_src changed from clkgen\_x1 to clkgen\_x4 scope.clock.adc\_freq changed from 29538459 to 27848922 scope.clock.adc\_rate changed from 29538459.0 to 27848922.0 scope.clock.clkgen\_div changed from 1 to 26 scope.clock.clkgen\_freq changed from 192000000.0 to 7384615.384615385 scope.io.tio1 changed from serial\_tx to serial\_rx scope.io.tio2 changed from serial\_rx to serial\_tx scope.io.hs2 changed from None to clkgen scope.glitch.mmcm\_locked changed from True to False
%%bash -s "$PLATFORM" "$SS_VER"
cd ../../../firmware/mcu/simpleserial-glitch
make PLATFORM=$1 CRYPTO_TARGET=NONE SS_VER=$2 -j
SS\_VER set to SS\_VER\_2\_1
SS\_VER set to SS\_VER\_2\_1
.
Welcome to another exciting ChipWhisperer target build!!
+--------------------------------------------------------
Size after:
arm-none-eabi-gcc (15:9-2019-q4-0ubuntu1) 9.2.1 20191025 (release) [ARM/arm-9-branch revision 277599]
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ Built for platform CW308T: STM32F4 Target with:
text data bss dec hex filename
4708 1084 1344 7136 1be0 simpleserial-glitch-CW308\_STM32F4.elf
+ CRYPTO\_TARGET = NONE
+ CRYPTO\_OPTIONS =
+--------------------------------------------------------
fw_path = "../../../firmware/mcu/simpleserial-glitch/simpleserial-glitch-{}.hex".format(PLATFORM)
cw.program_target(scope, prog, fw_path)
if SS_VER == 'SS_VER_2_1':
target.reset_comms()
Detected known STMF32: STM32F40xxx/41xxx Extended erase (0x44), this can take ten seconds or more
Attempting to program 5791 bytes at 0x8000000 STM32F Programming flash...
STM32F Reading flash...
Verified flash OK, 5791 bytes
def reboot_flush():
reset_target(scope)
#Flush garbage too
target.flush()
If we send a wrong password:
#Do glitch loop
pw = bytearray([0x00]*5)
target.simpleserial_write('p', pw)
val = target.simpleserial_read_witherrors('r', 1, glitch_timeout=10)#For loop check
valid = val['valid']
if valid:
response = val['payload']
raw_serial = val['full_response']
error_code = val['rv']
print(val)
#print(bytearray(val['full_response'].encode('latin-1')))
{'valid': True, 'payload': CWbytearray(b'00'), 'full\_response': CWbytearray(b'00 72 01 00 99 00'), 'rv': bytearray(b'\x00')}
We get a response of zero. But if we send the correct password:
#Do glitch loop
pw = bytearray([0x74, 0x6F, 0x75, 0x63, 0x68]) # correct password ASCII representation
target.simpleserial_write('p', pw)
val = target.simpleserial_read_witherrors('r', 1, glitch_timeout=10)#For loop check
valid = val['valid']
if valid:
response = val['payload']
raw_serial = val['full_response']
error_code = val['rv']
print(val)
{'valid': True, 'payload': CWbytearray(b'01'), 'full\_response': CWbytearray(b'00 72 01 01 d4 00'), 'rv': bytearray(b'\x00')}
We get a 1 back. Set the glitch up as in the previous part:
scope.cglitch_setup()
scope.clock.adc\_freq changed from 27848922 to 29538459 scope.clock.adc\_rate changed from 27848922.0 to 29538459.0
Update the code below to also add an ext offset parameter:
gc = cw.GlitchController(groups=["success", "reset", "normal"], parameters=["width", "offset", "ext_offset"])
gc.display_stats()
Like before, we'll plot our settings. The plot is 2D, so you'll need to make a decision about what to plot. In this case, we'll plot offset and ext_offset, but pick whichever you like:
gc.glitch_plot(plotdots={"success":"+g", "reset":"xr", "normal":None}, x_index="ext_offset", y_index="offset")
And make a glitch loop. Make sure you use some successful settings that you found in the previous lab, since it will make this one much shorter!
One change you probably want to make is to add a scan for ext_offset. The number of places we can insert a successful glitch has gone way down. Doing this will also be very important for future labs.
from tqdm.notebook import tqdm
import re
import struct
sample_size = 1
if scope._is_husky:
gc.set_range("width", 3500, 4500)
gc.set_range("offset", 1300, 3200)
gc.set_global_step([400, 200, 100])
gc.set_range("ext_offset", 60, 100)
gc.set_step("ext_offset", 1)
else:
gc.set_global_step(1)
if PLATFORM == "CWLITEXMEGA":
gc.set_range("width", 45, 49.8)
gc.set_range("offset", -46, -49.8)
gc.set_range("ext_offset", 4, 70)
sample_size = 10
elif PLATFORM == "CW308_STM32F4":
gc.set_range("width", 0.4, 10)
gc.set_range("offset", 40, 49.8)
elif PLATFORM == "CWLITEARM":
gc.set_range("width", 0.8, 3.6)
gc.set_range("offset", -4, -2)
gc.set_range("ext_offset", 0, 100)
gc.set_global_step(0.4)
gc.set_step("ext_offset", 1)
scope.glitch.repeat = 1
reboot_flush()
broken = False
scope.adc.timeout = 0.5
for glitch_settings in gc.glitch_values():
scope.glitch.offset = glitch_settings[1]
scope.glitch.width = glitch_settings[0]
scope.glitch.ext_offset = glitch_settings[2]
for i in range(sample_size):
if scope.adc.state:
# can detect crash here (fast) before timing out (slow)
print("Trigger still high!")
gc.add("reset")
#Device is slow to boot?
reboot_flush()
scope.arm()
target.simpleserial_write('p', bytearray([0]*5))
ret = scope.capture()
if ret:
print('Timeout - no trigger')
gc.add("reset")
#Device is slow to boot?
reboot_flush()
else:
val = target.simpleserial_read_witherrors('r', 1, glitch_timeout=10, timeout=50)#For loop check
if val['valid'] is False:
gc.add("reset")
else:
if val['payload'] == bytearray([1]): #for loop check
#broken = True
gc.add("success")
print(val['payload'])
print(scope.glitch.width, scope.glitch.offset, scope.glitch.ext_offset)
print("š", end="")
#break
else:
gc.add("normal")
if broken:
break
For this lab, you may want two copies of your results; one for finding effective ext_offsets:
results = gc.calc(ignore_params=["width", "offset"], sort="success_rate")
results
[((10.0,), {'total': 100, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 100, 'normal_rate': 1.0}), ((9.0,), {'total': 100, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 100, 'normal_rate': 1.0}), ((8.0,), {'total': 100, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 100, 'normal_rate': 1.0}), ((7.0,), {'total': 100, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 100, 'normal_rate': 1.0}), ((6.0,), {'total': 100, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 100, 'normal_rate': 1.0}), ((5.0,), {'total': 100, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 100, 'normal_rate': 1.0}), ((4.0,), {'total': 100, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 100, 'normal_rate': 1.0}), ((3.0,), {'total': 100, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 100, 'normal_rate': 1.0}), ((2.0,), {'total': 100, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 100, 'normal_rate': 1.0}), ((1.0,), {'total': 100, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 100, 'normal_rate': 1.0}), ((0.0,), {'total': 100, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 100, 'normal_rate': 1.0})]
And one for your width/offset settings:
results = gc.calc(ignore_params=["ext_offset"], sort="success_rate")
results
[((9.4, 49), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((9.4, 48), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((9.4, 47), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((9.4, 46), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((9.4, 45), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((9.4, 44), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((9.4, 43), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((9.4, 42), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((9.4, 41), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((9.4, 40), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((8.4, 49), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((8.4, 48), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((8.4, 47), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((8.4, 46), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((8.4, 45), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((8.4, 44), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((8.4, 43), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((8.4, 42), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((8.4, 41), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((8.4, 40), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((7.4, 49), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((7.4, 48), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((7.4, 47), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((7.4, 46), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((7.4, 45), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((7.4, 44), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((7.4, 43), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((7.4, 42), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((7.4, 41), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((7.4, 40), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((6.4, 49), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((6.4, 48), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((6.4, 47), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((6.4, 46), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((6.4, 45), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((6.4, 44), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((6.4, 43), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((6.4, 42), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((6.4, 41), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((6.4, 40), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((5.4, 49), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((5.4, 48), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((5.4, 47), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((5.4, 46), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((5.4, 45), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((5.4, 44), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((5.4, 43), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((5.4, 42), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((5.4, 41), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((5.4, 40), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((4.4, 49), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((4.4, 48), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((4.4, 47), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((4.4, 46), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((4.4, 45), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((4.4, 44), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((4.4, 43), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((4.4, 42), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((4.4, 41), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((4.4, 40), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((3.4, 49), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((3.4, 48), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((3.4, 47), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((3.4, 46), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((3.4, 45), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((3.4, 44), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((3.4, 43), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((3.4, 42), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((3.4, 41), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((3.4, 40), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((2.4, 49), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((2.4, 48), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((2.4, 47), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((2.4, 46), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((2.4, 45), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((2.4, 44), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((2.4, 43), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((2.4, 42), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((2.4, 41), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((2.4, 40), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((1.4, 49), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((1.4, 48), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((1.4, 47), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((1.4, 46), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((1.4, 45), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((1.4, 44), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((1.4, 43), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((1.4, 42), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((1.4, 41), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((1.4, 40), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((0.4, 49), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((0.4, 48), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((0.4, 47), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((0.4, 46), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((0.4, 45), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((0.4, 44), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((0.4, 43), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((0.4, 42), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((0.4, 41), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0}), ((0.4, 40), {'total': 11, 'success': 0, 'success_rate': 0.0, 'reset': 0, 'reset_rate': 0.0, 'normal': 11, 'normal_rate': 1.0})]
scope.dis()
target.dis()
assert broken is True
--------------------------------------------------------------------------- AssertionError Traceback (most recent call last) Cell In[15], line 1 ----> 1 assert broken is True AssertionError: