[Verizon Samsung Galaxy S III] Ralekdev:
It's been a few days so I wanted to give an update on the signature check on boot.img

As has been previously guessed, everything important in boot.img is included in the signature check

page_size is always 0x800 since we're using emmc boot

hash_size = 0x800 (read the first page with the boot_img_header)

hash_size += page_size * ((page_size + ramdisk_size - 1) / page_size)

hash_size += page_size * ((page_size + kernel_size - 1) / page_size)

hash_size += page_size * ((page_size + second_size - 1) / page_size)

For the stock boot.img, this should come out to be 0x573000, so the first 0x573000 bytes in boot.img are checked.

These bytes are then SHA1 hashed and passed to the verification function

After hash_size bytes is a series of 0x100 byte blocks that will be passed to the verification function (img_sig_data parameter below)

The verification function uses the following structure

struct sig_ctx_t {
int count;
int seed[65];
int subcheck_seed[64]; // possibly a modulus
}

This sig_ctx is located in aboot.img at file offset 0x12642C in VRALF2 and VRALG1 (It'll start with bytes 0x40, 0x00, 0x00, 0x00)

I've cleaned up the first function a bit from what IDA/Hex-Rays spit out, but the second function I haven't simplified as much

int signature_check_data(sig_ctx_t *sig_ctx, char *img_sig_data, signed int signature_len, char *sha1_of_contents) {

int* img_ofs_0x100 = (int*)(img_sig_data + 0x100);
int* img_ofs_0x200 = (int*)(img_sig_data + 0x200);
int* img_ofs_0x300 = (int*)(img_sig_data + 0x300);
int* img_ofs_0x400 = (int*)(img_sig_data + 0x400); // Temporary storage

// Copy 0x0 block to 0x100
memcpy(img_ofs_0x100, &img_sig_data[0], signature_len);

// ofs_0x200 is filled with byte-swapped ints from img_ofs_0x100
for (int i = 0; i < sig_ctx->count; i++) {
img_ofs_0x200[i] = htonl(img_ofs_0x100[sig_ctx->count - 1 - i]);
}

// subcheck(sig_block *block, int *output, int *input1, int *input2)
// multiplication maybe?
signature_subcheck(sig_ctx, img_ofs_0x300, img_ofs_0x200, sig_ctx->subcheck_seed);
signature_subcheck(sig_ctx, img_ofs_0x400, img_ofs_0x300, img_ofs_0x300);
signature_subcheck(sig_ctx, img_ofs_0x300, img_ofs_0x400, img_ofs_0x200);

if ( sig_ctx->count )
{
count_minus_1 = sig_ctx->count - 1;
v18 = img_ofs_0x300[sig_ctx->count - 1];
v19 = sig_ctx->seed[sig_ctx->count]; // seed[64]
// v19 = *(&sig_ctx->count + sig_ctx->count + 1);
if ( v18 >= v19 )
{
if ( v18 == v19 )
{
for (int i = 0; i < sig_ctx->count; i++) {
int v22 = img_ofs_0x300[sig_ctx->count - 1 - i];
int v23 = sig_ctx->seed[sig_ctx->count - 1 - i];
if (v22 < v23) {
goto LABEL_18
}
}
}
if ( sig_ctx->count > 0 )
{
int carry = 0;
for (int i = 0; i < sig_ctx->count; i++) {
uint64 temp = img_ofs_0x300[i] - (uint64)sig_ctx->seed[i + 1];
img_ofs_0x300[i] = img_ofs_0x300[i] - sig_ctx->seed[i + 1] + carry;
carry = (int)(temp >> 32); // get high 32 bits
}
}
}

LABEL_18:
// Store the calculation back into img_ofs_0x100
for (int i = 0; i < sig_ctx->count; i++) {
int val = img_ofs_0x300[sig_ctx->count - 1 - i];
char* dest = &img_ofs_0x100[i];

dest[0] = (val & 0xFF000000) >> 24;
dest[1] = ((val & 0x00FF0000) >> 16) & 0xFF;
dest[2] = ((val & 0x0000FF00) >> 8) & 0xFF;
dest[3] = (val & 0xFF);
}

if (memcmp(img_ofs_0x100, sig_check_compare_result, 236)) // sig_check_compare_result is a char[236] with the first 2 bytes 0x00, 0x01, and the rest 0xFF
return 0;

if (signature_len > 236) {
if (memcmp(&img_ofs_0x100[236], sha1_of_contents, signature_len - 236)) // 256-236 = 20
return 0;

// Signature passed
return 1;
}
}
return 0;
}

Here's the subcheck function, it looks like arbitrary-precision math, possibly mulmod

void __fastcall signature_subcheck(sig_ctx_t *sig_data, int *output, int *input1, int *input2)
{
int v5; // r3@2
int count; // r4@3
unsigned __int64 v7; // r2@6
unsigned __int64 v8; // r8@6
int inner_index; // r5@7
int block1_pos; // r4@7
int v11; // r5@14
__int64 v12; // r8@14
int v13; // r6@14
unsigned __int64 v14; // r2@15
int v15; // kr04_4@15
int v16; // [sp+18h] [bp-48h]@6
unsigned int v17; // [sp+1Ch] [bp-44h]@6
int outer_index; // [sp+2Ch] [bp-34h]@5

if ( sig_data->count > 0 )
{
v5 = 0;
do
{
output[v5++] = 0; // this do while is just memset(output, 0, 4 * sig_data->count)
count = sig_data->count;
}
while ( sig_data->count > v5 );
if ( count > 0 )
{
outer_index = 0;
do
{
v16 = input1[outer_index];
v7 = (unsigned int)v16 * (unsigned __int64)(unsigned int)*input2 + (unsigned int)*output;// v7 = input1[outer_index] * (uint64)input2[0] + output[0]
v17 = sig_data->seed[0] * v7;
v8 = sig_data->seed[1] * (unsigned __int64)v17 + (unsigned int)v7;
if ( count <= 1 )
{
block1_pos = 1;
}
else
{
inner_index = 0;
block1_pos = 1;
do
{
v7 = (unsigned int)v16 * (unsigned __int64)(unsigned int)input2[block1_pos]
+ (unsigned int)output[block1_pos]
+ HIDWORD(v7);
v8 = sig_data->seed[inner_index + 2] * (unsigned __int64)v17 + HIDWORD(v8) + (unsigned int)v7;
++block1_pos;
output[inner_index] = v8;
++inner_index;
}
while ( block1_pos < sig_data->count );
}
output[block1_pos - 1] = HIDWORD(v8) + HIDWORD(v7);
if ( (HIDWORD(v8) + (unsigned __int64)HIDWORD(v7)) >> 32 )
{
if ( sig_data->count <= 0 )
return;
v11 = 0;
v12 = 0LL;
v13 = 0;
do
{
v14 = (unsigned int)output[v11] - (unsigned __int64)sig_data->seed[v11 + 1];
v15 = output[v11] - sig_data->seed[v11 + 1];
output[v11] = output[v11] - sig_data->seed[v11 + 1] + v12;
count = sig_data->count;
++v13;
++v11;
v12 = (signed int)((__PAIR__(HIDWORD(v14), v15) + v12) >> 32);
}
while ( v13 < sig_data->count );
}
else
{
count = sig_data->count;
}
++outer_index;
}
while ( outer_index < count );
}
}
}

The goal is to make it so that after all the calculations the 256 byte block located at img_sig_data+0x100 has the contents 0x00, 0x01, 0xFF * 236, and then the sha1 of our boot.img

I'm in the middle of moving at the moment, so I don't have as much time as I would like to look at this right now, but that should clear up in a few days.

Also, if there's any interest I can post a guide on how to get the bootloader files loaded into IDA for analysis. Some knowledge of ARM assembly would be required though.

EDIT:

In other news, I found what keeps resetting the 16 byte encrypted romtype in param.img. It's libcordon.so, which is from /system/app/SysScope.apk (it'll also be copied to /system/lib/libcordon.so). It's using quite a few checks to see if you've modified your system.

There's an adb scanner, checking to see if you've changed the ro.secure or ro.debuggable props.

The root process scanner checks running processes and returns true if any are found running as root that are not one of:

"debuggerd", "init", "installd", "servicemanager", "vold", "zygote", "netd", "ueventd", "dock_kbd_attach", "pppd", "pppd_runner", "mpdecision", "thermald", "hdmid", "sec_keyboard", "seccmmond", "mfsc", "mfdp"

There's also a partition check, kernel checker, su scanner, and a file scanning mechanism using data from a sqlite db

So to completely remove the Samsung custom screen on bootup and 5 second delay you'd need to disable the SysScope.apk, then encrypt and write the 16 bytes yourself using 0xFF000000 as the first int to mark yourself as official