搜索
缓存时间03 现在时间03 缓存数据 我现在发现,很多人其实就是来我的世界转一圈看一看,觉得有意思就跟我多玩一会儿,觉得无聊就转身离开。我限制不了任何人的行为,不能留下任意一个,我只能把我的世界装扮得漂亮点,等下次有人来让他们愿意多玩一会儿。
查看: 274|回复: 2

ikuai软路由系统-固件解密

[复制链接]
发表于 2025-1-11 15:34:04 | 显示全部楼层 |阅读模式

厌倦了滚动浏览相同的帖子?当您创建帐户后,您将始终回到您离开的地方。注册帐户,不仅可以享受无广告的清爽界面!

您需要 登录 才可以下载或查看,没有账号?注册

×
原文摘自:https://c0ke.shop/2024/07/26/%E5%9B%BA%E4%BB%B6%E8%A7%A3%E5%AF%86/

在尝试了其他的找中间件的情况,发现没有中间件,都是加密的,而且每个版本的都可以单独安装,也就不存在需要中间版本的可能,下面是网上找到的解密文件的库

其中最为核心的解密代码如下,这段代码即是解密固件的文件系统的方法

#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 
[发帖际遇]: yagamil 发帖时在路边捡到 5 点数,偷偷放进了口袋. 幸运榜 / 衰神榜
爱生活,爱奶昔~
dqs
发表于 4 天前 | 显示全部楼层
看看
爱生活,爱奶昔~
回复 支持 反对

使用道具 举报

发表于 3 天前 | 显示全部楼层
技术大牛
爱生活,爱奶昔~
回复 支持 反对

使用道具 举报

Powered by Nyarime. Licensed

GMT+8, 2025-1-19 03:43 , Processed in 0.040624 second(s), 23 queries , Gzip On, Redis On
发帖际遇 ·手机版 ·小黑屋 ·RSS ·奶昔网 | 沪ICP备13020230号-1 |  沪公网安备 31010702007642号

登录切换风格
快速回复 返回顶部 返回列表