├── .gitignore ├── .gitmodules ├── Makefile ├── README.md ├── ent.plist ├── include └── .gitkeep └── src ├── batch.c ├── batch.h ├── libbootkit ├── boot.c ├── boot.h ├── config.h ├── dfu.c ├── dfu.h ├── libbootkit.h ├── log.c ├── log.h ├── ops.c ├── ops.h ├── payload.h ├── payload_watch.h ├── payloads │ ├── payload.S │ └── payload_watch.S ├── protocol.c └── protocol.h ├── main.c ├── utils.c └── utils.h /.gitignore: -------------------------------------------------------------------------------- 1 | /static-libs 2 | /build 3 | .DS_Store 4 | /include/IOKit 5 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lilirecovery"] 2 | path = lilirecovery 3 | url = git@github.com:NyanSatan/lilirecovery.git 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | MAC_CC = clang 2 | IOS_CC = xcrun --sdk iphoneos clang 3 | 4 | IOS_CODESIGN = codesign 5 | IOS_ENT = ent.plist 6 | 7 | MAC_ARCH = -arch x86_64 -arch arm64 8 | IOS_ARCH = -arch armv7 -arch arm64 9 | 10 | IOS_CFLAGS = -miphoneos-version-min=6.0 11 | IOS_CFLAGS += -Iinclude 12 | IOS_IOKIT_LINK = ln -fsh $(shell xcrun --sdk macosx --show-sdk-path)/System/Library/Frameworks/IOKit.framework/Versions/Current/Headers ./include/IOKit 13 | 14 | MAC_CFLAGS = -mmacosx-version-min=10.8 15 | 16 | CFLAGS = -O3 17 | CFLAGS += -Ililirecovery 18 | 19 | LDFLAGS = -framework IOKit -framework CoreFoundation 20 | 21 | ARM_CC = xcrun -sdk iphoneos clang -arch armv7 22 | VMACHO = vmacho 23 | 24 | BUILD_PATH = build 25 | 26 | SOURCES = \ 27 | src/main.c \ 28 | src/batch.c \ 29 | src/utils.c \ 30 | lilirecovery/lilirecovery.c \ 31 | src/libbootkit/log.c \ 32 | src/libbootkit/boot.c \ 33 | src/libbootkit/ops.c \ 34 | src/libbootkit/dfu.c \ 35 | src/libbootkit/protocol.c 36 | 37 | PAYLOAD_H = \ 38 | src/libbootkit/payload.h 39 | 40 | PAYLOAD_OBJ = \ 41 | $(BUILD_PATH)/payload.o 42 | 43 | PAYLOAD_SRC = \ 44 | src/libbootkit/payloads/payload.S 45 | 46 | PAYLOAD_WATCH_H = \ 47 | src/libbootkit/payload_watch.h 48 | 49 | PAYLOAD_WATCH_OBJ = \ 50 | $(BUILD_PATH)/payload_watch.o 51 | 52 | PAYLOAD_WATCH_SRC = \ 53 | src/libbootkit/payloads/payload_watch.S 54 | 55 | MAC_RESULT = $(BUILD_PATH)/checkm8_bootkit 56 | IOS_RESULT = $(BUILD_PATH)/checkm8_bootkit_ios 57 | 58 | DIR_HELPER = mkdir -p $(@D) 59 | 60 | .PHONY: all mac ios clean clean-headers 61 | 62 | all: $(PAYLOAD_H) $(PAYLOAD_WATCH_H) $(MAC_RESULT) $(IOS_RESULT) 63 | @echo "%%%%% done building" 64 | 65 | mac: $(MAC_RESULT) 66 | 67 | ios: $(IOS_RESULT) 68 | 69 | $(MAC_RESULT): $(SOURCES) 70 | @echo "\tbuilding checkm8_bootkit for Mac" 71 | @$(DIR_HELPER) 72 | @$(MAC_CC) $(MAC_ARCH) $(MAC_CFLAGS) $(CFLAGS) $(LDFLAGS) $^ -o $@ 73 | 74 | $(IOS_RESULT): $(SOURCES) 75 | @echo "\tbuilding checkm8_bootkit for iOS" 76 | @$(DIR_HELPER) 77 | @$(IOS_IOKIT_LINK) 78 | @$(IOS_CC) $(IOS_ARCH) $(IOS_CFLAGS) $(CFLAGS) $(LDFLAGS) $^ -o $@ 79 | @$(IOS_CODESIGN) -s - -f --entitlements $(IOS_ENT) $@ 80 | 81 | $(BUILD_PATH)/%.o: %.c 82 | @echo "\tbuilding C: $<" 83 | @$(DIR_HELPER) 84 | @$(CC) $(CFLAGS) -c $< -o $@ 85 | 86 | $(PAYLOAD_H): $(PAYLOAD_SRC) 87 | @echo "\tbuilding boot payload" 88 | @$(DIR_HELPER) 89 | @$(ARM_CC) -c $^ -o $(PAYLOAD_OBJ) 90 | @$(VMACHO) -f -C payload $(PAYLOAD_OBJ) $@ 91 | 92 | $(PAYLOAD_WATCH_H): $(PAYLOAD_WATCH_SRC) 93 | @echo "\tbuilding watch boot payload" 94 | @$(DIR_HELPER) 95 | @$(ARM_CC) -c $^ -o $(PAYLOAD_WATCH_OBJ) 96 | @$(VMACHO) -f -C payload_watch $(PAYLOAD_WATCH_OBJ) $@ 97 | 98 | clean: 99 | @rm -rf $(BUILD_PATH) 100 | @echo "%%%%% done cleaning" 101 | 102 | clean-headers: 103 | @rm -rf $(PAYLOAD_H) $(PAYLOAD_WATCH_H) 104 | @echo "%%%%% done cleaning headers" 105 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # checkm8_bootkit 2 | 3 | Little utility to boot **iBoot** on some **checkm8**-able platforms. Now also can decrypt KBAGs and demote 4 | 5 | It doesn't require any modifications to **ipwndfu**/**gaster**/etc. shellcodes since it utilizes **ipwndfu**'s [custom protocol](https://github.com/axi0mX/ipwndfu/blob/master/src/usb_0xA1_2_armv7.S) 6 | 7 | You can run it on iOS as well (if you are lucky) 8 | 9 | ## SoC support 10 | 11 | * **S5L8940X** - Apple A5 12 | * **S5L8942X** - Apple A5 (32nm) 13 | * **S5L8945X** - Apple A5X 14 | * **S5L8947X** - Apple A5 (single-core) 15 | * **S5L8950X** - Apple A6 16 | * **S5L8955X** - Apple A6X 17 | * **S5L8747X** - Haywire SoC 18 | * **S7002** - Apple S1 19 | * **T8002** - Apple S1P/S2/T1 20 | * **T8004** - Apple S3 21 | 22 | ## Usage 23 | 24 | ``` 25 | ➜ checkm8_bootkit git:(master) ✗ build/checkm8_bootkit 26 | usage: build/checkm8_bootkit VERB [args] 27 | 28 | where VERB is one of the following: 29 | boot 30 | kbag 31 | demote 32 | batch 33 | 34 | for batch KBAG processing, you must input a text file in following format: 35 | FIRMWARE0 FILE0 KBAG 36 | ... 37 | FIRMWAREn FILEn KBAG 38 | 39 | in return you'll get the same structure, but with IV+key pair appended to each entry 40 | 41 | supported platforms: 42 | s5l8747x, s5l8940x, s5l8942x, s5l8945x, s5l8947x, s5l8950x, s5l8955x, s7002, t8002, t8004 43 | ``` 44 | 45 | * `bootloader` must be a path to raw unpacked **iBoot** image (usually you'd want to load **iBSS**) 46 | * `kbag` must be a hex string 47 | 48 | Set **LIBBOOTKIT_DEBUG** environment variable to 1 to enable verbose logging 49 | 50 | ## Building 51 | 52 | Requirements: 53 | 54 | * [lilirecovery](https://github.com/NyanSatan/lilirecovery) 55 | * My little libirecovery fork 56 | * Included as a Git module 57 | 58 | * [vmacho](https://github.com/Siguza/misc/blob/master/vmacho.c) 59 | * Only needed if you want to rebuild the payloads 60 | 61 | Then just use `make`: 62 | 63 | ``` 64 | ➜ checkm8_bootkit git:(full) ✗ make 65 | building checkm8_bootkit for Mac 66 | building checkm8_bootkit for iOS 67 | %%%%% done building 68 | ``` 69 | -------------------------------------------------------------------------------- /ent.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | seatbelt-profiles 6 | 7 | accessoryd 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /include/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NyanSatan/checkm8_bootkit/e85b58477a5240c1b1247be8d9803e93b58c29f7/include/.gitkeep -------------------------------------------------------------------------------- /src/batch.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "batch.h" 5 | #include "utils.h" 6 | 7 | #define MAX_BATCH_COUNT 16384 8 | 9 | int entry_parse(char *input, struct batch_entry *output) { 10 | int idx = 0; 11 | 12 | char *curr = NULL; 13 | char *next = input; 14 | 15 | while ((curr = strsep(&next, " "))) { 16 | size_t len = strlen(curr); 17 | 18 | switch (idx) { 19 | case 0: { 20 | if (len > FIRMWARE_NAME_MAX_LEN) { 21 | printf("firmware name \"%s\" is too long (%d max)\n", curr, FIRMWARE_NAME_MAX_LEN); 22 | return -1; 23 | } 24 | 25 | strcpy(output->firmware, curr); 26 | break; 27 | } 28 | 29 | case 1: { 30 | if (len > FILE_NAME_MAX_LEN) { 31 | printf("file name \"%s\" is too long (%d max)\n", curr, FILE_NAME_MAX_LEN); 32 | return -1; 33 | } 34 | 35 | strcpy(output->file, curr); 36 | break; 37 | } 38 | 39 | case 2: { 40 | if (len != KBAG_LEN_256 * 2 && len != KBAG_LEN_128 * 2) { 41 | printf("KBAG %s is neither AES 256 nor 128\n", curr); 42 | return -1; 43 | } 44 | 45 | output->kbag_len = len / 2; 46 | 47 | if (str2hex(output->kbag_len, output->kbag, curr) != output->kbag_len) { 48 | printf("failed to decode KBAG %s\n", curr); 49 | return -1; 50 | } 51 | 52 | break; 53 | } 54 | 55 | default: { 56 | printf("entry has too many components\n"); 57 | return -1; 58 | } 59 | } 60 | 61 | idx++; 62 | } 63 | 64 | if (idx == 0) { 65 | return -2; 66 | } 67 | 68 | if (idx != 3) { 69 | printf("entry has too few components\n"); 70 | return -1; 71 | } 72 | 73 | return 0; 74 | } 75 | 76 | int batch_parse(char *input, struct batch_entry **output, int *count) { 77 | char *curr_ = input; 78 | int _count = 1; 79 | 80 | while ((curr_ = strchr(curr_, '\n'))) { 81 | if (_count > MAX_BATCH_COUNT) { 82 | printf("batch is too large (%d lines max)\n", MAX_BATCH_COUNT); 83 | return -1; 84 | } 85 | 86 | _count++; 87 | curr_++; 88 | } 89 | 90 | struct batch_entry *entries = calloc(sizeof(*entries), _count); 91 | if (!entries) { 92 | printf("out of memory?!\n"); 93 | return -1; 94 | } 95 | 96 | int raw_idx = 0; 97 | int real_idx = 0; 98 | 99 | char *curr = NULL; 100 | char *next = input; 101 | 102 | while ((curr = strsep(&next, "\n"))) { 103 | int ret = entry_parse(curr, &entries[real_idx]); 104 | 105 | switch (ret) { 106 | case 0: { 107 | raw_idx++; 108 | real_idx++; 109 | break; 110 | } 111 | 112 | case -2: { 113 | // empty line 114 | raw_idx++; 115 | break; 116 | } 117 | 118 | case -1: { 119 | printf("failed to decode line %d\n", raw_idx); 120 | return -1; 121 | } 122 | } 123 | } 124 | 125 | *output = entries; 126 | *count = real_idx; 127 | 128 | return 0; 129 | } 130 | -------------------------------------------------------------------------------- /src/batch.h: -------------------------------------------------------------------------------- 1 | #ifndef BATCH_H 2 | #define BATCH_H 3 | 4 | #include "libbootkit/ops.h" 5 | 6 | #define FIRMWARE_NAME_MAX_LEN 128 7 | #define FILE_NAME_MAX_LEN 64 8 | 9 | struct batch_entry { 10 | char firmware[FIRMWARE_NAME_MAX_LEN + 1]; 11 | char file[FILE_NAME_MAX_LEN + 1]; 12 | uint8_t kbag[KBAG_LEN_256]; 13 | int kbag_len; 14 | }; 15 | 16 | int batch_parse(char *input, struct batch_entry **output, int *count); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /src/libbootkit/boot.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "log.h" 11 | #include "dfu.h" 12 | #include "protocol.h" 13 | #include "config.h" 14 | #include "payload.h" 15 | #include "payload_watch.h" 16 | 17 | #define ARM_RESET_VECTOR 0xEA00000E 18 | 19 | /* 20 | * Watch-specific stuff 21 | */ 22 | 23 | typedef struct __attribute__((packed)) { 24 | uint32_t loadaddr; 25 | uint32_t security_consolidate_environment; 26 | uint32_t security_sidp_seal_rom_manifest; 27 | uint32_t platform_get_boot_trampoline; 28 | uint32_t platform_bootprep; 29 | uint32_t usb_controller_stop; 30 | uint32_t interrupt_mask_all; 31 | uint32_t timer_stop_all; 32 | uint32_t clocks_quiesce; 33 | uint32_t enter_critical_section; 34 | uint32_t arch_cpu_quiesce; 35 | } payload_offsets_watch_t; 36 | 37 | static unsigned char *construct_payload_watch(const rom_config_t *config) { 38 | debug("constructing payload...\n"); 39 | 40 | unsigned char *payload_copy = malloc(sizeof(payload_watch)); 41 | if (!payload_copy) { 42 | printf("out of memory\n"); 43 | return NULL; 44 | } 45 | 46 | memmove(payload_copy, &payload_watch, sizeof(payload_watch)); 47 | 48 | static const uint32_t magic = 0xDEAD0001; 49 | 50 | unsigned char *offset = memmem(payload_copy, sizeof(payload_watch), &magic, sizeof(magic)); 51 | if (!offset) { 52 | goto improper_payload; 53 | } 54 | 55 | if (sizeof(payload_watch) - (size_t)(offset - payload_copy) < sizeof(payload_offsets_watch_t)) { 56 | goto improper_payload; 57 | } 58 | 59 | payload_offsets_watch_t *payload_offsets = (payload_offsets_watch_t *)offset; 60 | 61 | payload_offsets->loadaddr = config->loadaddr; 62 | payload_offsets->security_consolidate_environment = config->boot_config_watch.security_consolidate_environment; 63 | payload_offsets->security_sidp_seal_rom_manifest = config->boot_config_watch.security_sidp_seal_rom_manifest; 64 | payload_offsets->platform_get_boot_trampoline = config->boot_config_watch.platform_get_boot_trampoline; 65 | payload_offsets->platform_bootprep = config->boot_config_watch.platform_bootprep; 66 | payload_offsets->usb_controller_stop = config->boot_config_watch.usb_controller_stop; 67 | payload_offsets->interrupt_mask_all = config->boot_config_watch.interrupt_mask_all; 68 | payload_offsets->timer_stop_all = config->boot_config_watch.timer_stop_all; 69 | payload_offsets->clocks_quiesce = config->boot_config_watch.clocks_quiesce; 70 | payload_offsets->enter_critical_section = config->boot_config_watch.enter_critical_section; 71 | payload_offsets->arch_cpu_quiesce = config->boot_config_watch.arch_cpu_quiesce; 72 | 73 | goto success; 74 | 75 | improper_payload: 76 | free(payload_copy); 77 | printf("improper payload\n"); 78 | return NULL; 79 | 80 | success: 81 | return payload_copy; 82 | } 83 | 84 | static int construct_command_watch(irecv_client_t client, 85 | const rom_config_t *config, 86 | const uint8_t *bootloader, 87 | size_t bootloader_length, 88 | unsigned char **result, 89 | size_t *result_length) { 90 | 91 | debug("constructing command...\n"); 92 | 93 | if (*(uint32_t*)bootloader != ARM_RESET_VECTOR) { 94 | printf("provided bootloader doesn't seem to be an ARM image\n"); 95 | return -1; 96 | } 97 | 98 | size_t command_length = sizeof(payload_watch) + bootloader_length; 99 | 100 | if (command_length > config->loadsize) { 101 | printf("resulting command is too big, use smaller bootloader (at least %lu smaller)\n", command_length - config->loadsize); 102 | return -1; 103 | } 104 | 105 | unsigned char *buffer = malloc(command_length); 106 | if (!buffer) { 107 | printf("out of memory"); 108 | return -1; 109 | } 110 | 111 | memset(buffer, 0x0, command_length); 112 | memmove(buffer, bootloader, bootloader_length); 113 | 114 | unsigned char *prepared_payload = construct_payload_watch(config); 115 | if (!prepared_payload) { 116 | printf("failed to construct payload\n"); 117 | free(buffer); 118 | return -1; 119 | } 120 | 121 | memmove(buffer + bootloader_length, prepared_payload, sizeof(payload_watch)); 122 | 123 | free(prepared_payload); 124 | 125 | *result = buffer; 126 | *result_length = command_length; 127 | 128 | return 0; 129 | } 130 | 131 | 132 | int dfu_boot_watch(irecv_client_t client, const uint8_t *bootloader, size_t bootloader_length, bool debug) { 133 | if (validate_device(client) != 0) { 134 | printf("device validation failed\n"); 135 | return -1; 136 | } 137 | 138 | const struct irecv_device_info *info = irecv_get_device_info(client); 139 | const rom_config_t *config = get_config(info->cpid); 140 | if (!config) { 141 | printf("no config available for CPID:%04X\n", info->cpid); 142 | return -1; 143 | } 144 | 145 | unsigned char *command = NULL; 146 | size_t command_length = 0; 147 | 148 | if (construct_command_watch(client, config, bootloader, bootloader_length, &command, &command_length) != 0) { 149 | printf("failed to construct command\n"); 150 | return -1; 151 | } 152 | 153 | int ret = -1; 154 | 155 | if (debug) { 156 | ret = save_command(client, command, command_length); 157 | goto out; 158 | } 159 | 160 | if (write32(client, config, config->boot_config_watch.func_ptr, config->loadaddr + bootloader_length + 1) != 0) { 161 | printf("failed to overwrite function pointer\n"); 162 | goto out; 163 | } 164 | 165 | if (send_command(client, command, command_length) != 0) { 166 | printf("failed to send command\n"); 167 | goto out; 168 | } 169 | 170 | debug("requesting DFU abort\n"); 171 | 172 | irecv_usb_control_transfer(client, 0x21, 4, 0, 0, NULL, 0, USB_SMALL_TIMEOUT); 173 | 174 | ret = 0; 175 | 176 | out: 177 | if (command) { 178 | free(command); 179 | } 180 | 181 | return ret; 182 | } 183 | 184 | /* 185 | * Older platform stuff 186 | */ 187 | 188 | typedef struct __attribute__((packed)) { 189 | uint32_t loadaddr; 190 | uint32_t imageaddr; 191 | uint32_t imagesize; 192 | uint32_t memmove; 193 | uint32_t platform_get_boot_trampoline; 194 | uint32_t platform_bootprep; 195 | uint32_t usb_quiesce_no_free; 196 | uint32_t interrupt_mask_all; 197 | uint32_t timer_stop_all; 198 | uint32_t clocks_quiesce; 199 | uint32_t enter_critical_section; 200 | uint32_t arch_cpu_quiesce; 201 | uint32_t prepare_and_jump; 202 | } payload_offsets_t; 203 | 204 | static unsigned char *construct_payload(const rom_config_t *config, off_t bootloader_offset, size_t bootloader_length) { 205 | debug("constructing payload...\n"); 206 | 207 | unsigned char *payload_copy = malloc(sizeof(payload)); 208 | if (!payload_copy) { 209 | printf("out of memory\n"); 210 | return NULL; 211 | } 212 | 213 | memmove(payload_copy, &payload, sizeof(payload)); 214 | 215 | static const uint32_t magic = 0xDEAD0001; 216 | 217 | unsigned char *offset = memmem(payload_copy, sizeof(payload), &magic, sizeof(magic)); 218 | if (!offset) { 219 | printf("improper payload\n"); 220 | return NULL; 221 | } 222 | 223 | if (sizeof(payload) - (size_t)(offset - payload_copy) < sizeof(payload_offsets_t)) { 224 | printf("improper payload\n"); 225 | return NULL; 226 | } 227 | 228 | payload_offsets_t *payload_offsets = (payload_offsets_t *)offset; 229 | 230 | payload_offsets->loadaddr = config->loadaddr; 231 | payload_offsets->imageaddr = config->loadaddr + bootloader_offset; 232 | payload_offsets->imagesize = bootloader_length; 233 | payload_offsets->memmove = config->boot_config.memmove; 234 | payload_offsets->platform_get_boot_trampoline = config->boot_config.platform_get_boot_trampoline; 235 | payload_offsets->platform_bootprep = config->boot_config.platform_bootprep; 236 | payload_offsets->usb_quiesce_no_free = config->boot_config.usb_quiesce_no_free; 237 | payload_offsets->interrupt_mask_all = config->boot_config.interrupt_mask_all; 238 | payload_offsets->timer_stop_all = config->boot_config.timer_stop_all; 239 | payload_offsets->clocks_quiesce = config->boot_config.clocks_quiesce; 240 | payload_offsets->enter_critical_section = config->boot_config.enter_critical_section; 241 | payload_offsets->arch_cpu_quiesce = config->boot_config.arch_cpu_quiesce; 242 | payload_offsets->prepare_and_jump = config->boot_config.prepare_and_jump; 243 | 244 | return payload_copy; 245 | } 246 | 247 | static int construct_command(irecv_client_t client, 248 | const uint8_t *bootloader, 249 | size_t bootloader_length, 250 | unsigned char **result, 251 | size_t *result_length) { 252 | 253 | debug("constructing command...\n"); 254 | 255 | if (*(uint32_t *)bootloader != ARM_RESET_VECTOR) { 256 | printf("provided bootloader doesn't seem to be an ARM image\n"); 257 | return -1; 258 | } 259 | 260 | const struct irecv_device_info *info = irecv_get_device_info(client); 261 | 262 | const rom_config_t *config = get_config(info->cpid); 263 | if (!config) { 264 | printf("no config available for CPID:%04X\n", info->cpid); 265 | return -1; 266 | } 267 | 268 | size_t command_length = sizeof(payload) + bootloader_length + sizeof(usb_command_t); 269 | 270 | if (command_length > config->loadsize) { 271 | printf("resulting command is too big, use smaller bootloader (at least %lu smaller)\n", command_length - config->loadsize); 272 | return -1; 273 | } 274 | 275 | unsigned char *buffer = malloc(command_length); 276 | if (!buffer) { 277 | printf("out of memory"); 278 | return -1; 279 | } 280 | 281 | memset(buffer, 0x0, command_length); 282 | 283 | off_t bootloader_offset = sizeof(usb_command_t); 284 | off_t payload_offset = bootloader_offset + bootloader_length; 285 | 286 | usb_command_t *command = (usb_command_t *)buffer; 287 | command->magic = USB_COMMAND_EXEC_MAGIC; 288 | command->magic2 = USB_COMMAND_EXEC_MAGIC; 289 | command->function = config->loadaddr + payload_offset + 1; 290 | memset(&command->padding, 0x0, sizeof(command->padding)); 291 | 292 | memmove(buffer + bootloader_offset, bootloader, bootloader_length); 293 | 294 | unsigned char *prepared_payload = construct_payload(config, bootloader_offset, bootloader_length); 295 | if (!prepared_payload) { 296 | printf("failed to construct payload\n"); 297 | free(buffer); 298 | return -1; 299 | } 300 | 301 | memmove(buffer + payload_offset, prepared_payload, sizeof(payload)); 302 | 303 | free(prepared_payload); 304 | 305 | *result = buffer; 306 | *result_length = command_length; 307 | 308 | return 0; 309 | } 310 | 311 | int dfu_boot(irecv_client_t client, const uint8_t *bootloader, size_t bootloader_length, bool debug) { 312 | if (validate_device(client) != 0) { 313 | printf("device validation failed\n"); 314 | return -1; 315 | } 316 | 317 | unsigned char *command = NULL; 318 | size_t command_length = 0; 319 | 320 | if (construct_command(client, bootloader, bootloader_length, &command, &command_length) != 0) { 321 | printf("failed to construct command\n"); 322 | return -1; 323 | } 324 | 325 | int ret = -1; 326 | 327 | if (debug) { 328 | ret = save_command(client, command, command_length); 329 | goto out; 330 | } 331 | 332 | if (send_command(client, command, command_length) != 0) { 333 | printf("failed to send command\n"); 334 | goto out; 335 | } 336 | 337 | trigger_command(client, NULL, 0); 338 | 339 | ret = 0; 340 | 341 | out: 342 | if (command) { 343 | free(command); 344 | } 345 | 346 | return ret; 347 | } 348 | -------------------------------------------------------------------------------- /src/libbootkit/boot.h: -------------------------------------------------------------------------------- 1 | #ifndef boot_H 2 | #define boot_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | int dfu_boot(irecv_client_t client, const uint8_t *bootloader, size_t bootloader_length, bool debug); 9 | int dfu_boot_watch(irecv_client_t client, const uint8_t *bootloader, size_t bootloader_length, bool debug); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /src/libbootkit/config.h: -------------------------------------------------------------------------------- 1 | #ifndef config_H 2 | #define config_H 3 | 4 | #include 5 | 6 | typedef struct { 7 | uint32_t memmove; 8 | uint32_t platform_get_boot_trampoline; 9 | uint32_t platform_bootprep; 10 | uint32_t usb_quiesce_no_free; 11 | uint32_t timer_stop_all; 12 | uint32_t interrupt_mask_all; 13 | uint32_t clocks_quiesce; 14 | uint32_t enter_critical_section; 15 | uint32_t arch_cpu_quiesce; 16 | uint32_t prepare_and_jump; 17 | } boot_config_t; 18 | 19 | typedef struct { 20 | uint32_t func_ptr; 21 | uint32_t security_consolidate_environment; 22 | uint32_t security_sidp_seal_rom_manifest; 23 | uint32_t platform_get_boot_trampoline; 24 | uint32_t platform_bootprep; 25 | uint32_t usb_controller_stop; 26 | uint32_t timer_stop_all; 27 | uint32_t interrupt_mask_all; 28 | uint32_t clocks_quiesce; 29 | uint32_t enter_critical_section; 30 | uint32_t arch_cpu_quiesce; 31 | } boot_config_watch_t; 32 | 33 | typedef struct { 34 | uint16_t cpid; 35 | const char *platform; 36 | uint32_t loadaddr; 37 | uint32_t loadsize; 38 | uint32_t demotion_reg; 39 | uint32_t aes_crypto_cmd; 40 | bool watch; 41 | union { 42 | const boot_config_t boot_config; 43 | const boot_config_watch_t boot_config_watch; 44 | }; 45 | } rom_config_t; 46 | 47 | static const rom_config_t rom_configs[] = { 48 | { 49 | .cpid = 0x8747, 50 | .platform = "s5l8747x", 51 | .loadaddr = 0x22000000, 52 | .loadsize = 0x19000, 53 | .aes_crypto_cmd = 0x6540 + 1, 54 | .boot_config = { 55 | .memmove = 0x8F50, 56 | .platform_get_boot_trampoline = 0x6164 + 1, 57 | .platform_bootprep = 0x4A0C + 1, 58 | .usb_quiesce_no_free = 0x2E50 + 1, 59 | .interrupt_mask_all = 0xC50 + 1, 60 | .timer_stop_all = 0x2594 + 1, 61 | .clocks_quiesce = 0x4F28 + 1, 62 | .enter_critical_section = 0x5578 + 1, 63 | .arch_cpu_quiesce = 0x6134 + 1 64 | } 65 | }, 66 | { 67 | .cpid = 0x8940, 68 | .platform = "s5l8940x", 69 | .loadaddr = 0x34000000, 70 | .loadsize = 0x2C000, 71 | .demotion_reg = 0x3F500000, 72 | .aes_crypto_cmd = 0x6F20 + 1, 73 | .boot_config = { 74 | .memmove = 0x916C, 75 | .platform_get_boot_trampoline = 0x6B64 + 1, 76 | .prepare_and_jump = 0x5E60 + 1 77 | } 78 | }, 79 | { 80 | .cpid = 0x8942, 81 | .platform = "s5l8942x", 82 | .loadaddr = 0x34000000, 83 | .loadsize = 0x2C000, 84 | .demotion_reg = 0x3F500000, 85 | .aes_crypto_cmd = 0x7300 + 1, 86 | .boot_config = { 87 | .memmove = 0x980C, 88 | .platform_get_boot_trampoline = 0x6F04 + 1, 89 | .prepare_and_jump = 0x60A0 + 1 90 | } 91 | }, 92 | { 93 | .cpid = 0x8945, 94 | .platform = "s5l8945x", 95 | .loadaddr = 0x34000000, 96 | .loadsize = 0x2C000, 97 | .demotion_reg = 0x3F500000, 98 | .aes_crypto_cmd = 0x7000 + 1, 99 | .boot_config = { 100 | .memmove = 0x942C, 101 | .platform_get_boot_trampoline = 0x6C44 + 1, 102 | .prepare_and_jump = 0x5E80 + 1 103 | } 104 | }, 105 | { 106 | .cpid = 0x8947, 107 | .platform = "s5l8947x", 108 | .loadaddr = 0x34000000, 109 | .loadsize = 0x2C000, 110 | .demotion_reg = 0x3F500000, 111 | .aes_crypto_cmd = 0x7060 + 1, 112 | .boot_config = { 113 | .memmove = 0x9A3C, 114 | .platform_get_boot_trampoline = 0x6C74 + 1, 115 | .platform_bootprep = 0x4EB0 + 1, 116 | .usb_quiesce_no_free = 0x324C + 1, 117 | .interrupt_mask_all = 0xD88 + 1, 118 | .timer_stop_all = 0xA338 + 1, //nullsub 119 | .clocks_quiesce = 0x5A88 + 1, 120 | .enter_critical_section = 0x6054 + 1, 121 | .arch_cpu_quiesce = 0x6C44 + 1 122 | } 123 | }, 124 | { 125 | .cpid = 0x8950, 126 | .platform = "s5l8950x", 127 | .loadaddr = 0x10000000, 128 | .loadsize = 0x60000, 129 | .demotion_reg = 0x3F500000, 130 | .aes_crypto_cmd = 0x7300 + 1, 131 | .boot_config = { 132 | .memmove = 0x9ACC, 133 | .platform_get_boot_trampoline = 0x6E84 + 1, 134 | .platform_bootprep = 0x562C + 1, 135 | .usb_quiesce_no_free = 0x381C + 1, 136 | .interrupt_mask_all = 0xDC4 + 1, 137 | .timer_stop_all = 0xAF40 + 1, //nullsub 138 | .clocks_quiesce = 0x5C38 + 1, 139 | .enter_critical_section = 0x6340 + 1, 140 | .arch_cpu_quiesce = 0x6ECC + 1 141 | } 142 | }, 143 | { 144 | .cpid = 0x8955, 145 | .platform = "s5l8955x", 146 | .loadaddr = 0x10000000, 147 | .loadsize = 0x60000, 148 | .demotion_reg = 0x3F500000, 149 | .aes_crypto_cmd = 0x7340 + 1, 150 | .boot_config = { 151 | .memmove = 0x9B0C, 152 | .platform_get_boot_trampoline = 0x6EC4 + 1, 153 | .platform_bootprep = 0x5628 + 1, 154 | .usb_quiesce_no_free = 0x381C + 1, 155 | .interrupt_mask_all = 0xDC4 + 1, 156 | .timer_stop_all = 0xAB7E + 1, //nullsub 157 | .clocks_quiesce = 0x5C70 + 1, 158 | .enter_critical_section = 0x6380 + 1, 159 | .arch_cpu_quiesce = 0x6F0C + 1 160 | } 161 | }, 162 | { 163 | .cpid = 0x7002, 164 | .platform = "s7002", 165 | .loadaddr = 0x46018000, 166 | .loadsize = 0x30000, 167 | .demotion_reg = 0x46C2A000, 168 | .aes_crypto_cmd = 0x6340 + 1, 169 | .watch = true, 170 | .boot_config_watch = { 171 | .func_ptr = 0x4600FFA4, 172 | .security_consolidate_environment = 0x59DC + 1, 173 | .security_sidp_seal_rom_manifest = 0x2218 + 1, //nullsub 174 | .platform_get_boot_trampoline = 0x46D8 + 1, 175 | .platform_bootprep = 0x42F4 + 1, 176 | .usb_controller_stop = 0x26FC + 1, 177 | .timer_stop_all = 0xD98 + 1, 178 | .interrupt_mask_all = 0x4C8C + 1, 179 | .clocks_quiesce = 0x4C0C + 1, 180 | .enter_critical_section = 0x50B0 + 1, 181 | .arch_cpu_quiesce = 0x5F24 + 1 182 | } 183 | }, 184 | { 185 | .cpid = 0x8002, 186 | .platform = "t8002", 187 | .loadaddr = 0x48818000, 188 | .loadsize = 0x108000, 189 | .demotion_reg = 0x481BC000, 190 | .aes_crypto_cmd = 0x86DC + 1, 191 | .watch = true, 192 | .boot_config_watch = { 193 | .func_ptr = 0x48806148, 194 | .security_consolidate_environment = 0x7DF4 + 1, 195 | .security_sidp_seal_rom_manifest = 0x3444 + 1, //nullsub 196 | .platform_get_boot_trampoline = 0x6774 + 1, 197 | .platform_bootprep = 0x6260 + 1, 198 | .usb_controller_stop = 0x3444 + 1, //nullsub 199 | .interrupt_mask_all = 0x1410 + 1, 200 | .timer_stop_all = 0x124C + 1, 201 | .clocks_quiesce = 0x6DE4 + 1, 202 | .enter_critical_section = 0x734C + 1, 203 | .arch_cpu_quiesce = 0x8328 + 1 204 | } 205 | }, 206 | { 207 | .cpid = 0x8004, 208 | .platform = "t8004", 209 | .loadaddr = 0x48818000, 210 | .loadsize = 0x128000, 211 | .demotion_reg = 0x481BC000, 212 | .aes_crypto_cmd = 0x786C + 1, 213 | .watch = true, 214 | .boot_config_watch = { 215 | .func_ptr = 0x48806178, 216 | .security_consolidate_environment = 0x6F34 + 1, 217 | .security_sidp_seal_rom_manifest = 0x6F94 + 1, 218 | .platform_get_boot_trampoline = 0x5864 + 1, 219 | .platform_bootprep = 0x52A0 + 1, 220 | .usb_controller_stop = 0x6264 + 1, //nullsub 221 | .interrupt_mask_all = 0x1414 + 1, 222 | .timer_stop_all = 0x1250 + 1, 223 | .clocks_quiesce = 0x5F24 + 1, 224 | .enter_critical_section = 0x648C + 1, 225 | .arch_cpu_quiesce = 0x74B8 + 1 226 | } 227 | } 228 | }; 229 | 230 | static const rom_config_t *get_config(uint32_t cpid) { 231 | for (int i = 0; i < sizeof(rom_configs) / sizeof(rom_config_t); i++) { 232 | const rom_config_t *config = &rom_configs[i]; 233 | if (config->cpid == cpid) { 234 | return config; 235 | } 236 | } 237 | 238 | return NULL; 239 | } 240 | 241 | #endif 242 | -------------------------------------------------------------------------------- /src/libbootkit/dfu.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "dfu.h" 5 | 6 | static size_t min(size_t first, size_t second) { 7 | if (first < second) { 8 | return first; 9 | } else { 10 | return second; 11 | } 12 | } 13 | 14 | int send_data(irecv_client_t client, unsigned char *data, size_t length) { 15 | size_t index = 0; 16 | 17 | while (index < length) { 18 | size_t amount = min(length - index, MAX_PACKET_SIZE); 19 | 20 | int ret = irecv_usb_control_transfer(client, 0x21, 1, 0, 0, data + index, amount, USB_TIMEOUT); 21 | 22 | if (ret != amount) { 23 | printf("DFU_DNLOAD ret = %d (%s)\n", ret, irecv_strerror(ret)); 24 | return -1; 25 | } 26 | 27 | index += amount; 28 | } 29 | 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /src/libbootkit/dfu.h: -------------------------------------------------------------------------------- 1 | #ifndef dfu_H 2 | #define dfu_H 3 | 4 | #include 5 | #include 6 | 7 | #define MAX_PACKET_SIZE 0x800 8 | #define USB_SMALL_TIMEOUT 100 9 | #define USB_TIMEOUT 1000 10 | 11 | int send_data(irecv_client_t client, unsigned char *data, size_t length); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /src/libbootkit/libbootkit.h: -------------------------------------------------------------------------------- 1 | #ifndef libbootkit_H 2 | #define libbootkit_H 3 | 4 | #include "config.h" 5 | #include "ops.h" 6 | #include "boot.h" 7 | #include "log.h" 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /src/libbootkit/log.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define DEBUG_ENABLED_VAR "LIBBOOTKIT_DEBUG" 5 | 6 | bool libbootkit_debug_enabled = false; 7 | 8 | __attribute__((constructor)) void libbootkit_log_init() { 9 | char *var = getenv(DEBUG_ENABLED_VAR); 10 | if (var) { 11 | char *stop = NULL; 12 | int val = strtol(var, &stop, 0); 13 | 14 | if (!*stop) { 15 | libbootkit_debug_enabled = (bool)val; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/libbootkit/log.h: -------------------------------------------------------------------------------- 1 | #ifndef LOG_H 2 | #define LOG_H 3 | 4 | #include 5 | 6 | extern bool libbootkit_debug_enabled; 7 | 8 | #define debug(fmt, ...) \ 9 | do { \ 10 | if (libbootkit_debug_enabled) { \ 11 | printf(fmt, ##__VA_ARGS__); \ 12 | } \ 13 | } while(0) 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /src/libbootkit/ops.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "log.h" 5 | #include "ops.h" 6 | #include "protocol.h" 7 | #include "config.h" 8 | 9 | int demote_op(irecv_client_t client, const rom_config_t *config, bool demote) { 10 | if (!config->demotion_reg) { 11 | printf("%s demotion is not supported\n", config->platform); 12 | return -1; 13 | } 14 | 15 | uint32_t value = 0; 16 | int ret = read32(client, config, config->demotion_reg, &value); 17 | if (ret != 0) { 18 | printf("couldn't read demotion register\n"); 19 | return -1; 20 | } 21 | 22 | debug("curr: 0x%08x\n", value); 23 | 24 | if (demote) { 25 | if ((value & 0x1) == 0x0) { 26 | printf("already demoted\n"); 27 | return -1; 28 | } 29 | 30 | value &= ~0x1; 31 | } else { 32 | value |= 0x1; 33 | } 34 | 35 | debug("new: 0x%08x\n", value); 36 | 37 | ret = write32(client, config, config->demotion_reg, value); 38 | if (ret != 0) { 39 | printf("couldn't write demotion register\n"); 40 | return -1; 41 | } 42 | 43 | return 0; 44 | } 45 | 46 | #define AES_OP_DECRYPT 0x11 47 | #define AES_KEY_GID0 0x200 48 | #define AES_256 0x20000000 49 | 50 | int aes_op(irecv_client_t client, const rom_config_t *config, uint8_t kbag[KBAG_LEN_256], size_t len) { 51 | struct { 52 | uint32_t op; 53 | uint32_t in; 54 | uint32_t out; 55 | uint32_t len; 56 | uint32_t key; 57 | uint32_t user_key; 58 | uint32_t user_iv; 59 | } args = { 0 }; 60 | 61 | args.op = AES_OP_DECRYPT; 62 | args.in = config->loadaddr + AUX_DATA_START; 63 | args.out = config->loadaddr + sizeof(usb_command_done_t); 64 | args.len = len; 65 | args.key = AES_KEY_GID0 | (len == KBAG_LEN_256 ? AES_256 : 0x0); 66 | 67 | return execute(client, config, kbag, len, config->aes_crypto_cmd, (uint32_t *)&args, kbag, len); 68 | } 69 | -------------------------------------------------------------------------------- /src/libbootkit/ops.h: -------------------------------------------------------------------------------- 1 | #ifndef ops_H 2 | #define ops_H 3 | 4 | #include 5 | #include 6 | #include "config.h" 7 | 8 | int demote_op(irecv_client_t client, const rom_config_t *config, bool demote); 9 | 10 | #define KBAG_LEN_256 0x30 11 | #define KBAG_LEN_128 0x20 12 | 13 | int aes_op(irecv_client_t client, const rom_config_t *config, uint8_t kbag[KBAG_LEN_256], size_t len); 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /src/libbootkit/payload.h: -------------------------------------------------------------------------------- 1 | unsigned char payload[] = { 2 | 0xdf, 0xf8, 0x6c, 0x00, 0xdf, 0xf8, 0x6c, 0x10, 0xdf, 0xf8, 0x6c, 0x20, 0xdf, 0xf8, 0x6c, 0x40, 3 | 0xa0, 0x47, 0xdf, 0xf8, 0x6c, 0x40, 0xa0, 0x47, 0xdf, 0xf8, 0x84, 0x40, 0x34, 0xb1, 0x01, 0x46, 4 | 0x4f, 0xf0, 0x00, 0x00, 0xdf, 0xf8, 0x48, 0x20, 0xa0, 0x47, 0x20, 0xe0, 0x05, 0x46, 0x4f, 0xf0, 5 | 0x00, 0x00, 0xdf, 0xf8, 0x50, 0x40, 0xa0, 0x47, 0xdf, 0xf8, 0x4c, 0x40, 0xa0, 0x47, 0xdf, 0xf8, 6 | 0x50, 0x40, 0xa0, 0x47, 0xdf, 0xf8, 0x44, 0x40, 0xa0, 0x47, 0xdf, 0xf8, 0x48, 0x40, 0xa0, 0x47, 7 | 0xdf, 0xf8, 0x44, 0x40, 0xa0, 0x47, 0xdf, 0xf8, 0x44, 0x40, 0xa0, 0x47, 0xdf, 0xf8, 0x10, 0x00, 8 | 0x4f, 0xf0, 0x00, 0x01, 0x4f, 0xf0, 0x00, 0x02, 0x4f, 0xf0, 0x00, 0x03, 0xa8, 0x47, 0xfe, 0xe7, 9 | 0x01, 0x00, 0xad, 0xde, 0x02, 0x00, 0xad, 0xde, 0x03, 0x00, 0xad, 0xde, 0x04, 0x00, 0xad, 0xde, 10 | 0x05, 0x00, 0xad, 0xde, 0x06, 0x00, 0xad, 0xde, 0x07, 0x00, 0xad, 0xde, 0x08, 0x00, 0xad, 0xde, 11 | 0x09, 0x00, 0xad, 0xde, 0x0a, 0x00, 0xad, 0xde, 0x0b, 0x00, 0xad, 0xde, 0x0c, 0x00, 0xad, 0xde, 12 | 0x0d, 0x00, 0xad, 0xde, 13 | }; 14 | -------------------------------------------------------------------------------- /src/libbootkit/payload_watch.h: -------------------------------------------------------------------------------- 1 | unsigned char payload_watch[] = { 2 | 0x10, 0x4c, 0xa0, 0x47, 0x10, 0x4c, 0xa0, 0x47, 0x10, 0x4c, 0xa0, 0x47, 0x05, 0x46, 0x4f, 0xf0, 3 | 0x00, 0x00, 0x0f, 0x4c, 0xa0, 0x47, 0x0f, 0x4c, 0xa0, 0x47, 0x10, 0x4c, 0xa0, 0x47, 0x0e, 0x4c, 4 | 0xa0, 0x47, 0x0f, 0x4c, 0xa0, 0x47, 0x0f, 0x4c, 0xa0, 0x47, 0x0f, 0x4c, 0xa0, 0x47, 0x04, 0x48, 5 | 0x4f, 0xf0, 0x00, 0x01, 0x4f, 0xf0, 0x00, 0x02, 0x4f, 0xf0, 0x00, 0x03, 0xa8, 0x47, 0xfe, 0xe7, 6 | 0x01, 0x00, 0xad, 0xde, 0x02, 0x00, 0xad, 0xde, 0x03, 0x00, 0xad, 0xde, 0x04, 0x00, 0xad, 0xde, 7 | 0x05, 0x00, 0xad, 0xde, 0x06, 0x00, 0xad, 0xde, 0x07, 0x00, 0xad, 0xde, 0x08, 0x00, 0xad, 0xde, 8 | 0x09, 0x00, 0xad, 0xde, 0x0a, 0x00, 0xad, 0xde, 0x0b, 0x00, 0xad, 0xde, 9 | }; 10 | -------------------------------------------------------------------------------- /src/libbootkit/payloads/payload.S: -------------------------------------------------------------------------------- 1 | .text 2 | .globl _start 3 | .code 16 4 | _start: 5 | LDR R0, LOADADDR 6 | LDR R1, IMAGEADDR 7 | LDR R2, IMAGESIZE 8 | LDR R4, memmove 9 | BLX R4 10 | 11 | LDR R4, platform_get_boot_trampoline 12 | BLX R4 13 | 14 | LDR R4, prepare_and_jump 15 | CBZ R4, avoid_free 16 | 17 | MOV R1, R0 18 | MOV R0, #0x0 19 | LDR R2, LOADADDR 20 | 21 | BLX R4 22 | 23 | B loop 24 | 25 | avoid_free: 26 | MOV R5, R0 27 | 28 | MOV R0, #0x0 29 | LDR R4, platform_bootprep 30 | BLX R4 31 | 32 | LDR R4, usb_quiesce_no_free 33 | BLX R4 34 | 35 | LDR R4, timer_stop_all 36 | BLX R4 37 | 38 | LDR R4, interrupt_mask_all 39 | BLX R4 40 | 41 | LDR R4, clocks_quiesce 42 | BLX R4 43 | 44 | LDR R4, enter_critical_section 45 | BLX R4 46 | 47 | LDR R4, arch_cpu_quiesce 48 | BLX R4 49 | 50 | LDR R0, LOADADDR 51 | MOV R1, #0x0 52 | MOV R2, #0x0 53 | MOV R3, #0x0 54 | 55 | BLX R5 56 | 57 | loop: 58 | B loop 59 | 60 | LOADADDR: 61 | .long 0xDEAD0001 62 | 63 | IMAGEADDR: 64 | .long 0xDEAD0002 65 | 66 | IMAGESIZE: 67 | .long 0xDEAD0003 68 | 69 | memmove: 70 | .long 0xDEAD0004 71 | 72 | platform_get_boot_trampoline: 73 | .long 0xDEAD0005 74 | 75 | platform_bootprep: 76 | .long 0xDEAD0006 77 | 78 | usb_quiesce_no_free: 79 | .long 0xDEAD0007 80 | 81 | interrupt_mask_all: 82 | .long 0xDEAD0008 83 | 84 | timer_stop_all: 85 | .long 0xDEAD0009 86 | 87 | clocks_quiesce: 88 | .long 0xDEAD000A 89 | 90 | enter_critical_section: 91 | .long 0xDEAD000B 92 | 93 | arch_cpu_quiesce: 94 | .long 0xDEAD000C 95 | 96 | prepare_and_jump: 97 | .long 0xDEAD000D 98 | -------------------------------------------------------------------------------- /src/libbootkit/payloads/payload_watch.S: -------------------------------------------------------------------------------- 1 | .text 2 | .globl _start 3 | .code 16 4 | _start: 5 | LDR R4, security_consolidate_environment 6 | BLX R4 7 | 8 | LDR R4, security_sidp_seal_rom_manifest 9 | BLX R4 10 | 11 | LDR R4, platform_get_boot_trampoline 12 | BLX R4 13 | 14 | MOV R5, R0 15 | 16 | MOV R0, #0x0 17 | LDR R4, platform_bootprep 18 | BLX R4 19 | 20 | LDR R4, usb_controller_stop 21 | BLX R4 22 | 23 | LDR R4, timer_stop_all 24 | BLX R4 25 | 26 | LDR R4, interrupt_mask_all 27 | BLX R4 28 | 29 | LDR R4, clocks_quiesce 30 | BLX R4 31 | 32 | LDR R4, enter_critical_section 33 | BLX R4 34 | 35 | LDR R4, arch_cpu_quiesce 36 | BLX R4 37 | 38 | LDR R0, LOADADDR 39 | MOV R1, #0x0 40 | MOV R2, #0x0 41 | MOV R3, #0x0 42 | 43 | BLX R5 44 | 45 | loop: 46 | B loop 47 | 48 | 49 | LOADADDR: 50 | .long 0xDEAD0001 51 | 52 | security_consolidate_environment: 53 | .long 0xDEAD0002 54 | 55 | security_sidp_seal_rom_manifest: 56 | .long 0xDEAD0003 57 | 58 | platform_get_boot_trampoline: 59 | .long 0xDEAD0004 60 | 61 | platform_bootprep: 62 | .long 0xDEAD0005 63 | 64 | usb_controller_stop: 65 | .long 0xDEAD0006 66 | 67 | interrupt_mask_all: 68 | .long 0xDEAD0007 69 | 70 | timer_stop_all: 71 | .long 0xDEAD0008 72 | 73 | clocks_quiesce: 74 | .long 0xDEAD0009 75 | 76 | enter_critical_section: 77 | .long 0xDEAD000A 78 | 79 | arch_cpu_quiesce: 80 | .long 0xDEAD000B 81 | -------------------------------------------------------------------------------- /src/libbootkit/protocol.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "log.h" 13 | #include "config.h" 14 | #include "dfu.h" 15 | #include "protocol.h" 16 | 17 | int validate_device(irecv_client_t client) { 18 | const struct irecv_device_info *info = irecv_get_device_info(client); 19 | 20 | int mode; 21 | 22 | if (irecv_get_mode(client, &mode) != IRECV_E_SUCCESS) { 23 | printf("failed to get device mode\n"); 24 | return -1; 25 | } 26 | 27 | if (mode != IRECV_K_DFU_MODE) { 28 | printf("non-DFU device found\n"); 29 | return -1; 30 | } 31 | 32 | if (!info->srtg) { 33 | printf("soft-DFU device found\n"); 34 | return -1; 35 | } 36 | 37 | if (!strstr(info->serial_string, "PWND:[checkm8]")) { 38 | printf("non-pwned-DFU device found\n"); 39 | return -1; 40 | } 41 | 42 | return 0; 43 | } 44 | 45 | #define ASSERT(cond, label) \ 46 | do { \ 47 | if (!(cond)) { \ 48 | goto label; \ 49 | } \ 50 | } while (0) 51 | 52 | uint8_t dummy_data[16] = { 0 }; 53 | 54 | int send_command(irecv_client_t client, unsigned char *command, size_t length) { 55 | debug("sending command...\n"); 56 | 57 | /* 58 | * I stole this blindly from ipwndfu, 59 | * it seems to clear counters without re-entering DFU (?) 60 | */ 61 | 62 | ASSERT(send_data(client, dummy_data, sizeof(dummy_data)) == 0, preflight_fail); 63 | ASSERT( 64 | irecv_usb_control_transfer(client, 0x21, 1, 0, 0, NULL, 0, USB_SMALL_TIMEOUT) == 0, 65 | preflight_fail 66 | ); 67 | 68 | uint8_t dummy_status[6]; 69 | 70 | for (int i = 0; i < 2; i++) { 71 | ASSERT( 72 | irecv_usb_control_transfer(client, 0xA1, 3, 0, 0, dummy_status, sizeof(dummy_status), USB_SMALL_TIMEOUT) == sizeof(dummy_status), 73 | preflight_fail 74 | ); 75 | } 76 | 77 | /* 78 | * ROM seems to advertise 50ms in status response, 79 | * but 1ms seems to be actually enough for us 80 | */ 81 | 82 | usleep(1 * 1000); 83 | 84 | if (send_data(client, command, length) != 0) { 85 | printf("failed to send command buffer\n"); 86 | return -1; 87 | } 88 | 89 | return 0; 90 | 91 | preflight_fail: 92 | printf("failed to prepare command sending\n"); 93 | return -1; 94 | } 95 | 96 | int trigger_command(irecv_client_t client, unsigned char *response, size_t response_length) { 97 | if (!response_length) { 98 | uint8_t dummy_byte = 0; 99 | return irecv_usb_control_transfer(client, 0xA1, 2, 0xFFFF, 0, &dummy_byte, sizeof(dummy_byte), USB_TIMEOUT); 100 | } else { 101 | return irecv_usb_control_transfer(client, 0xA1, 2, 0xFFFF, 0, (unsigned char*)response, response_length, USB_TIMEOUT); 102 | } 103 | } 104 | 105 | int save_command(irecv_client_t client, unsigned char *command, size_t length) { 106 | const struct irecv_device_info *info = irecv_get_device_info(client); 107 | 108 | char path[40]; 109 | snprintf((char *)&path, sizeof(path), "/tmp/%04X-%016llX_%08X", info->cpid, info->ecid, arc4random_uniform(UINT32_MAX)); 110 | 111 | int fd = open(path, O_WRONLY | O_CREAT, 0644); 112 | 113 | if (fd < 0) { 114 | printf("failed to create output file\n"); 115 | return -1; 116 | } 117 | 118 | if (write(fd, command, length) != length) { 119 | printf("failed to write to output file\n"); 120 | return -1; 121 | } 122 | 123 | printf("written to %s\n", path); 124 | 125 | return 0; 126 | } 127 | 128 | int read32(irecv_client_t client, const rom_config_t *config, uint32_t address, uint32_t *dest) { 129 | debug("reading 32-bits from 0x%x...\n", address); 130 | 131 | struct { 132 | usb_command_memc_t header; 133 | } cmd = { 0 }; 134 | 135 | cmd.header.magic = USB_COMMAND_MEM_MAGIC; 136 | cmd.header.magic2 = USB_COMMAND_MEM_MAGIC; 137 | cmd.header.dest_ptr = config->loadaddr + sizeof(usb_command_done_t); 138 | cmd.header.src_ptr = address; 139 | cmd.header.length = sizeof(uint32_t); 140 | 141 | if (send_command(client, (unsigned char*)&cmd, sizeof(cmd)) != 0) { 142 | printf("failed to send command\n"); 143 | return -1; 144 | } 145 | 146 | struct { 147 | usb_command_done_t header; 148 | uint32_t data; 149 | } resp = { 0 }; 150 | 151 | if (trigger_command(client, (unsigned char*)&resp, sizeof(resp)) != sizeof(resp)) { 152 | printf("failed to receive data\n"); 153 | return -1; 154 | } 155 | 156 | if (resp.header.magic != USB_COMMAND_DONE_MAGIC || resp.header.magic2 != USB_COMMAND_DONE_MAGIC) { 157 | printf("invalid response from device\n"); 158 | return -1; 159 | } 160 | 161 | *dest = resp.data; 162 | 163 | return 0; 164 | } 165 | 166 | int write32(irecv_client_t client, const rom_config_t *config, uint32_t address, uint32_t value) { 167 | debug("writing 0x%x to 0x%x...\n", value, address); 168 | 169 | struct { 170 | usb_command_memc_t header; 171 | uint32_t data; 172 | } cmd = { 0 }; 173 | 174 | cmd.header.magic = USB_COMMAND_MEM_MAGIC; 175 | cmd.header.magic2 = USB_COMMAND_MEM_MAGIC; 176 | cmd.header.dest_ptr = address; 177 | cmd.header.src_ptr = config->loadaddr + sizeof(usb_command_memc_t); 178 | cmd.header.length = sizeof(uint32_t); 179 | cmd.data = value; 180 | 181 | if (send_command(client, (unsigned char*)&cmd, sizeof(cmd)) != 0) { 182 | printf("failed to send command\n"); 183 | return -1; 184 | } 185 | 186 | trigger_command(client, NULL, 0); 187 | 188 | uint32_t verify = 0; 189 | 190 | if (read32(client, config, address, &verify) != 0) { 191 | printf("failed to re-read value\n"); 192 | return -1; 193 | } 194 | 195 | if (verify != value) { 196 | printf("re-read value doesn't match with requested one\n"); 197 | return -1; 198 | } 199 | 200 | return 0; 201 | } 202 | 203 | int execute(irecv_client_t client, const rom_config_t *config, uint8_t *output, size_t output_len, uint32_t address, uint32_t args[MAX_ARGS], uint8_t *aux_data, size_t aux_data_len) { 204 | if (output_len > MAX_OUTPUT_LEN) { 205 | printf("output length is too large\n"); 206 | return -1; 207 | } 208 | 209 | struct { 210 | usb_command_t header; 211 | uint32_t args[MAX_ARGS]; 212 | } cmd = { 0 }; 213 | 214 | cmd.header.magic = USB_COMMAND_EXEC_MAGIC; 215 | cmd.header.magic2 = USB_COMMAND_EXEC_MAGIC; 216 | cmd.header.function = address; 217 | 218 | memcpy(cmd.args, args, sizeof(cmd.args)); 219 | 220 | if (send_command(client, (uint8_t *)&cmd, sizeof(cmd)) != 0) { 221 | printf("failed to send execute command\n"); 222 | return -1; 223 | } 224 | 225 | if (aux_data) { 226 | if (send_data(client, aux_data, aux_data_len) != 0) { 227 | printf("failed to send command auxilary data\n"); 228 | return -1; 229 | } 230 | } 231 | 232 | struct { 233 | usb_command_done_t header; 234 | uint8_t buffer[MAX_OUTPUT_LEN]; 235 | } resp; 236 | 237 | trigger_command(client, (uint8_t *)&resp, sizeof(usb_command_done_t) + output_len); 238 | 239 | if (resp.header.magic != USB_COMMAND_DONE_MAGIC || resp.header.magic2 != USB_COMMAND_DONE_MAGIC) { 240 | printf("invalid response from device\n"); 241 | return -1; 242 | } 243 | 244 | memcpy(output, resp.buffer, output_len); 245 | 246 | return 0; 247 | } 248 | -------------------------------------------------------------------------------- /src/libbootkit/protocol.h: -------------------------------------------------------------------------------- 1 | #ifndef protocol_H 2 | #define protocol_H 3 | 4 | #include "config.h" 5 | 6 | typedef struct { 7 | uint32_t magic; 8 | uint32_t magic2; 9 | #define USB_COMMAND_EXEC_MAGIC 'exec' 10 | uint32_t function; 11 | uint8_t padding[4]; 12 | } usb_command_t; 13 | 14 | typedef struct __attribute__((packed)) { 15 | uint32_t magic; 16 | uint32_t magic2; 17 | #define USB_COMMAND_MEM_MAGIC 'memc' 18 | uint8_t padding[8]; 19 | uint32_t dest_ptr; 20 | uint32_t src_ptr; 21 | uint32_t length; 22 | } usb_command_memc_t; 23 | 24 | typedef struct __attribute__((packed)) { 25 | uint32_t magic; 26 | uint32_t magic2; 27 | #define USB_COMMAND_DONE_MAGIC 'done' 28 | uint32_t ret; 29 | uint8_t padding[4]; 30 | } usb_command_done_t; 31 | 32 | int read32(irecv_client_t client, const rom_config_t *config, uint32_t address, uint32_t *dest); 33 | int write32(irecv_client_t client, const rom_config_t *config, uint32_t address, uint32_t value); 34 | 35 | #define MAX_ARGS 8 36 | #define AUX_DATA_START (sizeof(usb_command_t) + sizeof(uint32_t) * MAX_ARGS) 37 | #define MAX_OUTPUT_LEN 0x400 38 | 39 | int execute(irecv_client_t client, const rom_config_t *config, uint8_t *output, size_t output_len, uint32_t address, uint32_t args[MAX_ARGS], uint8_t *aux_data, size_t aux_data_len); 40 | 41 | int validate_device(irecv_client_t client); 42 | 43 | int send_command(irecv_client_t client, unsigned char *command, size_t length); 44 | int trigger_command(irecv_client_t client, unsigned char *response, size_t response_length); 45 | 46 | int save_command(irecv_client_t client, unsigned char *command, size_t length); 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "libbootkit/libbootkit.h" 8 | #include "batch.h" 9 | #include "utils.h" 10 | 11 | void print_usb_serial(irecv_client_t client) { 12 | const struct irecv_device_info *info = irecv_get_device_info(client); 13 | printf("found: %s\n", info->serial_string); 14 | } 15 | 16 | void print_usage(const char *program_name) { 17 | printf("usage: %s VERB [args]\n", program_name); 18 | printf("\nwhere VERB is one of the following:\n"); 19 | printf("\tboot \n"); 20 | printf("\tkbag \n"); 21 | printf("\tdemote\n"); 22 | printf("\tbatch \n"); 23 | 24 | printf("\nfor batch KBAG processing, you must input a text file in following format:\n"); 25 | printf("\tFIRMWARE0 FILE0 KBAG\n"); 26 | printf("\t...\n"); 27 | printf("\tFIRMWAREn FILEn KBAG\n"); 28 | 29 | printf("\nin return you'll get the same structure, but with IV+key pair appended to each entry\n"); 30 | 31 | printf("\nsupported platforms:\n\t"); 32 | 33 | size_t number_of_configs = sizeof(rom_configs) / sizeof(rom_config_t); 34 | 35 | for (int i = 0; i < number_of_configs; i++) { 36 | const rom_config_t *config = &rom_configs[i]; 37 | (i != number_of_configs - 1) ? printf("%s, ", config->platform) : printf("%s", config->platform); 38 | } 39 | 40 | printf("\n"); 41 | } 42 | 43 | static bool kbag_len_validate(size_t len, uint16_t cpid) { 44 | bool haywire = cpid == 0x8747; 45 | 46 | if (len == KBAG_LEN_128) { 47 | if (!haywire) { 48 | printf("128-bit KBAG provided for non-Haywire target\n"); 49 | return false; 50 | } 51 | } else if (len == KBAG_LEN_256) { 52 | if (haywire) { 53 | printf("256-bit KBAG provided for Haywire target\n"); 54 | return false; 55 | } 56 | } else { 57 | printf("KBAG is neither 256-bit nor 128?!\n"); 58 | return false; 59 | } 60 | 61 | return true; 62 | } 63 | 64 | enum { 65 | VERB_NONE = -1, 66 | VERB_BOOT, 67 | VERB_KBAG, 68 | VERB_DEMOTE, 69 | VERB_BATCH 70 | }; 71 | 72 | int main(int argc, char *argv[]) { 73 | if (argc < 2) { 74 | goto usage; 75 | } 76 | 77 | int ret = -1; 78 | int verb = VERB_NONE; 79 | bool debug = false; 80 | 81 | int fd = -1; 82 | int out_fd = -1; 83 | 84 | uint8_t *bootloader_buffer = NULL; 85 | size_t bootloader_size = 0; 86 | 87 | char *batch_input_buffer = NULL; 88 | size_t batch_input_size = 0; 89 | struct batch_entry *batch_entries = NULL; 90 | int batch_entry_count = 0; 91 | 92 | uint8_t kbag[KBAG_LEN_256] = { 0 }; 93 | size_t kbag_len = 0; 94 | 95 | irecv_client_t client = NULL; 96 | 97 | if (strcmp(argv[1], "boot") == 0) { 98 | verb = VERB_BOOT; 99 | 100 | if (argc != 3) { 101 | goto missing_arg; 102 | } 103 | 104 | const char *bootloader = argv[2]; 105 | 106 | fd = open(bootloader, O_RDONLY); 107 | if (fd < 0) { 108 | printf("failed to open bootloader\n"); 109 | goto out; 110 | } 111 | 112 | bootloader_size = lseek(fd, 0, SEEK_END); 113 | if (!bootloader_size) { 114 | printf("bootloader is empty?!\n"); 115 | goto out; 116 | } 117 | 118 | bootloader_buffer = malloc(bootloader_size); 119 | if (!bootloader_buffer) { 120 | printf("out of memory\n"); 121 | goto out; 122 | } 123 | 124 | if (pread(fd, bootloader_buffer, bootloader_size, 0) != bootloader_size) { 125 | printf("failed to read bootloader\n"); 126 | goto out; 127 | } 128 | 129 | close(fd); 130 | fd = 0; 131 | 132 | } else if (strcmp(argv[1], "kbag") == 0) { 133 | verb = VERB_KBAG; 134 | 135 | if (argc != 3) { 136 | goto missing_arg; 137 | } 138 | 139 | const char *raw_kbag = argv[2]; 140 | 141 | size_t raw_kbag_len = strlen(raw_kbag); 142 | 143 | if (raw_kbag_len == KBAG_LEN_256 * 2) { 144 | kbag_len = KBAG_LEN_256; 145 | } else if (raw_kbag_len == KBAG_LEN_128 * 2) { 146 | kbag_len = KBAG_LEN_128; 147 | } else { 148 | printf("KBAG must be %d bytes in length (or %d bytes for Haywire)\n", KBAG_LEN_256, KBAG_LEN_128); 149 | return -1; 150 | } 151 | 152 | if (str2hex(kbag_len, kbag, raw_kbag) != kbag_len) { 153 | printf("failed to decode KBAG\n"); 154 | return -1; 155 | } 156 | 157 | } else if (strcmp(argv[1], "demote") == 0) { 158 | verb = VERB_DEMOTE; 159 | } else if (strcmp(argv[1], "batch") == 0) { 160 | verb = VERB_BATCH; 161 | 162 | if (argc != 4) { 163 | goto missing_arg; 164 | } 165 | 166 | const char *input = argv[2]; 167 | 168 | fd = open(input, O_RDONLY); 169 | if (fd < 0) { 170 | printf("failed to open batch input\n"); 171 | goto out; 172 | } 173 | 174 | batch_input_size = lseek(fd, 0, SEEK_END); 175 | if (!batch_input_size) { 176 | printf("batch input is empty?!\n"); 177 | goto out; 178 | } 179 | 180 | batch_input_buffer = malloc(batch_input_size + 1); 181 | if (!batch_input_buffer) { 182 | printf("out of memory\n"); 183 | goto out; 184 | } 185 | 186 | if (pread(fd, batch_input_buffer, batch_input_size, 0) != batch_input_size) { 187 | printf("failed to read batch input\n"); 188 | goto out; 189 | } 190 | 191 | batch_input_buffer[batch_input_size] = '\0'; 192 | 193 | close(fd); 194 | fd = -1; 195 | 196 | if (batch_parse(batch_input_buffer, &batch_entries, &batch_entry_count) != 0) { 197 | goto out; 198 | } 199 | 200 | } 201 | 202 | if (verb == VERB_NONE) { 203 | goto usage; 204 | } 205 | 206 | if (irecv_open_with_ecid(&client, 0) != IRECV_E_SUCCESS) { 207 | printf("ERROR: failed to open DFU-device\n"); 208 | return -1; 209 | } 210 | 211 | print_usb_serial(client); 212 | 213 | const struct irecv_device_info *info = irecv_get_device_info(client); 214 | const rom_config_t *config = get_config(info->cpid); 215 | 216 | switch (verb) { 217 | case VERB_BOOT: { 218 | printf("loading iBoot...\n"); 219 | 220 | if (!config->watch) { 221 | ret = dfu_boot(client, bootloader_buffer, bootloader_size, debug); 222 | } else { 223 | ret = dfu_boot_watch(client, bootloader_buffer, bootloader_size, debug); 224 | } 225 | 226 | goto out; 227 | } 228 | 229 | case VERB_KBAG: { 230 | printf("decrypting KBAG...\n"); 231 | 232 | if (!kbag_len_validate(kbag_len, config->cpid)) { 233 | goto out; 234 | } 235 | 236 | #define AES_IV_SIZE 0x10 237 | 238 | ret = aes_op(client, config, kbag, kbag_len); 239 | if (ret == 0) { 240 | printf("iv: "); 241 | for (size_t i = 0; i < AES_IV_SIZE; i++) { 242 | printf("%02X", kbag[i]); 243 | } 244 | 245 | printf(" "); 246 | 247 | printf("key: "); 248 | for (size_t i = AES_IV_SIZE; i < kbag_len; i++) { 249 | printf("%02X", kbag[i]); 250 | } 251 | 252 | printf("\n"); 253 | } 254 | 255 | break; 256 | } 257 | 258 | case VERB_DEMOTE: { 259 | printf("demoting...\n"); 260 | ret = demote_op(client, config, true); 261 | break; 262 | } 263 | 264 | case VERB_BATCH: { 265 | out_fd = open(argv[3], O_WRONLY | O_CREAT, 0644); 266 | if (out_fd < 0) { 267 | printf("failed to create output file\n"); 268 | goto out; 269 | } 270 | 271 | printf("decrypting KBAG batch...\n"); 272 | 273 | for (int i = 0; i < batch_entry_count; i++) { 274 | struct batch_entry *curr = &batch_entries[i]; 275 | 276 | uint8_t kbag[KBAG_LEN_256] = { 0 }; 277 | int kbag_len = curr->kbag_len; 278 | 279 | memcpy(kbag, curr->kbag, kbag_len); 280 | 281 | if (!kbag_len_validate(kbag_len, config->cpid)) { 282 | goto out; 283 | } 284 | 285 | write(out_fd, curr->firmware, strlen(curr->firmware)); 286 | write(out_fd, " ", 1); 287 | write(out_fd, curr->file, strlen(curr->file)); 288 | write(out_fd, " ", 1); 289 | 290 | char kbag_str[KBAG_LEN_256 * 2 + 1] = { 0 }; 291 | hex2str(kbag_str, kbag_len, curr->kbag); 292 | 293 | write(out_fd, kbag_str, kbag_len * 2); 294 | write(out_fd, " ", 1); 295 | 296 | ret = aes_op(client, config, kbag, kbag_len); 297 | if (ret != 0) { 298 | goto out; 299 | } 300 | 301 | char key_str[KBAG_LEN_256 * 2 + 1] = { 0 }; 302 | hex2str(key_str, kbag_len, kbag); 303 | 304 | write(out_fd, key_str, kbag_len * 2); 305 | write(out_fd, "\n", 1); 306 | 307 | if (!libbootkit_debug_enabled) { 308 | printf("\rdecrypting: %d/%d", i + 1, batch_entry_count); 309 | } 310 | } 311 | 312 | if (!libbootkit_debug_enabled) { 313 | printf("\n"); 314 | } 315 | } 316 | } 317 | 318 | out: 319 | if (fd != -1) { 320 | close(fd); 321 | } 322 | 323 | if (out_fd != -1) { 324 | close(out_fd); 325 | } 326 | 327 | if (bootloader_buffer) { 328 | free(bootloader_buffer); 329 | } 330 | 331 | if (batch_input_buffer) { 332 | free(batch_input_buffer); 333 | } 334 | 335 | if (batch_entries) { 336 | free(batch_entries); 337 | } 338 | 339 | if (client) { 340 | irecv_close(client); 341 | } 342 | 343 | if (ret == 0) { 344 | printf("DONE\n"); 345 | } 346 | 347 | return ret; 348 | 349 | missing_arg: 350 | printf("\"%s\" needs argument!\n", argv[1]); 351 | 352 | usage: 353 | print_usage(argv[0]); 354 | return -1; 355 | } 356 | -------------------------------------------------------------------------------- /src/utils.c: -------------------------------------------------------------------------------- 1 | /* stolen from @xerub's ibex */ 2 | #include "utils.h" 3 | 4 | void hex2str(char *str, int buflen, const unsigned char *buf) { 5 | static const char h2a[] = "0123456789abcdef"; 6 | for (; buflen > 0; --buflen) { 7 | unsigned char byte = *buf++; 8 | *str++ = h2a[byte >> 4]; 9 | *str++ = h2a[byte & 0xF]; 10 | } 11 | *str = '\0'; 12 | } 13 | 14 | int str2hex(size_t buflen, uint8_t *buf, const char *str) { 15 | unsigned char *ptr = buf; 16 | int seq = -1; 17 | while (buflen > 0) { 18 | int nibble = *str++; 19 | if (nibble >= '0' && nibble <= '9') { 20 | nibble -= '0'; 21 | } else { 22 | nibble |= 0x20; 23 | if (nibble < 'a' || nibble > 'f') { 24 | break; 25 | } 26 | nibble -= 'a' - 10; 27 | } 28 | if (seq >= 0) { 29 | *buf++ = (seq << 4) | nibble; 30 | buflen--; 31 | seq = -1; 32 | } else { 33 | seq = nibble; 34 | } 35 | } 36 | 37 | return buf - ptr; 38 | } 39 | -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS_H 2 | #define UTILS_H 3 | 4 | #include 5 | #include 6 | 7 | void hex2str(char *str, int buflen, const unsigned char *buf); 8 | int str2hex(size_t buflen, uint8_t *buf, const char *str); 9 | 10 | #endif 11 | --------------------------------------------------------------------------------