├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── FindCapstone.cmake ├── README.md ├── codeblock.cpp ├── codeblock.h ├── example ├── data_v117.txt ├── firmware_v117.bin ├── symbols_v117.sym ├── symbols_v117.txt └── vma.ld ├── imageelement.cpp ├── imageelement.h ├── imagemodel.cpp ├── imagemodel.h ├── main.cpp └── reas.sh /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # QtCreator 3 | *.autosave 4 | *.user 5 | *.user.* 6 | *.0000000 7 | 8 | # Apple 9 | .DS_Store 10 | 11 | # Vim 12 | *.swp 13 | tags 14 | .tags 15 | 16 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libchaos"] 2 | path = libchaos 3 | url = https://github.com/ChaoticConundrum/libchaos.git 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED(VERSION 2.8) 2 | PROJECT(ReAssemble) 3 | 4 | ADD_SUBDIRECTORY(libchaos) 5 | 6 | LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}") 7 | FIND_PACKAGE(Capstone REQUIRED) 8 | 9 | SET(SOURCES 10 | main.cpp 11 | codeblock.h 12 | codeblock.cpp 13 | imagemodel.h 14 | imagemodel.cpp 15 | imageelement.h 16 | imageelement.cpp 17 | ) 18 | 19 | ADD_EXECUTABLE(reassemble ${SOURCES}) 20 | TARGET_INCLUDE_DIRECTORIES(reassemble PRIVATE "${CAPSTONE_INCLUDE_DIRS}") 21 | TARGET_LINK_LIBRARIES(reassemble chaos "${CAPSTONE_LIBRARIES}") 22 | SET_PROPERTY(TARGET reassemble PROPERTY CXX_STANDARD 11) 23 | 24 | INSTALL(TARGETS reassemble RUNTIME DESTINATION bin) 25 | 26 | SET(FILES 27 | README.md 28 | reas.sh 29 | example/firmware_v117.bin 30 | example/symbols_v117.txt 31 | example/data_v117.txt 32 | example/vma.ld 33 | ) 34 | ADD_CUSTOM_TARGET(reassemble-dummy SOURCES ${FILES}) 35 | -------------------------------------------------------------------------------- /FindCapstone.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find CAPSTONE 2 | # Once done, this will define 3 | # CAPSTONE_FOUND - system has CAPSTONE 4 | # CAPSTONE_INCLUDE_DIRS - the CAPSTONE include directories 5 | # CAPSTONE_LIBRARIES - link these to use CAPSTONE 6 | 7 | find_package(PkgConfig) 8 | pkg_check_modules(PC_CAPSTONE capstone) 9 | 10 | find_path(CAPSTONE_INCLUDE_DIR 11 | capstone/capstone.h 12 | HINTS ${PC_CAPSTONE_INCLUDEDIR} ${PC_CAPSTONE_INCLUDE_DIRS} 13 | PATH_SUFFIXES capstone 14 | ) 15 | 16 | find_library(CAPSTONE_LIBRARY 17 | NAMES capstone 18 | HINTS ${PC_CAPSTONE_LIBDIR} ${PC_CAPSTONE_LIBRARY_DIRS} 19 | ) 20 | 21 | include(FindPackageHandleStandardArgs) 22 | # handle the QUIETLY and REQUIRED arguments and set CAPSTONE_FOUND to TRUE 23 | # if all listed variables are TRUE 24 | find_package_handle_standard_args(Capstone DEFAULT_MSG 25 | CAPSTONE_LIBRARY CAPSTONE_INCLUDE_DIR) 26 | 27 | mark_as_advanced(CAPSTONE_INCLUDE_DIR CAPSTONE_LIBRARY) 28 | 29 | set(CAPSTONE_LIBRARIES ${CAPSTONE_LIBRARY}) 30 | set(CAPSTONE_INCLUDE_DIRS ${CAPSTONE_INCLUDE_DIR}) 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## ReAssembler 2 | 3 | The ReAssembler is a tool for producing editable assembly code from compiled or assembled 4 | machine instructions. Currently only Thumb2 is supported (the author's motivation). 5 | 6 | ### Features 7 | - Instructions disassembled 8 | - Branch and call addresses replaced with labels 9 | - Non-code data preserved in assembly output 10 | - PC-relative loads given labels (pc offsets remain) 11 | - Function and data pointers replaced with labels 12 | 13 | This tool is intended as a convenience, and cannot guarantee the output will be completely 14 | independent of address dependence (e.g. a vector table, obscure function pointers). 15 | The output will likely need to be verified manually. 16 | 17 | In order to follow the code, this tool must be provided with all unique entry points, 18 | including the reset handler, IRQs, and addresses referenced outside the code 19 | (e.g. by a bootloader). 20 | 21 | I will admit the current incarnation is somewhat sloppy, but effective. Function and data 22 | pointer auto-analysis is limited. However, you can provide lists of functions, data, and 23 | code/data pointers, with custom lables, and the tool will disassemble as necessary, 24 | add labels, and reference labels in pointers, so address values are re-generated 25 | appropriately by the linker. See the examples directory for sample symbol/pointer lists. 26 | 27 | If at this point you do not understand what this tool does, it is probably not for you. 28 | To use this tool correctly, you need to understand the information you are providing it, 29 | know what to expect in the output, and know how to validate the output. 30 | 31 | ### Usage 32 | 33 | reassemble input_binary output_asm 34 | [-V] [-E] [-a image_vma] 35 | [-s symbol_address_file] 36 | [-d data_address_file] 37 | 38 | ### Example 39 | 40 | # Disassemble to assembly 41 | reassemble example/firmware.bin out.s -a 2c00 -s example/symbols.sym -d example/data.sym 42 | # Reassemble with standard tools, compare output and source binaries 43 | reas.sh out.s example/firmware_v117 44 | 45 | ### Symbol Address File 46 | example/symbols.sym 47 | 48 | # Function function1 at 0x2c04 49 | 2c04: function1 50 | # Address of function2 at 0x2d08 51 | * 2d08: function2 52 | # Automatically name function at 0x3a00 53 | 0x3a00 54 | 55 | # Define the number of cases in switch instruction at 2ddc as 12 56 | & 2ddc: 12 57 | 58 | ### Data Address File 59 | example/data.sym 60 | 61 | # Data data1 at 0x2c04 62 | 2c04: data1 63 | # Address of data2 at 0x2d08 64 | * 2d08: data2 65 | -------------------------------------------------------------------------------- /codeblock.cpp: -------------------------------------------------------------------------------- 1 | #include "codeblock.h" 2 | #include "zlog.h" 3 | 4 | CodeBlock::CodeBlock(zu64 iaddr) : addr(iaddr){ 5 | 6 | } 7 | 8 | bool CodeBlock::addCode(ZString instr, zu16 size){ 9 | Insn insn; 10 | insn.type = NORMAL; 11 | insn.prefix = instr; 12 | insn.size = size; 13 | insns.push(insn); 14 | return true; 15 | } 16 | 17 | bool CodeBlock::addBranch(ZString prefix, zu64 jaddr, ZString suffix, zu16 size, ZArray addrs){ 18 | Insn insn; 19 | insn.type = BRANCH; 20 | insn.prefix = prefix; 21 | insn.addr = jaddr; 22 | insn.suffix = suffix; 23 | insn.size = size; 24 | insns.push(insn); 25 | // Next block addresses 26 | next_addrs = addrs; 27 | return true; 28 | } 29 | 30 | ZString CodeBlock::toString() const { 31 | ZString str; 32 | for(zu64 i = 0; i < insns.size(); ++i){ 33 | Insn insn = insns[i]; 34 | ZString istr; 35 | switch(insn.type){ 36 | case NORMAL: 37 | istr = insn.prefix; 38 | break; 39 | case BRANCH: 40 | case LOAD: 41 | istr = insn.prefix + ZString::ItoS(insn.addr, 16) + insn.suffix; 42 | break; 43 | default: 44 | ELOG("Unknown instruction type"); 45 | istr = "ERROR"; 46 | break; 47 | } 48 | str += (istr + "\n"); 49 | } 50 | return str; 51 | } 52 | 53 | zu64 CodeBlock::size() const { 54 | zu64 len = 0; 55 | for(zu64 i = 0; i < insns.size(); ++i) 56 | len += insns[i].size; 57 | return len; 58 | } 59 | -------------------------------------------------------------------------------- /codeblock.h: -------------------------------------------------------------------------------- 1 | #ifndef CODEBLOCK_H 2 | #define CODEBLOCK_H 3 | 4 | #include 5 | 6 | #include "zstring.h" 7 | #include "zarray.h" 8 | using namespace LibChaos; 9 | 10 | class CodeBlock { 11 | public: 12 | enum insntype { 13 | NORMAL, 14 | BRANCH, 15 | LOAD, 16 | }; 17 | 18 | struct Insn { 19 | insntype type; 20 | ZString prefix; 21 | zu64 addr; 22 | ZString suffix; 23 | zu16 size; 24 | }; 25 | 26 | public: 27 | //! New basic block at addr. 28 | CodeBlock(zu64 addr); 29 | 30 | //! Add normal instruction to block. 31 | bool addCode(ZString instr, zu16 size); 32 | //! Add branch instruction to block. 33 | bool addBranch(ZString prefix, zu64 jaddr, ZString suffix, zu16 size, ZArray addrs); 34 | 35 | ZString toString() const; 36 | 37 | zu64 size() const; 38 | 39 | private: 40 | zu64 addr; 41 | ZArray insns; 42 | ZArray next_addrs; 43 | }; 44 | 45 | #endif // CODEBLOCK_H 46 | -------------------------------------------------------------------------------- /example/data_v117.txt: -------------------------------------------------------------------------------- 1 | 2 | 8998: usb_report0_desc 3 | 89d8: usb_report1_desc 4 | 89fc: usb_report2_desc 5 | 8ac4: usb_device_desc 6 | 8b34: usb_str0_desc 7 | 8b38: usb_str1_desc 8 | 8b54: update_handlers 9 | 10 | 8b70: data_4 11 | 8bb2: data_8 12 | 8c36: data_13 13 | 8d34: data_15 14 | 8d44: data_3 15 | 8d50: data_16 16 | 8de0: data_12 17 | 8e80: data_10 18 | 8ec8: data_7 19 | 8f10: data_5 20 | 8f58: data_6 21 | 8fa0: data_14 22 | 8fa8: data_9 23 | 24 | 8ff0: task_1 25 | 9000: task_2 26 | 9010: sram_init_data 27 | 28 | * 2df8 29 | * 2dfc 30 | * 3000 31 | * 3938 32 | * 3944 33 | * 3d64 34 | * 3d6c 35 | * 3d70 36 | * 3d74 37 | * 3d78 38 | * 3f88 39 | * 45ec 40 | * 4948 41 | * 4c30 42 | * 5828 43 | * 582c 44 | * 5830 45 | * 5834 46 | * 5968 47 | * 609c 48 | * 634c 49 | * 64c8 50 | * 651c 51 | * 6570 52 | * 6ae4 53 | * 6d28 54 | * 7768 55 | * 776c 56 | * 7770 57 | * 7774 58 | * 7910 59 | * 7b4c 60 | * 7b8c 61 | * 7e78 62 | * 7f14 63 | * 8098 64 | * 826c 65 | * 82c0 66 | * 830c 67 | * 8788 68 | * 87f8 69 | 70 | * 8ff0! 71 | * 9000! 72 | -------------------------------------------------------------------------------- /example/firmware_v117.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChaoticEnigma/reassemble/d9675435184f8c791a0ca58482ae8b620327e6a4/example/firmware_v117.bin -------------------------------------------------------------------------------- /example/symbols_v117.sym: -------------------------------------------------------------------------------- 1 | 2 | [code] 3 | 4 | * 2c04: reset 5 | * 2c08: nmi 6 | * 2c0c: hard_fault 7 | * 2c10: mcu_fault 8 | * 2c14: bus_fault 9 | * 2c18: usage_fault 10 | 11 | * 2c2c: svccall_intr 12 | * 2c30: debug_intr 13 | 14 | * 2c38: pendsv_intr 15 | * 2c3c: systick_intr 16 | * 2c40 17 | * 2c44 18 | * 2c48 19 | * 2c4c 20 | * 2c50 21 | * 2c54 22 | * 2c58 23 | * 2c5c 24 | * 2c60 25 | * 2c64 26 | * 2c68 27 | * 2c6c 28 | * 2c70 29 | * 2c74 30 | * 2c78 31 | * 2c7c 32 | * 2c80 33 | * 2c84 34 | * 2c88 35 | * 2c8c 36 | * 2c90 37 | * 2c94 38 | * 2c98 39 | * 2c9c 40 | * 2ca0 41 | * 2ca4 42 | 43 | * 2cac 44 | * 2cb0 45 | * 2cb4 46 | * 2cb8 47 | * 2cbc 48 | * 2cc0 49 | * 2cc4 50 | * 2cc8 51 | * 2ccc 52 | * 2cd0 53 | 54 | * 2ce4: bftm0_intr 55 | * 2ce8: bftm1_intr 56 | * 2cec 57 | * 2cf0: i2c1_intr 58 | * 2cf4 59 | * 2cf8 60 | * 2cfc 61 | * 2d00 62 | * 2d04 63 | * 2d08 64 | * 2d0c 65 | * 2d10 66 | * 2d14: usb_intr 67 | 68 | * 2d1c 69 | * 2d20 70 | * 2d24 71 | * 2d28 72 | * 2d2c 73 | * 2d30 74 | * 2d34 75 | * 2d38 76 | 77 | * 2d50 78 | 79 | #2d54: reset 80 | #2d60: nmi 81 | #2d62: hard_fault 82 | #2d64: mcu_fault 83 | #2d66: bus_fault 84 | #2d68: usage_fault 85 | #2d6a: svccall_intr 86 | #2d6c: debug_intr 87 | #2d6e: pendsv_intr 88 | #2d70: systick_intr 89 | 90 | 2d72: generic_intr 91 | 92 | 2d80: entry 93 | 2d94: rand 94 | 2da8: srand 95 | 2db8: memcpy 96 | 2ddc: sram_init 97 | 2e00: sram_decode_static 98 | 2e5c: function_e_32 99 | 2f50: afio_exti_ssr_write 100 | 2f78: handle_update_packet 101 | 3004: function_e_33 102 | 3068: reset_cmd 103 | 104 | #30c8: bftm0_intr 105 | #3250: bftm1_intr 106 | 107 | 3264: function_44 108 | 3272: function_45 109 | 3286: function_46 110 | 329a: function_47 111 | 329e: function_48 112 | 32a4: afio_pin_config 113 | 32cc: spin_wait_maybe 114 | 32d8: setup_afio_gpio 115 | 338c: function_e_34 116 | 339e: usb_disable_dppu 117 | 33a4: clear_two_words 118 | 33ac: clear_two_words_and_write_two_bytes 119 | 33b8: function_e_2 120 | 33e0: function_14 121 | 3412: function_e_35 122 | 3424: ckcu_ahb_clock_enable 123 | 343c: ckcu_apb0_clock_enable 124 | 3454: ckcu_apb1_clock_enable 125 | 346c: ckcu_set_usb_prescaler 126 | 3480: crc16 127 | 34c4: flash_page_erase 128 | 34d8: flash_get_protection_status 129 | 34e4: flash_write 130 | 351c: gpio_set_input_output 131 | 352c: gpio_set_output_current 132 | 353c: gpio_set_input_enable 133 | 354c: gpio_set_open_drain 134 | 355c: gpio_set_pin_pull_up_down 135 | 3584: gpio_set_pin 136 | 3588: gpio_set_reset_pin 137 | 3592: gptm_enable_disable 138 | 35a6: function_62 139 | 35fc: function_e_36 140 | 141 | #364c: i2c1_intr 142 | 143 | 377c: function_57 144 | 3790: function_58 145 | 379a: function_59 146 | 37ac: function_60 147 | 37c8: function_61 148 | 37dc: nop_1 149 | 37e0: function_13 150 | 3860: exti_set_wakup 151 | 3870: exti_check_wakup 152 | 3884: function_74 153 | 394c: function_e_17 154 | 3f90: function_e_18 155 | 425c: function_e_19 156 | 4618: nop_3 157 | 461a: nop_2 158 | 461c: function_37 159 | 46c4: function_e_21 160 | 4798: function_e_22 161 | 47bc: function_e_23 162 | 4924: function_e_24 163 | 4860: function_e_25 164 | 4958: function_e_26 165 | 4c44: function_e_27 166 | 4c58: function_e_28 167 | 4c6c: function_e_29 168 | 4cde: clear_some_mem 169 | 4cf6: function_e_30 170 | 4d26: function_e_31 171 | 4d3c: spi_flash_read_status 172 | 4d64: spi_init 173 | 4dd8: spi_flash_read_wip 174 | 4de4: spi_gpio_init 175 | 4e34: pinmux_select_b789 176 | 4e40: pinmux_b789_spi 177 | 4e84: pinmux_b789_gpio 178 | 4ecc: spi_flash_page_program 179 | 4f18: spi_flash_sector_erase 180 | 4f50: spi_flash_write_status_register 181 | 4f7c: function_78 182 | 4fa0: spi_flash_deep_power_down 183 | 4fc8: spi_flash_read_data 184 | 5008: spi_write_read 185 | 50a0: function_e_40 186 | 50f0: function_27 187 | 50c8: spi_flash_release_sleep 188 | 5168: spi_flash_write_enable 189 | 518c: spi_flash_set_status_reg 190 | 51b0: setup_intr_priority 191 | 5240: calc_nvic_priority 192 | 5272: ppb_set_nvic_priority 193 | 5294: nvic_set_vtor 194 | 52a8: spi_flash_init 195 | 52f0: spi_gpio_setup 196 | 5308: jump_2 197 | 530c: function_e_41 198 | 5380: function_e_42 199 | 53e4: function_e_43 200 | 5970: debug_cmd 201 | 59bc: function_63 202 | 5bc0: function_64 203 | 5c9c: function_36 204 | 5db0: function_50 205 | 5eb8: function_3 206 | 5efc: reset_peripheral 207 | 5f18: function_e_46 208 | 5f88: function_e_47 209 | 60c0: function_e_48 210 | 6170: function_51 211 | 6350: function_e_49 212 | 6414: function_e_50 213 | 6478: function_e_51 214 | 64cc: function_e_6 215 | 6520: function_e_52 216 | 6578: spi_enable 217 | 658c: spi_config 218 | 65f0: spi_slave_select_output 219 | 6604: function_e_4 220 | 6624: function_17 221 | 66b8: clock_init 222 | 6718: function_e_56 223 | 6780: function_e_57 224 | 6808: function_68 225 | 6814: function_69 226 | 6820: function_70 227 | 6854: function_e_58 228 | 68d8: ptr_write_func_ptrs 229 | 6918: function_66 230 | 695c: function_67 231 | 6a10: function_65 232 | 6a74: function_81 233 | 6a84: usb_get_descriptor 234 | 6ae8: function_6 235 | 6af2: usb_entry 236 | 6ca8: function_23 237 | 6cc6: function_75 238 | 6d06: gen_remote_wakeup 239 | 6d14: ptr_set_desc_ptr 240 | 6d30: usb_set_intr_flags 241 | 6d3c: usb_init 242 | 6d94: usb_dp_wake_up 243 | 6db0: usb_dp_pull_up 244 | 6dcc: usb_ckcu_voodoo 245 | 6dd4: usb_ep_data_toggle 246 | 6de4: usb_ep_stall 247 | 6df4: usb_set_ep_intr 248 | 6e00: usb_get_ep_buff 249 | 6e18: usb_get_ep_buff_end 250 | 6e3c: usb_get_ep_len 251 | 6e4a: function_e_67 252 | 6e64: usb_get_ep_intr 253 | 6e72: usb_get_ep_tcnt 254 | 6e84: function_e_70 255 | 6eaa: usb_read_ep_buff 256 | 6ee4: usb_recv 257 | 6f14: function_e_71 258 | 6f24: function_e_72 259 | 6f30: function_e_73 260 | 6f44: usb_copy_send 261 | 6f8c: usb_enable_intr 262 | 6f9c: function_e_75 263 | 6fbc: usb_get_intr_flags 264 | 6fcc: jump_1 265 | 6fd0: usb_set_lpmode_pdwn 266 | 6fe0: usb_clear_csr 267 | 6ff0: function_82 268 | 7064: ptr_write_data 269 | 70a8: usb_copy_ep_setup 270 | 70b8: usb_set_genrsm 271 | 70c8: usb_set_adrset 272 | 273 | #70dc: usb_intr 274 | 275 | 70e8: watchdog_init 276 | 7144: watchdog_reload_timer 277 | 278 | 7154: disconnect_cmd 279 | 7158: flash_crc_cmd 280 | 7178: flash_erase_cmd 281 | 71b8: flash_read_write_cmd 282 | 729c: update_start_cmd 283 | 284 | 72b0: function_e_78 285 | 7328: function_e_79 286 | 7444: function_e_80 287 | 7490: word_copy 288 | 74a0: usb_get_ep_reg 289 | 74b0: function_e_81_unsued 290 | 74c0: sram_stack_zero 291 | 74d0: function_e_82 292 | 7780: function_e_83 293 | 77b8: function_e_84 294 | 7848: function_e_85 295 | 7918: function_e_86 296 | 79d4: function_e_87 297 | 7a50: function_e_88 298 | 7a7c: function_e_89 299 | 7a98: function_e_90 300 | 7ac4: function_e_91 301 | 7b50: function_24 302 | 7b94: main 303 | 7cdc: function_e_92 304 | 7dc8: function_e_93 305 | 7e08: function_e_94 306 | 7e7c: function_e_95 307 | 7f18: function_e_8 308 | 80a4: function_e_7 309 | 8160: function_e_5 310 | 8278: function_e_9 311 | 82c4: function_e_10 312 | 8310: function_e_11 313 | 833c: function_1 314 | 83bc: function_e_14 315 | 8580: function_e_1 316 | 864a: function_e_96 317 | 8718: function_e_12 318 | 878c: function_e_13 319 | 87fc: function_e_15 320 | 88bc: function_80 321 | 88ec: function_26 322 | 8934: function_e_97 323 | 324 | * 3948 325 | 326 | * 68fc 327 | * 6900 328 | * 6904 329 | * 6908 330 | * 690c 331 | * 6910 332 | * 6914 333 | * 6a0c 334 | * 6d8c 335 | 336 | * 8b54 337 | * 8b58 338 | * 8b5c 339 | * 8b60 340 | * 8b64 341 | * 8b68 342 | * 8b6c 343 | 344 | #* 8ffc 345 | #* 900c 346 | 347 | [data] 348 | 349 | 2c00: vectors 350 | 351 | 8998: usb_report0_desc 352 | 89d8: usb_report1_desc 353 | 89fc: usb_report2_desc 354 | 8ac4: usb_device_desc 355 | 8b34: usb_str0_desc 356 | 8b38: usb_str1_desc 357 | 8b54: update_handlers 358 | 359 | 8b70: data_4 360 | 8bb2: data_8 361 | 8c36: data_13 362 | 8d34: data_15 363 | 8d44: data_3 364 | 8d50: data_16 365 | 8de0: data_12 366 | 8e80: data_10 367 | 8ec8: data_7 368 | 8f10: data_5 369 | 8f58: data_6 370 | 8fa0: data_14 371 | 8fa8: data_9 372 | 373 | 8ff0: task_1 374 | 9000: task_2 375 | 9010: sram_init_data 376 | 377 | * 2df8 378 | * 2dfc 379 | * 3000 380 | * 3938 381 | * 3944 382 | * 3d64 383 | * 3d6c 384 | * 3d70 385 | * 3d74 386 | * 3d78 387 | * 3f88 388 | * 45ec 389 | * 4948 390 | * 4c30 391 | * 5828 392 | * 582c 393 | * 5830 394 | * 5834 395 | * 5968 396 | * 609c 397 | * 634c 398 | * 64c8 399 | * 651c 400 | * 6570 401 | * 6ae4 402 | * 6d28 403 | * 7768 404 | * 776c 405 | * 7770 406 | * 7774 407 | * 7910 408 | * 7b4c 409 | * 7b8c 410 | * 7e78 411 | * 7f14 412 | * 8098 413 | * 826c 414 | * 82c0 415 | * 830c 416 | * 8788 417 | * 87f8 418 | 419 | * 8ff0! 420 | * 9000! 421 | 422 | -------------------------------------------------------------------------------- /example/symbols_v117.txt: -------------------------------------------------------------------------------- 1 | 2 | * 2c04 3 | * 2c08 4 | * 2c0c 5 | * 2c10 6 | * 2c14 7 | * 2c18 8 | * 2c2c 9 | * 2c30 10 | * 2c38 11 | * 2c3c 12 | * 2c40 13 | * 2c44 14 | * 2c48 15 | * 2c4c 16 | * 2c50 17 | * 2c54 18 | * 2c58 19 | * 2c5c 20 | * 2c60 21 | * 2c64 22 | * 2c68 23 | * 2c6c 24 | * 2c70 25 | * 2c74 26 | * 2c78 27 | * 2c7c 28 | * 2c80 29 | * 2c84 30 | * 2c88 31 | * 2c8c 32 | * 2c90 33 | * 2c94 34 | * 2c98 35 | * 2c9c 36 | * 2ca0 37 | * 2ca4 38 | * 2cac 39 | * 2cb0 40 | * 2cb4 41 | * 2cb8 42 | * 2cbc 43 | * 2cc0 44 | * 2cc4 45 | * 2cc8 46 | * 2ccc 47 | * 2cd0 48 | * 2ce4 49 | * 2ce8 50 | * 2cec 51 | * 2cf0 52 | * 2cf4 53 | * 2cf8 54 | * 2cfc 55 | * 2d00 56 | * 2d04 57 | * 2d08 58 | * 2d0c 59 | * 2d10 60 | * 2d14 61 | * 2d1c 62 | * 2d20 63 | * 2d24 64 | * 2d28 65 | * 2d2c 66 | * 2d30 67 | * 2d34 68 | * 2d38 69 | * 2d50 70 | 71 | 2d54: reset 72 | 2d60: nmi 73 | 2d62: hard_fault 74 | 2d64: mcu_fault 75 | 2d66: bus_fault 76 | 2d68: usage_fault 77 | 2d6a: svccall_intr 78 | 2d6c: debug_intr 79 | 2d6e: pendsv_intr 80 | 2d70: systick_intr 81 | 2d72: generic_intr 82 | 83 | 2d80: entry 84 | 2d94: rand 85 | 2da8: srand 86 | 2db8: memcpy 87 | 2ddc: sram_init 88 | 2e00: sram_decode_static 89 | 2e5c: function_e_32 90 | 2f50: afio_exti_ssr_write 91 | 2f78: handle_update_packet 92 | 3004: function_e_33 93 | 3068: reset_cmd 94 | 30c8: bftm0_intr 95 | 3250: bftm1_intr 96 | 3264: function_44 97 | 3272: function_45 98 | 3286: function_46 99 | 329a: function_47 100 | 329e: function_48 101 | 32a4: afio_pin_config 102 | 32cc: spin_wait_maybe 103 | 32d8: setup_afio_gpio 104 | 338c: function_e_34 105 | 339e: usb_disable_dppu 106 | 33a4: clear_two_words 107 | 33ac: clear_two_words_and_write_two_bytes 108 | 33b8: function_e_2 109 | 33e0: function_14 110 | 3412: function_e_35 111 | 3424: ckcu_ahb_clock_enable 112 | 343c: ckcu_apb0_clock_enable 113 | 3454: ckcu_apb1_clock_enable 114 | 346c: ckcu_set_usb_prescaler 115 | 3480: crc16 116 | 34c4: flash_page_erase 117 | 34d8: flash_get_protection_status 118 | 34e4: flash_write 119 | 351c: gpio_set_input_output 120 | 352c: gpio_set_output_current 121 | 353c: gpio_set_input_enable 122 | 354c: gpio_set_open_drain 123 | 355c: gpio_set_pin_pull_up_down 124 | 3584: gpio_set_pin 125 | 3588: gpio_set_reset_pin 126 | 3592: gptm_enable_disable 127 | 35a6: function_62 128 | 35fc: function_e_36 129 | 364c: i2c1_intr 130 | 377c: function_57 131 | 3790: function_58 132 | 379a: function_59 133 | 37ac: function_60 134 | 37c8: function_61 135 | 37dc: nop_1 136 | 37e0: function_13 137 | 3860: exti_set_wakup 138 | 3870: exti_check_wakup 139 | 3884: function_74 140 | 394c: function_e_17 141 | 3f90: function_e_18 142 | 425c: function_e_19 143 | 4618: nop_3 144 | 461a: nop_2 145 | 461c: function_37 146 | 46c4: function_e_21 147 | 4798: function_e_22 148 | 47bc: function_e_23 149 | 4924: function_e_24 150 | 4860: function_e_25 151 | 4958: function_e_26 152 | 4c44: function_e_27 153 | 4c58: function_e_28 154 | 4c6c: function_e_29 155 | 4cde: clear_some_mem 156 | 4cf6: function_e_30 157 | 4d26: function_e_31 158 | 4d3c: spi_flash_read_status 159 | 4d64: spi_init 160 | 4dd8: spi_flash_read_wip 161 | 4de4: spi_gpio_init 162 | 4e34: pinmux_select_b789 163 | 4e40: pinmux_b789_spi 164 | 4e84: pinmux_b789_gpio 165 | 4ecc: spi_flash_page_program 166 | 4f18: spi_flash_sector_erase 167 | 4f50: spi_flash_write_status_register 168 | 4f7c: function_78 169 | 4fa0: spi_flash_deep_power_down 170 | 4fc8: spi_flash_read_data 171 | 5008: spi_write_read 172 | 50a0: function_e_40 173 | 50f0: function_27 174 | 50c8: spi_flash_release_sleep 175 | 5168: spi_flash_write_enable 176 | 518c: spi_flash_set_status_reg 177 | 51b0: setup_intr_priority 178 | 5240: calc_nvic_priority 179 | 5272: ppb_set_nvic_priority 180 | 5294: nvic_set_vtor 181 | 52a8: spi_flash_init 182 | 52f0: spi_gpio_setup 183 | 5308: jump_2 184 | 530c: function_e_41 185 | 5380: function_e_42 186 | 53e4: function_e_43 187 | 5970: debug_cmd 188 | 59bc: function_63 189 | 5bc0: function_64 190 | 5c9c: function_36 191 | 5db0: function_50 192 | 5eb8: function_3 193 | 5efc: ckcu_voodoo1 194 | 5f18: function_e_46 195 | 5f88: function_e_47 196 | 60c0: function_e_48 197 | 6170: function_51 198 | 6350: function_e_49 199 | 6414: function_e_50 200 | 6478: function_e_51 201 | 64cc: function_e_6 202 | 6520: function_e_52 203 | 6578: spi_enable 204 | 658c: spi_config 205 | 65f0: spi_slave_select_output 206 | 6604: function_e_4 207 | 6624: function_17 208 | 66b8: clock_init 209 | 6718: function_e_56 210 | 6780: function_e_57 211 | 6808: function_68 212 | 6814: function_69 213 | 6820: function_70 214 | 6854: function_e_58 215 | 68d8: ptr_write_func_ptrs 216 | 6918: function_66 217 | 695c: function_67 218 | 6a10: function_65 219 | 6a74: function_81 220 | 6a84: usb_get_descriptor 221 | 6ae8: function_6 222 | 6af2: usb_entry 223 | 6ca8: function_23 224 | 6cc6: function_75 225 | 6d06: gen_remote_wakeup 226 | 6d14: ptr_set_desc_ptr 227 | 6d30: usb_set_intr_flags 228 | 6d3c: usb_init 229 | 6d94: usb_dp_wake_up 230 | 6db0: usb_dp_pull_up 231 | 6dcc: usb_ckcu_voodoo 232 | 6dd4: usb_ep_data_toggle 233 | 6de4: usb_ep_stall 234 | 6df4: usb_set_ep_intr 235 | 6e00: usb_get_ep_buff 236 | 6e18: usb_get_ep_buff_end 237 | 6e3c: usb_get_ep_len 238 | 6e4a: function_e_67 239 | 6e64: usb_get_ep_intr 240 | 6e72: usb_get_ep_tcnt 241 | 6e84: function_e_70 242 | 6eaa: usb_read_ep_buff 243 | 6ee4: usb_recv 244 | 6f14: function_e_71 245 | 6f24: function_e_72 246 | 6f30: function_e_73 247 | 6f44: usb_copy_send 248 | 6f8c: usb_enable_intr 249 | 6f9c: function_e_75 250 | 6fbc: usb_get_intr_flags 251 | 6fcc: jump_1 252 | 6fd0: usb_set_lpmode_pdwn 253 | 6fe0: usb_clear_csr 254 | 6ff0: function_82 255 | 7064: ptr_write_data 256 | 70a8: usb_copy_ep_setup 257 | 70b8: usb_set_genrsm 258 | 70c8: usb_set_adrset 259 | 70dc: usb_intr 260 | 70e8: watchdog_init 261 | 7144: watchdog_reload_timer 262 | 7154: disconnect_cmd 263 | 7158: flash_crc_cmd 264 | 7178: flash_erase_cmd 265 | 71b8: flash_read_write_cmd 266 | 729c: update_start_cmd 267 | 72b0: function_e_78 268 | 7328: function_e_79 269 | 7444: function_e_80 270 | 7490: word_copy 271 | 74a0: usb_get_ep_reg 272 | 74b0: function_e_81_unsued 273 | 74c0: sram_stack_zero 274 | 74d0: function_e_82 275 | 7780: function_e_83 276 | 77b8: function_e_84 277 | 7848: function_e_85 278 | 7918: function_e_86 279 | 79d4: function_e_87 280 | 7a50: function_e_88 281 | 7a7c: function_e_89 282 | 7a98: function_e_90 283 | 7ac4: function_e_91 284 | 7b50: function_24 285 | 7b94: main 286 | 7cdc: function_e_92 287 | 7dc8: function_e_93 288 | 7e08: function_e_94 289 | 7e7c: function_e_95 290 | 7f18: function_e_8 291 | 80a4: function_e_7 292 | 8160: function_e_5 293 | 8278: function_e_9 294 | 82c4: function_e_10 295 | 8310: function_e_11 296 | 833c: function_1 297 | 83bc: function_e_14 298 | 8580: function_e_1 299 | 864a: function_e_96 300 | 8718: function_e_12 301 | 878c: function_e_13 302 | 87fc: function_e_15 303 | 88bc: function_80 304 | 88ec: function_26 305 | 8934: function_e_97 306 | 307 | * 3948 308 | 309 | * 68fc 310 | * 6900 311 | * 6904 312 | * 6908 313 | * 690c 314 | * 6910 315 | * 6914 316 | * 6a0c 317 | * 6d8c 318 | 319 | #* 8ffc 320 | #* 900c 321 | -------------------------------------------------------------------------------- /example/vma.ld: -------------------------------------------------------------------------------- 1 | 2 | OUTPUT_FORMAT ("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") 3 | OUTPUT_ARCH(arm) 4 | 5 | MEMORY { 6 | flash (rx) : ORIGIN = 0x0, LENGTH = 128K 7 | } 8 | 9 | IMAGE_BASE = 0x0; 10 | 11 | SECTIONS { 12 | .text IMAGE_BASE : { 13 | *(.text) 14 | } > flash 15 | } 16 | -------------------------------------------------------------------------------- /imageelement.cpp: -------------------------------------------------------------------------------- 1 | #include "imageelement.h" 2 | 3 | ImageElement::ImageElement() : type(RAW), size(0), ctype(NORMAL), ftype(F_STRING){ 4 | 5 | } 6 | -------------------------------------------------------------------------------- /imageelement.h: -------------------------------------------------------------------------------- 1 | #ifndef IMAGEELEMENT_H 2 | #define IMAGEELEMENT_H 3 | 4 | #include "zbinary.h" 5 | 6 | using namespace LibChaos; 7 | 8 | class ImageElement { 9 | public: 10 | enum reftype { 11 | DATA, 12 | CODE, 13 | RAW, 14 | }; 15 | enum codetype { 16 | NORMAL, 17 | DBRANCH, 18 | IBRANCH, 19 | LOAD, 20 | }; 21 | enum fmtype { 22 | F_STRING, 23 | F_TARGET, 24 | }; 25 | enum refflags { 26 | THUMBFUNC = 1, 27 | }; 28 | 29 | public: 30 | ImageElement(); 31 | 32 | public: 33 | reftype type; 34 | zu16 size; 35 | ZString str; 36 | 37 | codetype ctype; 38 | zu64 target; 39 | 40 | fmtype ftype; 41 | ZString suffix; 42 | }; 43 | 44 | #endif // IMAGEELEMENT_H 45 | -------------------------------------------------------------------------------- /imagemodel.cpp: -------------------------------------------------------------------------------- 1 | #include "imagemodel.h" 2 | #include "zlog.h" 3 | 4 | ImageModel::ImageModel(bool oequiv, bool overbose) : equiv(oequiv), verbose(overbose), base(0){ 5 | int mode = CS_MODE_THUMB | CS_MODE_MCLASS; 6 | err = cs_open(CS_ARCH_ARM, (cs_mode)mode, &handle); 7 | if(err != CS_ERR_OK){ 8 | ELOG("failed to open capstone"); 9 | return; 10 | } 11 | err = cs_option(handle, CS_OPT_DETAIL, CS_OPT_ON); 12 | if(err != CS_ERR_OK){ 13 | ELOG("failed to set capstone option"); 14 | return; 15 | } 16 | } 17 | 18 | ImageModel::~ImageModel(){ 19 | cs_close(&handle); 20 | } 21 | 22 | void ImageModel::loadImage(const ZBinary &inbin, zu64 offset){ 23 | base = offset; 24 | image = inbin; 25 | } 26 | 27 | zu64 ImageModel::addEntry(zu64 addr, ZString name, ArZ params){ 28 | addLabel(addr, CODE, NAMED, name); 29 | if(params.size()){ 30 | ZString str = "(" + ZString::join(params, ", ") + ")"; 31 | lparams.add(addr, str); 32 | } 33 | return disassembleAddress(addr); 34 | } 35 | 36 | zu64 ImageModel::addCodePointer(zu64 addr, ZString name){ 37 | zu64 offset = _addrToOffset(addr); 38 | 39 | if(code.contains(addr)){ 40 | ELOG("pointer is code"); 41 | return 0; 42 | } 43 | 44 | image.seek(offset); 45 | zu64 taddr = image.readleu32() & ~(zu64)1; 46 | 47 | if(base > taddr){ 48 | ELOG("pointer in wrong range"); 49 | return 0; 50 | } 51 | zu64 toffset = taddr - base; 52 | if(toffset >= image.size()){ 53 | ELOG("pointer out of bounds " << HEX(addr) << " " << HEX(taddr)); 54 | return 0; 55 | } 56 | 57 | DataWord dword; 58 | dword.type = CPTR; 59 | dword.data = taddr; 60 | data.add(addr, dword); 61 | 62 | addLabel(taddr, CODE, NAMED, name, true); 63 | return disassembleAddress(taddr); 64 | } 65 | 66 | zu64 ImageModel::addData(zu64 addr, ZString name, zu64 words){ 67 | addLabel(addr, DATA, NAMED, name); 68 | for(zu64 i = 0; i < words; ++i){ 69 | zu64 offset = _addrToOffset(addr); 70 | image.seek(offset); 71 | zu64 word = image.readleu32(); 72 | if(!data.contains(addr)) 73 | data.add(addr, { VALUE, word }); 74 | addr += 4; 75 | } 76 | return 0; 77 | } 78 | 79 | zu64 ImageModel::addDataPointer(zu64 addr, ZString name, zu64 words){ 80 | zu64 offset = _addrToOffset(addr); 81 | 82 | if(code.contains(addr)){ 83 | ELOG("pointer is code"); 84 | return 0; 85 | } 86 | 87 | image.seek(offset); 88 | zu64 taddr = image.readleu32(); 89 | 90 | if(base > taddr){ 91 | ELOG("pointer in wrong range: " << HEX(addr) << " " << HEX(taddr)); 92 | return 0; 93 | } 94 | zu64 toffset = taddr - base; 95 | if(toffset > image.size()){ 96 | ELOG("pointer out of bounds " << HEX(addr) << " " << HEX(taddr)); 97 | return 0; 98 | } 99 | 100 | DataWord dword; 101 | dword.type = DPTR; 102 | dword.data = taddr; 103 | data.add(addr, dword); 104 | 105 | return addData(taddr, name, words); 106 | } 107 | 108 | zu64 ImageModel::disassembleAddress(zu64 start_addr, ZStack stack){ 109 | // Odd alignment, do not disassemble 110 | ZASSERT((start_addr & 0x1) == 0, ZString("not disassembling odd address ") + HEX(start_addr)); 111 | 112 | zu64 start_offset = _addrToOffset(start_addr); 113 | 114 | // if this address is already disassembled we're done 115 | if(insns.contains(start_addr)){ 116 | return 0; 117 | } 118 | // LOG("Disassemble from 0x" << HEX(addr) << " (" << label.str << ")"); 119 | 120 | const zu8 *iptr = image.raw() + start_offset; 121 | zu64 isize = image.size() - start_offset; 122 | zu64 iaddr = start_addr; 123 | cs_insn *insn = cs_malloc(handle); 124 | zu64 total = 0; 125 | 126 | // vars for indirect jumps 127 | unsigned ldr_reg = ARM_REG_INVALID; 128 | zu64 ldr_addr = 0; 129 | zu64 ldr_data = 0; 130 | bool ldr_flag = false; 131 | 132 | bool stop = false; 133 | 134 | ZPointer block = new CodeBlock(start_addr); 135 | code.add(start_addr, block); 136 | 137 | // disassemble instructions 138 | while(true){ 139 | // next instruction 140 | zu64 addr = iaddr; 141 | zu64 offset = iaddr - base; 142 | if(!cs_disasm_iter(handle, &iptr, (size_t *)&isize, &iaddr, insn)){ 143 | // disassemble error 144 | ELOG("disassemble error: 0x" << HEX(base + offset) << " " << 145 | cs_strerror(cs_errno(handle))); 146 | // print jump stack 147 | for(zu64 i = 0; i < stack.size(); ++i){ 148 | ELOG(ZLog::RAW << i << ": 0x" << stack.peek() << ZLog::NEWLN); 149 | stack.pop(); 150 | } 151 | ZASSERT(false, "invalid instruction " + HEX(base + offset)); 152 | break; 153 | } 154 | 155 | ZASSERT(addr == insn->address, "insn address mismatch"); 156 | 157 | if(insns.contains(insn->address)){ 158 | // ran into already disassembled code 159 | cs_free(insn, 1); 160 | return total; 161 | } 162 | 163 | ZString insnstr = ZString(insn->mnemonic) + " " + insn->op_str; 164 | 165 | if(verbose) LOG(HEX(insn->address) << ": " << insnstr); 166 | 167 | CodeBlock::Insn cins; 168 | cins.type = CodeBlock::NORMAL; 169 | cins.prefix = insnstr; 170 | cins.size = insn->size; 171 | insns.add(insn->address, cins); 172 | 173 | // Handle instruction 174 | switch(insn->id){ 175 | // Jumps change control flow 176 | case ARM_INS_B: { 177 | // Direct Branch 178 | zassert(insn->detail->arm.op_count == 1 && 179 | insn->detail->arm.operands[0].type == ARM_OP_IMM); 180 | 181 | zu64 jaddr = insn->detail->arm.operands[0].imm; 182 | ZString bstr = ZString(insn->mnemonic) + " "; 183 | 184 | // add branch insn 185 | if(insn->detail->arm.cc == ARM_CC_AL){ 186 | block->addBranch(bstr, jaddr, "", insn->size, { jaddr }); 187 | // stop if unconditional 188 | stop = true; 189 | } else { 190 | block->addBranch(bstr, jaddr, "", insn->size, { insn->address + insn->size, jaddr }); 191 | block = new CodeBlock(start_addr); 192 | code.add(start_addr, block); 193 | } 194 | 195 | cins.type = CodeBlock::BRANCH; 196 | cins.prefix = bstr; 197 | cins.addr = jaddr; 198 | 199 | addLabel(jaddr, CODE, JUMP); 200 | stack.push(HEX(insn->address) + " " + insnstr); 201 | total += disassembleAddress(jaddr, stack); 202 | stack.pop(); 203 | break; 204 | } 205 | case ARM_INS_CBZ: 206 | case ARM_INS_CBNZ: { 207 | // Conditional Branch 208 | zu64 jaddr = insn->detail->arm.operands[1].imm; 209 | ZString bstr = ZString(insn->mnemonic) + " " + 210 | cs_reg_name(handle, insn->detail->arm.operands[0].reg) + ", "; 211 | 212 | // add branch insn 213 | block->addBranch(bstr, jaddr, "", insn->size, { insn->address + insn->size, jaddr }); 214 | 215 | cins.type = CodeBlock::BRANCH; 216 | cins.prefix = bstr; 217 | cins.addr = jaddr; 218 | 219 | addLabel(jaddr, CODE, JUMP); 220 | stack.push(HEX(insn->address) + " " + insnstr); 221 | total += disassembleAddress(jaddr, stack); 222 | stack.pop(); 223 | 224 | block = new CodeBlock(start_addr); 225 | code.add(start_addr, block); 226 | break; 227 | } 228 | case ARM_INS_BX: 229 | // Branch register 230 | if(ldr_reg != ARM_REG_INVALID && insn->detail->arm.operands[0].reg == ldr_reg){ 231 | // Indirect jump 232 | zu64 jaddr = ldr_data & ~(zu64)1; 233 | 234 | if(verbose) LOG("-> " << HEX(jaddr)); 235 | 236 | // change data type 237 | data[ldr_addr].type = CPTR; 238 | data[ldr_addr].data = jaddr; 239 | 240 | // add branch insn 241 | ZString bstr = ZString(insn->mnemonic) + " " + insn->op_str; 242 | block->addBranch(bstr + " /* ", jaddr, " */", insn->size, { jaddr }); 243 | 244 | // add insn 245 | cins.type = CodeBlock::BRANCH; 246 | cins.prefix = bstr + " /* "; 247 | cins.addr = jaddr; 248 | cins.suffix = " */"; 249 | 250 | addLabel(jaddr, CODE, JUMP, "", true); 251 | stack.push(HEX(insn->address) + " " + insnstr); 252 | total += disassembleAddress(jaddr, stack); 253 | stack.pop(); 254 | 255 | } else if(insn->detail->arm.operands[0].reg == ARM_REG_LR){ 256 | // return 257 | if(verbose) LOG("<-"); 258 | } else { 259 | DLOG("branch register at " << HEX(insn->address)); 260 | 261 | // add branch insn 262 | block->addCode(ZString(insn->mnemonic) + " " + insn->op_str, insn->size); 263 | 264 | // add insn 265 | cins.type = CodeBlock::NORMAL; 266 | cins.prefix = ZString(insn->mnemonic) + " " + insn->op_str; 267 | } 268 | // unconditional 269 | stop = true; 270 | break; 271 | 272 | // Sometimes changes control flow 273 | case ARM_INS_POP: 274 | // Pop stack 275 | for(int i = 0; i < insn->detail->arm.op_count; ++i){ 276 | if(insn->detail->arm.operands[i].type == ARM_OP_REG && 277 | insn->detail->arm.operands[i].reg == ARM_REG_PC){ 278 | // PC popped 279 | if(verbose) LOG("<-"); 280 | // return 281 | stop = true; 282 | } 283 | } 284 | break; 285 | 286 | // Calls reference new functions 287 | case ARM_INS_BL: { 288 | // Branch and link 289 | zassert(insn->detail->arm.op_count == 1 && 290 | insn->detail->arm.operands[0].type == ARM_OP_IMM); 291 | 292 | // Direct call 293 | zu64 jaddr = insn->detail->arm.operands[0].imm; 294 | 295 | if(jaddr < base + image.size()){ 296 | 297 | ZString bstr = ZString(insn->mnemonic) + " "; 298 | cins.type = CodeBlock::BRANCH; 299 | cins.prefix = bstr; 300 | cins.addr = jaddr; 301 | 302 | addLabel(jaddr, CODE, CALL); 303 | stack.push(HEX(insn->address) + " " + insnstr); 304 | total += disassembleAddress(jaddr, stack); 305 | stack.pop(); 306 | } 307 | 308 | break; 309 | } 310 | case ARM_INS_BLX: 311 | // Branch and link register 312 | if(ldr_reg != ARM_REG_INVALID && insn->detail->arm.operands[0].reg == ldr_reg){ 313 | // Indirect call 314 | zu64 caddr = ldr_data & ~(zu64)1; 315 | ZString bstr = ZString(insn->mnemonic) + " " + insn->op_str; 316 | 317 | if(verbose) LOG("-> " << HEX(caddr)); 318 | 319 | // change data type 320 | data[ldr_addr].type = CPTR; 321 | data[ldr_addr].data = caddr; 322 | 323 | // add insn 324 | cins.type = CodeBlock::BRANCH; 325 | cins.prefix = bstr + " /* "; 326 | cins.addr = caddr; 327 | cins.suffix = " */"; 328 | 329 | addLabel(caddr, CODE, CALL, "", true); 330 | stack.push(HEX(insn->address) + " " + insnstr); 331 | total += disassembleAddress(caddr, stack); 332 | stack.pop(); 333 | 334 | } else { 335 | DLOG("call register at " << HEX(insn->address)); 336 | 337 | // add branch insn 338 | block->addCode(ZString(insn->mnemonic) + " " + insn->op_str, insn->size); 339 | } 340 | break; 341 | 342 | // Table branches 343 | case ARM_INS_TBB: { 344 | // Table branch byte 345 | if(insn->detail->arm.op_count == 1 && 346 | insn->detail->arm.operands[0].type == ARM_OP_MEM && 347 | insn->detail->arm.operands[0].mem.base == ARM_REG_PC){ 348 | DLOG("tbb @ " << HEX(addr)); 349 | // PC relative 350 | zu64 pc = addr + insn->size; 351 | 352 | // Keep track of lowest switch handler 353 | zu64 min = ZU64_MAX; 354 | // get max cases 355 | zu64 maxcases = 0xFF; 356 | if(switches.contains(addr)){ 357 | maxcases = switches[addr]; 358 | } 359 | for(zu64 i = 0; i < maxcases; ++i){ 360 | if(pc + i < min && 361 | !insns.contains(pc + i) && 362 | !data.contains(pc + i) && 363 | !labels.contains(pc + i)){ 364 | zu64 bboff = pc - base + i; 365 | zu8 bbyte = image[bboff]; 366 | zu64 bbaddr = _offsetToAddr(bboff); 367 | zu64 baddr = pc + (bbyte << 1); 368 | // Check that branch address is after the table so far 369 | if(baddr > pc + i){ 370 | min = MIN(min, baddr); 371 | DLOG("Switch Case " << HEX(bbaddr) << " -> " << HEX(baddr)); 372 | addLabel(baddr, CODE, SWITCH); 373 | addAnnotation(bbaddr, "case switch_" + HEX_PAD(baddr, 4)); 374 | stack.push(HEX(insn->address) + " " + insnstr); 375 | total += disassembleAddress(baddr, stack); 376 | stack.pop(); 377 | } else { 378 | // invalid branch value 379 | break; 380 | } 381 | } else { 382 | break; 383 | } 384 | } 385 | } 386 | // Instructions immediately after this are junk 387 | stop = true; 388 | break; 389 | } 390 | 391 | // Load from memory 392 | case ARM_INS_LDR: { 393 | // Load 394 | if(insn->detail->arm.op_count == 2 && 395 | insn->detail->arm.operands[1].type == ARM_OP_MEM && 396 | insn->detail->arm.operands[1].mem.base == ARM_REG_PC){ 397 | // PC-relative load 398 | zu64 pc = (base + offset + 4) & ~(zu64)3; 399 | zu64 laddr = pc + insn->detail->arm.operands[1].mem.disp; 400 | image.seek(laddr - base); 401 | 402 | // save for next loop, next insn->may use 403 | ldr_addr = laddr; 404 | ldr_reg = insn->detail->arm.operands[0].reg; 405 | ldr_data = image.readleu32(); 406 | ldr_flag = true; 407 | 408 | // add insn 409 | cins.type = CodeBlock::LOAD; 410 | cins.prefix = ZString(insn->mnemonic) + " " + insn->op_str + " /* "; 411 | cins.addr = laddr; 412 | cins.suffix = " */"; 413 | 414 | // add label 415 | addLabel(laddr, DATA, LDR); 416 | 417 | DataWord dword; 418 | dword.type = VALUE; 419 | dword.data = ldr_data; 420 | data.add(laddr, dword); 421 | 422 | } else if(insn->detail->arm.operands[0].reg == ARM_REG_PC){ 423 | // Load into PC breaks control flow 424 | stop = true; 425 | } 426 | break; 427 | } 428 | 429 | case ARM_INS_ADD: 430 | case ARM_INS_SUB: { 431 | // Some instructions are encoded differently by GNU AS 432 | if(!equiv && 433 | insn->size == 2 && 434 | insn->detail->arm.op_count == 3 && 435 | insn->detail->arm.operands[0].type == ARM_OP_REG && 436 | insn->detail->arm.operands[1].type == ARM_OP_REG && 437 | insn->detail->arm.operands[0].reg == insn->detail->arm.operands[1].reg && 438 | insn->detail->arm.operands[2].type == ARM_OP_IMM && 439 | insn->detail->arm.operands[2].imm < 8){ 440 | // Some add/sub formats will be displayed as shorts 441 | image.seek(offset); 442 | ZString istr = ZString(insn->mnemonic) + " " + insn->op_str; 443 | cins.type = CodeBlock::NORMAL; 444 | cins.prefix = ".short 0x" + HEX(image.readleu16()) + " /* " + istr + " */ "; 445 | } else if(insn->detail->arm.operands[0].reg == ARM_REG_PC){ 446 | // Add/sub to PC breaks control flow 447 | stop = true; 448 | } 449 | break; 450 | } 451 | 452 | case ARM_INS_MOV: 453 | if(insn->detail->arm.operands[0].reg == ARM_REG_PC){ 454 | // Mov to PC breaks control flow 455 | stop = true; 456 | } 457 | break; 458 | 459 | 460 | default: 461 | break; 462 | } 463 | 464 | // add instructiopn 465 | insns.add(insn->address, cins); 466 | 467 | total++; 468 | 469 | if(stop) 470 | break; 471 | 472 | if(ldr_flag){ 473 | ldr_flag = false; 474 | } else { 475 | ldr_reg = ARM_REG_INVALID; 476 | } 477 | } 478 | 479 | cs_free(insn, 1); 480 | 481 | return total; 482 | } 483 | 484 | ZBinary ImageModel::makeCode(bool offsets, bool annotate){ 485 | ZString asem; 486 | asem += ".syntax unified\n"; 487 | asem += ".cpu cortex-m3\n"; 488 | asem += ".text\n"; 489 | asem += ".thumb\n\n"; 490 | 491 | ImageElement::reftype prev = ImageElement::RAW; 492 | 493 | for(zu64 i = 0; i <= image.size();){ 494 | zu64 addr = base + i; 495 | 496 | // make label 497 | ZString labelstr; 498 | if(labels.contains(addr)){ 499 | Label label = labels[addr]; 500 | // LOG("label " << HEX(addr) << " " << label.str); 501 | // asem += "\n"; 502 | if(label.thumbfunc || (label.ltype == CODE && (label.ntype == CALL || label.ntype == NAMED))){ 503 | labelstr += "\n"; 504 | if(offsets) labelstr += " "; 505 | labelstr += ".thumb_func\n"; 506 | } 507 | if(offsets) labelstr += " "; 508 | labelstr += label.str; 509 | labelstr += ":"; 510 | if(lparams.contains(addr)){ 511 | labelstr += " /* " + lparams[addr] + " */"; 512 | } 513 | labelstr += "\n"; 514 | } 515 | 516 | if(insns.contains(addr) && data.contains(addr) && !forcetype.contains(addr)){ 517 | ELOG("Both code and data at " << HEX_PAD(addr, 4)); 518 | } 519 | 520 | if(insns.contains(addr) && 521 | (forcetype.contains(addr) ? forcetype[addr] == CODE : true)){ 522 | // Code 523 | if(prev != ImageElement::CODE) 524 | asem += "\n"; 525 | asem += labelstr; 526 | 527 | CodeBlock::Insn insn = insns[addr]; 528 | // LOG("code " << HEX(addr) << " " << insn.size << " " << insn.prefix); 529 | if(insn.size == 0){ 530 | ELOG("size zero ref " << HEX(base + i)); 531 | ++i; 532 | continue; 533 | } 534 | 535 | ZString istr; 536 | switch(insn.type){ 537 | case CodeBlock::NORMAL: 538 | // Just a string 539 | istr = insn.prefix; 540 | break; 541 | 542 | case CodeBlock::BRANCH: 543 | case CodeBlock::LOAD: 544 | // Get the label name of the target 545 | if(labels.contains(insn.addr)){ 546 | istr = insn.prefix + labels[insn.addr].str + insn.suffix; 547 | if(lparams.contains(insn.addr)){ 548 | addAnnotation(addr, lparams[insn.addr]); 549 | } 550 | } else { 551 | ELOG("missing target label " << HEX(insn.addr)); 552 | istr = insn .prefix + "0x" + HEX(insn.addr) + insn.suffix; 553 | } 554 | break; 555 | 556 | default: 557 | break; 558 | } 559 | 560 | if(offsets) asem += "/*0x" + HEX_PAD(addr, 4) + "*/ "; 561 | asem += " "; 562 | asem += istr; 563 | 564 | if(annotate && annotations.contains(addr)){ 565 | asem += (" /* " + annotations[addr] + " */"); 566 | } 567 | asem += "\n"; 568 | 569 | prev = ImageElement::CODE; 570 | i += insn.size; 571 | 572 | } else if(data.contains(addr) && 573 | (forcetype.contains(addr) ? forcetype[addr] == DATA : true)){ 574 | // Data 575 | if(prev != ImageElement::DATA) 576 | asem += "\n"; 577 | asem += labelstr; 578 | 579 | if(offsets) asem += "/*0x" + HEX_PAD(addr, 4) + "*/ "; 580 | 581 | DataWord dword = data[addr]; 582 | switch(dword.type){ 583 | case VALUE: 584 | asem += (".word 0x" + HEX_PAD(dword.data, 8)); 585 | if(dword.data >= base && dword.data < (base + image.size())){ 586 | LOG("Possible pointer @ " << HEX_PAD(addr, 4) << ": " << HEX_PAD(dword.data, 8)); 587 | if(!annotations.contains(addr)) 588 | addAnnotation(addr, "possible pointer"); 589 | } 590 | break; 591 | 592 | case CPTR: 593 | case DPTR: 594 | if(labels.contains(dword.data)){ 595 | asem += (".word " + labels[dword.data].str); 596 | } else { 597 | ELOG("missing pointer label " << HEX_PAD(dword.data, 4)); 598 | asem += (".word 0x" + HEX_PAD(dword.data, 4)); 599 | } 600 | break; 601 | 602 | default: 603 | break; 604 | } 605 | 606 | if(annotate && annotations.contains(addr)){ 607 | asem += (" /* " + annotations[addr] + " */"); 608 | } 609 | asem += "\n"; 610 | 611 | prev = ImageElement::DATA; 612 | i += 4; 613 | 614 | } else if(i < image.size()){ 615 | // Raw 616 | if(prev != ImageElement::RAW) 617 | asem += "\n"; 618 | asem += labelstr; 619 | 620 | if(offsets) asem += "/*0x" + HEX_PAD(addr, 4) + "*/ "; 621 | asem += (".byte 0x" + HEX_PAD(image[i], 2)); 622 | if(annotate && annotations.contains(i)){ 623 | asem += (" /* " + annotations[i] + " */"); 624 | } 625 | 626 | if(annotate && annotations.contains(addr)){ 627 | asem += (" /* " + annotations[addr] + " */"); 628 | } 629 | asem += "\n"; 630 | 631 | prev = ImageElement::RAW; 632 | i += 1; 633 | 634 | } else { 635 | if(!labelstr.isEmpty()){ 636 | asem += "\n"; 637 | asem += labelstr; 638 | } 639 | break; 640 | } 641 | } 642 | return asem; 643 | } 644 | 645 | void ImageModel::addLabel(zu64 addr, labeltype ltype, nametype ntype, ZString name, bool thumbfunc){ 646 | if(name.isEmpty()){ 647 | // if(ntype == NAMED) 648 | // ntype = AUTO; 649 | 650 | if(ltype == CODE){ 651 | if(ntype == CALL){ 652 | name = "call_"; 653 | } else if(ntype == JUMP){ 654 | name = "jump_"; 655 | } else if(ntype == SWITCH){ 656 | name = "switch_"; 657 | } else { 658 | name = "loc_"; 659 | } 660 | } else { 661 | name = "data_"; 662 | } 663 | 664 | name += HEX(addr); 665 | } 666 | 667 | if(labels.contains(addr)){ 668 | // label exists 669 | if(ltype == labels[addr].ltype){ 670 | // same label types 671 | if(ntype <= labels[addr].ntype){ 672 | // Only change label if higher priority 673 | labels.add(addr, { ltype, ntype, name, labels[addr].thumbfunc || thumbfunc }); 674 | } 675 | } else { 676 | ELOG("will not change label type " << labels[addr].ltype << " -> " << ltype << " @ " << HEX(addr)); 677 | return; 678 | } 679 | 680 | } else { 681 | // new label 682 | labels.add(addr, { ltype, ntype, name, thumbfunc }); 683 | } 684 | } 685 | 686 | void ImageModel::setSwitchLen(zu64 addr, zu64 len){ 687 | switches[addr] = len; 688 | } 689 | 690 | void ImageModel::setForced(zu64 addr, ImageModel::labeltype type){ 691 | forcetype[addr] = type; 692 | } 693 | 694 | void ImageModel::addAnnotation(zu64 addr, ZString note){ 695 | ZString old; 696 | if(annotations.contains(addr) && !annotations[addr].isEmpty()){ 697 | old = annotations[addr] + "; "; 698 | } 699 | if(!note.isEmpty()){ 700 | annotations.add(addr, old + note); 701 | } 702 | } 703 | 704 | zu64 ImageModel::numInsns() const { 705 | return insns.size(); 706 | } 707 | 708 | zu64 ImageModel::_addrToOffset(zu64 addr) const { 709 | if(!(addr >= base)){ 710 | ZASSERT(addr >= base, "address " + HEX(addr) + " in wrong range"); 711 | } 712 | zu64 offset = addr - base; 713 | if(!(offset < image.size())){ 714 | ZASSERT(offset < image.size(), "address " + HEX(offset) + " out of bounds"); 715 | } 716 | return offset; 717 | } 718 | 719 | zu64 ImageModel::_offsetToAddr(zu64 offset) const { 720 | return base + offset; 721 | } 722 | -------------------------------------------------------------------------------- /imagemodel.h: -------------------------------------------------------------------------------- 1 | #ifndef IMAGEMODEL_H 2 | #define IMAGEMODEL_H 3 | 4 | #include "codeblock.h" 5 | #include "imageelement.h" 6 | 7 | #include "zbinary.h" 8 | #include "zmap.h" 9 | #include "zlist.h" 10 | #include "zstack.h" 11 | 12 | #include 13 | 14 | #define HEX(A) (ZString::ItoS((zu64)(A), 16)) 15 | #define HEX_PAD(A, B) (ZString::ItoS((zu64)(A), 16, (B))) 16 | //#define HEX(A) (ZString("0x")+ZString::ItoS((A), 16)) 17 | 18 | using namespace LibChaos; 19 | 20 | class ImageModel { 21 | public: 22 | enum labeltype { 23 | CODE, 24 | DATA, 25 | }; 26 | 27 | enum nametype { 28 | NAMED, 29 | CALL, 30 | SWITCH, 31 | JUMP, 32 | LDR, 33 | AUTO 34 | }; 35 | 36 | struct Label { 37 | labeltype ltype; 38 | nametype ntype; 39 | ZString str; 40 | bool thumbfunc; 41 | }; 42 | 43 | enum datatype { 44 | VALUE, 45 | CPTR, 46 | DPTR, 47 | }; 48 | 49 | struct DataWord { 50 | datatype type; 51 | zu64 data; 52 | }; 53 | 54 | public: 55 | ImageModel(bool equiv, bool verbose); 56 | ~ImageModel(); 57 | 58 | //! Load a binary image at the given offset. 59 | void loadImage(const ZBinary &bin, zu64 offset); 60 | //! Add a code entry point in the provided binary. 61 | zu64 addEntry(zu64 addr, ZString name = ZString(), ArZ params = ArZ()); 62 | zu64 addCodePointer(zu64 addr, ZString name = ZString()); 63 | 64 | zu64 addData(zu64 addr, ZString name = ZString(), zu64 words = 0); 65 | zu64 addDataPointer(zu64 addr, ZString name = ZString(), zu64 words = 0); 66 | 67 | zu64 disassembleAddress(zu64 addr, ZStack stack = ZStack()); 68 | 69 | ZBinary makeCode(bool offsets = false, bool annotate = false); 70 | 71 | void addLabel(zu64 addr, labeltype ltype, nametype ntype, ZString name = ZString(), bool thumbfunc = false); 72 | 73 | void setSwitchLen(zu64 addr, zu64 len); 74 | void setForced(zu64 addr, labeltype type); 75 | void addAnnotation(zu64 addr, ZString note); 76 | 77 | zu64 numInsns() const; 78 | 79 | private: 80 | zu64 _addrToOffset(zu64 addr) const; 81 | zu64 _offsetToAddr(zu64 offset) const; 82 | 83 | public: 84 | bool equiv; 85 | bool verbose; 86 | 87 | //! Image base offset. 88 | zu64 base; 89 | //! Image data. 90 | ZBinary image; 91 | 92 | //! Code and data labels. 93 | ZMap labels; 94 | ZMap lparams; 95 | //! Disassembled instructions. 96 | ZMap insns; 97 | //! Data referenced by code. 98 | ZMap data; 99 | ZMap forcetype; 100 | 101 | ZMap> code; 102 | //! Additional annotations. 103 | ZMap annotations; 104 | 105 | ZMap switches; 106 | 107 | // Capstone 108 | csh handle; 109 | cs_err err; 110 | }; 111 | 112 | #endif // IMAGEMODEL_H 113 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include "imagemodel.h" 2 | 3 | #include "zlog.h" 4 | #include "zpath.h" 5 | #include "zfile.h" 6 | #include "zoptions.h" 7 | 8 | #include 9 | 10 | using namespace LibChaos; 11 | 12 | #define OPT_VMA "vma" 13 | #define OPT_SYMBOLS "symbols" 14 | #define OPT_DATA "data" 15 | #define OPT_EXACT "exact" 16 | #define OPT_VERBOSE "verbose" 17 | #define OPT_OFFSETS "offsets" 18 | #define OPT_ANNOTATE "annotate" 19 | 20 | const ZArray optdef = { 21 | { OPT_VMA, 'a', ZOptions::INTEGER }, // Input image memory start address (VMA/LMA) 22 | { OPT_SYMBOLS, 's', ZOptions::LIST }, // Symbol files list 23 | { OPT_EXACT, 'E', ZOptions::NONE }, // Produce code that will assemble to identical binary 24 | { OPT_VERBOSE, 'V', ZOptions::NONE }, // Verbose log of disassembly 25 | { OPT_OFFSETS, 'O', ZOptions::NONE }, // Add instruction and data offsets to code 26 | { OPT_ANNOTATE, 'A', ZOptions::NONE }, // Add more annotations to output code 27 | }; 28 | 29 | struct Symbol { 30 | zu64 addr; 31 | ZString name; 32 | bool ptr; 33 | }; 34 | 35 | enum Section { 36 | SEC_NONE, 37 | SEC_CODE, 38 | SEC_DATA, 39 | SEC_SWITCH, 40 | SEC_ANNOTE, 41 | }; 42 | 43 | zu64 parseSymbolFile(ZPath file, ImageModel *model){ 44 | LOG("Loading Symbol File " << file); 45 | 46 | ZFile inadd(file, ZFile::READ); 47 | if(!inadd.isOpen()){ 48 | ELOG("failed to open symbol file"); 49 | return 1; 50 | } 51 | 52 | ZString addstr('0', inadd.fileSize()); 53 | inadd.read((zbyte *)addstr.c(), addstr.size()); 54 | inadd.close(); 55 | 56 | // current label 57 | Section section = SEC_NONE; 58 | zu64 total = 0; 59 | zu64 prevaddr = 0; 60 | 61 | // loop over lines 62 | ArZ lines = addstr.explode('\n'); 63 | for(zu64 i = 0; i < lines.size(); ++i){ 64 | // strip whitespace 65 | lines[i].strip('\r').strip('\t').strip(' '); 66 | if(lines[i].isEmpty()) 67 | continue; 68 | // skip comments 69 | if(lines[i].beginsWith("#", true)) 70 | continue; 71 | 72 | // read label 73 | if(lines[i].beginsWith("[", true)){ 74 | // update current label 75 | ZString label = ZString::substr(lines[i], lines[i].findFirst("[")+1); 76 | label.substr(0, label.findFirst("]")); 77 | label.toLower(); 78 | LOG("Label: " << label); 79 | if(label == "code"){ 80 | section = SEC_CODE; 81 | } else if(label == "data"){ 82 | section = SEC_DATA; 83 | } else if(label == "switch"){ 84 | section = SEC_SWITCH; 85 | } else if(label == "annote"){ 86 | section = SEC_ANNOTE; 87 | } else { 88 | ELOG("Invalid label!"); 89 | break; 90 | } 91 | continue; 92 | } 93 | 94 | bool stop = false; 95 | switch(section){ 96 | case SEC_SWITCH: 97 | if(lines[i].beginsWith("&", true)){ 98 | lines[i].substr(1); 99 | 100 | ArZ line = lines[i].explode(':'); 101 | if(line.size()){ 102 | if(line[0].isEmpty()) 103 | break; 104 | 105 | ZString adr = line[0]; 106 | adr.strip('\r').strip(' ').strip('\t').strip(' '); 107 | zu64 addr; 108 | if(adr.beginsWith("+")){ 109 | zu64 tmp = adr.substr(1).toUint(16); 110 | if(tmp == ZU64_MAX){ 111 | LOG(file << ": bad offset"); 112 | stop = true; 113 | break; 114 | } 115 | addr = prevaddr + tmp; 116 | } else { 117 | addr = adr.toUint(16); 118 | if(addr == ZU64_MAX){ 119 | LOG(file << ": bad offset"); 120 | stop = true; 121 | break; 122 | } 123 | } 124 | prevaddr = addr; 125 | 126 | ZString len = line[1]; 127 | len.strip('\r').strip(' ').strip('\t').strip(' '); 128 | zu64 length = len.toUint(); 129 | 130 | LOG("Max Switch Cases: " << HEX(addr) << " " << length); 131 | model->setSwitchLen(addr, length); 132 | } 133 | } 134 | break; 135 | 136 | case SEC_CODE: { 137 | // format: 138 | // [*]address[:name [.type [count]]] 139 | 140 | ArZ line = lines[i].explode(':'); 141 | if(line.size() > 2){ 142 | ELOG("Invalid line: " << lines[i]); 143 | stop = true; 144 | } else if(line.size()){ 145 | if(line[0].isEmpty()) 146 | break; 147 | 148 | ZString adr = line[0]; 149 | adr.strip('\r').strip(' ').strip('\t').strip(' '); 150 | 151 | bool force = false; 152 | if(adr.endsWith("!")){ 153 | force = true; 154 | adr.substr(0, adr.size()-1); 155 | } 156 | 157 | bool ptr = false; 158 | if(adr.beginsWith("*")){ 159 | ptr = true; 160 | adr.substr(1); 161 | } 162 | adr.strip('\r').strip(' ').strip('\t').strip(' '); 163 | 164 | zu64 addr; 165 | if(adr.beginsWith("+")){ 166 | zu64 tmp = adr.substr(1).toUint(16); 167 | if(tmp == ZU64_MAX){ 168 | LOG(file << ": bad offset"); 169 | stop = true; 170 | break; 171 | } 172 | addr = prevaddr + tmp; 173 | } else { 174 | addr = adr.toUint(16); 175 | if(addr == ZU64_MAX){ 176 | LOG(file << ": bad offset"); 177 | stop = true; 178 | break; 179 | } 180 | } 181 | prevaddr = addr; 182 | 183 | ZString name; 184 | ArZ args; 185 | 186 | if(line.size() > 1){ 187 | ZString dstr = line[1]; 188 | dstr.strip('\r').strip(' ').strip('\t').strip(' '); 189 | ArZ desc = dstr.explode(' '); 190 | 191 | if(desc.size() && !desc[0].isEmpty()){ 192 | name = desc[0]; 193 | } 194 | 195 | for(zu64 j = 1; j < desc.size(); ++j){ 196 | ZString arg = desc[j]; 197 | arg.strip('\r').strip(' ').strip('\t').strip(' '); 198 | args.push(arg); 199 | } 200 | } 201 | 202 | if(ptr){ 203 | zu64 count = model->addCodePointer(addr, name); 204 | total += count; 205 | LOG("Code Pointer 0x" << ZString::ItoS(addr, 16) << ": " << name << " [" << count << " insns]"); 206 | if(force){ 207 | model->setForced(addr, ImageModel::CODE); 208 | } 209 | } else { 210 | zu64 count = model->addEntry(addr, name, args); 211 | total += count; 212 | LOG("Symbol 0x" << ZString::ItoS(addr, 16) << ": " << name << (args.size() ? "(" + ZString::join(args, ", ") + ")" : "") << " [" << count << " insns]"); 213 | } 214 | } 215 | break; 216 | } 217 | 218 | case SEC_DATA: { 219 | // format: 220 | // [*]address[:name [args..]] 221 | 222 | ArZ line = lines[i].explode(':'); 223 | if(line.size() > 2){ 224 | ELOG("Invalid line: " << lines[i]); 225 | stop = true; 226 | } else if(line.size()){ 227 | if(line[0].isEmpty()) 228 | break; 229 | 230 | ZString adr = line[0]; 231 | adr.strip('\r').strip(' ').strip('\t').strip(' '); 232 | 233 | bool force = false; 234 | if(adr.endsWith("!")){ 235 | force = true; 236 | adr.substr(0, adr.size()-1); 237 | } 238 | 239 | bool ptr = false; 240 | if(adr.beginsWith("*")){ 241 | ptr = true; 242 | adr.substr(1); 243 | } 244 | adr.strip('\r').strip(' ').strip('\t').strip(' '); 245 | 246 | zu64 addr; 247 | if(adr.beginsWith("+")){ 248 | zu64 tmp = adr.substr(1).toUint(16); 249 | if(tmp == ZU64_MAX){ 250 | LOG(file << ": bad offset"); 251 | stop = true; 252 | break; 253 | } 254 | addr = prevaddr + tmp; 255 | } else { 256 | addr = adr.toUint(16); 257 | if(addr == ZU64_MAX){ 258 | LOG(file << ": bad offset"); 259 | stop = true; 260 | break; 261 | } 262 | } 263 | prevaddr = addr; 264 | 265 | ZString name; 266 | ZString type = ".byte"; 267 | zu64 size = 1; 268 | 269 | if(line.size() > 1){ 270 | ZString dstr = line[1]; 271 | dstr.strip('\r').strip(' ').strip('\t').strip(' '); 272 | ArZ desc = dstr.explode(' '); 273 | if(desc.size() > 3){ 274 | ELOG("Invalid desc: " << dstr); 275 | stop = true; 276 | break; 277 | } 278 | if(desc.size() && !desc[0].isEmpty()){ 279 | name = desc[0]; 280 | } 281 | if(desc.size() > 1){ 282 | type = desc[1]; 283 | } 284 | if(desc.size() > 2){ 285 | size = desc[2].toUint(10); 286 | } 287 | } 288 | 289 | if(ptr){ 290 | LOG("Data Pointer 0x" << ZString::ItoS(addr, 16) << ": " << name); 291 | if(type == ".word"){ 292 | model->addDataPointer(addr, name, size); 293 | } else { 294 | model->addDataPointer(addr, name); 295 | } 296 | } else { 297 | LOG("Data 0x" << ZString::ItoS(addr, 16) << ": " << name); 298 | if(type == ".word"){ 299 | model->addData(addr, name, size); 300 | } else { 301 | model->addData(addr, name); 302 | } 303 | } 304 | if(force){ 305 | model->setForced(addr, ImageModel::DATA); 306 | } 307 | } 308 | break; 309 | } 310 | 311 | case SEC_ANNOTE: { 312 | ArZ line = lines[i].explode(':'); 313 | if(line.size()){ 314 | if(line[0].isEmpty()) 315 | break; 316 | 317 | ZString adr = line[0]; 318 | adr.strip('\r').strip(' ').strip('\t').strip(' '); 319 | zu64 addr; 320 | if(adr.beginsWith("+")){ 321 | zu64 tmp = adr.substr(1).toUint(16); 322 | if(tmp == ZU64_MAX){ 323 | LOG(file << ": bad offset"); 324 | stop = true; 325 | break; 326 | } 327 | addr = prevaddr + tmp; 328 | } else { 329 | addr = adr.toUint(16); 330 | if(addr == ZU64_MAX){ 331 | LOG(file << ": bad offset"); 332 | stop = true; 333 | break; 334 | } 335 | } 336 | prevaddr = addr; 337 | 338 | ZString note = line[1]; 339 | note.strip('\r').strip(' ').strip('\t').strip(' '); 340 | 341 | model->addAnnotation(addr, note); 342 | } 343 | break; 344 | } 345 | 346 | default: 347 | ELOG("No section defined!"); 348 | stop = true; 349 | } 350 | 351 | if(stop) 352 | break; 353 | } 354 | 355 | return total; 356 | } 357 | 358 | int main(int argc, char **argv){ 359 | ZLog::logLevelStdOut(ZLog::INFO, "%clock% N %log%"); 360 | ZLog::logLevelStdErr(ZLog::ERRORS, "%clock% E [%function%|%file%:%line%] %log%"); 361 | 362 | try { 363 | ZOptions options(optdef); 364 | if(!options.parse(argc, argv)) 365 | return 1; 366 | 367 | ZArray args = options.getArgs(); 368 | ZMap opts = options.getOpts(); 369 | 370 | if(args.size() == 2){ 371 | ZPath input = args[0]; 372 | ZPath output = args[1]; 373 | 374 | bool exact = opts.contains(OPT_EXACT); 375 | bool verbose = opts.contains(OPT_VERBOSE); 376 | bool offsets = opts.contains(OPT_OFFSETS); 377 | bool annotate = opts.contains(OPT_ANNOTATE); 378 | 379 | if(verbose){ 380 | ZLog::logLevelStdOut(ZLog::DEBUG, "%clock% D %log%\x1b[m"); 381 | } 382 | 383 | ImageModel model(!exact, verbose); 384 | 385 | LOG("Reading"); 386 | 387 | ZFile in(input, ZFile::READ); 388 | if(!in.isOpen()){ 389 | ELOG("failed to open"); 390 | return -1; 391 | } 392 | ZBinary image; 393 | in.read(image, in.fileSize()); 394 | in.close(); 395 | 396 | zu64 vma = 0; 397 | if(opts.contains(OPT_VMA)){ 398 | vma = opts[OPT_VMA].toUint(16); 399 | LOG("VMA: 0x" << HEX(vma)); 400 | } 401 | 402 | model.loadImage(image, vma); 403 | 404 | zu64 total = 0; 405 | 406 | LOG("Parsing"); 407 | 408 | if(opts.contains(OPT_SYMBOLS)){ 409 | ArZ list = opts[OPT_SYMBOLS].explode(','); 410 | for(auto it = list.begin(); it.more(); ++it){ 411 | // get symbol definitions 412 | total += parseSymbolFile(it.get(), &model); 413 | } 414 | } 415 | 416 | LOG("Total Instructions: " << total); 417 | 418 | ZBinary code = model.makeCode(offsets, annotate); 419 | LOG("Output: " << code.size() << " bytes"); 420 | 421 | LOG("Writing"); 422 | ZFile out(output, ZFile::WRITE | ZFile::TRUNCATE); 423 | if(!out.isOpen()){ 424 | ELOG("failed to open"); 425 | return -2; 426 | } 427 | if(out.write(code) != code.size()){ 428 | ELOG("failed to write"); 429 | } 430 | out.close(); 431 | 432 | } else { 433 | RLOG("Usage: reassemble input_binary output_asm" << ZLog::NEWLN << 434 | " [-AEOV] [-a image_vma]" << ZLog::NEWLN << 435 | " [-s symbol_address_file]" << ZLog::NEWLN); 436 | return 1; 437 | } 438 | 439 | } catch(ZException ex){ 440 | ELOG("Exception: " << ex.what()); 441 | } 442 | 443 | return 0; 444 | } 445 | 446 | -------------------------------------------------------------------------------- /reas.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 4 | 5 | if [ -z "$1" ]; then 6 | exit 7 | fi 8 | 9 | rm -f /tmp/out.o "$1.elf" 10 | 11 | arm-none-eabi-as -mcpu=cortex-m3 -mthumb --no-pad-sections "$1" -o /tmp/out.o 12 | arm-none-eabi-ld -T "$DIR/vma.ld" -o "$1.elf" /tmp/out.o 13 | arm-none-eabi-objcopy "$1.elf" -O binary "$1.bin" 14 | 15 | if [ ! -z "$2" ]; then 16 | md5sum "$1.bin" "$2" 17 | fi 18 | --------------------------------------------------------------------------------