ikuai软路由系统-固件解密
原文摘自:https://c0ke.shop/2024/07/26/%E5%9B%BA%E4%BB%B6%E8%A7%A3%E5%AF%86/在尝试了其他的找中间件的情况,发现没有中间件,都是加密的,而且每个版本的都可以单独安装,也就不存在需要中间版本的可能,下面是网上找到的[解密文件的库](https://github.com/2512500960/ikuaifw?tab=readme-ov-file)
其中最为核心的解密代码如下,这段代码即是解密固件的文件系统的方法
```
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
uint8_t ext_key;
uint8_t key = { 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;
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 + 19916032 * ((int32_t)key_index + 1) / 131u;
++key_index;
} while (key_index != 1024);
for (iter = 0;
initrd_real_size > (uint32_t)iter;
initrd_start = ((initrd_start - (uint8_t)tmp_var) << ((uint8_t)tmp_var % 7u + 1)) | ((int32_t)(uint8_t)(initrd_start - 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 * 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 + 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 = ((initrd_start - (uint8_t)tmp_var) << ((uint8_t)tmp_var % 7u + 1)) | ((int32_t)(uint8_t)(initrd_start - 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 * 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 = ((initrd_start - (uint8_t)tmp_var) << ((uint8_t)tmp_var % 7u + 1)) | ((int32_t)(uint8_t)(initrd_start - tmp_var) >> (8 - ((uint8_t)tmp_var % 7u + 1))))
```
这是一个复杂的解密操作,涉及到移位和按位或运算。
`initrd_start` 代表当前迭代所处理的字节。
`(initrd_start - (uint8_t)tmp_var)` 减去 `tmp_var` 后进行左移和右移操作。
`((initrd_start - (uint8_t)tmp_var) << ((uint8_t)tmp_var % 7u + 1))` 左移 `tmp_var % 7 + 1` 位。
`((int32_t)(uint8_t)(initrd_start - 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 * var_1);
```
- `tmp_var` 的低字节被更新为 `(initrd_size_low_byte + ext_key * var_1)`。
- `ext_key` 从 `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;
uint8_t key = { 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;
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 + 19916032 * ((int32_t)key_index + 1) / 131u;
++key_index;
} while (key_index != 1024);
for (iter = 0;
initrd_real_size > (uint32_t)iter;
initrd_start = ((initrd_start - (uint8_t)tmp_var) << ((uint8_t)tmp_var % 7u + 1)) | ((int32_t)(uint8_t)(initrd_start - 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 * 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/
bindevetcliblib64lost+foundmntoverlayprocromrootsbinsystmpusrvar
``` {:5_150:}{:5_150:}看看 技术大牛{:5_150:}{:5_150:}{:5_150:}
页:
[1]