forked from rampageX/firmware-mod-kit
-
Notifications
You must be signed in to change notification settings - Fork 0
/
extract-firmware.sh
executable file
·277 lines (240 loc) · 8.64 KB
/
extract-firmware.sh
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
#!/bin/bash
BINDIR=`dirname $0`
. "$BINDIR/common.inc"
IMG="${1}"
DIR="${2}"
if [ "${DIR}" = "" ]; then
DIR="fmk"
fi
# Need to extract file systems as ROOT
if [ "$(id -ru)" != "0" ]; then
SUDO="sudo"
else
SUDO=""
fi
IMG=$(readlink -f "$IMG")
DIR=$(readlink -f "$DIR")
# Make sure we're operating out of the FMK directory
cd $(dirname $(readlink -f $0))
# Source in/Import shared settings. ${DIR} MUST be defined prior to this!
. ./shared-ng.inc
printf "Firmware Mod Kit (extract) ${VERSION}, (c)2011-2013 Craig Heffner, Jeremy Collake\n\n"
# Check usage
if [ "${IMG}" = "" ] || [ "${IMG}" = "-h" ]; then
printf "Usage: ${0} <firmware image>\n\n"
exit 1
fi
if [ ! -f "${IMG}" ]; then
echo "File does not exist!"
exit 1
fi
if [ -e "${DIR}" ]; then
echo "Directory ${DIR} already exists! Quitting..."
exit 1
fi
Build_Tools
# Get the size, in bytes, of the target firmware image
FW_SIZE=$(ls -l "${IMG}" | cut -d' ' -f5)
# Create output directories
mkdir -p "${DIR}/logs"
mkdir -p "${DIR}/image_parts"
echo "Scanning firmware..."
# Log binwalk results to the ${BINLOG} file, disable default filters, exclude invalid results,
# and search only for trx, uimage, dlob, squashfs, and cramfs results.
#${BINWALK} -f "${BINLOG}" -d -x invalid -y trx -y uimage -y dlob -y squashfs -y cramfs "${IMG}"
${BINWALK} -f "${BINLOG}" "${IMG}"
# Set Internal Field Separator (IFS) via two lines to newline only (bashism would be $'\n')
IFS='
'
# Header image offset is ALWAYS 0. Header checksums are simply updated by build-ng.sh.
HEADER_IMAGE_OFFSET=0
# Loop through binwalk log file
for LINE in IFS='
'$(sort -n ${BINLOG} | grep -v -e '^DECIMAL' -e '^---')
do
# Get decimal file offset and the first word of the description
OFFSET=$(echo ${LINE} | awk '{print $1}')
DESCRIPTION=$(echo ${LINE} | awk '{print tolower($3)}')
# Offset 0 is firmware header
if [ "${OFFSET}" = "0" ]; then
HEADER_OFFSET=${OFFSET}
HEADER_TYPE=${DESCRIPTION}
HEADER_SIZE=$(echo ${LINE} | sed -e 's/.*header size: //' | cut -d' ' -f1)
HEADER_IMAGE_SIZE=$(echo ${LINE} | sed -e 's/.*image size: //' | cut -d' ' -f1)
# Check to see if this line is a file system entry
elif [ "$(echo ${LINE} | grep -i filesystem)" != "" ]
then
FS_OFFSET=${OFFSET}
FS_TYPE=${DESCRIPTION}
# Need to know endianess for re-assembly
if [ "$(echo ${LINE} | grep -i 'big endian')" != "" ]; then
ENDIANESS="-be"
else
ENDIANESS="-le"
fi
# Check for compression type of the file system. Default to LZMA
if [ "$(echo ${LINE} | grep -i 'gzip')" != "" ]; then
FS_COMPRESSION="gzip"
elif [ "$(echo ${LINE} | grep -i 'xz')" != "" ]; then
FS_COMPRESSION="xz"
else
FS_COMPRESSION="lzma"
fi
# Check for a block size (used only by squashfs)
if [ "$(echo ${LINE} | grep -i 'blocksize')" != "" ]; then
set -f
IFS=,
for fsparam in ${LINE}
do
if [[ $fsparam = *blocksize* ]]; then
fsparam="${fsparam##*blocksize: }"
FS_BLOCKSIZE="${fsparam%* bytes}"
break
fi
done
fi
set +f; unset IFS
fi
done
# Header image size is everything from the header image offset (0) up to the file system
HEADER_IMAGE_SIZE=$((${FS_OFFSET}-${HEADER_IMAGE_OFFSET}))
# Extract the header + image up to the file system
echo "Extracting ${HEADER_IMAGE_SIZE} bytes of ${HEADER_TYPE} header image at offset ${HEADER_IMAGE_OFFSET}"
dd if="${IMG}" bs=${HEADER_IMAGE_SIZE} skip=${HEADER_IMAGE_OFFSET} count=1 of="${HEADER_IMAGE}" 2>/dev/null
if [ "${FS_OFFSET}" != "" ]; then
echo "Extracting ${FS_TYPE} file system at offset ${FS_OFFSET}"
dd if="${IMG}" bs=${FS_OFFSET} skip=1 of="${FSIMG}" 2>/dev/null
else
echo "ERROR: No supported file system found! Aborting..."
rm -rf "${DIR}"
exit 1
fi
FOOTER_SIZE=0
FOOTER_OFFSET=0
# Get Hex + ASCII output of image file, filter for header between preceding repeating lines which show up as * in terminal. Dump into footer.txt
hexdump -C "$IMG" | tail -11 | sed -n '/^*/,$p' > footer.txt
# Get first line after *, most likely start of header, get hex address and convert to decimal
FOOTER_FIRST=$((16#$(head -2 footer.txt | tail -1 | awk '{print $1}')))
echo "$FOOTER_FIRST"
# Get last address, most likely end of header, get hex address and convert to decimal
FOOTER_LAST=$((16#$(tail -1 footer.txt)))
echo "$FOOTER_LAST"
FOOTER_SIZE=$(($FOOTER_LAST - $FOOTER_FIRST))
echo "$FOOTER_SIZE"
#If a footer was found, dump it out
if [ "${FOOTER_SIZE}" != "0" ]; then
FOOTER_OFFSET=$((${FW_SIZE}-${FOOTER_SIZE}))
echo "Extracting ${FOOTER_SIZE} byte footer from offset ${FOOTER_FIRST}"
dd if="${IMG}" bs=1 skip=${FOOTER_FIRST} count=${FOOTER_SIZE} of="${FOOTER_IMAGE}" 2>/dev/null
else
FOOTER_OFFSET=${FW_SIZE}
fi
# Try to determine if there is a footer at the end of the firmware image.
# Grab the last 10 lines of a hexdump of the firmware image, excluding the
# last line in the hexdump. Reverse the line order and replace any lines
# that start with '*' with the word 'FILLER'.
# for LINE in $(hexdump -C ${IMG} | tail -11 | head -10 | sed -n '1!G;h;$p' | sed -e 's/^*/FILLER/')
# do
# if [ "${LINE}" = "FILLER" ]; then
# break
# else
# FOOTER_SIZE=$((${FOOTER_SIZE}+16))
# fi
# done
#
# # If a footer was found, dump it out
# if [ "${FOOTER_SIZE}" != "0" ]; then
# FOOTER_OFFSET=$((${FW_SIZE}-${FOOTER_SIZE}))
# echo "Extracting ${FOOTER_SIZE} byte footer from offset ${FOOTER_OFFSET}"
# dd if="${IMG}" bs=1 skip=${FOOTER_OFFSET} count=${FOOTER_SIZE} of="${FOOTER_IMAGE}" 2>/dev/null
# else
# FOOTER_OFFSET=${FW_SIZE}
# fi
# Log the parsed values to the CONFLOG for use when re-building the firmware
echo "FW_SIZE='${FW_SIZE}'" > ${CONFLOG}
echo "HEADER_TYPE='${HEADER_TYPE}'" >> ${CONFLOG}
echo "HEADER_SIZE='${HEADER_SIZE}'" >> ${CONFLOG}
echo "HEADER_IMAGE_SIZE='${HEADER_IMAGE_SIZE}'" >> ${CONFLOG}
echo "HEADER_IMAGE_OFFSET='${HEADER_IMAGE_OFFSET}'" >> ${CONFLOG}
echo "FOOTER_SIZE='${FOOTER_SIZE}'" >> ${CONFLOG}
echo "FOOTER_OFFSET='${FOOTER_OFFSET}'" >> ${CONFLOG}
echo "FS_TYPE='${FS_TYPE}'" >> ${CONFLOG}
echo "FS_OFFSET='${FS_OFFSET}'" >> ${CONFLOG}
echo "FS_COMPRESSION='${FS_COMPRESSION}'" >> ${CONFLOG}
echo "FS_BLOCKSIZE='${FS_BLOCKSIZE}'" >> ${CONFLOG}
echo "ENDIANESS='${ENDIANESS}'" >> ${CONFLOG}
# Extract the file system and save the MKFS variable to the CONFLOG
case ${FS_TYPE} in
"squashfs")
echo "Extracting squashfs files..."
FS_ARGS=""
FS_MKFS=$(${SUDO} ./unsquashfs_all.sh "${FSIMG}" "${ROOTFS}" 2>/dev/null | grep MKFS)
echo ${FS_MKFS} >> "${CONFLOG}"
MKFS=$(echo ${FS_MKFS} | awk -F"[=]" '{print $2}' | sed 's/\"//g')
if [ "${MKFS}" != "" ]; then
MKFS_DIR=$(dirname ${MKFS})
UNSQUASHFS="${MKFS_DIR}/unsquashfs"
if [ "${UNSQUASHFS}" != "" ]; then
#Xattrs are not stored
Xattrs=$(${UNSQUASHFS} -s ${FSIMG} | grep -i Xattrs)
if [ "${Xattrs}" != "" ]; then
if [ "$(echo ${Xattrs} | grep -i not)" != "" ]; then
echo "COMP_XZ_XATTRS='-no-xattrs'" >> ${CONFLOG}
fi
fi
#Number of ids 5
NumOfIds=$(${UNSQUASHFS} -s ${FSIMG} | grep -i "Number of ids")
if [ "${NumOfIds}" != "" ]; then
if [ "$(echo ${NumOfIds} | grep -P '\d+' -o)" = "1" ]; then
echo "COMP_XZ_ALL_ROOT='-all-root'" >> ${CONFLOG}
fi
fi
fi
fi
if [ "${FS_COMPRESSION}" = "xz" ]; then
if [ "$(echo $MKFS | grep 'squashfs-4.2')" != "" ]; then
typeset -u XZ_MAGIC
#FD377A585A xz block magic 7zXZ, '-noappend' offset is 96 without '-noappend' offeset is 106
XZ_MAGIC=$(dd if="${FSIMG}" bs=1 count=5 skip=96 2>/dev/zero | xxd -p)
if [ "${XZ_MAGIC}" = "FD377A585A" ]; then
FS_ARGS="${FS_ARGS} -noappend"
echo "FS_ARGS='${FS_ARGS}'" >> ${CONFLOG}
fi
fi
fi
;;
"cramfs")
echo "Extracting CramFS file system..."
${SUDO} ./uncramfs_all.sh "${FSIMG}" "${ROOTFS}" ${ENDIANESS} 2>/dev/null | grep MKFS >> "${CONFLOG}"
;;
"yaffs")
echo "Extracting YAFFS file system..."
${SUDO} ./src/yaffs2utils/unyaffs2 "${FSIMG}" "${ROOTFS}" 2>/dev/null
echo "MKFS='./src/yaffs2utils/mkyaffs2'" >> "${CONFLOG}"
;;
"jffs2")
echo "Extracting JFFS2 file system..."
${SUDO} ./src/jffs2/unjffs2 "${FSIMG}" 1>&2 2>/dev/null
# unjffs2 extracts to hard-coded directory of 'rootfs'
if [ -e "rootfs" ]
then
${SUDO} mv rootfs "${ROOTFS}"
fi
echo "MKFS='./src/jffs2/mkfs.jffs2'" >> "${CONFLOG}"
;;
*)
echo "Unsupported file system '${FS_TYPE}'! Quitting..."
rm -rf "${DIR}"
exit 1
;;
esac
# Check if file system extraction was successful
if [ ${?} -eq 0 ]; then
echo "Firmware extraction successful!"
echo "Firmware parts can be found in '${DIR}/*'"
else
echo "Firmware extraction failed!"
rm -rf "${DIR}"
exit 1
fi
exit 0