1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
|
#!/usr/bin/env python3
# Script for injecting SPDs into APCB_v3a binaries.
import re
import argparse
from collections import namedtuple
from struct import *
APCB_CHECKSUM_OFFSET = 16
SPD_ENTRY_MAGIC = bytes.fromhex('0200480000000000')
SPD_SIZE = 512
EMPTY_SPD = b'\x00' * SPD_SIZE
ZERO_BLOCKS = (2, 4, 6, 7)
SPD_BLOCK_SIZE = 64
SPD_BLOCK_HEADER_FMT = '<HHHH'
SPD_BLOCK_HEADER_SIZE = calcsize(SPD_BLOCK_HEADER_FMT)
spd_block_header = namedtuple(
'spd_block_header', 'Type, Length, Key, Reserved')
def parseargs():
parser = argparse.ArgumentParser(description='Inject SPDs into APCB binaries')
parser.add_argument(
'apcb_in',
type=str,
help='APCB input file')
parser.add_argument(
'apcb_out',
type=str,
help='APCB output file')
parser.add_argument(
'--spd_sources',
nargs='+',
help='List of SPD sources')
return parser.parse_args()
# Calculate checksum of APCB binary
def chksum(data):
sum = 0
for i, v in enumerate(data):
if i == APCB_CHECKSUM_OFFSET: continue
sum = (sum + v) & 0xff
return (0x100 - sum) & 0xff
# Inject bytes into binary blob by overwriting
def inject(orig, insert, offset):
return b''.join([orig[:offset], insert, orig[offset + len(insert):]])
def main():
args = parseargs()
# Load input APCB
print(f'Reading input APCB from {args.apcb_in}')
with open(args.apcb_in, 'rb') as f:
apcb = f.read()
assert chksum(apcb) == apcb[APCB_CHECKSUM_OFFSET], 'Initial checksum is invalid'
orig_apcb_len = len(apcb)
# Load SPDs
print(f'Using SPD Sources = {", ".join(args.spd_sources)}')
spds = []
for spd_source in args.spd_sources:
with open(spd_source, 'rb') as f:
spd_data = bytes.fromhex(re.sub(r'\s+', '', f.read().decode()))
assert len(spd_data) == SPD_SIZE, f'{spd_source} is not {SPD_SIZE} bytes'
# Verify ZERO_BLOCKS are zero
for b in ZERO_BLOCKS:
assert all(v==0 for v in spd_data[b*SPD_BLOCK_SIZE:(b+1)*SPD_BLOCK_SIZE]), f'SPD block #{b} is not zero'
spds.append(spd_data)
assert len(spds) > 0, "No SPDs provided"
# Inject SPDs into APCB
apcb_offset = 0
spd_idx = 0
while True:
apcb_offset = apcb.find(SPD_ENTRY_MAGIC, apcb_offset)
if apcb_offset < 0:
print(f'No more SPD entries found')
assert spd_idx >= len(spds), f'Not enough SPD entries in APCB. Need {len(spds)}, found {spd_idx}'
break
if spd_idx < len(spds):
print(f'Injecting SPD instance {spd_idx}')
spd = spds[spd_idx]
else:
print(f'Injecting empty SPD for instance {spd_idx}')
spd = EMPTY_SPD
# Inject SPD blocks
for b in range(int(SPD_SIZE/SPD_BLOCK_SIZE)):
if b in ZERO_BLOCKS: continue
header_data = apcb[apcb_offset:apcb_offset + SPD_BLOCK_HEADER_SIZE]
header = spd_block_header._make(unpack(SPD_BLOCK_HEADER_FMT, header_data))
socket = (header.Key >> 12) & 0xF
channel = (header.Key >> 8) & 0xF
dimm = (header.Key >> 4) & 0xF
block_id = (header.Key >> 0) & 0xF
assert header.Type == 2
assert header.Length == SPD_BLOCK_HEADER_SIZE + SPD_BLOCK_SIZE
assert socket == 0
assert channel == 0
assert block_id == b
assert dimm == 0
assert header.Reserved == 0
spd_block = spd[b*SPD_BLOCK_SIZE:(b+1)*SPD_BLOCK_SIZE]
apcb_offset += SPD_BLOCK_HEADER_SIZE
apcb = inject(apcb, spd_block, apcb_offset)
apcb_offset += SPD_BLOCK_SIZE
spd_idx += 1
# Fix APCB checksum
print(f'Fixing APCB checksum')
apcb = inject(apcb, bytes([chksum(apcb)]), APCB_CHECKSUM_OFFSET)
assert chksum(apcb) == apcb[APCB_CHECKSUM_OFFSET], 'Final checksum is invalid'
assert orig_apcb_len == len(apcb), 'The size of the APCB changed.'
# Write APCB to file
print(f'Writing {len(apcb)} byte APCB to {args.apcb_out}')
with open(args.apcb_out, 'wb') as f:
f.write(apcb)
if __name__ == "__main__":
main()
|