yagamil 发表于 2025-1-11 15:34:04

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

dqs 发表于 4 天前

{:5_150:}{:5_150:}看看

wwdzcdb 发表于 3 天前

技术大牛{:5_150:}{:5_150:}{:5_150:}
页: [1]
查看完整版本: ikuai软路由系统-固件解密