在尝试了其他的找中间件的情况,发现没有中间件,都是加密的,而且每个版本的都可以单独安装,也就不存在需要中间版本的可能,下面是网上找到的解密文件的库
其中最为核心的解密代码如下,这段代码即是解密固件的文件系统的方法
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
uint8_t ext_key[1024];
uint8_t key[17] = { 0x77, 0xb1, 0xfa, 0x93, 0x74, 0x2c, 0xb3, 0x9d,
0x33, 0x83, 0x55, 0x3e, 0x84, 0x8a, 0x52, 0x91, 0x00 };
uint8_t hash_table[] = {
0x00, 0x00, 0x00, 0x00, 0x64, 0x10, 0xB7, 0x1D, 0xC8, 0x20,
0x6E, 0x3B, 0xAC, 0x30, 0xD9, 0x26, 0x90, 0x41, 0xDC, 0x76,
0xF4, 0x51, 0x6B, 0x6B, 0x58, 0x61, 0xB2, 0x4D, 0x3C, 0x71,
0x05, 0x50, 0x20, 0x83, 0xB8, 0xED, 0x44, 0x93, 0x0F, 0xF0,
0xE8, 0xA3, 0xD6, 0xD6, 0x8C, 0xB3, 0x61, 0xCB, 0xB0, 0xC2,
0x64, 0x9B, 0xD4, 0xD2, 0xD3, 0x86, 0x78, 0xE2, 0x0A, 0xA0,
0x1C, 0xF2, 0xBD, 0xBD
};
int32_t hash(uint8_t* a1, uint32_t a2)
{
uint64_t v2 = 0;
uint32_t result = 0xffffffff;
uint8_t v4;
uint32_t v5;
while (a2 != (uint32_t)v2) {
v4 = a1[v2++];
v5 = hash_table[(v4 ^ (uint8_t)result) & 0xf] ^ ((uint32_t)result >> 4);
result = hash_table[((uint8_t)v5 ^ (v4 >> 4)) & 0xf] ^ (v5 >> 4);
}
return result;
}
int main()
{
int64_t key_index = 0;
uint32_t initrd_real_size, tmp_var, hash_var;
int8_t initrd_size_low_byte;
int32_t var_0 = 0, var_1 = 1;
uint8_t* initrd_start;
int64_t initrd_size;
struct stat* initrd_stat;
FILE* f;
uint8_t* p_hash;
int64_t iter = 0;
f = fopen("./rootfs.raw", "rb");
initrd_stat = (struct stat*)malloc(sizeof(struct stat));
fstat(fileno(f), initrd_stat);
initrd_size = initrd_stat->st_size;
initrd_start = (uint8_t*)malloc(initrd_size);
fread(initrd_start, sizeof(uint8_t), initrd_size, f);
fclose(f);
// initrd_real_size = initrd_size - 4;
initrd_real_size = initrd_size - 4;
initrd_size_low_byte = (int8_t)(initrd_size - 4);
do {
ext_key[key_index] = key[key_index & 0xf] + 19916032 * ((int32_t)key_index + 1) / 131u;
++key_index;
} while (key_index != 1024);
for (iter = 0;
initrd_real_size > (uint32_t)iter;
initrd_start[iter - 1] = ((initrd_start[iter - 1] - (uint8_t)tmp_var) << ((uint8_t)tmp_var % 7u + 1)) | ((int32_t)(uint8_t)(initrd_start[iter - 1] - tmp_var) >> (8 - ((uint8_t)tmp_var % 7u + 1)))) {
tmp_var = var_0 + iter++;
*(uint8_t*)&tmp_var = (uint8_t)(initrd_size_low_byte + ext_key[tmp_var % 1024u] * var_1);
}
printf("initrd_size:%ld\n"
"initrd_real_size:%d\n"
"iter:%ld\n",
initrd_size, initrd_real_size, iter);
hash_var = __builtin_bswap32(~hash(initrd_start, initrd_size - 4));
hash_var = __builtin_bswap32(~hash((uint8_t*)&hash_var, 4));
p_hash = (uint8_t*)(initrd_start + initrd_size - 4);
if (*(uint32_t*)p_hash == hash_var) {
printf("success\n");
}
f = fopen("./rootfs.decode", "wb");
fwrite(initrd_start, sizeof(uint8_t), initrd_size, f);
fclose(f);
free(initrd_start);
free(initrd_stat);
return 0;
}
再简单分析一下,发现主要步骤如下
初始化扩展密钥:
do {
ext_key[key_index] = key[key_index & 0xf] + 19916032 * ((int32_t)key_index + 1) / 131u;
++key_index;
} while (key_index != 1024);
这一部分代码使用 key
数组和一些算术运算来生成 ext_key
数组。ext_key
数组长度为1024
解密过程:
for (iter = 0;
initrd_real_size > (uint32_t)iter;
initrd_start[iter - 1] = ((initrd_start[iter - 1] - (uint8_t)tmp_var) << ((uint8_t)tmp_var % 7u + 1)) | ((int32_t)(uint8_t)(initrd_start[iter - 1] - tmp_var) >> (8 - ((uint8_t)tmp_var % 7u + 1)))) {
tmp_var = var_0 + iter++;
*(uint8_t*)&tmp_var = (uint8_t)(initrd_size_low_byte + ext_key[tmp_var % 1024u] * var_1);
}
这个循环的核心是对 initrd_start
数组进行解密操作。具体的解密步骤如下
初始化
for (iter = 0;
initrd_real_size > (uint32_t)iter;
iter
变量初始化为 0
,用于遍历 initrd_start
数组。
- 循环条件是
initrd_real_size
大于 iter
,确保遍历范围不超过 initrd_real_size
。
解密
initrd_start[iter - 1] = ((initrd_start[iter - 1] - (uint8_t)tmp_var) << ((uint8_t)tmp_var % 7u + 1)) | ((int32_t)(uint8_t)(initrd_start[iter - 1] - tmp_var) >> (8 - ((uint8_t)tmp_var % 7u + 1))))
这是一个复杂的解密操作,涉及到移位和按位或运算。
initrd_start[iter - 1]
代表当前迭代所处理的字节。
(initrd_start[iter - 1] - (uint8_t)tmp_var)
减去 tmp_var
后进行左移和右移操作。
((initrd_start[iter - 1] - (uint8_t)tmp_var) << ((uint8_t)tmp_var % 7u + 1))
左移 tmp_var % 7 + 1
位。
((int32_t)(uint8_t)(initrd_start[iter - 1] - tmp_var) >> (8 - ((uint8_t)tmp_var % 7u + 1)))
右移 8 - (tmp_var % 7 + 1)
位。
使用按位或运算 |
将左移和右移的结果组合,最终得到解密后的值。
更新 tmp_var
tmp_var = var_0 + iter++;
tmp_var
被更新为 var_0 + iter
,然后 iter
自增。
修改 tmp_var
低字节
*(uint8_t*)&tmp_var = (uint8_t)(initrd_size_low_byte + ext_key[tmp_var % 1024u] * var_1);
tmp_var
的低字节被更新为 (initrd_size_low_byte + ext_key[tmp_var % 1024u] * var_1)
。
ext_key[tmp_var % 1024u]
从 ext_key
数组中获取一个值。
- 乘以
var_1
(在该代码段中,var_1
始终为 1
)。
- 加上
initrd_size_low_byte
,最终结果赋值给 tmp_var
的低字节。
计算哈希值并比较
hash_var = __builtin_bswap32(~hash(initrd_start, initrd_size - 4));
hash_var = __builtin_bswap32(~hash((uint8_t*)&hash_var, 4));
p_hash = (uint8_t*)(initrd_start + initrd_size - 4);
if (*(uint32_t*)p_hash == hash_var) {
printf("success\n");
}
哈希值计算:
hash_var = __builtin_bswap32(~hash(initrd_start, initrd_size - 4));
- 调用
hash
函数对解密后的数据(除去最后 4 字节)进行哈希计算。
- 结果取反(按位取反运算符
~
)。
- 使用
__builtin_bswap32
进行字节顺序交换(大端到小端或小端到大端)。
再次计算哈希值:
hash_var = __builtin_bswap32(~hash((uint8_t*)&hash_var, 4));
- 对上一步得到的
hash_var
进行哈希计算(长度为 4 字节)。
- 结果再次取反并进行字节顺序交换。
验证哈希值
获取原始数据中的哈希值:
p_hash = (uint8_t*)(initrd_start + initrd_size - 4);
p_hash
指向 initrd_start
数组中最后 4 字节,这些字节包含了原始数据中的哈希值。
比较哈希值:
if (*(uint32_t*)p_hash == hash_var) {
printf("success\n");
}
- 将
p_hash
指向的 4 字节数据解释为一个 uint32_t
类型的值,并与 hash_var
比较。
- 如果相等,打印
success
表示解密成功。
结论
这个解密算法首先生成一个扩展密钥,然后通过一系列位移和异或操作对固件文件进行解密,并通过哈希校验确保解密过程的正确性。该算法的核心在于扩展密钥的生成和解密过程中复杂的位移运算。
附录
最后还会遇到解压缩的问题,解决办法如下
https://blog.csdn.net/zzlufida/article/details/84594447
可以直接使用的解密脚本如下
#!/bin/bash
srcfile=$1
UPDATE_DIR=.
headlen=$(printf "%u" 0x$(hexdump -v -n 4 $srcfile -e '4/1 "%02x"'))
echo $headlen
printf "\x1f\x8b\x08\x00\x6f\x9b\x4b\x59\x02\x03" > $UPDATE_DIR/header.bin
dd if=$srcfile bs=1 skip=4 count=$headlen >> $UPDATE_DIR/header.bin 2>/dev/null
gunzip < $UPDATE_DIR/header.bin > $UPDATE_DIR/header_info.json
dd if=$srcfile bs=$((headlen+4)) skip=1 >> $UPDATE_DIR/firmware.bin 2>/dev/null
gzip -d < firmware.bin > firmware.bin.decompressed
mkdir mount
sudo mount firmware.bin.decompressed mount
cp mount/boot/rootfs ./
cp mount/boot/vmlinuz ./
echo '#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
uint8_t ext_key[1024];
uint8_t key[17] = { 0x77, 0xb1, 0xfa, 0x93, 0x74, 0x2c, 0xb3, 0x9d,
0x33, 0x83, 0x55, 0x3e, 0x84, 0x8a, 0x52, 0x91, 0x00 };
uint8_t hash_table[] = {
0x00, 0x00, 0x00, 0x00, 0x64, 0x10, 0xB7, 0x1D, 0xC8, 0x20,
0x6E, 0x3B, 0xAC, 0x30, 0xD9, 0x26, 0x90, 0x41, 0xDC, 0x76,
0xF4, 0x51, 0x6B, 0x6B, 0x58, 0x61, 0xB2, 0x4D, 0x3C, 0x71,
0x05, 0x50, 0x20, 0x83, 0xB8, 0xED, 0x44, 0x93, 0x0F, 0xF0,
0xE8, 0xA3, 0xD6, 0xD6, 0x8C, 0xB3, 0x61, 0xCB, 0xB0, 0xC2,
0x64, 0x9B, 0xD4, 0xD2, 0xD3, 0x86, 0x78, 0xE2, 0x0A, 0xA0,
0x1C, 0xF2, 0xBD, 0xBD
};
int32_t hash(uint8_t* a1, uint32_t a2)
{
uint64_t v2 = 0;
uint32_t result = 0xffffffff;
uint8_t v4;
uint32_t v5;
while (a2 != (uint32_t)v2) {
v4 = a1[v2++];
v5 = hash_table[(v4 ^ (uint8_t)result) & 0xf] ^ ((uint32_t)result >> 4);
result = hash_table[((uint8_t)v5 ^ (v4 >> 4)) & 0xf] ^ (v5 >> 4);
}
return result;
}
int main()
{
int64_t key_index = 0;
uint32_t initrd_real_size, tmp_var, hash_var;
int8_t initrd_size_low_byte;
int32_t var_0 = 0, var_1 = 1;
uint8_t* initrd_start;
int64_t initrd_size;
struct stat* initrd_stat;
FILE* f;
uint8_t* p_hash;
int64_t iter = 0;
f = fopen("./rootfs.raw", "rb");
initrd_stat = (struct stat*)malloc(sizeof(struct stat));
fstat(fileno(f), initrd_stat);
initrd_size = initrd_stat->st_size;
initrd_start = (uint8_t*)malloc(initrd_size);
fread(initrd_start, sizeof(uint8_t), initrd_size, f);
fclose(f);
// initrd_real_size = initrd_size - 4;
initrd_real_size = initrd_size - 4;
initrd_size_low_byte = (int8_t)(initrd_size - 4);
do {
ext_key[key_index] = key[key_index & 0xf] + 19916032 * ((int32_t)key_index + 1) / 131u;
++key_index;
} while (key_index != 1024);
for (iter = 0;
initrd_real_size > (uint32_t)iter;
initrd_start[iter - 1] = ((initrd_start[iter - 1] - (uint8_t)tmp_var) << ((uint8_t)tmp_var % 7u + 1)) | ((int32_t)(uint8_t)(initrd_start[iter - 1] - tmp_var) >> (8 - ((uint8_t)tmp_var % 7u + 1)))) {
tmp_var = var_0 + iter++;
*(uint8_t*)&tmp_var = (uint8_t)(initrd_size_low_byte + ext_key[tmp_var % 1024u] * var_1);
}
printf("initrd_size:%ld\n"
"initrd_real_size:%d\n"
"iter:%ld\n",
initrd_size, initrd_real_size, iter);
hash_var = __builtin_bswap32(~hash(initrd_start, initrd_size - 4));
hash_var = __builtin_bswap32(~hash((uint8_t*)&hash_var, 4));
p_hash = (uint8_t*)(initrd_start + initrd_size - 4);
if (*(uint32_t*)p_hash == hash_var) {
printf("success\n");
}
f = fopen("./rootfs.decode", "wb");
fwrite(initrd_start, sizeof(uint8_t), initrd_size, f);
fclose(f);
free(initrd_start);
free(initrd_stat);
return 0;
}' >dec.c
gcc -o dec dec.c
cp rootfs rootfs.raw
./dec
mv rootfs.decode rootfs.xz
umount mount
rm -r mount
rm -r rootfs
FILENAME="./rootfs.xz"
SEQUENCE="\x59\x5A"
# 将字节序列转换为十六进制表示
HEX_SEQUENCE=$(echo -n -e "$SEQUENCE" | hexdump -v -e '/1 "%02x"' | tr -d '\n')
# 计算文件大小
FILE_SIZE=$(stat -c%s "$FILENAME")
# 创建一个临时文件来存储查找结果
TMP_FILE=$(mktemp)
# 从文件末尾开始读取
for OFFSET in $(seq 0 4096 $FILE_SIZE); do
READ_OFFSET=$((FILE_SIZE - OFFSET - 4096))
[ $READ_OFFSET -lt 0 ] && READ_OFFSET=0
# 读取文件块
dd if="$FILENAME" bs=1 skip="$READ_OFFSET" count=4096 2>/dev/null | hexdump -v -e '1/1 "%.2x"' > $TMP_FILE
# 查找字节序列的位置
grep -ob "$HEX_SEQUENCE" $TMP_FILE | head -n 1 | cut -d: -f1 > /dev/null 2>&1
if [ $? -eq 0 ]; then
# 如果找到字节序列
FOUND_OFFSET=$((READ_OFFSET + $(grep -ob "$HEX_SEQUENCE" $TMP_FILE | head -n 1 | cut -d: -f1) / 2))
break
fi
done
# 删除临时文件
rm -f $TMP_FILE
# 如果未找到字节序列,则退出
if [ -z "$FOUND_OFFSET" ]; then
echo "Sequence not found in the file."
exit 1
fi
# 计算新文件的大小
NEW_SIZE=$((FOUND_OFFSET + 2)) # 保留最后一个字节序列
# 使用 dd 截断文件
dd if="$FILENAME" of="${FILENAME}.tmp" bs=1 count="$NEW_SIZE" status=none
# 用截断后的文件替换原文件
mv "${FILENAME}.tmp" "$FILENAME"
echo "File $FILENAME truncated after the first occurrence of the sequence from the end, keeping the sequence."
sudo xz -d rootfs.xz
运行结果
iot@research:~/Desktop/iKuai/boot$ sudo ./decode.sh iKuai8_x64_3.7.13_Build202406212115.bin
188
initrd_size:34528532
initrd_real_size:34528528
iter:34528528
File ./rootfs.xz truncated after the first occurrence of the sequence from the end, keeping the sequence.
iot@research:~/Desktop/iKuai/boot$ file rootfs
rootfs: Linux rev 1.0 ext2 filesystem data (mounted or unclean), UUID=57f8f4bc-abf4-655f-bf67-946fc0f9f25b (extents) (large files)
iot@research:~/Desktop/iKuai/boot$ mkdir mount
iot@research:~/Desktop/iKuai/boot$ sudo mount ./rootfs ./mount/
iot@research:~/Desktop/iKuai/boot$ ls ./mount/
bin dev etc lib lib64 lost+found mnt overlay proc rom root sbin sys tmp usr var