hardsploit_gui 2.2 → 2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/hardsploit_gui +2 -2
- data/lib/{Firmware → Firmwares}/FPGA/I2C/I2C_INTERACT/HARDSPLOIT_FIRMWARE_FPGA_I2C_INTERACT.rpd +0 -0
- data/lib/{Firmware/FPGA/SWD/SWD_INTERACT/HARDSPLOIT_FIRMWARE_FPGA_SWD_INTERACT.rpd → Firmwares/FPGA/PARALLEL/NO_MUX_PARALLEL_MEMORY/HARDSPLOIT_FIRMWARE_FPGA_NO_MUX_PARALLEL_MEMORY.rpd} +0 -0
- data/lib/Firmwares/FPGA/SPI/SPI_INTERACT/HARDSPLOIT_FIRMWARE_FPGA_SPI_INTERACT.rpd +0 -0
- data/lib/{Firmware/FPGA/SPI/SPI_INTERACT/HARDSPLOIT_FIRMWARE_FPGA_SPI_INTERACT.rpd → Firmwares/FPGA/SPI/SPI_SNIFFER/HARDSPLOIT_FIRMWARE_FPGA_SPI_SNIFFER.rpd} +0 -0
- data/lib/Firmwares/FPGA/SWD/SWD_INTERACT/HARDSPLOIT_FIRMWARE_FPGA_SWD_INTERACT.rpd +0 -0
- data/lib/{Firmware → Firmwares}/FPGA/TEST/TEST_INTERACT/HARDSPLOIT_FIRMWARE_FPGA_TEST_INTERACT.rpd +0 -0
- data/lib/Firmwares/FPGA/UART/UART_INTERACT/HARDSPLOIT_FIRMWARE_FPGA_UART_INTERACT.rpd +0 -0
- data/lib/{Firmware → Firmwares}/FPGA/VersionFPGA.rb +1 -1
- data/lib/{Firmware → Firmwares}/UC/HARDSPLOIT_FIRMWARE_UC.bin +0 -0
- data/lib/{Firmware → Firmwares}/UC/VersionUC.rb +1 -1
- data/lib/HardsploitAPI/Core/HardsploitAPI.rb +210 -0
- data/lib/HardsploitAPI/Core/HardsploitAPI_CONSTANT.rb +150 -0
- data/lib/HardsploitAPI/Core/HardsploitAPI_ERROR.rb +109 -0
- data/lib/HardsploitAPI/Core/HardsploitAPI_FIRMWARE.rb +305 -0
- data/lib/HardsploitAPI/{HardsploitAPI_PROGRESS.rb → Core/HardsploitAPI_PROGRESS.rb} +0 -0
- data/lib/HardsploitAPI/Core/HardsploitAPI_USB_COMMUNICATION.rb +166 -0
- data/lib/HardsploitAPI/Modules/I2C/HardsploitAPI_I2C.rb +356 -0
- data/lib/HardsploitAPI/{HardsploitAPI_NO_MUX_PARALLELE_MEMORY.rb → Modules/NO_MUX_PARALLEL_MEMORY/HardsploitAPI_NO_MUX_PARALLEL_MEMORY.rb} +26 -49
- data/lib/HardsploitAPI/Modules/NRF24L01/HardsploitAPI_NRF24L01.rb +306 -0
- data/lib/HardsploitAPI/Modules/SPI/HardsploitAPI_SPI.rb +340 -0
- data/lib/HardsploitAPI/Modules/SPI_SNIFFER/HardsploitAPI_SPI_SNIFFER.rb +83 -0
- data/lib/HardsploitAPI/Modules/SWD/HardsploitAPI_SWD.rb +367 -0
- data/lib/HardsploitAPI/Modules/SWD/HardsploitAPI_SWD_DEBUG.rb +89 -0
- data/lib/HardsploitAPI/Modules/SWD/HardsploitAPI_SWD_MEM_AP.rb +61 -0
- data/lib/HardsploitAPI/{SWD → Modules/SWD}/HardsploitAPI_SWD_STM32.rb +32 -15
- data/lib/HardsploitAPI/{HardsploitAPI_TEST_INTERACT.rb → Modules/TEST/HardsploitAPI_TEST_INTERACT.rb} +1 -1
- data/lib/HardsploitAPI/Modules/UART/HardsploitAPI_UART.rb +196 -0
- data/lib/Hardsploit_gui.rb +96 -0
- data/lib/class/Chip_editor.rb +186 -330
- data/lib/class/Chip_management.rb +496 -0
- data/lib/class/Command_editor.rb +130 -182
- data/lib/class/Command_table.rb +16 -22
- data/lib/class/Console.rb +0 -2
- data/lib/class/ErrorMsg.rb +312 -0
- data/lib/class/Export.rb +140 -0
- data/lib/class/Export_manager.rb +43 -43
- data/lib/class/Firmware.rb +52 -11
- data/lib/class/Generic_commands.rb +180 -190
- data/lib/class/Import.rb +193 -0
- data/lib/class/Progress_bar.rb +1 -0
- data/lib/class/Signal_mapper.rb +120 -0
- data/lib/class/Wire_helper.rb +132 -148
- data/lib/class/{I2C → i2c}/I2c_command.rb +16 -13
- data/lib/class/i2c/I2c_export.rb +95 -0
- data/lib/class/i2c/I2c_import.rb +117 -0
- data/lib/class/i2c/I2c_scanner.rb +114 -0
- data/lib/class/i2c/I2c_settings.rb +148 -0
- data/lib/class/parallel/Parallel_export.rb +118 -0
- data/lib/class/parallel/Parallel_import.rb +113 -0
- data/lib/class/parallel/Parallel_settings.rb +81 -0
- data/lib/class/spi/Spi_export.rb +108 -0
- data/lib/class/spi/Spi_import.rb +159 -0
- data/lib/class/spi/Spi_settings.rb +108 -0
- data/lib/class/spi/Spi_sniffer.rb +101 -0
- data/lib/class/swd/Swd.rb +125 -0
- data/lib/class/swd/Swd_scanner.rb +121 -0
- data/lib/class/swd/Swd_settings.rb +76 -0
- data/lib/class/uart/Uart_baudrate.rb +62 -0
- data/lib/class/uart/Uart_console.rb +115 -0
- data/lib/class/uart/Uart_settings.rb +102 -0
- data/lib/db/associations.rb +42 -29
- data/lib/db/database.rb +4 -0
- data/lib/db/development.sqlite3 +0 -0
- data/lib/db/migrate/004_create_manufacturers.rb +13 -0
- data/lib/db/migrate/005_create_packages.rb +13 -0
- data/lib/db/migrate/006_create_chip_types.rb +11 -0
- data/lib/db/migrate/007_create_buses.rb +11 -0
- data/lib/db/migrate/008_create_signals.rb +14 -0
- data/lib/db/migrate/009_create_chips.rb +25 -0
- data/lib/db/migrate/010_create_commands.rb +21 -0
- data/lib/db/migrate/011_create_bytes.rb +19 -0
- data/lib/db/migrate/012_create_i2c_settings.rb +21 -0
- data/lib/db/migrate/013_create_spi_settings.rb +26 -0
- data/lib/db/migrate/014_create_parallel_settings.rb +21 -0
- data/lib/db/migrate/015_create_pins.rb +19 -0
- data/lib/db/migrate/016_create_uses.rb +17 -0
- data/lib/db/migrate/017_create_swd_settings.rb +19 -0
- data/lib/db/migrate/018_create_uart_settings.rb +22 -0
- data/lib/db/schema.rb +157 -0
- data/lib/db/seeds.rb +161 -0
- data/lib/gui/gui_chip_editor.rb +23 -22
- data/lib/gui/gui_chip_management.rb +43 -38
- data/lib/gui/gui_command_editor.rb +2 -1
- data/lib/gui/gui_export.rb +132 -0
- data/lib/gui/gui_generic_commands.rb +69 -31
- data/lib/gui/gui_generic_export.rb +18 -2
- data/lib/gui/gui_generic_import.rb +18 -2
- data/lib/gui/gui_i2c_command.rb +2 -1
- data/lib/gui/gui_i2c_settings.rb +2 -2
- data/lib/gui/gui_import.rb +131 -0
- data/lib/gui/gui_parallel_settings.rb +2 -1
- data/lib/gui/gui_progress_bar.rb +2 -1
- data/lib/gui/gui_signal_mapper.rb +121 -0
- data/lib/gui/gui_signal_scanner.rb +146 -0
- data/lib/gui/gui_spi_settings.rb +6 -2
- data/lib/gui/gui_spi_sniffer.rb +112 -0
- data/lib/gui/gui_swd_settings.rb +166 -0
- data/lib/gui/gui_uart_baudrate.rb +114 -0
- data/lib/gui/gui_uart_console.rb +164 -0
- data/lib/gui/gui_uart_settings.rb +243 -0
- data/lib/gui_designer/gui_chip_editor.ui +9 -6
- data/lib/gui_designer/gui_chip_management.ui +79 -35
- data/lib/gui_designer/gui_command_editor.ui +3 -0
- data/lib/gui_designer/gui_export.ui +171 -0
- data/lib/gui_designer/gui_generic_commands.ui +274 -190
- data/lib/gui_designer/gui_generic_export.ui +24 -1
- data/lib/gui_designer/gui_generic_import.ui +25 -2
- data/lib/gui_designer/gui_i2c_command.ui +3 -0
- data/lib/gui_designer/gui_i2c_settings.ui +2 -2
- data/lib/gui_designer/gui_import.ui +168 -0
- data/lib/gui_designer/gui_parallel_settings.ui +4 -1
- data/lib/gui_designer/gui_progress_bar.ui +3 -0
- data/lib/gui_designer/gui_signal_mapper.ui +179 -0
- data/lib/gui_designer/gui_signal_scanner.ui +261 -0
- data/lib/gui_designer/gui_spi_settings.ui +15 -2
- data/lib/gui_designer/gui_spi_sniffer.ui +156 -0
- data/lib/gui_designer/gui_swd_settings.ui +189 -0
- data/lib/gui_designer/gui_uart_baudrate.ui +161 -0
- data/lib/gui_designer/gui_uart_console.ui +284 -0
- data/lib/gui_designer/gui_uart_settings.ui +280 -0
- data/lib/logs/error.log +63 -0
- data/lib/models/bus.rb +19 -0
- data/lib/models/byte.rb +29 -0
- data/lib/models/chip.rb +41 -0
- data/lib/models/chip_type.rb +14 -0
- data/lib/models/command.rb +20 -0
- data/lib/models/i2c_setting.rb +41 -0
- data/lib/models/manufacturer.rb +14 -0
- data/lib/models/package.rb +26 -0
- data/lib/models/parallel_setting.rb +37 -0
- data/lib/models/pin.rb +14 -0
- data/lib/models/signall.rb +20 -0
- data/lib/models/spi_setting.rb +67 -0
- data/lib/models/swd_setting.rb +25 -0
- data/lib/models/uart_setting.rb +52 -0
- data/lib/models/use.rb +6 -0
- data/lib/startHardsploit.rb +2 -2
- metadata +106 -41
- data/lib/Firmware/FPGA/PARALLEL/NO_MUX_PARALLEL_MEMORY/HARDSPLOIT_FIRMWARE_FPGA_NO_MUX_PARALLEL_MEMORY.rpd +0 -0
- data/lib/HardsploitAPI/HardsploitAPI.rb +0 -133
- data/lib/HardsploitAPI/HardsploitAPI_CONSTANT.rb +0 -145
- data/lib/HardsploitAPI/HardsploitAPI_ERROR.rb +0 -38
- data/lib/HardsploitAPI/HardsploitAPI_FIRMWARE.rb +0 -311
- data/lib/HardsploitAPI/HardsploitAPI_I2C.rb +0 -360
- data/lib/HardsploitAPI/HardsploitAPI_SPI.rb +0 -369
- data/lib/HardsploitAPI/HardsploitAPI_USB_COMMUNICATION.rb +0 -148
- data/lib/HardsploitAPI/LICENSE.txt +0 -674
- data/lib/HardsploitAPI/README.md +0 -22
- data/lib/HardsploitAPI/SWD/HardsploitAPI_SWD.rb +0 -249
- data/lib/HardsploitAPI/SWD/HardsploitAPI_SWD_DEBUG.rb +0 -102
- data/lib/HardsploitAPI/SWD/HardsploitAPI_SWD_MEM_AP.rb +0 -78
- data/lib/HardsploitAPI/TRADEMARK +0 -3
- data/lib/class/HardsploitGUI.rb +0 -463
- data/lib/class/I2C/I2c_export.rb +0 -118
- data/lib/class/I2C/I2c_import.rb +0 -79
- data/lib/class/I2C/I2c_settings.rb +0 -129
- data/lib/class/PARALLEL/Parallel_export.rb +0 -146
- data/lib/class/PARALLEL/Parallel_import.rb +0 -88
- data/lib/class/PARALLEL/Parallel_settings.rb +0 -102
- data/lib/class/SPI/Spi_export.rb +0 -138
- data/lib/class/SPI/Spi_import.rb +0 -113
- data/lib/class/SPI/Spi_settings.rb +0 -134
- data/lib/db/hs.db +0 -0
- data/lib/hardsploit.rb +0 -131
data/lib/class/Export_manager.rb
CHANGED
@@ -11,64 +11,64 @@ class Export_manager < Qt::Widget
|
|
11
11
|
|
12
12
|
def initialize(bus, result, spiDataSended = [])
|
13
13
|
super()
|
14
|
-
@
|
14
|
+
@view = Ui_Export_manager.new
|
15
15
|
centerWindow(self)
|
16
|
-
@
|
16
|
+
@view.setupUi(self)
|
17
17
|
@bus = bus
|
18
18
|
# Check the bus type to adapt the table column
|
19
19
|
if bus == 'SPI'
|
20
20
|
create_result_table('SPI')
|
21
|
-
@
|
22
|
-
@
|
21
|
+
@view.cbx_export.hide
|
22
|
+
@view.tbl_result.setRowCount(result.length)
|
23
23
|
result.length.times do |i|
|
24
|
-
@
|
25
|
-
@
|
24
|
+
@view.tbl_result.setItem(i, 0, Qt::TableWidgetItem.new(spiDataSended[i].to_s))
|
25
|
+
@view.tbl_result.setItem(i, 1, Qt::TableWidgetItem.new(result[i].to_s))
|
26
26
|
end
|
27
|
-
@
|
28
|
-
@
|
29
|
-
@
|
27
|
+
@view.tbl_result.resizeColumnsToContents
|
28
|
+
@view.tbl_result.resizeRowsToContents
|
29
|
+
@view.tbl_result.horizontalHeader.stretchLastSection = true
|
30
30
|
else
|
31
31
|
create_result_table('I2C')
|
32
|
-
@
|
32
|
+
@view.tbl_result.setRowCount(result.length / 2)
|
33
33
|
(0..result.length - 1).step(2).each_with_index do |i, v|
|
34
34
|
case result[i]
|
35
35
|
when 0
|
36
|
-
@
|
37
|
-
@
|
38
|
-
@
|
36
|
+
@view.tbl_result.setItem(v, 0, Qt::TableWidgetItem.new('Write'))
|
37
|
+
@view.tbl_result.setItem(v, 1, Qt::TableWidgetItem.new('ACK'))
|
38
|
+
@view.tbl_result.setItem(v, 2, Qt::TableWidgetItem.new(result[i + 1].to_s))
|
39
39
|
when 1
|
40
|
-
@
|
41
|
-
@
|
42
|
-
@
|
40
|
+
@view.tbl_result.setItem(v, 0, Qt::TableWidgetItem.new('Read'))
|
41
|
+
@view.tbl_result.setItem(v, 1, Qt::TableWidgetItem.new('ACK'))
|
42
|
+
@view.tbl_result.setItem(v, 2, Qt::TableWidgetItem.new(result[i + 1].to_s))
|
43
43
|
when 2
|
44
|
-
@
|
45
|
-
@
|
46
|
-
@
|
44
|
+
@view.tbl_result.setItem(v, 0, Qt::TableWidgetItem.new('Write'))
|
45
|
+
@view.tbl_result.setItem(v, 1, Qt::TableWidgetItem.new('NACK'))
|
46
|
+
@view.tbl_result.setItem(v, 2, Qt::TableWidgetItem.new(result[i + 1].to_s))
|
47
47
|
else
|
48
|
-
@
|
49
|
-
@
|
50
|
-
@
|
48
|
+
@view.tbl_result.setItem(v, 0, Qt::TableWidgetItem.new('Write'))
|
49
|
+
@view.tbl_result.setItem(v, 1, Qt::TableWidgetItem.new('NACK'))
|
50
|
+
@view.tbl_result.setItem(v, 2, Qt::TableWidgetItem.new(result[i + 1].to_s))
|
51
51
|
end
|
52
52
|
end
|
53
|
-
@
|
54
|
-
@
|
55
|
-
@
|
53
|
+
@view.tbl_result.resizeColumnsToContents
|
54
|
+
@view.tbl_result.resizeRowsToContents
|
55
|
+
@view.tbl_result.horizontalHeader.stretchLastSection = true
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
59
59
|
def create_result_table(bus)
|
60
60
|
if bus == 'SPI'
|
61
|
-
@
|
62
|
-
@
|
63
|
-
@
|
64
|
-
@
|
61
|
+
@view.tbl_result.insertColumn(0)
|
62
|
+
@view.tbl_result.setHorizontalHeaderItem(0, Qt::TableWidgetItem.new('Data send'))
|
63
|
+
@view.tbl_result.insertColumn(1)
|
64
|
+
@view.tbl_result.setHorizontalHeaderItem(1, Qt::TableWidgetItem.new('Data receive'))
|
65
65
|
else
|
66
|
-
@
|
67
|
-
@
|
68
|
-
@
|
69
|
-
@
|
70
|
-
@
|
71
|
-
@
|
66
|
+
@view.tbl_result.insertColumn(0)
|
67
|
+
@view.tbl_result.setHorizontalHeaderItem(0, Qt::TableWidgetItem.new('R/W'))
|
68
|
+
@view.tbl_result.insertColumn(1)
|
69
|
+
@view.tbl_result.setHorizontalHeaderItem(1, Qt::TableWidgetItem.new('(N)ACK)'))
|
70
|
+
@view.tbl_result.insertColumn(2)
|
71
|
+
@view.tbl_result.setHorizontalHeaderItem(2, Qt::TableWidgetItem.new('DATA'))
|
72
72
|
end
|
73
73
|
end
|
74
74
|
|
@@ -76,7 +76,7 @@ class Export_manager < Qt::Widget
|
|
76
76
|
result_file = Qt::FileDialog.getSaveFileName(self, tr('Create a file'), '/', tr('All files (*)'))
|
77
77
|
unless result_file.nil?
|
78
78
|
if @bus == 'I2C'
|
79
|
-
if @
|
79
|
+
if @view.cbx_export.currentIndex == 0
|
80
80
|
save_i2c_csv(result_file)
|
81
81
|
else
|
82
82
|
save_i2c(result_file)
|
@@ -94,8 +94,8 @@ class Export_manager < Qt::Widget
|
|
94
94
|
def save_i2c_csv(result_file)
|
95
95
|
CSV.open("#{result_file}.csv", 'wb', :col_sep => ';') do |csv|
|
96
96
|
csv << ['R/W', '(N)ACK', 'DATA']
|
97
|
-
@
|
98
|
-
csv << [@
|
97
|
+
@view.tbl_result.rowCount.times do |i|
|
98
|
+
csv << [@view.tbl_result.item(i, 0).text, @view.tbl_result.item(i, 1).text, @view.tbl_result.item(i, 2).text]
|
99
99
|
end
|
100
100
|
end
|
101
101
|
end
|
@@ -103,9 +103,9 @@ class Export_manager < Qt::Widget
|
|
103
103
|
def save_i2c(result_file)
|
104
104
|
file = File.open("#{result_file}", 'w')
|
105
105
|
result = []
|
106
|
-
@
|
107
|
-
if @
|
108
|
-
result.push(@
|
106
|
+
@view.tbl_result.rowCount.times do |i|
|
107
|
+
if @view.tbl_result.item(i, 0).text == 'Read' && @view.tbl_result.item(i, 1).text == 'ACK'
|
108
|
+
result.push(@view.tbl_result.item(i, 2).text.to_i)
|
109
109
|
end
|
110
110
|
end
|
111
111
|
file.write(result.pack('C*'))
|
@@ -115,8 +115,8 @@ class Export_manager < Qt::Widget
|
|
115
115
|
def save_spi(result_file)
|
116
116
|
file = File.open("#{result_file}", 'w')
|
117
117
|
result = []
|
118
|
-
@
|
119
|
-
result.push(@
|
118
|
+
@view.tbl_result.rowCount.times do |i|
|
119
|
+
result.push(@view.tbl_result.item(i, 1).text.to_i)
|
120
120
|
end
|
121
121
|
file.write(result.pack('C*'))
|
122
122
|
file.close
|
data/lib/class/Firmware.rb
CHANGED
@@ -6,24 +6,65 @@
|
|
6
6
|
#===================================================
|
7
7
|
|
8
8
|
class Firmware
|
9
|
-
def initialize(
|
10
|
-
|
9
|
+
def initialize(firmware)
|
10
|
+
unless $currentFirmware == firmware
|
11
|
+
unless $currentFirmware == 'uC'
|
12
|
+
$pgb = Progress_bar.new("Upload firmware :")
|
13
|
+
$pgb.show
|
14
|
+
end
|
15
|
+
base_path = File.expand_path(File.dirname(__FILE__)) + '/../Firmwares/FPGA/'
|
11
16
|
case firmware
|
12
17
|
when 'I2C'
|
13
|
-
|
18
|
+
firmware_path = base_path + 'I2C/I2C_INTERACT/HARDSPLOIT_FIRMWARE_FPGA_I2C_INTERACT.rpd'
|
19
|
+
HardsploitAPI.instance.uploadFirmware(pathFirmware: firmware_path, checkFirmware: false)
|
14
20
|
when 'SPI'
|
15
|
-
|
16
|
-
|
17
|
-
|
21
|
+
firmware_path = base_path + 'SPI/SPI_INTERACT/HARDSPLOIT_FIRMWARE_FPGA_SPI_INTERACT.rpd'
|
22
|
+
HardsploitAPI.instance.uploadFirmware(pathFirmware: firmware_path, checkFirmware: false)
|
23
|
+
when 'SPI_SNIFFER'
|
24
|
+
firmware_path = base_path + 'SPI/SPI_SNIFFER/HARDSPLOIT_FIRMWARE_FPGA_SPI_SNIFFER.rpd'
|
25
|
+
HardsploitAPI.instance.uploadFirmware(pathFirmware: firmware_path, checkFirmware: false)
|
26
|
+
when 'PARALLEL'
|
27
|
+
firmware_path = base_path + 'PARALLEL/NO_MUX_PARALLEL_MEMORY/HARDSPLOIT_FIRMWARE_FPGA_NO_MUX_PARALLEL_MEMORY.rpd'
|
28
|
+
HardsploitAPI.instance.uploadFirmware(pathFirmware: firmware_path, checkFirmware: false)
|
18
29
|
when 'SWD'
|
19
|
-
|
30
|
+
firmware_path = base_path + 'SWD/SWD_INTERACT/HARDSPLOIT_FIRMWARE_FPGA_SWD_INTERACT.rpd'
|
31
|
+
HardsploitAPI.instance.uploadFirmware(pathFirmware: firmware_path, checkFirmware: false)
|
32
|
+
when 'UART'
|
33
|
+
firmware_path = base_path + 'UART/UART_INTERACT/HARDSPLOIT_FIRMWARE_FPGA_UART_INTERACT.rpd'
|
34
|
+
HardsploitAPI.instance.uploadFirmware(pathFirmware: firmware_path, checkFirmware: false)
|
35
|
+
when 'uC'
|
36
|
+
msg = Qt::MessageBox.new
|
37
|
+
msg.setWindowTitle("Microcontroller update")
|
38
|
+
msg.setText("Hardsploit must be in bootloader mode and dfu-util package must be installed in order to continue. Proceed ?")
|
39
|
+
msg.setIcon(Qt::MessageBox::Question)
|
40
|
+
msg.setStandardButtons(Qt::MessageBox::Cancel | Qt::MessageBox::Ok)
|
41
|
+
msg.setDefaultButton(Qt::MessageBox::Cancel)
|
42
|
+
if msg.exec == Qt::MessageBox::Ok
|
43
|
+
system("dfu-util -D 0483:df11 -a 0 -s 0x08000000 -R --download #{File.expand_path(File.dirname(__FILE__))}'/../Firmware/UC/HARDSPLOIT_FIRMWARE_UC.bin'")
|
44
|
+
end
|
20
45
|
end
|
21
|
-
$currentFirmware = firmware
|
46
|
+
$currentFirmware = firmware unless firmware == 'uC'
|
47
|
+
firmware = "SPI" if firmware == "SPI_SNIFFER"
|
48
|
+
$pgb.close
|
22
49
|
sleep(2)
|
23
50
|
end
|
51
|
+
|
52
|
+
case firmware
|
53
|
+
when 'PARALLEL', 'SWD', 'UART', 'I2C', 'SPI'
|
54
|
+
# CrossWiring
|
55
|
+
crossvalue = []
|
56
|
+
for i in 0..63
|
57
|
+
crossvalue.push i
|
58
|
+
end
|
59
|
+
pin_group = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']
|
60
|
+
Bus.find_by(name: firmware).signalls.each do |s|
|
61
|
+
hardsploit_pin_number = pin_group.index(s.pin[0]) * 8 + s.pin[1].to_i
|
62
|
+
crossvalue[hardsploit_pin_number] = HardsploitAPI.getSignalId(signal: s.name)
|
63
|
+
crossvalue[HardsploitAPI.getSignalId(signal: s.name)] = hardsploit_pin_number
|
64
|
+
end
|
65
|
+
HardsploitAPI.instance.setCrossWiring(value: crossvalue)
|
66
|
+
end
|
24
67
|
rescue Exception => msg
|
25
|
-
|
26
|
-
logger.error msg
|
27
|
-
Qt::MessageBox.new(Qt::MessageBox::Critical, 'Critical error', 'Error while loading the firmware. Consult the log for more details').exec
|
68
|
+
ErrorMsg.new.unknown(msg)
|
28
69
|
end
|
29
70
|
end
|
@@ -5,64 +5,161 @@
|
|
5
5
|
# License URI: http://www.gnu.org/licenses/gpl.txt
|
6
6
|
#===================================================
|
7
7
|
|
8
|
-
require_relative '../HardsploitAPI/HardsploitAPI'
|
9
8
|
require_relative '../gui/gui_generic_commands'
|
10
9
|
require_relative '../gui/gui_export_manager'
|
11
|
-
require_relative '../class/I2C/I2c_command'
|
12
|
-
require_relative '../class/Export_manager'
|
13
|
-
require_relative '../class/Command_editor'
|
14
10
|
|
15
|
-
|
16
|
-
class Generic_commands < Qt::Widget
|
11
|
+
class Generic_commands < Qt::MainWindow
|
17
12
|
slots 'feed_cmd_array()'
|
18
|
-
slots '
|
19
|
-
slots '
|
13
|
+
slots 'execute()'
|
14
|
+
slots 'create()'
|
15
|
+
slots 'edit()'
|
16
|
+
slots 'delete()'
|
17
|
+
slots 'template()'
|
18
|
+
slots 'concatenate()'
|
20
19
|
|
21
|
-
def initialize(
|
20
|
+
def initialize(chip, bus_name)
|
22
21
|
super()
|
23
|
-
@
|
22
|
+
@view = Ui_Generic_commands.new
|
24
23
|
centerWindow(self)
|
25
|
-
@
|
26
|
-
@
|
27
|
-
@
|
28
|
-
inputRestrict(@
|
29
|
-
@generic_command_gui.check_result.setChecked(true)
|
30
|
-
@api = api
|
24
|
+
@view.setupUi(self)
|
25
|
+
@view.lbl_chip.setText(chip.reference)
|
26
|
+
@view.lbl_search.setPixmap(Qt::Pixmap.new('images/search.png'))
|
27
|
+
inputRestrict(@view.lie_search, 2)
|
31
28
|
@chip = chip
|
32
29
|
@bus_name = bus_name
|
33
|
-
@bus_id = Bus.find_by(
|
30
|
+
@bus_id = Bus.find_by(name: bus_name).id
|
34
31
|
select_chip_settings(bus_name)
|
35
32
|
feed_cmd_array
|
36
33
|
end
|
37
34
|
|
35
|
+
def contextMenuEvent(event)
|
36
|
+
if @view.tbl_cmd.currentItem.nil?
|
37
|
+
return false
|
38
|
+
else
|
39
|
+
return false if @view.tbl_cmd.currentItem.column == 1
|
40
|
+
menu = Qt::Menu.new(self)
|
41
|
+
menu.addAction(@view.actionExecute)
|
42
|
+
menu.addAction(@view.actionEdit)
|
43
|
+
menu.addAction(@view.actionTemplate)
|
44
|
+
menu.addAction(@view.actionDelete)
|
45
|
+
menu.addAction(@view.actionConcatenate)
|
46
|
+
menu.exec(event.globalPos())
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def execute
|
51
|
+
return ErrorMsg.new.hardsploit_not_found unless HardsploitAPI.getNumberOfBoardAvailable > 0
|
52
|
+
return ErrorMsg.new.no_cmd_selected if @view.tbl_cmd.currentItem.nil?
|
53
|
+
cmd_array = prepare_cmd
|
54
|
+
result = exec_cmd(@bus_name, cmd_array)
|
55
|
+
if @view.check_result.isChecked && result != false
|
56
|
+
export_manager = Export_manager.new(@bus_name, result, cmd_array)
|
57
|
+
export_manager.setWindowModality(Qt::ApplicationModal)
|
58
|
+
export_manager.show
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def create
|
63
|
+
if @bus_name == 'I2C'
|
64
|
+
cmdBase = I2c_command.new(@chip, @bus_id, self)
|
65
|
+
else
|
66
|
+
cmdBase = Command_editor.new(0, nil, @chip, @bus_id, self)
|
67
|
+
end
|
68
|
+
cmdBase.show
|
69
|
+
end
|
70
|
+
|
71
|
+
def edit
|
72
|
+
return ErrorMsg.new.no_cmd_selected if @view.tbl_cmd.currentItem.nil?
|
73
|
+
cmdBase = Command_editor.new(2, @view.tbl_cmd.currentItem.text, @chip, @bus_id, self)
|
74
|
+
cmdBase.show
|
75
|
+
end
|
76
|
+
|
77
|
+
def template
|
78
|
+
return ErrorMsg.new.no_cmd_selected if @view.tbl_cmd.currentItem.nil?
|
79
|
+
cmdBase = Command_editor.new(1, @view.tbl_cmd.currentItem.text, @chip, @bus_id, self)
|
80
|
+
cmdBase.show
|
81
|
+
end
|
82
|
+
|
83
|
+
def delete
|
84
|
+
return ErrorMsg.new.no_cmd_selected if @view.tbl_cmd.currentItem.nil?
|
85
|
+
msg = Qt::MessageBox.new
|
86
|
+
msg.setWindowTitle('Delete command')
|
87
|
+
msg.setText('Confirm the delete command action ?')
|
88
|
+
msg.setIcon(Qt::MessageBox::Question)
|
89
|
+
msg.setStandardButtons(Qt::MessageBox::Cancel | Qt::MessageBox::Ok)
|
90
|
+
msg.setDefaultButton(Qt::MessageBox::Cancel)
|
91
|
+
if msg.exec == Qt::MessageBox::Ok
|
92
|
+
@chip.commands.find_by(name: @view.tbl_cmd.currentItem.text).destroy
|
93
|
+
feed_cmd_array
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def concatenate
|
98
|
+
return ErrorMsg.new.concat_disallow unless @bus_name == "I2C"
|
99
|
+
return ErrorMsg.new.concat_nbr unless @view.tbl_cmd.selectedItems.count == 2
|
100
|
+
cmd1 = Command.find_by(name: @view.tbl_cmd.selectedItems[0].text)
|
101
|
+
cmd2 = Command.find_by(name: @view.tbl_cmd.selectedItems[1].text)
|
102
|
+
return false unless check_concatenation_size(cmd1.bytes, cmd2.bytes)
|
103
|
+
|
104
|
+
cmd_1 = @view.tbl_cmd.selectedItems[0].text
|
105
|
+
cmd_2 = @view.tbl_cmd.selectedItems[1].text
|
106
|
+
cmd = Command.create(
|
107
|
+
name: 'New concatenation',
|
108
|
+
description: "Concatenation of #{cmd1.name} and #{cmd2.name} commands",
|
109
|
+
bus_id: @bus_id,
|
110
|
+
chip_id: @chip.id
|
111
|
+
)
|
112
|
+
unless check_for_errors(cmd)
|
113
|
+
cmd1.bytes.each do |b1|
|
114
|
+
byte1 = Byte.create(
|
115
|
+
index: b1.index,
|
116
|
+
value: b1.value,
|
117
|
+
description: b1.description,
|
118
|
+
iteration: b1.iteration,
|
119
|
+
command_id: Command.last.id
|
120
|
+
)
|
121
|
+
check_for_errors(byte1)
|
122
|
+
end
|
123
|
+
cmd2.bytes.each do |b2|
|
124
|
+
byte2 = Byte.create(
|
125
|
+
index: Byte.last.index + 1,
|
126
|
+
value: b2.value,
|
127
|
+
description: b2.description,
|
128
|
+
iteration: b2.iteration,
|
129
|
+
command_id: Command.last.id
|
130
|
+
)
|
131
|
+
check_for_errors(byte2)
|
132
|
+
end
|
133
|
+
feed_cmd_array
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
38
137
|
def feed_cmd_array
|
39
|
-
@
|
40
|
-
cmd = @chip.
|
41
|
-
unless @
|
42
|
-
cmd = cmd.where('
|
138
|
+
@view.tbl_cmd.clearContents
|
139
|
+
cmd = @chip.commands.where(bus_id: @bus_id)
|
140
|
+
unless @view.lie_search.text.empty?
|
141
|
+
cmd = cmd.where('name LIKE ?', "%#{@view.lie_search.text}%")
|
43
142
|
end
|
44
|
-
@
|
143
|
+
@view.tbl_cmd.setRowCount(cmd.count);
|
45
144
|
cmd.to_enum.with_index(0).each do |c, i|
|
46
|
-
it1 = Qt::TableWidgetItem.new(c.
|
145
|
+
it1 = Qt::TableWidgetItem.new(c.name)
|
47
146
|
it1.setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled)
|
48
|
-
it2 = Qt::TableWidgetItem.new(c.
|
147
|
+
it2 = Qt::TableWidgetItem.new(c.description)
|
49
148
|
it2.setFlags(Qt::ItemIsEnabled)
|
50
|
-
@
|
51
|
-
@
|
149
|
+
@view.tbl_cmd.setItem(i, 0, it1);
|
150
|
+
@view.tbl_cmd.setItem(i, 1, it2);
|
52
151
|
end
|
53
|
-
@
|
54
|
-
@
|
55
|
-
@
|
152
|
+
@view.tbl_cmd.resizeColumnsToContents
|
153
|
+
@view.tbl_cmd.resizeRowsToContents
|
154
|
+
@view.tbl_cmd.horizontalHeader.stretchLastSection = true
|
56
155
|
rescue Exception => msg
|
57
|
-
|
58
|
-
logger.error msg
|
59
|
-
Qt::MessageBox.new(Qt::MessageBox::Critical, 'Critical error', 'Error while loading the command array. Consult the log for more details').exec
|
156
|
+
ErrorMsg.new.unknown(msg)
|
60
157
|
end
|
61
158
|
|
62
159
|
def select_chip_settings(bus)
|
63
160
|
case bus
|
64
161
|
when 'SPI'
|
65
|
-
@chip_settings =
|
162
|
+
@chip_settings = SpiSetting.find_by(chip_id: @chip.id)
|
66
163
|
@speeds = {
|
67
164
|
'25.00' => 3,
|
68
165
|
'18.75' => 4,
|
@@ -80,191 +177,84 @@ class Generic_commands < Qt::Widget
|
|
80
177
|
'0.29' => 255
|
81
178
|
}
|
82
179
|
when 'I2C'
|
83
|
-
@chip_settings =
|
84
|
-
@generic_command_gui.cbx_action.insertItem(3, 'Concatenate')
|
180
|
+
@chip_settings = @chip.i2c_setting
|
85
181
|
end
|
86
182
|
end
|
87
183
|
|
88
|
-
# Execute action
|
89
|
-
def exec_action
|
90
|
-
if @generic_command_gui.tbl_cmd.currentItem.nil?
|
91
|
-
Qt::MessageBox.new(Qt::MessageBox::Critical, 'Missing command', 'Select a command in the array first').exec
|
92
|
-
return 0
|
93
|
-
end
|
94
|
-
case @generic_command_gui.cbx_action.currentText
|
95
|
-
when 'Execute'
|
96
|
-
cmd_array = prepare_cmd
|
97
|
-
result = exec_cmd(@bus_name, cmd_array)
|
98
|
-
if @generic_command_gui.check_result.isChecked
|
99
|
-
export_manager = Export_manager.new(@bus_name, result, cmd_array)
|
100
|
-
export_manager.setWindowModality(Qt::ApplicationModal)
|
101
|
-
export_manager.show
|
102
|
-
end
|
103
|
-
when 'Template'
|
104
|
-
open_cmd_form(:option_1 => 'temp')
|
105
|
-
when 'Edit'
|
106
|
-
open_cmd_form(:option_1 => 'edit')
|
107
|
-
when 'Concatenate'
|
108
|
-
concatenate_cmds
|
109
|
-
when 'Delete'
|
110
|
-
delete_cmd
|
111
|
-
else
|
112
|
-
Qt::MessageBox.new(Qt::MessageBox::Critical, 'Wrong option', 'Please choose a correct action').exec
|
113
|
-
end
|
114
|
-
rescue Exception => msg
|
115
|
-
Qt::MessageBox.new(Qt::MessageBox::Critical, 'Critical error', 'Error occured while executing the action. Consult the logs for more details').exec
|
116
|
-
logger = Logger.new($logFilePath)
|
117
|
-
logger.error msg
|
118
|
-
end
|
119
|
-
|
120
184
|
def prepare_cmd
|
121
|
-
byte_list = Byte.where(
|
122
|
-
|
185
|
+
byte_list = Byte.where(command_id: Command.find_by(name: @view.tbl_cmd.currentItem.text))
|
186
|
+
packet = []
|
123
187
|
byte_list.each do |bl|
|
124
|
-
|
125
|
-
|
126
|
-
array_sent.push(bl.byte_value.to_i(16))
|
127
|
-
end
|
188
|
+
unless bl.iteration == 0 && bl.iteration.nil?
|
189
|
+
packet.push(bl.value.to_i(16))
|
128
190
|
else
|
129
|
-
|
191
|
+
for i in 1..bl.iteration.to_i do
|
192
|
+
packet.push(bl.value.to_i(16))
|
193
|
+
end
|
130
194
|
end
|
131
195
|
end
|
132
|
-
return
|
133
|
-
rescue Exception => msg
|
134
|
-
logger = Logger.new($logFilePath)
|
135
|
-
logger.error msg
|
136
|
-
Qt::MessageBox.new(Qt::MessageBox::Critical, 'Critical error', 'Error occured when preparing the command. Consult the log for more details').exec
|
196
|
+
return packet
|
137
197
|
end
|
138
198
|
|
139
199
|
def exec_cmd(bus, array_sent)
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
msg.setWindowTitle('Delete command')
|
156
|
-
msg.setText('Confirm the delete command action ?')
|
157
|
-
msg.setIcon(Qt::MessageBox::Question)
|
158
|
-
msg.setStandardButtons(Qt::MessageBox::Cancel | Qt::MessageBox::Ok)
|
159
|
-
msg.setDefaultButton(Qt::MessageBox::Cancel)
|
160
|
-
if msg.exec == Qt::MessageBox::Ok
|
161
|
-
@chip.cmd.find_by(cmd_name: @generic_command_gui.tbl_cmd.currentItem.text).destroy
|
162
|
-
feed_cmd_array
|
163
|
-
end
|
164
|
-
rescue Exception => msg
|
165
|
-
logger = Logger.new($logFilePath)
|
166
|
-
logger.error msg
|
167
|
-
Qt::MessageBox.new(Qt::MessageBox::Critical, 'Critical error', 'Error occured when deleting the command. Consult the log for more details').exec
|
168
|
-
end
|
169
|
-
|
170
|
-
def concatenate_cmds
|
171
|
-
if @generic_command_gui.tbl_cmd.selectedItems.count != 2
|
172
|
-
Qt::MessageBox.new(Qt::MessageBox::Critical, 'Wrong selection', 'Select two commands in the table to concatenate them').exec
|
173
|
-
return 0
|
174
|
-
end
|
175
|
-
bytesCmd1 = Byte.where(byte_cmd: Cmd.find_by(cmd_name: @generic_command_gui.tbl_cmd.selectedItems[0].text).cmd_id)
|
176
|
-
bytesCmd2 = Byte.where(byte_cmd: Cmd.find_by(cmd_name: @generic_command_gui.tbl_cmd.selectedItems[1].text).cmd_id)
|
177
|
-
if check_concatenation_size(bytesCmd1, bytesCmd2)
|
178
|
-
# Save cmd
|
179
|
-
cmd = Cmd.new
|
180
|
-
cmd.cmd_name = 'New concatenation'
|
181
|
-
cmd.cmd_desc = "Concatenation of #{@generic_command_gui.tbl_cmd.selectedItems[0].text} and #{@generic_command_gui.tbl_cmd.selectedItems[1].text} commands"
|
182
|
-
cmd.cmd_bus = @bus_id
|
183
|
-
cmd.cmd_chip = @chip.chip_id
|
184
|
-
cmd.save
|
185
|
-
# Save cmd bytes
|
186
|
-
bytesCmd1.each do |b1|
|
187
|
-
byte = Byte.new
|
188
|
-
byte.byte_index = b1.byte_index
|
189
|
-
byte.byte_value = b1.byte_value
|
190
|
-
byte.byte_description = b1.byte_description
|
191
|
-
byte.byte_iteration = b1.byte_iteration
|
192
|
-
byte.byte_cmd = Cmd.ids.last
|
193
|
-
byte.save
|
194
|
-
end
|
195
|
-
bytesCmd2.each do |b2|
|
196
|
-
byte2 = Byte.new
|
197
|
-
byte2.byte_index = Byte.last.byte_index + 1
|
198
|
-
byte2.byte_value = b2.byte_value
|
199
|
-
byte2.byte_description = b2.byte_description
|
200
|
-
byte2.byte_iteration = b2.byte_iteration
|
201
|
-
byte2.byte_cmd = Cmd.ids.last
|
202
|
-
byte2.save
|
200
|
+
Firmware.new(bus)
|
201
|
+
case bus
|
202
|
+
when 'SPI'
|
203
|
+
spi = HardsploitAPI_SPI.new(
|
204
|
+
speed: @speeds[@chip.spi_setting.frequency],
|
205
|
+
mode: @chip.spi_setting.mode
|
206
|
+
)
|
207
|
+
return spi.spi_Interact(payload: array_sent)
|
208
|
+
when 'I2C'
|
209
|
+
if [40, 100, 400, 1000].include?(@chip.i2c_setting.frequency)
|
210
|
+
speed = 0 if @chip.i2c_setting.frequency == 100
|
211
|
+
speed = 1 if @chip.i2c_setting.frequency == 400
|
212
|
+
speed = 2 if @chip.i2c_setting.frequency == 1000
|
213
|
+
speed = 3 if @chip.i2c_setting.frequency == 40
|
214
|
+
i2c = HardsploitAPI_I2C.new(speed: speed)
|
203
215
|
end
|
204
|
-
|
216
|
+
return i2c.i2c_Interact(payload: array_sent)
|
205
217
|
end
|
218
|
+
rescue HardsploitAPI::ERROR::HARDSPLOIT_NOT_FOUND
|
219
|
+
ErrorMsg.new.hardsploit_not_found
|
220
|
+
return false
|
221
|
+
rescue HardsploitAPI::ERROR::USB_ERROR
|
222
|
+
ErrorMsg.new.usb_error
|
223
|
+
return false
|
206
224
|
rescue Exception => msg
|
207
|
-
|
208
|
-
|
209
|
-
Qt::MessageBox.new(Qt::MessageBox::Critical, 'Critical error', 'Error occured when concatenating the command. Consult the log for more details').exec
|
225
|
+
ErrorMsg.new.unknown(msg)
|
226
|
+
return false
|
210
227
|
end
|
211
228
|
|
212
|
-
def check_concatenation_size(
|
213
|
-
|
214
|
-
|
215
|
-
|
229
|
+
def check_concatenation_size(bytes_cmd_1, bytes_cmd_2)
|
230
|
+
check_size = []
|
231
|
+
bytes_cmd_1.each do |b1|
|
232
|
+
check_size.push(b1.value)
|
216
233
|
end
|
217
|
-
|
218
|
-
|
234
|
+
bytes_cmd_2.each do |b2|
|
235
|
+
check_size.push(b2.value)
|
219
236
|
end
|
220
237
|
count = 0
|
221
238
|
i = 0
|
222
|
-
while i <= (
|
223
|
-
lowByte =
|
224
|
-
highByte =
|
225
|
-
commandType =
|
226
|
-
count += (lowByte.to_i(16) + (highByte.to_i(16)<<8))
|
239
|
+
while i <= (check_size.size) - 1 do
|
240
|
+
lowByte = check_size[i]
|
241
|
+
highByte = check_size[i + 1]
|
242
|
+
commandType = check_size[i + 2]
|
243
|
+
count += (lowByte.to_i(16) + (highByte.to_i(16) << 8))
|
227
244
|
if commandType.to_i(16) % 2 == 0 #WRITE
|
228
|
-
i += ((lowByte.to_i(16) + (highByte.to_i(16)<<8)) + 3)
|
245
|
+
i += ((lowByte.to_i(16) + (highByte.to_i(16) << 8)) + 3)
|
229
246
|
else #READ
|
230
247
|
i = (i + 3)
|
231
248
|
end
|
232
249
|
end
|
233
250
|
if count > 2000
|
234
|
-
Qt::MessageBox.new(
|
251
|
+
Qt::MessageBox.new(
|
252
|
+
Qt::MessageBox::Critical,
|
253
|
+
'Critical error',
|
254
|
+
'Command too big: unable to concatenate'
|
255
|
+
).exec
|
235
256
|
return false
|
236
257
|
end
|
237
258
|
return true
|
238
259
|
end
|
239
|
-
|
240
|
-
def open_cmd_form(options={})
|
241
|
-
if options[:option_1].nil?
|
242
|
-
if @bus_name == 'I2C'
|
243
|
-
cmdBase = I2c_command.new(@api, @chip, @bus_id, self)
|
244
|
-
else
|
245
|
-
cmdBase = Command_editor.new(0, nil, @chip, @bus_id, self, @api)
|
246
|
-
end
|
247
|
-
else
|
248
|
-
if options[:option_1] == 'temp'
|
249
|
-
cmdBase = Command_editor.new(1, @generic_command_gui.tbl_cmd.currentItem.text, @chip, @bus_id, self, @api)
|
250
|
-
else
|
251
|
-
cmdBase = Command_editor.new(2, @generic_command_gui.tbl_cmd.currentItem.text, @chip, @bus_id, self, @api)
|
252
|
-
end
|
253
|
-
end
|
254
|
-
cmdBase.setWindowModality(Qt::ApplicationModal)
|
255
|
-
cmdBase.show
|
256
|
-
end
|
257
|
-
|
258
|
-
def check_send_and_received_data(value)
|
259
|
-
case value
|
260
|
-
when HardsploitAPI::USB_STATE::PACKET_IS_TOO_LARGE
|
261
|
-
Qt::MessageBox.new(Qt::MessageBox::Critical, 'Critical error', "PACKET_IS_TOO_LARGE max: #{HardsploitAPI::USB::USB_TRAME_SIZE}").exec
|
262
|
-
when HardsploitAPI::USB_STATE::ERROR_SEND
|
263
|
-
Qt::MessageBox.new(Qt::MessageBox::Critical, 'Critical error', 'ERROR_SEND').exec
|
264
|
-
when HardsploitAPI::USB_STATE::BUSY
|
265
|
-
Qt::MessageBox.new(Qt::MessageBox::Warning, 'BUSY', 'Device busy').exec
|
266
|
-
else
|
267
|
-
return value
|
268
|
-
end
|
269
|
-
end
|
270
260
|
end
|