Introduce hwpatcher, a tool to patch binaries

This tool is a scriptable (lua) tool to patch binaries, it supports:
- raw binary
- ELF
- SB(v1/v2)
It also contains some basic routines to parse and generate useful arm/thumb code
like jump or register load/store. This is very useful to take a firmware and
patch an interrupt vector or some code to jump to an extra payload added to
the binary. Examples are provided for several STMP based target which the payload
is expected to be hwstub, and also for the Sansa View. A typical patcher usually
requires three elements:
- the lua patcher itself
- the payload (hwstub for example)
- (optional) a small stub either to jump properly to the payload or determine
  under which circumstance to do the jump (hold a key for example)

Change-Id: I6d36020a3bc9e636615ac8221b7591ade5f251e3
This commit is contained in:
Amaury Pouly 2014-06-24 18:04:17 +02:00
parent 761f59c9e3
commit c9a028cc18
17 changed files with 2228 additions and 0 deletions

27
utils/hwpatcher/Makefile Normal file
View file

@ -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)

View file

@ -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)

205
utils/hwpatcher/arm.lua Normal file
View file

@ -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

View file

@ -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])

49
utils/hwpatcher/fuzep.lua Normal file
View file

@ -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])

View file

@ -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])

View file

@ -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])

1123
utils/hwpatcher/hwpatcher.c Normal file

File diff suppressed because it is too large Load diff

107
utils/hwpatcher/lib.lua Normal file
View file

@ -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

246
utils/hwpatcher/md5.c Normal file
View file

@ -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 );
}

25
utils/hwpatcher/md5.h Normal file
View file

@ -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 */

87
utils/hwpatcher/patch.S Normal file
View file

@ -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:

View file

@ -0,0 +1 @@

38
utils/hwpatcher/view.lua Normal file
View file

@ -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])

1
utils/hwpatcher/zen.lua Normal file
View file

@ -0,0 +1 @@

50
utils/hwpatcher/zxfi2.lua Normal file
View file

@ -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])

49
utils/hwpatcher/zxfi3.lua Normal file
View file

@ -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])