diff options
Diffstat (limited to 'utils')
-rw-r--r-- | utils/hwpatcher/Makefile | 27 | ||||
-rw-r--r-- | utils/hwpatcher/Makefile.patch | 25 | ||||
-rw-r--r-- | utils/hwpatcher/arm.lua | 205 | ||||
-rw-r--r-- | utils/hwpatcher/creative.lua | 49 | ||||
-rw-r--r-- | utils/hwpatcher/fuzep.lua | 49 | ||||
-rw-r--r-- | utils/hwpatcher/fuzep_rb.lua | 38 | ||||
-rw-r--r-- | utils/hwpatcher/generic_stmp.lua | 108 | ||||
-rw-r--r-- | utils/hwpatcher/hwpatcher.c | 1123 | ||||
-rw-r--r-- | utils/hwpatcher/lib.lua | 107 | ||||
-rw-r--r-- | utils/hwpatcher/md5.c | 246 | ||||
-rw-r--r-- | utils/hwpatcher/md5.h | 25 | ||||
-rw-r--r-- | utils/hwpatcher/patch.S | 87 | ||||
-rw-r--r-- | utils/hwpatcher/patch_viewbl.lua | 1 | ||||
-rw-r--r-- | utils/hwpatcher/view.lua | 38 | ||||
-rw-r--r-- | utils/hwpatcher/zen.lua | 1 | ||||
-rw-r--r-- | utils/hwpatcher/zxfi2.lua | 50 | ||||
-rw-r--r-- | utils/hwpatcher/zxfi3.lua | 49 |
17 files changed, 2228 insertions, 0 deletions
diff --git a/utils/hwpatcher/Makefile b/utils/hwpatcher/Makefile new file mode 100644 index 0000000000..c64ff302cc --- /dev/null +++ b/utils/hwpatcher/Makefile @@ -0,0 +1,27 @@ +CC=gcc +CXX=g++ +LD=g++ +SBTOOLS_DIR=../imxtools/sbtools +CFLAGS=-Wall -O3 -std=c99 -g -I$(SBTOOLS_DIR) `pkg-config --cflags lua5.2` +LDFLAGS=`pkg-config --libs lua5.2` -L$(REGTOOLS_LIB_DIR) `xml2-config --libs` -lreadline +EXEC=hwpatcher +SBTOOLS_SRC=elf.c crypto.c sb.c sb1.c aes128.c crc.c misc.c sha1.c xorcrypt.c +SBTOOLS_OBJ=$(SBTOOLS_SRC:.c=.o) +SRC=$(wildcard *.c) +OBJ=$(SRC:.c=.o) + +all: $(EXEC) + +%.o: $(SBTOOLS_DIR)/%.c + $(CC) $(CFLAGS) -c -o $@ $< + +%.o: $%.c + $(CC) $(CFLAGS) -c -o $@ $< + +hwpatcher: hwpatcher.o md5.o $(SBTOOLS_OBJ) + $(LD) -o $@ $^ $(LDFLAGS) + +clean: + rm -rf $(SBTOOLS_OBJ) $(OBJ) $(EXEC) + + diff --git a/utils/hwpatcher/Makefile.patch b/utils/hwpatcher/Makefile.patch new file mode 100644 index 0000000000..61ea4f439e --- /dev/null +++ b/utils/hwpatcher/Makefile.patch @@ -0,0 +1,25 @@ +PREFIX?=arm-elf-eabi- +AS=$(PREFIX)gcc +ASFLAGS=-nostdlib -ffreestanding -mcpu=arm926ej-s +OC=$(PREFIX)objcopy +TARGETS=fuzeplus zenxfi2 zenxfi3 zen nwz zenxfistyle +OPT_fuzeplus=-DSANSA_FUZEPLUS +OPT_zenxfi2=-DCREATIVE_ZENXFI2 +OPT_zenxfi3=-DCREATIVE_ZENXFI3 +OPT_zen=-DCREATIVE_ZEN +OPT_nwz=-DSONY_NWZ +OPT_zenxfistyle=-DCREATIVE_ZENXFISTYLE + +BOOTBINS=$(patsubst %, patch_%.bin, $(TARGETS)) +BOOTELF=$(patsubst %, patch_%.elf, $(TARGETS)) + +all: $(BOOTBINS) + +patch_%.bin: patch_%.elf + $(OC) -O binary $^ $@ + +patch_%.elf: patch.S + $(AS) $(ASFLAGS) $(OPT_$(@:patch_%.elf=%)) -o $@ $< + +clean: + rm -rf $(BOOTBINS) $(BOOTELF) diff --git a/utils/hwpatcher/arm.lua b/utils/hwpatcher/arm.lua new file mode 100644 index 0000000000..0211244d11 --- /dev/null +++ b/utils/hwpatcher/arm.lua @@ -0,0 +1,205 @@ +--[[ +hwpatcher arm decoding/encoding library + +]]-- +arm = {} + +-- determines whether an address is in Thumb code or not +function arm.is_thumb(addr) + return bit32.extract(addr.addr, 0) == 1 +end + +-- translate address to real address (ie without Thumb bit) +-- produces an error if address is not properly aligned in ARM mode +function arm.xlate_addr(addr) + local res = hwp.deepcopy(addr) + if arm.is_thumb(addr) then + res.addr = bit32.replace(addr.addr, 0, 0) + elseif bit32.extract(addr.addr, 0, 2) ~= 0 then + error("ARM address is not word-aligned") + end + return res +end + +-- switch between arm and thumb +function arm.to_thumb(addr) + local res = hwp.deepcopy(addr) + res.addr = bit32.bor(addr.addr, 1) + return res +end + +function arm.to_arm(addr) + return arm.xlate_addr(addr) +end + +-- sign extend a value to 32-bits +-- only the lower 'bits' bits are considered, everything else is trashed +-- watch out arithmetic vs logical shift ! +function arm.sign32(v) + if bit32.extract(v, 31) == 1 then + return -1 - bit32.bnot(v) + else + return v + end +end +function arm.sign_extend(val, bits) + return arm.sign32(bit32.arshift(bit32.lshift(val, 32 - bits), 32 - bits)) +end + +-- check that a signed value fits in some field +function arm.check_sign_truncation(val, bits) + return val == arm.sign_extend(val, bits) +end + +-- create a branch description +function arm.make_branch(addr, link) + local t = {type = "branch", addr = addr, link = link} + local branch_to_string = function(self) + return string.format("branch(%s,%s)", self.addr, self.link) + end + setmetatable(t, {__tostring = branch_to_string}) + return t +end + +-- parse a jump and returns its description +function arm.parse_branch(fw, addr) + local opcode = hwp.read32(fw, arm.xlate_addr(addr)) + if arm.is_thumb(addr) then + if bit32.band(opcode, 0xf800) ~= 0xf000 then + error("first instruction is not a bl(x) prefix") + end + local to_thumb = false + if bit32.band(opcode, 0xf8000000) == 0xf8000000 then + to_thumb = true + elseif bit32.band(opcode, 0xf8000000) ~= 0xe8000000 then + error("second instruction is not a bl(x) suffix") + end + local dest = hwp.make_addr(bit32.lshift(arm.sign_extend(opcode, 11), 12) + + arm.xlate_addr(addr).addr + 4 + + bit32.rshift(bit32.band(opcode, 0x7ff0000), 16) * 2, addr.section) + if to_thumb then + dest = arm.to_thumb(dest) + else + dest.addr = bit32.replace(dest.addr, 0, 0, 2) + end + return arm.make_branch(dest, true) + else + if bit32.band(opcode, 0xfe000000) == 0xfa000000 then -- BLX + local dest = hwp.make_addr(arm.sign_extend(opcode, 24) * 4 + 8 + + bit32.extract(opcode, 24) * 2 + arm.xlate_addr(addr).addr, addr.section) + return arm.make_branch(arm.to_thumb(dest), true) + elseif bit32.band(opcode, 0xfe000000) == 0xea000000 then -- B(L) + local dest = hwp.make_addr(arm.sign_extend(opcode, 24) * 4 + 8 + + arm.xlate_addr(addr).addr, addr.section) + return arm.make_branch(arm.to_arm(dest), bit32.extract(opcode, 24)) + else + error("instruction is not a valid branch") + end + end +end + +-- generate the encoding of a branch +-- if the branch cannot be encoded using an immediate branch, it is generated +-- with an indirect form like a ldr, using a pool +function arm.write_branch(fw, addr, dest, pool) + local offset = arm.xlate_addr(dest.addr).addr - arm.xlate_addr(addr).addr + local exchange = arm.is_thumb(addr) ~= arm.is_thumb(dest.addr) + local opcode = 0 + if arm.is_thumb(addr) then + if not dest.link then + return arm.write_load_pc(fw, addr, dest, pool) + end + offset = offset - 4 -- in Thumb, PC+4 relative + -- NOTE: BLX is undefined if the resulting offset has bit 0 set, follow + -- the procedure from the manual to ensure correct operation + if bit32.extract(offset, 1) ~= 0 then + offset = offset + 2 + end + offset = offset / 2 + if not arm.check_sign_truncation(offset, 22) then + error("destination is too far for immediate branch from thumb") + end + opcode = 0xf000 + -- BL/BLX prefix + bit32.band(bit32.rshift(offset, 11), 0x7ff) + -- offset (high part) + bit32.lshift(exchange and 0xe800 or 0xf800,16) + -- BLX suffix + bit32.lshift(bit32.band(offset, 0x7ff), 16) -- offset (low part) + else + offset = offset - 8 -- in ARM, PC+8 relative + if exchange and not dest.link then + return arm.write_load_pc(fw, addr, dest, pool) + end + offset = offset / 4 + if not arm.check_sign_truncation(offset, 24) then + if pool == nil then + error("destination is too far for immediate branch from arm (no pool available)") + else + return arm.write_load_pc(fw, addr, dest, pool) + end + end + opcode = bit32.lshift(exchange and 0xf or 0xe, 28) + -- BLX / AL cond + bit32.lshift(0xa, 24) + -- branch + bit32.lshift(exchange and bit32.extract(offset, 1) or dest.link and 1 or 0, 24) + -- link / bit1 + bit32.band(offset, 0xffffff) + end + return hwp.write32(fw, arm.xlate_addr(addr), opcode) +end + +function arm.write_load_pc(fw, addr, dest, pool) + -- write pool + hwp.write32(fw, pool, dest.addr.addr) + -- write "ldr pc, [pool]" + local opcode + if arm.is_thumb(addr) then + error("unsupported pc load in thumb mode") + else + local offset = pool.addr - arm.xlate_addr(addr).addr - 8 -- ARM is PC+8 relative + local add = offset >= 0 and 1 or 0 + offset = math.abs(offset) + opcode = bit32.lshift(0xe, 28) + -- AL cond + bit32.lshift(1, 26) + -- ldr/str + bit32.lshift(1, 24) + -- P + bit32.lshift(add, 23) + -- U + bit32.lshift(1, 20) + -- ldr + bit32.lshift(15, 16) + -- Rn=PC + bit32.lshift(15, 12) + -- Rd=PC + bit32.band(offset, 0xfff) + end + return hwp.write32(fw, arm.xlate_addr(addr), opcode) +end + +-- generate the encoding of a "bx lr" +function arm.write_return(fw, addr) + if arm.is_thumb(addr) then + error("unsupported return from thumb code") + end + local opcode = bit32.lshift(0xe, 28) + -- AL cond + bit32.lshift(0x12, 20) + -- BX + bit32.lshift(1, 4) + -- BX + bit32.lshift(0xfff, 8) + -- SBO + 14 -- LR + hwp.write32(fw, arm.xlate_addr(addr), opcode) +end + +function arm.write_xxx_regs(fw, addr, load) + if arm.is_thumb(addr) then + error("unsupported save/restore regs from thumb code") + end + -- STMFD sp!,{r0-r12, lr} + local opcode = bit32.lshift(0xe, 28) + -- AL cond + bit32.lshift(0x4, 25) + -- STM/LDM + bit32.lshift(load and 0 or 1,24) + -- P + bit32.lshift(load and 1 or 0, 23) + -- U + bit32.lshift(1, 21) +-- W + bit32.lshift(load and 1 or 0, 20) + -- L + bit32.lshift(13, 16) + -- base = SP + 0x5fff -- R0-R12,LR + return hwp.write32(fw, addr, opcode) +end + +function arm.write_save_regs(fw, addr) + return arm.write_xxx_regs(fw, addr, false) +end + +function arm.write_restore_regs(fw, addr) + return arm.write_xxx_regs(fw, addr, true) +end
\ No newline at end of file diff --git a/utils/hwpatcher/creative.lua b/utils/hwpatcher/creative.lua new file mode 100644 index 0000000000..437785a0d5 --- /dev/null +++ b/utils/hwpatcher/creative.lua @@ -0,0 +1,49 @@ +--[[ +Creative ZEN hacking +required argument (in order): +- path to firmware +- path to output firmware +- path to blob +- path to stub +]]-- + +if #arg < 4 then + error("not enough argument to fuzep patcher") +end + +local fw = hwp.load_file(arg[1]) +local irq_addr_pool = hwp.make_addr(0x38) +local proxy_addr = arm.to_arm(hwp.make_addr(0x402519A0)) +-- read old IRQ address pool +local old_irq_addr = hwp.make_addr(hwp.read32(fw, irq_addr_pool)) +print(string.format("Old IRQ address: %s", old_irq_addr)) +-- put stub at the beginning of the proxy +local stub = hwp.load_bin_file(arg[4]) +local stub_info = hwp.section_info(stub, "") +local stub_data = hwp.read(stub, hwp.make_addr(stub_info.addr, ""), stub_info.size) +hwp.write(fw, proxy_addr, stub_data) +local stub_addr = proxy_addr +proxy_addr = hwp.inc_addr(proxy_addr, stub_info.size) +-- modify irq +hwp.write32(fw, irq_addr_pool, proxy_addr.addr) +print(string.format("New IRQ address: %s", proxy_addr)) +-- in proxy, save registers +arm.write_save_regs(fw, proxy_addr) +proxy_addr = hwp.inc_addr(proxy_addr, 4) +-- load blob +local blob = hwp.load_bin_file(arg[3]) +local blob_info = hwp.section_info(blob, "") +-- patch blob with stub address +hwp.write32(blob, hwp.make_addr(blob_info.addr + 4, ""), stub_addr.addr) +-- write it ! +local blob_data = hwp.read(blob, hwp.make_addr(blob_info.addr, ""), blob_info.size) +hwp.write(fw, proxy_addr, blob_data) +proxy_addr = hwp.inc_addr(proxy_addr, blob_info.size) +-- restore registers +arm.write_restore_regs(fw, proxy_addr) +proxy_addr = hwp.inc_addr(proxy_addr, 4) +-- branch to old code +local branch_to_old = arm.make_branch(old_irq_addr, false) +arm.write_branch(fw, proxy_addr, branch_to_old, hwp.inc_addr(proxy_addr, 4)) +-- save +hwp.save_file(fw, arg[2]) diff --git a/utils/hwpatcher/fuzep.lua b/utils/hwpatcher/fuzep.lua new file mode 100644 index 0000000000..6dfdad763a --- /dev/null +++ b/utils/hwpatcher/fuzep.lua @@ -0,0 +1,49 @@ +--[[ +Fuze+ 2.36.8 hacking +required argument (in order): +- path to firmware +- path to output firmware +- path to blob +- path to stub +]]-- + +if #arg < 4 then + error("not enough argument to fuzep patcher") +end + +local fw = hwp.load_file(arg[1]) +local irq_addr_pool = hwp.make_addr(0x41172d44) +local proxy_addr = arm.to_arm(hwp.make_addr(0x40f35874)) +-- read old IRQ address pool +local old_irq_addr = hwp.make_addr(hwp.read32(fw, irq_addr_pool)) +print(string.format("Old IRQ address: %s", old_irq_addr)) +-- put stub at the beginning of the proxy +local stub = hwp.load_bin_file(arg[4]) +local stub_info = hwp.section_info(stub, "") +local stub_data = hwp.read(stub, hwp.make_addr(stub_info.addr, ""), stub_info.size) +hwp.write(fw, proxy_addr, stub_data) +local stub_addr = proxy_addr +proxy_addr = hwp.inc_addr(proxy_addr, stub_info.size) +-- modify irq +hwp.write32(fw, irq_addr_pool, proxy_addr.addr) +print(string.format("New IRQ address: %s", proxy_addr)) +-- in proxy, save registers +arm.write_save_regs(fw, proxy_addr) +proxy_addr = hwp.inc_addr(proxy_addr, 4) +-- load blob +local blob = hwp.load_bin_file(arg[3]) +local blob_info = hwp.section_info(blob, "") +-- patch blob with stub address +hwp.write32(blob, hwp.make_addr(blob_info.addr + 4, ""), stub_addr.addr) +-- write it ! +local blob_data = hwp.read(blob, hwp.make_addr(blob_info.addr, ""), blob_info.size) +hwp.write(fw, proxy_addr, blob_data) +proxy_addr = hwp.inc_addr(proxy_addr, blob_info.size) +-- restore registers +arm.write_restore_regs(fw, proxy_addr) +proxy_addr = hwp.inc_addr(proxy_addr, 4) +-- branch to old code +local branch_to_old = arm.make_branch(old_irq_addr, false) +arm.write_branch(fw, proxy_addr, branch_to_old, hwp.inc_addr(proxy_addr, 4)) +-- save +hwp.save_file(fw, arg[2])
\ No newline at end of file diff --git a/utils/hwpatcher/fuzep_rb.lua b/utils/hwpatcher/fuzep_rb.lua new file mode 100644 index 0000000000..f47a4983fa --- /dev/null +++ b/utils/hwpatcher/fuzep_rb.lua @@ -0,0 +1,38 @@ +--[[ +Fuze+ RB hacking +required argument (in order): +- path to firmware +- path to output firmware +- path to blob +]]-- + +if #arg < 3 then + error("not enough argument to fuzep patcher") +end + +local fw = hwp.load_file(arg[1]) +local irq_addr_pool = hwp.make_addr(0x38) +local proxy_addr = arm.to_arm(hwp.make_addr(0x60115ba4)) +-- read old IRQ address pool +local old_irq_addr = hwp.make_addr(hwp.read32(fw, irq_addr_pool)) +print(string.format("Old IRQ address: %s", old_irq_addr)) +-- modify it +hwp.write32(fw, irq_addr_pool, proxy_addr.addr) +print(string.format("New IRQ address: %s", proxy_addr)) +-- in proxy, save registers +arm.write_save_regs(fw, proxy_addr) +proxy_addr = hwp.inc_addr(proxy_addr, 4) +-- do some work +local blob = hwp.load_bin_file(arg[3]) +local blob_info = hwp.section_info(blob, "") +local blob_data = hwp.read(blob, hwp.make_addr(blob_info.addr, ""), blob_info.size) +hwp.write(fw, proxy_addr, blob_data) +proxy_addr = hwp.inc_addr(proxy_addr, blob_info.size) +-- restore registers +arm.write_restore_regs(fw, proxy_addr) +proxy_addr = hwp.inc_addr(proxy_addr, 4) +-- branch to old code +local branch_to_old = arm.make_branch(old_irq_addr, false) +arm.write_branch(fw, proxy_addr, branch_to_old) +-- save +hwp.save_file(fw, arg[2])
\ No newline at end of file diff --git a/utils/hwpatcher/generic_stmp.lua b/utils/hwpatcher/generic_stmp.lua new file mode 100644 index 0000000000..538e269e60 --- /dev/null +++ b/utils/hwpatcher/generic_stmp.lua @@ -0,0 +1,108 @@ +--[[ +Generic STMP hacking +required argument (in order): +- path to firmware +- path to output firmware +- path to blob +- path to stub +]]-- +require("lib") +require("arm") + +if #arg < 4 then + error("usage: <fw file> <out file> <blob> <stub>") +end + +-- compute MD5 +print("Computing MD5 sum of the firmware...") +local md5 = hwp.md5sum(arg[1]) +print("=> " .. hwp.md5str(md5)) + +local md5_db = +{ + ["d0047f8a87d456a0032297b3c802a1ff"] = + { + model = "Sony NWZ-E3600 1.0.0", + irq_addr_pool = 0x40A314E4, + irq_addr_pool_sec = "play.1", + -- proxy_addr = 0x4005C1E0, + -- proxy_addr_sec = "play.1" + proxy_addr = 0x4007C258, + proxy_addr_sec = "play.1", + -- stub_addr = 0x1971C8, + -- stub_addr_virt = 0x2971C8, + -- stub_addr_sec = "pvmi", + }, + ["f42742d4d90d88e2fb6ff468c1389f5f"] = + { + model = "Creative ZEN X-Fi Style 1.03.04", + irq_addr_pool = 0x402D3A64, + irq_addr_pool_sec = "play.1", + proxy_addr = 0x402E076C, + proxy_addr_sec = "play.1" + }, + ["c180f57e2b2d62620f87a1d853f349ff"] = + { + model = "Creative ZEN X-Fi3 1.00.25e", + irq_addr_pool = 0x405916f0, + proxy_addr = 0x40384674, + } +} + +local db_entry = md5_db[hwp.md5str(md5)] +if db_entry == nil then + error("Cannot find device in the DB") + os.exit(1) +end +print("Model: " .. db_entry.model) + +local fw = hwp.load_file(arg[1]) +local irq_addr_pool = hwp.make_addr(db_entry.irq_addr_pool, db_entry.irq_addr_pool_sec) +local proxy_addr = arm.to_arm(hwp.make_addr(db_entry.proxy_addr, db_entry.proxy_addr_sec)) +-- read old IRQ address pool +local old_irq_addr = hwp.make_addr(hwp.read32(fw, irq_addr_pool)) +print(string.format("Old IRQ address: %s", old_irq_addr)) +-- put stub at the beginning of the proxy +local stub = hwp.load_bin_file(arg[4]) +local stub_info = hwp.section_info(stub, "") +local stub_data = hwp.read(stub, hwp.make_addr(stub_info.addr, ""), stub_info.size) +local stub_addr = nil +local stub_addr_virt = nil +if db_entry.stub_addr ~= nil then + stub_addr = arm.to_arm(hwp.make_addr(db_entry.stub_addr, db_entry.stub_addr_sec)) + if db_entry.stub_addr_virt ~= nil then + stub_addr_virt = arm.to_arm(hwp.make_addr(db_entry.stub_addr_virt, db_entry.stub_addr_sec)) + else + stub_addr_virt = stub_addr + end + hwp.write(fw, stub_addr, stub_data) +else + stub_addr = proxy_addr + stub_addr_virt = stub_addr + hwp.write(fw, stub_addr, stub_data) + proxy_addr = hwp.inc_addr(proxy_addr, stub_info.size) +end +-- modify irq +hwp.write32(fw, irq_addr_pool, proxy_addr.addr) +print(string.format("New IRQ address: %s", proxy_addr)) +-- in proxy, save registers +arm.write_save_regs(fw, proxy_addr) +proxy_addr = hwp.inc_addr(proxy_addr, 4) +-- load blob +local blob = hwp.load_bin_file(arg[3]) +local blob_info = hwp.section_info(blob, "") +-- patch blob with stub address +hwp.write32(blob, hwp.make_addr(blob_info.addr + 4, ""), stub_addr_virt.addr) +-- write it ! +local blob_data = hwp.read(blob, hwp.make_addr(blob_info.addr, ""), blob_info.size) +hwp.write(fw, proxy_addr, blob_data) +proxy_addr = hwp.inc_addr(proxy_addr, blob_info.size) +-- restore registers +arm.write_restore_regs(fw, proxy_addr) +proxy_addr = hwp.inc_addr(proxy_addr, 4) +-- branch to old code +local branch_to_old = arm.make_branch(old_irq_addr, false) +arm.write_branch(fw, proxy_addr, branch_to_old, hwp.inc_addr(proxy_addr, 4)) +-- save +hwp.save_file(fw, arg[2]) + diff --git a/utils/hwpatcher/hwpatcher.c b/utils/hwpatcher/hwpatcher.c new file mode 100644 index 0000000000..60e142bbc1 --- /dev/null +++ b/utils/hwpatcher/hwpatcher.c @@ -0,0 +1,1123 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2013 Amaury Pouly + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#define _ISOC99_SOURCE /* snprintf() */ +#define _POSIX_C_SOURCE 200809L /* for strdup */ +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <time.h> +#include <stdarg.h> +#include <strings.h> +#include <getopt.h> +#include <lua.h> +#include <lualib.h> +#include <lauxlib.h> +#include <readline/readline.h> +#include <readline/history.h> + +#if LUA_VERSION_NUM < 502 +#warning You need at least lua 5.2 +#endif + +#include "crypto.h" +#include "elf.h" +#include "sb.h" +#include "sb1.h" +#include "misc.h" +#include "md5.h" + +lua_State *g_lua; +bool g_exit = false; + +/** + * FW object library + */ + +enum fw_type_t +{ + FW_UNK, FW_ELF, FW_SB1, FW_SB2, FW_BIN, FW_EDOC +}; + +struct bin_file_t +{ + size_t size; + void *data; +}; + +struct edoc_section_t +{ + uint32_t addr; + size_t size; + void *data; +}; + +struct edoc_file_t +{ + int nr_sections; + struct edoc_section_t *sections; +}; + +struct edoc_header_t +{ + char magic[4]; + uint32_t total_size; + uint32_t zero; +} __attribute__((packed)); + +struct edoc_section_header_t +{ + uint32_t addr; + uint32_t size; + uint32_t checksum; +} __attribute__((packed)); + +uint32_t edoc_checksum(void *buffer, size_t size) +{ + uint32_t c = 0; + uint32_t *p = buffer; + while(size >= 4) + { + c += *p + (*p >> 16); + p++; + size -= 4; + } + if(size != 0) + printf("[Checksum section size is not a multiple of 4 bytes !]\n"); + return c & 0xffff; +} + +#define FWOBJ_MAGIC ('F' | 'W' << 8 | 'O' << 16 | 'B' << 24) + +struct fw_object_t +{ + uint32_t magic; + enum fw_type_t type; + union + { + struct sb_file_t *sb; + struct sb1_file_t *sb1; + struct elf_params_t *elf; + struct bin_file_t *bin; + struct edoc_file_t *edoc; + }u; +}; + +typedef struct fw_addr_t +{ + uint32_t addr; + const char *section; +}fw_addr_t; + +#define INVALID_FW_ADDR ((struct fw_addr_t){.addr = (uint32_t)-1, .section = ""}) +#define IS_VALID_FW_ADDR(x) !(x.addr == (uint32_t)-1 && x.section && strlen(x.section) == 0) + +typedef struct fw_sym_addr_t +{ + const char *name; + const char *section; +}fw_sym_addr_t; + +struct fw_section_info_t +{ + uint32_t addr; + uint32_t size; +}; + +static inline struct fw_addr_t make_addr(uint32_t addr, const char *section) +{ + return (struct fw_addr_t){.addr = addr, .section = section}; +} + +static enum fw_type_t fw_guess(const char *filename) +{ + enum sb_version_guess_t ver = guess_sb_version(filename); + if(ver == SB_VERSION_ERR) + { + printf("Cannot open/read SB file: %m\n"); + return FW_UNK; + } + if(ver == SB_VERSION_1) return FW_SB1; + if(ver == SB_VERSION_2) return FW_SB2; + FILE *fd = fopen(filename, "rb"); + if(fd == NULL) + { + printf("Cannot open '%s' for reading: %m\n", filename); + return FW_UNK; + } + uint8_t sig[4]; + if(fread(sig, 4, 1, fd) == 1) + { + if(sig[0] == 'E' && sig[1] == 'D' && sig[2] == 'O' && sig[3] == 'C') + return FW_EDOC; + } + bool is_elf = elf_guess(elf_std_read, fd); + fclose(fd); + return is_elf ? FW_ELF : FW_BIN; +} + +static const char *fw_type_name(enum fw_type_t t) +{ + switch(t) + { + case FW_SB1: return "SB1"; + case FW_SB2: return "SB2"; + case FW_ELF: return "ELF"; + case FW_BIN: return "binary"; + case FW_EDOC: return "EDOC"; + default: return "<unk>"; + } +} + +static struct fw_object_t *fw_load(const char *filename, enum fw_type_t force) +{ + if(force == FW_UNK) + force = fw_guess(filename); + if(force == FW_UNK) + { + printf("Cannot guess type of file: %m. This probably indicates the file cannot be read.\n"); + return NULL; + } + + struct fw_object_t *obj = malloc(sizeof(struct fw_object_t)); + memset(obj, 0, sizeof(struct fw_object_t)); + obj->magic = FWOBJ_MAGIC; + + printf("Loading '%s' as %s file...\n", filename, fw_type_name(force)); + color(OFF); + if(force == FW_SB2) + { + enum sb_error_t err; + obj->type = FW_SB2; + obj->u.sb = sb_read_file(filename, false, NULL, generic_std_printf, &err); + if(obj->u.sb == NULL) + { + printf("SB read failed: %d\n", err); + goto Lerr; + } + return obj; + } + else if(force == FW_SB1) + { + enum sb1_error_t err; + obj->type = FW_SB1; + obj->u.sb1 = sb1_read_file(filename, NULL, generic_std_printf, &err); + if(obj->u.sb1 == NULL) + { + printf("SB1 read failed: %d\n", err); + goto Lerr; + } + return obj; + } + else if(force == FW_ELF) + { + FILE *fd = fopen(filename, "rb"); + if(fd == NULL) + { + printf("cannot open '%s' for reading: %m\n", filename); + goto Lerr; + } + obj->type = FW_ELF; + obj->u.elf = malloc(sizeof(struct elf_params_t)); + elf_init(obj->u.elf); + bool loaded = elf_read_file(obj->u.elf, elf_std_read, generic_std_printf, fd); + fclose(fd); + if(!loaded) + { + printf("error loading elf file '%s'\n", filename); + free(obj->u.elf); + goto Lerr; + } + return obj; + } + else if(force == FW_EDOC) + { + FILE *fd = fopen(filename, "rb"); + if(fd == NULL) + { + printf("cannot open '%s' for reading: %m\n", filename); + goto Lerr; + } + struct edoc_header_t hdr; + if(fread(&hdr, sizeof(hdr), 1, fd) != 1) + { + printf("cannot read EDOC header: %m\n"); + goto Lerr; + } + if(strncmp(hdr.magic, "EDOC", 4) != 0) + { + printf("EDOC signature mismatch\n"); + goto Lerr; + } + struct edoc_file_t *file = xmalloc(sizeof(struct edoc_file_t)); + memset(file, 0, sizeof(struct edoc_file_t)); + for(size_t pos = sizeof(hdr); pos < hdr.total_size + 8;) + { + struct edoc_section_header_t shdr; + if(fread(&shdr, sizeof(shdr), 1, fd) != 1) + { + printf("cannot read EDOC section header: %m\n"); + goto Lerr; + } + file->sections = realloc(file->sections, (file->nr_sections + 1) * + sizeof(struct edoc_section_t)); + file->sections[file->nr_sections].addr = shdr.addr; + file->sections[file->nr_sections].size = shdr.size; + file->sections[file->nr_sections].data = xmalloc(shdr.size); + if(fread(file->sections[file->nr_sections].data, shdr.size, 1, fd) != 1) + { + printf("cannot read EDOC section: %m\n"); + goto Lerr; + } + if(edoc_checksum(file->sections[file->nr_sections].data, shdr.size) != shdr.checksum) + { + printf("EDOC section checksum mismatch\n"); + goto Lerr; + } + file->nr_sections++; + pos += sizeof(shdr) + shdr.size; + } + fclose(fd); + obj->type = FW_EDOC; + obj->u.edoc = file; + return obj; + } + else + { + FILE *fd = fopen(filename, "rb"); + if(fd == NULL) + { + printf("cannot open '%s' for reading: %m\n", filename); + goto Lerr; + } + obj->u.bin = malloc(sizeof(struct bin_file_t)); + obj->type = FW_BIN; + fseek(fd, 0, SEEK_END); + obj->u.bin->size = ftell(fd); + fseek(fd, 0, SEEK_SET); + obj->u.bin->data = xmalloc(obj->u.bin->size); + if(fread(obj->u.bin->data, obj->u.bin->size, 1, fd) != 1) + { + printf("cannot read '%s': %m\n", filename); + free(obj->u.bin->data); + free(obj->u.bin); + goto Lerr; + } + fclose(fd); + return obj; + } + +Lerr: + free(obj); + return NULL; +} + +static bool fw_save(struct fw_object_t *obj, const char *filename) +{ + if(obj->type == FW_ELF) + { + FILE *fd = fopen(filename, "wb"); + if(fd == NULL) + { + printf("Cannot open '%s' for writing: %m\n", filename); + return false; + } + elf_write_file(obj->u.elf, elf_std_write, generic_std_printf, fd); + fclose(fd); + return true; + } + else if(obj->type == FW_SB2) + { + /* sb_read_file will fill real key and IV but we don't want to override + * them when looping back otherwise the output will be inconsistent and + * garbage */ + obj->u.sb->override_real_key = false; + obj->u.sb->override_crypto_iv = false; + enum sb_error_t err = sb_write_file(obj->u.sb, filename, NULL, generic_std_printf); + if(err != SB_SUCCESS) + { + printf("Cannot write '%s': %d\n", filename, err); + return false; + } + return true; + } + else if(obj->type == FW_BIN) + { + FILE *fd = fopen(filename, "wb"); + if(fd == NULL) + { + printf("Cannot open '%s' for writing: %m\n", filename); + return false; + } + fwrite(obj->u.bin->data, 1, obj->u.bin->size, fd); + fclose(fd); + return true; + } + else if(obj->type == FW_EDOC) + { + FILE *fd = fopen(filename, "wb"); + if(fd == NULL) + { + printf("Cannot open '%s' for writing: %m\n", filename); + return false; + } + struct edoc_header_t hdr; + strncpy(hdr.magic, "EDOC", 4); + hdr.zero = 0; + hdr.total_size = 4; + for(int i = 0; i < obj->u.edoc->nr_sections; i++) + hdr.total_size += sizeof(struct edoc_section_header_t) + + obj->u.edoc->sections[i].size; + fwrite(&hdr, sizeof(hdr), 1, fd); + for(int i = 0; i < obj->u.edoc->nr_sections; i++) + { + struct edoc_section_header_t shdr; + shdr.addr = obj->u.edoc->sections[i].addr; + shdr.size = obj->u.edoc->sections[i].size; + shdr.checksum = edoc_checksum(obj->u.edoc->sections[i].data, shdr.size); + fwrite(&shdr, sizeof(shdr), 1, fd); + fwrite(obj->u.edoc->sections[i].data, shdr.size, 1, fd); + } + fclose(fd); + return true; + } + else + { + printf("Unimplemented fw_save\n"); + return false; + } +} + +static void fw_free(struct fw_object_t *obj) +{ + switch(obj->type) + { + case FW_SB1: + sb1_free(obj->u.sb1); + break; + case FW_SB2: + sb_free(obj->u.sb); + break; + case FW_ELF: + elf_release(obj->u.elf); + free(obj->u.elf); + break; + case FW_BIN: + free(obj->u.bin->data); + free(obj->u.bin); + case FW_EDOC: + for(int i = 0; i < obj->u.edoc->nr_sections; i++) + free(obj->u.edoc->sections[i].data); + free(obj->u.edoc->sections); + free(obj->u.edoc); + default: + break; + } + free(obj); +} + +static struct elf_section_t *elf_find_section(struct elf_params_t *elf, fw_addr_t addr) +{ + struct elf_section_t *match = NULL; + for(struct elf_section_t *sec = elf->first_section; sec; sec = sec->next) + { + if(addr.section && strcmp(addr.section, sec->name) != 0) + continue; + if(addr.addr < sec->addr || addr.addr >= sec->addr + sec->size) + continue; + if(match != NULL) + { + printf("Error: there are several match for address %#x@%s\n", + (unsigned)addr.addr, addr.section); + return NULL; + } + match = sec; + } + if(match == NULL) + { + printf("Error: there is no match for address %#x@%s\n", (unsigned)addr.addr, + addr.section); + } + return match; +} + +static bool fw_elf_rw(struct elf_params_t *elf, fw_addr_t addr, void *buffer, size_t size, bool read) +{ + struct elf_section_t *sec = elf_find_section(elf, addr); + if(sec == NULL) + return false; + if(addr.addr + size > sec->addr + sec->size) + { + printf("Unsupported read/write across section boundary in ELF firmware\n"); + return false; + } + if(sec->type != EST_LOAD) + { + printf("Error: unimplemented read/write to a fill section (ELF)\n"); + return false; + } + void *data = sec->section + addr.addr - sec->addr; + if(read) + memcpy(buffer, data, size); + else + memcpy(data, buffer, size); + return true; +} + +static struct sb_inst_t *sb2_find_section(struct sb_file_t *sb_file, fw_addr_t addr) +{ + struct sb_inst_t *match = NULL; + uint32_t sec_id = 0xffffffff; + int inst_nr = -1; + if(addr.section) + { + /* must be of the form name[.index] */ + const char *mid = strchr(addr.section, '.'); + char *end; + if(mid) + { + inst_nr = strtol(mid + 1, &end, 0); + if(*end) + { + printf("Warning: ignoring invalid section name '%s' (invalid inst nr)\n", addr.section); + goto Lscan; + } + } + else + mid = addr.section + strlen(addr.section); + if(mid - addr.section > 4) + { + printf("Warning: ignoring invalid section name '%s' (sec id too long)\n", addr.section); + goto Lscan; + } + sec_id = 0; + for(int i = 0; i < mid - addr.section; i++) + sec_id = sec_id << 8 | addr.section[i]; + } + + Lscan: + for(int i = 0; i < sb_file->nr_sections; i++) + { + struct sb_section_t *sec = &sb_file->sections[i]; + if(addr.section && sec->identifier != sec_id) + continue; + int cur_blob = 0; + for(int j = 0; j < sec->nr_insts; j++) + { + struct sb_inst_t *inst = &sec->insts[j]; + if(inst->inst == SB_INST_CALL || inst->inst == SB_INST_JUMP) + cur_blob++; + if(inst_nr >= 0 && cur_blob != inst_nr) + continue; + if(inst->inst != SB_INST_LOAD && inst->inst != SB_INST_FILL && inst->inst != SB_INST_DATA) + continue; + /* only consider data sections if section has been explicitely stated */ + if(inst->inst == SB_INST_DATA && !addr.section) + continue; + /* for data sections, address will be 0 */ + if(addr.addr < inst->addr || addr.addr > inst->addr + inst->size) + continue; + if(match != NULL) + { + printf("Error: there are several match for address %#x@%s\n", + (unsigned)addr.addr, addr.section); + return NULL; + } + match = inst; + } + } + if(match == NULL) + { + printf("Error: there is no match for address %#x@%s\n", (unsigned)addr.addr, + addr.section); + } + return match; +} + +static bool fw_sb2_rw(struct sb_file_t *sb_file, fw_addr_t addr, void *buffer, size_t size, bool read) +{ + struct sb_inst_t *inst = sb2_find_section(sb_file, addr); + if(inst == NULL) + return false; + if(addr.addr + size > inst->addr + inst->size) + { + printf("Unsupported read/write across instruction boundary in SB firmware\n"); + return false; + } + if(inst->inst != SB_INST_LOAD && inst->inst != SB_INST_DATA) + { + printf("Error: unimplemented read/write to a fill instruction (SB)\n"); + return false; + } + void *data = inst->data + addr.addr - inst->addr; + if(read) + memcpy(buffer, data, size); + else + memcpy(data, buffer, size); + return true; +} + +static bool fw_bin_rw(struct bin_file_t *bin_file, fw_addr_t addr, void *buffer, size_t size, bool read) +{ + if(addr.addr + size > bin_file->size) + { + printf("Unsupport read/write accross boundary in binary firmware\n"); + return false; + } + void *data = bin_file->data + addr.addr; + if(read) + memcpy(buffer, data, size); + else + memcpy(data, buffer, size); + return true; +} + +static bool fw_edoc_rw(struct edoc_file_t *edoc_file, fw_addr_t addr, void *buffer, size_t size, bool read) +{ + for(int i = 0; i < edoc_file->nr_sections; i++) + { + if(addr.addr < edoc_file->sections[i].addr || + addr.addr + size >= edoc_file->sections[i].addr + edoc_file->sections[i].size) + continue; + void *data = edoc_file->sections[i].data + addr.addr - edoc_file->sections[i].addr; + if(read) + memcpy(buffer, data, size); + else + memcpy(data, buffer, size); + return true; + } + printf("Unsupport read/write accross boundary in EDOC firmware\n"); + return false; +} + +static bool fw_rw(struct fw_object_t *obj, fw_addr_t addr, void *buffer, size_t size, bool read) +{ + switch(obj->type) + { + case FW_ELF: return fw_elf_rw(obj->u.elf, addr, buffer, size, read); + case FW_SB2: return fw_sb2_rw(obj->u.sb, addr, buffer, size, read); + case FW_BIN: return fw_bin_rw(obj->u.bin, addr, buffer, size, read); + case FW_EDOC: return fw_edoc_rw(obj->u.edoc, addr, buffer, size, read); + default: + printf("Error: unimplemented read/write for type %d\n", obj->type); + return false; + } +} + +static bool fw_read(struct fw_object_t *obj, fw_addr_t addr, void *buffer, size_t size) +{ + return fw_rw(obj, addr, buffer, size, true); +} + +static bool fw_write(struct fw_object_t *obj, fw_addr_t addr, const void *buffer, size_t size) +{ + return fw_rw(obj, addr, (void *)buffer, size, false); +} + +static bool elf_find_sym(struct elf_params_t *elf, fw_sym_addr_t addr, fw_addr_t *out_addr) +{ + bool found = false; + for(struct elf_symbol_t *cur = elf->first_symbol; cur; cur = cur->next) + { + if(strcmp(cur->name, addr.name) != 0) + continue; + if(addr.section && strcmp(cur->section, addr.section) != 0) + continue; + if(found) + { + printf("Error: there are several match for symbol %s@%s\n", addr.name, addr.section); + return false; + } + out_addr->addr = cur->addr; + out_addr->section = cur->section; + found = true; + } + return found; +} + +static bool fw_find_sym(struct fw_object_t *obj, fw_sym_addr_t addr, fw_addr_t *out_addr) +{ + switch(obj->type) + { + case FW_ELF: return elf_find_sym(obj->u.elf, addr, out_addr); + case FW_SB2: case FW_SB1: case FW_BIN: return false; + default: + printf("Error: unimplemented find addr for type %d\n", obj->type); + return false; + } +} + +static bool fw_bin_section_info(struct bin_file_t *obj, const char *sec, struct fw_section_info_t *out) +{ + // the only valid section names are NULL and "" + if(sec != NULL && strlen(sec) != 0) + return false; + out->addr = 0; + out->size = obj->size; + return true; +} + +static bool fw_section_info(struct fw_object_t *obj, const char *sec, struct fw_section_info_t *out) +{ + switch(obj->type) + { + case FW_BIN: return fw_bin_section_info(obj->u.bin, sec, out); + default: + printf("Error: unimplemented get section info for type %d\n", obj->type); + return false; + } +} + +/** + * LUA library + */ + +struct fw_object_t *my_lua_get_object(lua_State *state, int index) +{ + struct fw_object_t *obj = lua_touserdata(state, index); + if(obj == NULL || obj->magic != FWOBJ_MAGIC) + luaL_error(state, "invalid parameter: not a firmware object"); + return obj; +} + +const char *my_lua_get_string(lua_State *state, int index) +{ + return luaL_checkstring(state, index); +} + +lua_Unsigned my_lua_get_unsigned(lua_State *state, int index) +{ + lua_Integer i = luaL_checkinteger(state, index); + if(i < 0) + luaL_error(state, "invalid parameter: not an unsigned value"); + return i; +} + +fw_addr_t my_lua_get_addr(lua_State *state, int index) +{ + if(!lua_istable(state, index)) + luaL_error(state, "invalid parameter: not an address table"); + lua_getfield(state, index, "addr"); + if(lua_isnil(state, -1)) + luaL_error(state, "invalid parameter: address has not field 'addr'"); + uint32_t addr = my_lua_get_unsigned(state, -1); + lua_pop(state, 1); + + char *sec = NULL; + lua_getfield(state, index, "section"); + if(!lua_isnil(state, -1)) + sec = strdup(my_lua_get_string(state, -1)); + lua_pop(state, 1); + return make_addr(addr, sec); +} + +void my_lua_pushbuffer(lua_State *state, void *buffer, size_t len) +{ + uint8_t *p = buffer; + lua_createtable(state, len, 0); + for(int i = 0; i < len; i++) + { + lua_pushinteger(state, i + 1); + lua_pushinteger(state, p[i]); + lua_settable(state, -3); + } +} + +void *my_lua_get_buffer(lua_State *state, int index, size_t *len) +{ + if(!lua_istable(state, index)) + luaL_error(state, "invalid parameter: not a data table"); + *len = lua_rawlen(state, index); + uint8_t *buf = xmalloc(*len); + for(int i = 0; i < *len; i++) + { + lua_pushinteger(state, i + 1); + lua_gettable(state, index); + if(lua_isnil(state, -1)) + { + free(buf); + luaL_error(state, "invalid parameter: not a data table, missing some fields"); + } + int v = luaL_checkinteger(state, -1); + lua_pop(state, 1); + if(v < 0 || v > 0xff) + { + free(buf); + luaL_error(state, "invalid parameter: not a data table, field is not a byte"); + } + buf[i] = v; + } + return buf; +} + +int my_lua_load_file(lua_State *state) +{ + int n = lua_gettop(state); + if(n != 1) + return luaL_error(state, "load_file takes one argument: a filename"); + enum fw_type_t type = lua_tounsigned(state, lua_upvalueindex(1)); + const char *filename = my_lua_get_string(state, 1); + struct fw_object_t *obj = fw_load(filename, type); + if(obj) + lua_pushlightuserdata(state, obj); + else + lua_pushnil(state); + return 1; +} + +int my_lua_save_file(lua_State *state) +{ + int n = lua_gettop(state); + if(n != 2) + return luaL_error(state, "load_file takes two arguments: a firmware and a filename"); + struct fw_object_t *obj = my_lua_get_object(state, 1); + const char *filename = my_lua_get_string(state, 2); + lua_pushboolean(state, fw_save(obj, filename)); + return 1; +} + +int my_lua_read(lua_State *state) +{ + int n = lua_gettop(state); + if(n != 3) + return luaL_error(state, "read takes three arguments: a firmware, an address and a length"); + struct fw_object_t *obj = my_lua_get_object(state, 1); + fw_addr_t addr = my_lua_get_addr(state, 2); + size_t len = my_lua_get_unsigned(state, 3); + void *buffer = xmalloc(len); + bool ret = fw_read(obj, addr, buffer, len); + if(ret) + my_lua_pushbuffer(state, buffer, len); + else + lua_pushnil(state); + free(buffer); + return 1; +} + +int my_lua_write(lua_State *state) +{ + int n = lua_gettop(state); + if(n != 3) + return luaL_error(state, "write takes three arguments: a firmware, an address and a data table"); + struct fw_object_t *obj = my_lua_get_object(state, 1); + fw_addr_t addr = my_lua_get_addr(state, 2); + size_t len; + void *buf = my_lua_get_buffer(state, 3, &len); + fw_write(obj, addr, buf, len); + free(buf); + return 0; +} + +int my_lua_section_info(lua_State *state) +{ + int n = lua_gettop(state); + if(n != 2) + return luaL_error(state, "section_info takes two arguments: a firmware and a section name"); + struct fw_object_t *obj = my_lua_get_object(state, 1); + const char *secname = my_lua_get_string(state, 2); + struct fw_section_info_t seci; + if(fw_section_info(obj, secname, &seci)) + { + lua_createtable(state, 0, 0); + lua_pushinteger(state, seci.addr); + lua_setfield(state, -2, "addr"); + lua_pushinteger(state, seci.size); + lua_setfield(state, -2, "size"); + } + else + lua_pushnil(state); + return 1; +} + +/* compute MD5 sum of a buffer */ +static bool compute_md5sum_buf(void *buf, size_t sz, uint8_t file_md5sum[16]) +{ + md5_context ctx; + md5_starts(&ctx); + md5_update(&ctx, buf, sz); + md5_finish(&ctx, file_md5sum); + return true; +} + +/* read a file to a buffer */ +static bool read_file(const char *file, void **buffer, size_t *size) +{ + FILE *f = fopen(file, "rb"); + if(f == NULL) + { + printf("Error: cannot open file for reading: %m\n"); + return false; + } + fseek(f, 0, SEEK_END); + *size = ftell(f); + fseek(f, 0, SEEK_SET); + *buffer = xmalloc(*size); + if(fread(*buffer, *size, 1, f) != 1) + { + printf("Error: cannot read file: %m\n"); + free(*buffer); + fclose(f); + return false; + } + fclose(f); + return true; +} + +/* compute MD5 of a file */ +static bool compute_md5sum(const char *file, uint8_t file_md5sum[16]) +{ + void *buf; + size_t sz; + if(!read_file(file, &buf, &sz)) + return false; + compute_md5sum_buf(buf, sz, file_md5sum); + free(buf); + return true; +} + +int my_lua_md5sum(lua_State *state) +{ + int n = lua_gettop(state); + if(n != 1) + return luaL_error(state, "md5sum takes one argument: a filename"); + const char *filename = my_lua_get_string(state, 1); + uint8_t md5sum[16]; + if(!compute_md5sum(filename, md5sum)) + return luaL_error(state, "cannot compute md5sum of the file"); + my_lua_pushbuffer(state, md5sum, sizeof(md5sum)); + return 1; +} + +static bool init_lua_hwp(void) +{ + lua_pushunsigned(g_lua, FW_UNK); + lua_pushcclosure(g_lua, my_lua_load_file, 1); + lua_setfield(g_lua, -2, "load_file"); + + lua_pushunsigned(g_lua, FW_ELF); + lua_pushcclosure(g_lua, my_lua_load_file, 1); + lua_setfield(g_lua, -2, "load_elf_file"); + + lua_pushunsigned(g_lua, FW_SB2); + lua_pushcclosure(g_lua, my_lua_load_file, 1); + lua_setfield(g_lua, -2, "load_sb_file"); + + lua_pushunsigned(g_lua, FW_SB1); + lua_pushcclosure(g_lua, my_lua_load_file, 1); + lua_setfield(g_lua, -2, "load_sb1_file"); + + lua_pushunsigned(g_lua, FW_BIN); + lua_pushcclosure(g_lua, my_lua_load_file, 1); + lua_setfield(g_lua, -2, "load_bin_file"); + + lua_pushcfunction(g_lua, my_lua_save_file); + lua_setfield(g_lua, -2, "save_file"); + + lua_pushcfunction(g_lua, my_lua_read); + lua_setfield(g_lua, -2, "read"); + + lua_pushcfunction(g_lua, my_lua_write); + lua_setfield(g_lua, -2, "write"); + + lua_pushcfunction(g_lua, my_lua_section_info); + lua_setfield(g_lua, -2, "section_info"); + + lua_pushcfunction(g_lua, my_lua_md5sum); + lua_setfield(g_lua, -2, "md5sum"); + + return true; +} + +int my_lua_exit(lua_State *state) +{ + g_exit = true; + return 0; +} + +bool my_lua_create_arg(lua_State *state, int argc, char **argv) +{ + lua_newtable(state); // arg + for(int i = 0; i < argc; i++) + { + lua_pushinteger(state, i + 1); + lua_pushstring(state, argv[i]); + lua_settable(state, -3); + } + lua_setglobal(state, "arg"); + return true; +} + +static bool init_lua(void) +{ + g_lua = luaL_newstate(); + if(g_lua == NULL) + { + printf("Cannot create lua state\n"); + return 1; + } + // open all standard libraires + luaL_openlibs(g_lua); + + lua_newtable(g_lua); // hwp + if(!init_lua_hwp()) + return false; + lua_setglobal(g_lua, "hwp"); + + lua_pushcfunction(g_lua, my_lua_exit); + lua_setglobal(g_lua, "exit"); + + lua_pushcfunction(g_lua, my_lua_exit); + lua_setglobal(g_lua, "quit"); + + return true; +} + +static void usage(void) +{ + printf("Usage: hwpatcher [options] [--] [arguments]\n"); + printf("Options:\n"); + printf(" -?/--help Display this message\n"); + printf(" -d/--debug Enable debug output\n"); + printf(" -n/--no-color Disable color output\n"); + printf(" -i/--interactive Enter interactive mode after all files have run\n"); + printf(" -f/--do-file <f> Do lua file\n"); + printf(" -k <file> Add key file\n"); + printf(" -z Add zero key\n"); + printf(" --add-key <key> Add single key (hex)\n"); + printf(" -x Use default sb1 key\n"); + printf("All files executed are provided with the extra arguments in the 'arg' table\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + bool interactive = false; + if(argc <= 1) + usage(); + if(!init_lua()) + return 1; + char **do_files = xmalloc(argc * sizeof(char *)); + int nr_do_files = 0; + while(1) + { + static struct option long_options[] = + { + {"help", no_argument, 0, '?'}, + {"debug", no_argument, 0, 'd'}, + {"no-color", no_argument, 0, 'n'}, + {"interactive", no_argument, 0, 'i'}, + {"do-file", required_argument, 0, 'f'}, + {"add-key", required_argument, 0, 'a'}, + {0, 0, 0, 0} + }; + + int c = getopt_long(argc, argv, "?dif:zx", long_options, NULL); + if(c == -1) + break; + switch(c) + { + case -1: + break; + case 'n': + enable_color(false); + break; + case 'd': + g_debug = true; + break; + case '?': + usage(); + break; + case 'i': + interactive = true; + break; + case 'f': + do_files[nr_do_files++] = optarg; + break; + case 'z': + { + struct crypto_key_t g_zero_key; + sb_get_zero_key(&g_zero_key); + add_keys(&g_zero_key, 1); + break; + } + case 'x': + { + struct crypto_key_t key; + sb1_get_default_key(&key); + add_keys(&key, 1); + break; + } + case 'a': + { + struct crypto_key_t key; + char *s = optarg; + if(!parse_key(&s, &key)) + bug("Invalid key specified as argument\n"); + if(*s != 0) + bug("Trailing characters after key specified as argument\n"); + add_keys(&key, 1); + break; + } + default: + printf("Internal error: unknown option '%c'\n", c); + return 1; + } + } + + if(!my_lua_create_arg(g_lua, argc - optind, argv + optind)) + return 1; + + for(int i = 0; i < nr_do_files; i++) + { + if(luaL_dofile(g_lua, do_files[i])) + { + printf("error in %s: %s\n", do_files[i], lua_tostring(g_lua, -1)); + return 1; + } + lua_pop(g_lua, lua_gettop(g_lua)); + } + + if(nr_do_files == 0 && optind < argc) + printf("Warning: extra unused arguments on command lines\n"); + + if(interactive) + { + printf("Entering interactive mode. You can use 'quit()' or 'exit()' to quit.\n"); + rl_bind_key('\t', rl_complete); + while(!g_exit) + { + char *input = readline("> "); + if(!input) + break; + add_history(input); + // evaluate string + if(luaL_dostring(g_lua, input)) + printf("error: %s\n", lua_tostring(g_lua, -1)); + // pop everything to start from a clean stack + lua_pop(g_lua, lua_gettop(g_lua)); + free(input); + } + } + + return 0; +} diff --git a/utils/hwpatcher/lib.lua b/utils/hwpatcher/lib.lua new file mode 100644 index 0000000000..7a3e4a4115 --- /dev/null +++ b/utils/hwpatcher/lib.lua @@ -0,0 +1,107 @@ +--[[ +hwpatcher library + +The C code provides the following functions. + +At global level: +- quit() Quit the interactive mode +- exit() Same as quit() + +In the hwp table: +- load_file(filename) Load a firmware and guess type +- load_elf_file(filename) Load a firmware as ELF +- load_sb_file(filename) Load a firmware as SB +- load_sb1_file(filename) Load a firmware as SB1 +- load_bin_file(filename) Load a firmware as binary +- save_file(obj, filename) Save a firmware to a file +- read(obj, addr, len) Read data from a firmware +- write(obj, addr, data) Write data to a firmware +- section_info(obj, sec) Return information about a section in a table (or nil) +- md5sum(filename) Compute the MD5 sum of a file + +Data read/written from/to a firmware must must be an array of bytes. +The address must be a table of the following fields: +- address: contain the address +- section: optional section name +Data section information is a table with the following fields: +- address: first address if the section +- size: size of the section +We provide the following functions to help dealing with addresses: +- make_addr + +]]-- + +function hwp.deepcopy(o, seen) + seen = seen or {} + if o == nil then return nil end + if seen[o] then return seen[o] end + + local no + if type(o) == 'table' then + no = {} + seen[o] = no + + for k, v in next, o, nil do + no[hwp.deepcopy(k, seen)] = hwp.deepcopy(v, seen) + end + setmetatable(no, hwp.deepcopy(getmetatable(o), seen)) + else -- number, string, boolean, etc + no = o + end + return no +end + +function hwp.make_addr(addr, section) + local t = {addr = addr, section = section} + local addr_to_string = function(self) + if self.section == nil then + return string.format("%#x", self.addr) + else + return string.format("%#x@%s", self.addr, self.section) + end + end + setmetatable(t, {__tostring = addr_to_string}) + return t +end + +function hwp.inc_addr(addr, amount) + return hwp.make_addr(addr.addr + amount, addr.section) +end + +-- pack an array of bytes in a integer (little-endian) +function hwp.pack(arr) + local v = 0 + for i = #arr, 1, -1 do + v = bit32.bor(bit32.lshift(v, 8),bit32.band(arr[i], 0xff)) + end + return v +end + +-- do the converse +function hwp.unpack(v, n) + local t = {} + for i = 1, n do + t[i] = bit32.band(v, 0xff) + v = bit32.rshift(v, 8) + end + return t +end + +-- read a 32-bit value +function hwp.read32(obj, addr) + return hwp.pack(hwp.read(obj, addr, 4)) +end + +-- write a 32-bit value +function hwp.write32(obj, addr, v) + return hwp.write(obj, addr, hwp.unpack(v, 4)) +end + +-- convert a MD5 hash to a string +function hwp.md5str(md5) + local s = "" + for i = 1, #md5 do + s = s .. string.format("%02x", md5[i]) + end + return s +end diff --git a/utils/hwpatcher/md5.c b/utils/hwpatcher/md5.c new file mode 100644 index 0000000000..530d8df15a --- /dev/null +++ b/utils/hwpatcher/md5.c @@ -0,0 +1,246 @@ +/* + * RFC 1321 compliant MD5 implementation + * + * Copyright (C) 2001-2003 Christophe Devine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <string.h> + +#include "md5.h" + +#define GET_UINT32(n,b,i) \ +{ \ + (n) = ( (uint32) (b)[(i) ] ) \ + | ( (uint32) (b)[(i) + 1] << 8 ) \ + | ( (uint32) (b)[(i) + 2] << 16 ) \ + | ( (uint32) (b)[(i) + 3] << 24 ); \ +} + +#define PUT_UINT32(n,b,i) \ +{ \ + (b)[(i) ] = (uint8) ( (n) ); \ + (b)[(i) + 1] = (uint8) ( (n) >> 8 ); \ + (b)[(i) + 2] = (uint8) ( (n) >> 16 ); \ + (b)[(i) + 3] = (uint8) ( (n) >> 24 ); \ +} + +void md5_starts( md5_context *ctx ) +{ + ctx->total[0] = 0; + ctx->total[1] = 0; + + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xEFCDAB89; + ctx->state[2] = 0x98BADCFE; + ctx->state[3] = 0x10325476; +} + +void md5_process( md5_context *ctx, uint8 data[64] ) +{ + uint32 X[16], A, B, C, D; + + GET_UINT32( X[0], data, 0 ); + GET_UINT32( X[1], data, 4 ); + GET_UINT32( X[2], data, 8 ); + GET_UINT32( X[3], data, 12 ); + GET_UINT32( X[4], data, 16 ); + GET_UINT32( X[5], data, 20 ); + GET_UINT32( X[6], data, 24 ); + GET_UINT32( X[7], data, 28 ); + GET_UINT32( X[8], data, 32 ); + GET_UINT32( X[9], data, 36 ); + GET_UINT32( X[10], data, 40 ); + GET_UINT32( X[11], data, 44 ); + GET_UINT32( X[12], data, 48 ); + GET_UINT32( X[13], data, 52 ); + GET_UINT32( X[14], data, 56 ); + GET_UINT32( X[15], data, 60 ); + +#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n))) + +#define P(a,b,c,d,k,s,t) \ +{ \ + a += F(b,c,d) + X[k] + t; a = S(a,s) + b; \ +} + + A = ctx->state[0]; + B = ctx->state[1]; + C = ctx->state[2]; + D = ctx->state[3]; + +#define F(x,y,z) (z ^ (x & (y ^ z))) + + P( A, B, C, D, 0, 7, 0xD76AA478 ); + P( D, A, B, C, 1, 12, 0xE8C7B756 ); + P( C, D, A, B, 2, 17, 0x242070DB ); + P( B, C, D, A, 3, 22, 0xC1BDCEEE ); + P( A, B, C, D, 4, 7, 0xF57C0FAF ); + P( D, A, B, C, 5, 12, 0x4787C62A ); + P( C, D, A, B, 6, 17, 0xA8304613 ); + P( B, C, D, A, 7, 22, 0xFD469501 ); + P( A, B, C, D, 8, 7, 0x698098D8 ); + P( D, A, B, C, 9, 12, 0x8B44F7AF ); + P( C, D, A, B, 10, 17, 0xFFFF5BB1 ); + P( B, C, D, A, 11, 22, 0x895CD7BE ); + P( A, B, C, D, 12, 7, 0x6B901122 ); + P( D, A, B, C, 13, 12, 0xFD987193 ); + P( C, D, A, B, 14, 17, 0xA679438E ); + P( B, C, D, A, 15, 22, 0x49B40821 ); + +#undef F + +#define F(x,y,z) (y ^ (z & (x ^ y))) + + P( A, B, C, D, 1, 5, 0xF61E2562 ); + P( D, A, B, C, 6, 9, 0xC040B340 ); + P( C, D, A, B, 11, 14, 0x265E5A51 ); + P( B, C, D, A, 0, 20, 0xE9B6C7AA ); + P( A, B, C, D, 5, 5, 0xD62F105D ); + P( D, A, B, C, 10, 9, 0x02441453 ); + P( C, D, A, B, 15, 14, 0xD8A1E681 ); + P( B, C, D, A, 4, 20, 0xE7D3FBC8 ); + P( A, B, C, D, 9, 5, 0x21E1CDE6 ); + P( D, A, B, C, 14, 9, 0xC33707D6 ); + P( C, D, A, B, 3, 14, 0xF4D50D87 ); + P( B, C, D, A, 8, 20, 0x455A14ED ); + P( A, B, C, D, 13, 5, 0xA9E3E905 ); + P( D, A, B, C, 2, 9, 0xFCEFA3F8 ); + P( C, D, A, B, 7, 14, 0x676F02D9 ); + P( B, C, D, A, 12, 20, 0x8D2A4C8A ); + +#undef F + +#define F(x,y,z) (x ^ y ^ z) + + P( A, B, C, D, 5, 4, 0xFFFA3942 ); + P( D, A, B, C, 8, 11, 0x8771F681 ); + P( C, D, A, B, 11, 16, 0x6D9D6122 ); + P( B, C, D, A, 14, 23, 0xFDE5380C ); + P( A, B, C, D, 1, 4, 0xA4BEEA44 ); + P( D, A, B, C, 4, 11, 0x4BDECFA9 ); + P( C, D, A, B, 7, 16, 0xF6BB4B60 ); + P( B, C, D, A, 10, 23, 0xBEBFBC70 ); + P( A, B, C, D, 13, 4, 0x289B7EC6 ); + P( D, A, B, C, 0, 11, 0xEAA127FA ); + P( C, D, A, B, 3, 16, 0xD4EF3085 ); + P( B, C, D, A, 6, 23, 0x04881D05 ); + P( A, B, C, D, 9, 4, 0xD9D4D039 ); + P( D, A, B, C, 12, 11, 0xE6DB99E5 ); + P( C, D, A, B, 15, 16, 0x1FA27CF8 ); + P( B, C, D, A, 2, 23, 0xC4AC5665 ); + +#undef F + +#define F(x,y,z) (y ^ (x | ~z)) + + P( A, B, C, D, 0, 6, 0xF4292244 ); + P( D, A, B, C, 7, 10, 0x432AFF97 ); + P( C, D, A, B, 14, 15, 0xAB9423A7 ); + P( B, C, D, A, 5, 21, 0xFC93A039 ); + P( A, B, C, D, 12, 6, 0x655B59C3 ); + P( D, A, B, C, 3, 10, 0x8F0CCC92 ); + P( C, D, A, B, 10, 15, 0xFFEFF47D ); + P( B, C, D, A, 1, 21, 0x85845DD1 ); + P( A, B, C, D, 8, 6, 0x6FA87E4F ); + P( D, A, B, C, 15, 10, 0xFE2CE6E0 ); + P( C, D, A, B, 6, 15, 0xA3014314 ); + P( B, C, D, A, 13, 21, 0x4E0811A1 ); + P( A, B, C, D, 4, 6, 0xF7537E82 ); + P( D, A, B, C, 11, 10, 0xBD3AF235 ); + P( C, D, A, B, 2, 15, 0x2AD7D2BB ); + P( B, C, D, A, 9, 21, 0xEB86D391 ); + +#undef F + + ctx->state[0] += A; + ctx->state[1] += B; + ctx->state[2] += C; + ctx->state[3] += D; +} + +void md5_update( md5_context *ctx, uint8 *input, uint32 length ) +{ + uint32 left, fill; + + if( ! length ) return; + + left = ctx->total[0] & 0x3F; + fill = 64 - left; + + ctx->total[0] += length; + ctx->total[0] &= 0xFFFFFFFF; + + if( ctx->total[0] < length ) + ctx->total[1]++; + + if( left && length >= fill ) + { + memcpy( (void *) (ctx->buffer + left), + (void *) input, fill ); + md5_process( ctx, ctx->buffer ); + length -= fill; + input += fill; + left = 0; + } + + while( length >= 64 ) + { + md5_process( ctx, input ); + length -= 64; + input += 64; + } + + if( length ) + { + memcpy( (void *) (ctx->buffer + left), + (void *) input, length ); + } +} + +static uint8 md5_padding[64] = +{ + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +void md5_finish( md5_context *ctx, uint8 digest[16] ) +{ + uint32 last, padn; + uint32 high, low; + uint8 msglen[8]; + + high = ( ctx->total[0] >> 29 ) + | ( ctx->total[1] << 3 ); + low = ( ctx->total[0] << 3 ); + + PUT_UINT32( low, msglen, 0 ); + PUT_UINT32( high, msglen, 4 ); + + last = ctx->total[0] & 0x3F; + padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); + + md5_update( ctx, md5_padding, padn ); + md5_update( ctx, msglen, 8 ); + + PUT_UINT32( ctx->state[0], digest, 0 ); + PUT_UINT32( ctx->state[1], digest, 4 ); + PUT_UINT32( ctx->state[2], digest, 8 ); + PUT_UINT32( ctx->state[3], digest, 12 ); +} + diff --git a/utils/hwpatcher/md5.h b/utils/hwpatcher/md5.h new file mode 100644 index 0000000000..71fa395548 --- /dev/null +++ b/utils/hwpatcher/md5.h @@ -0,0 +1,25 @@ +#ifndef _MD5_H +#define _MD5_H + +#ifndef uint8 +#define uint8 unsigned char +#endif + +#ifndef uint32 +#define uint32 unsigned long int +#endif + +typedef struct +{ + uint32 total[2]; + uint32 state[4]; + uint8 buffer[64]; +} +md5_context; + +void md5_starts( md5_context *ctx ); +void md5_update( md5_context *ctx, uint8 *input, uint32 length ); +void md5_finish( md5_context *ctx, uint8 digest[16] ); + +#endif /* md5.h */ + diff --git a/utils/hwpatcher/patch.S b/utils/hwpatcher/patch.S new file mode 100644 index 0000000000..6c27e299d9 --- /dev/null +++ b/utils/hwpatcher/patch.S @@ -0,0 +1,87 @@ +.text +.global _start +_start: + b exec +branch_addr: + .word kill +hw_power_sts: +#if defined(CREATIVE_ZEN) + .word 0x800440b0 /* STMP3700 */ +#else + .word 0x800440c0 /* IMX233 */ +#endif +hw_pinctrl_din0: + .word 0x80018600 +hw_pinctrl_din1: + .word 0x80018610 +hw_pinctrl_din2: + .word 0x80018620 +kill: + ldr pc, branch_addr +exec: +#if defined(SANSA_FUZEPLUS) + /* check PSWITCH=1 (power button pressed) */ + ldr r0, hw_power_sts + ldr r0, [r0] + mov r0, r0, lsr #20 + and r0, #3 + cmp r0, #1 + bne ret + /* check B1P30=0 (volume down pressed) */ + ldr r0, hw_pinctrl_din1 + ldr r0, [r0] + mov r0, r0, lsr #30 + ands r0, #1 + beq kill +#elif defined(CREATIVE_ZENXFI3) + /* check PSWITCH=1 (power button pressed) */ + ldr r0, hw_power_sts + ldr r0, [r0] + mov r0, r0, lsr #20 + and r0, #3 + cmp r0, #1 + bne ret + /* check B2P07=0 (volume down pressed) */ + ldr r0, hw_pinctrl_din2 + ldr r0, [r0] + mov r0, r0, lsr #7 + ands r0, #1 + beq kill +#elif defined(CREATIVE_ZENXFI2) + /* check B0P11=0 (power button pressed) and B0P14 (select button pressed) */ + ldr r0, hw_pinctrl_din0 + ldr r0, [r0] + mov r0, r0, lsr #11 + tst r0, #1 + bne ret + mov r0, r0, lsr #3 + tst r0, #1 + beq kill +#elif defined(CREATIVE_ZEN) + /* check PSWITCH=1 (power button pressed) */ + ldr r0, hw_power_sts + ldr r0, [r0] + mov r0, r0, lsr #18 + and r0, #3 + cmp r0, #0 + bne kill +#elif defined(SONY_NWZ) + /* check PSWITCH=3 (power button pressed) */ + ldr r0, hw_power_sts + ldr r0, [r0] + mov r0, r0, lsr #20 + and r0, #3 + cmp r0, #3 + beq kill +#elif defined(CREATIVE_ZENXFISTYLE) + /* check PSWITCH=1 (power button pressed) */ + ldr r0, hw_power_sts + ldr r0, [r0] + mov r0, r0, lsr #20 + and r0, #3 + cmp r0, #1 + beq kill +#else +#error implement me +#endif +ret:
\ No newline at end of file diff --git a/utils/hwpatcher/patch_viewbl.lua b/utils/hwpatcher/patch_viewbl.lua new file mode 100644 index 0000000000..8d1c8b69c3 --- /dev/null +++ b/utils/hwpatcher/patch_viewbl.lua @@ -0,0 +1 @@ + diff --git a/utils/hwpatcher/view.lua b/utils/hwpatcher/view.lua new file mode 100644 index 0000000000..35c383800f --- /dev/null +++ b/utils/hwpatcher/view.lua @@ -0,0 +1,38 @@ +--[[ +Sansa View bootloader hacking +required argument (in order): +- path to bootloader +- path to output bootloader +- path to stub +]]-- +require("lib") +require("arm") + +if #arg < 3 then + error("not enough argument to fuzep patcher") +end + +local md5 = hwp.md5sum(arg[1]) +if hwp.md5str(md5) ~= "4bc1760327c37b9ffd00315c8aa7f376" then + error("MD5 sum of the file doesn't match") +end + +local fw = hwp.load_file(arg[1]) +local jump_instr_addr = arm.to_thumb(hwp.make_addr(0x753C)) +local stub_addr = hwp.make_addr(0x137B0) +-- read old jump address +--local old_jump = arm.parse_branch(fw, jump_instr_addr) +--print(string.format("Old jump address: %s", old_jump)) +-- put stub at the right place +local stub = hwp.load_bin_file(arg[3]) +local stub_info = hwp.section_info(stub, "") +local stub_data = hwp.read(stub, hwp.make_addr(stub_info.addr, ""), stub_info.size) +hwp.write(fw, stub_addr, stub_data) +-- patch jump +local branch_to_stub = arm.make_branch(arm.to_arm(stub_addr), true) +arm.write_branch(fw, jump_instr_addr, branch_to_stub, hwp.inc_addr(stub_addr, stub_info.size)) +-- read jump address +local new_jump = arm.parse_branch(fw, jump_instr_addr) +print(string.format("New jump address: %s", new_jump)) +-- save +hwp.save_file(fw, arg[2]) diff --git a/utils/hwpatcher/zen.lua b/utils/hwpatcher/zen.lua new file mode 100644 index 0000000000..8d1c8b69c3 --- /dev/null +++ b/utils/hwpatcher/zen.lua @@ -0,0 +1 @@ + diff --git a/utils/hwpatcher/zxfi2.lua b/utils/hwpatcher/zxfi2.lua new file mode 100644 index 0000000000..fc3bdf800b --- /dev/null +++ b/utils/hwpatcher/zxfi2.lua @@ -0,0 +1,50 @@ +--[[ +Zen X-Fi2 1.23.01e NAND hacking +required argument (in order): +- path to firmware +- path to output firmware +- path to blob +- path to stub +]]-- + +if #arg < 4 then + error("not enough argument to fuzep patcher") +end + +local fw = hwp.load_file(arg[1]) +local irq_addr_pool = hwp.make_addr(0x4035e154, "play") +local proxy_addr = arm.to_arm(hwp.make_addr(0x402f06f8, "play")) +-- read old IRQ address pool +local old_irq_addr = hwp.make_addr(hwp.read32(fw, irq_addr_pool)) +print(string.format("Old IRQ address: %s", old_irq_addr)) +-- put stub at the beginning of the proxy +local stub = hwp.load_bin_file(arg[4]) +local stub_info = hwp.section_info(stub, "") +local stub_data = hwp.read(stub, hwp.make_addr(stub_info.addr, ""), stub_info.size) +hwp.write(fw, proxy_addr, stub_data) +local stub_addr = proxy_addr +proxy_addr = hwp.inc_addr(proxy_addr, stub_info.size) +-- modify irq +hwp.write32(fw, irq_addr_pool, proxy_addr.addr) +print(string.format("New IRQ address: %s", proxy_addr)) +-- in proxy, save registers +arm.write_save_regs(fw, proxy_addr) +proxy_addr = hwp.inc_addr(proxy_addr, 4) +-- load blob +local blob = hwp.load_bin_file(arg[3]) +local blob_info = hwp.section_info(blob, "") +-- patch blob with stub address +hwp.write32(blob, hwp.make_addr(blob_info.addr + 4, ""), stub_addr.addr) +-- write it ! +local blob_data = hwp.read(blob, hwp.make_addr(blob_info.addr, ""), blob_info.size) +hwp.write(fw, proxy_addr, blob_data) +proxy_addr = hwp.inc_addr(proxy_addr, blob_info.size) +-- restore registers +arm.write_restore_regs(fw, proxy_addr) +proxy_addr = hwp.inc_addr(proxy_addr, 4) +-- branch to old code +local branch_to_old = arm.make_branch(old_irq_addr, false) +arm.write_branch(fw, proxy_addr, branch_to_old, hwp.inc_addr(proxy_addr, 4)) +-- save +hwp.save_file(fw, arg[2]) + diff --git a/utils/hwpatcher/zxfi3.lua b/utils/hwpatcher/zxfi3.lua new file mode 100644 index 0000000000..003f0f7482 --- /dev/null +++ b/utils/hwpatcher/zxfi3.lua @@ -0,0 +1,49 @@ +--[[ +Zen X-Fi3 1.00.25e hacking +required argument (in order): +- path to firmware +- path to output firmware +- path to blob +- path to stub +]]-- + +if #arg < 4 then + error("not enough argument to fuzep patcher") +end + +local fw = hwp.load_file(arg[1]) +local irq_addr_pool = hwp.make_addr(0x405916f0) +local proxy_addr = arm.to_arm(hwp.make_addr(0x40384674)) +-- read old IRQ address pool +local old_irq_addr = hwp.make_addr(hwp.read32(fw, irq_addr_pool)) +print(string.format("Old IRQ address: %s", old_irq_addr)) +-- put stub at the beginning of the proxy +local stub = hwp.load_bin_file(arg[4]) +local stub_info = hwp.section_info(stub, "") +local stub_data = hwp.read(stub, hwp.make_addr(stub_info.addr, ""), stub_info.size) +hwp.write(fw, proxy_addr, stub_data) +local stub_addr = proxy_addr +proxy_addr = hwp.inc_addr(proxy_addr, stub_info.size) +-- modify irq +hwp.write32(fw, irq_addr_pool, proxy_addr.addr) +print(string.format("New IRQ address: %s", proxy_addr)) +-- in proxy, save registers +arm.write_save_regs(fw, proxy_addr) +proxy_addr = hwp.inc_addr(proxy_addr, 4) +-- load blob +local blob = hwp.load_bin_file(arg[3]) +local blob_info = hwp.section_info(blob, "") +-- patch blob with stub address +hwp.write32(blob, hwp.make_addr(blob_info.addr + 4, ""), stub_addr.addr) +-- write it ! +local blob_data = hwp.read(blob, hwp.make_addr(blob_info.addr, ""), blob_info.size) +hwp.write(fw, proxy_addr, blob_data) +proxy_addr = hwp.inc_addr(proxy_addr, blob_info.size) +-- restore registers +arm.write_restore_regs(fw, proxy_addr) +proxy_addr = hwp.inc_addr(proxy_addr, 4) +-- branch to old code +local branch_to_old = arm.make_branch(old_irq_addr, false) +arm.write_branch(fw, proxy_addr, branch_to_old, hwp.inc_addr(proxy_addr, 4)) +-- save +hwp.save_file(fw, arg[2]) |