ID com.nubee.valkyriecrusade
we look at the games files and they are engrypted.
So 1st step is load it up in IDA pro or ghidra.
This is a custom game engine so a good place to start is with a file open instruction
if we search for that we find nb::File::openRead.
Code: Select all
/* nb::File::openRead(char const*, unsigned int*, nb::Drive::Base, nb::File::Filter) */
void * nb::File::openRead(char *param_1,uint *param_2,Base param_3,Filter param_4)
{
void *pvVar1;
int iVar2;
void *__ptr;
void *local_50;
Info aIStack76 [8];
void *local_44;
File aFStack64 [8];
int local_38;
void *local_34;
local_50 = (void *)0x0;
File(aFStack64);
open(aFStack64,param_1,1,param_3);
if (local_38 == 0) {
__ptr = (void *)0x0;
}
else {
local_50 = local_34;
pvVar1 = malloc((size_t)local_34);
if (pvVar1 == (void *)0x0) {
iVar2 = Compress::getInfo((void *)0x0,(uint)local_34,aIStack76);
}
else {
read(aFStack64,pvVar1,(uint)local_34);
iVar2 = Compress::getInfo(pvVar1,(uint)local_50,aIStack76);
}
__ptr = pvVar1;
if (iVar2 != 0) {
__ptr = (void *)Compress::uncompress(aIStack76);
local_50 = local_44;
free(pvVar1);
}
if ((param_4 == 1) || ((param_4 == 2 && (iVar2 = Coder::isCode(__ptr), iVar2 != 0)))) {
pvVar1 = (void *)Coder::decode(__ptr,(uint)local_50,(uint *)&local_50);
free(__ptr);
__ptr = pvVar1;
if (pvVar1 == (void *)0x0) {
local_50 = pvVar1;
}
}
}
if (param_2 != (uint *)0x0) {
*param_2 = (uint)local_50;
}
~File(aFStack64);
return __ptr;
}
So if we look at this we notice something interesting.
Code: Select all
pvVar1 = (void *)Coder::decode(__ptr,(uint)local_50,(uint *)&local_50);
So in our decompiler lets look at Coder::decode function.
Code: Select all
/* nb::Coder::decode(void const*, unsigned int, unsigned int*) */
int * nb::Coder::decode(void *param_1,uint param_2,uint *param_3)
{
uint *puVar1;
int iVar2;
uint uVar3;
uint __size;
uint *__src;
int *piVar4;
uint uVar5;
int *local_2c;
iVar2 = isCode(param_1);
if (iVar2 == 0) {
return (int *)0x0;
}
__size = param_2 - 0x10;
local_2c = (int *)malloc(__size);
__src = (uint *)((int)param_1 + 0x10);
memcpy(local_2c,__src,__size);
iVar2 = *(int *)((int)param_1 + 0xc);
uVar3 = 4;
if (3 < __size) {
uVar5 = (param_2 - 0x11) * 0x8000000 >> 0x1d;
piVar4 = local_2c;
if (uVar5 != 0) {
*local_2c = (*__src ^ 0x45af6e5d) - iVar2;
piVar4 = local_2c + 1;
__src = (uint *)((int)param_1 + 0x14);
if ((__size < 5) || (uVar3 = 8, __size < 8)) goto LAB_005fe4fe;
if (uVar5 != 1) {
if (uVar5 != 2) {
if (uVar5 != 3) {
if (uVar5 != 4) {
if (uVar5 != 5) {
if (uVar5 != 6) {
uVar5 = *__src;
__src = (uint *)((int)param_1 + 0x18);
uVar3 = 0xc;
*piVar4 = (uVar5 ^ 0x45af6e5d) - iVar2;
piVar4 = local_2c + 2;
if (__size < 0xc) goto LAB_005fe4fe;
}
uVar5 = *__src;
__src = __src + 1;
uVar3 = uVar3 + 4;
*piVar4 = (uVar5 ^ 0x45af6e5d) - iVar2;
piVar4 = piVar4 + 1;
if (__size < uVar3) goto LAB_005fe4fe;
}
uVar5 = *__src;
__src = __src + 1;
uVar3 = uVar3 + 4;
*piVar4 = (uVar5 ^ 0x45af6e5d) - iVar2;
piVar4 = piVar4 + 1;
if (__size < uVar3) goto LAB_005fe4fe;
}
uVar5 = *__src;
__src = __src + 1;
uVar3 = uVar3 + 4;
*piVar4 = (uVar5 ^ 0x45af6e5d) - iVar2;
piVar4 = piVar4 + 1;
if (__size < uVar3) goto LAB_005fe4fe;
}
uVar5 = *__src;
__src = __src + 1;
uVar3 = uVar3 + 4;
*piVar4 = (uVar5 ^ 0x45af6e5d) - iVar2;
piVar4 = piVar4 + 1;
if (__size < uVar3) goto LAB_005fe4fe;
}
uVar5 = *__src;
__src = __src + 1;
uVar3 = uVar3 + 4;
*piVar4 = (uVar5 ^ 0x45af6e5d) - iVar2;
piVar4 = piVar4 + 1;
if (__size < uVar3) goto LAB_005fe4fe;
}
}
do {
*piVar4 = (*__src ^ 0x45af6e5d) - iVar2;
if (((((__size <= uVar3) || (__size < uVar3 + 4)) ||
(piVar4[1] = (__src[1] ^ 0x45af6e5d) - iVar2, __size < uVar3 + 8)) ||
((piVar4[2] = (__src[2] ^ 0x45af6e5d) - iVar2, __size < uVar3 + 0xc ||
(piVar4[3] = (__src[3] ^ 0x45af6e5d) - iVar2, __size < uVar3 + 0x10)))) ||
((piVar4[4] = (__src[4] ^ 0x45af6e5d) - iVar2, __size < uVar3 + 0x14 ||
((piVar4[5] = (__src[5] ^ 0x45af6e5d) - iVar2, __size < uVar3 + 0x18 ||
(piVar4[6] = (__src[6] ^ 0x45af6e5d) - iVar2, __size < uVar3 + 0x1c)))))) break;
puVar1 = __src + 7;
uVar3 = uVar3 + 0x20;
__src = __src + 8;
piVar4[7] = (*puVar1 ^ 0x45af6e5d) - iVar2;
piVar4 = piVar4 + 8;
} while (uVar3 <= __size);
}
LAB_005fe4fe:
iVar2 = makeCheckSum((uchar *)local_2c,__size);
if (*(int *)((int)param_1 + 8) != iVar2) {
free(local_2c);
local_2c = (int *)0x0;
__size = 0;
}
if (param_3 != (uint *)0x0) {
*param_3 = __size;
}
return local_2c;
}
This look very long and complicated but its not too bad if we break it down.
The start
Code: Select all
iVar2 = isCode(param_1);
if (iVar2 == 0) {
return (int *)0x0;
}
the isCode function checks for the valid file header.
So as we can see its checking if it starts with CODE
then 0x100 as a short.
Code: Select all
43 4F 44 45 00 01
Then the next few variables are defined
Code: Select all
__size = param_2 - 0x10;
Code: Select all
local_2c = (int *)malloc(__size);
Code: Select all
__src = (uint *)((int)param_1 + 0x10);
Code: Select all
memcpy(local_2c,__src,__size);
Code: Select all
iVar2 = *(int *)((int)param_1 + 0xc);
We can skip all the checks in the middle for file size and just go do the actual decryption portion.
Code: Select all
do {
*piVar4 = (*__src ^ 0x45af6e5d) - iVar2;
if (((((__size <= uVar3) || (__size < uVar3 + 4)) ||
(piVar4[1] = (__src[1] ^ 0x45af6e5d) - iVar2, __size < uVar3 + 8)) ||
((piVar4[2] = (__src[2] ^ 0x45af6e5d) - iVar2, __size < uVar3 + 0xc ||
(piVar4[3] = (__src[3] ^ 0x45af6e5d) - iVar2, __size < uVar3 + 0x10)))) ||
((piVar4[4] = (__src[4] ^ 0x45af6e5d) - iVar2, __size < uVar3 + 0x14 ||
((piVar4[5] = (__src[5] ^ 0x45af6e5d) - iVar2, __size < uVar3 + 0x18 ||
(piVar4[6] = (__src[6] ^ 0x45af6e5d) - iVar2, __size < uVar3 + 0x1c)))))) break;
puVar1 = __src + 7;
uVar3 = uVar3 + 0x20;
__src = __src + 8;
piVar4[7] = (*puVar1 ^ 0x45af6e5d) - iVar2;
piVar4 = piVar4 + 8;
} while (uVar3 <= __size);
This can be simplified down all this code is doing is xoring 4 bytes with 0x45af6e5d then subtracting the seed value iVar2
from the result the program is just doing it 0x20 bytes at a time but we can just do it 4 bytes at a time.
so we only need
*piVar4 = (*__src ^ 0x45af6e5d) - iVar2;
which is basically
Code: Select all
buffer[i] = (buffer[i] ^ secretKey) - seed;
working bms script to show the code in action.
viewtopic.php?f=9&t=15201
The file header is.
magic 4 : CODE
short 0x100
short 0x7755
u32 CRC32
u32 random seed & 0xFFFF
(obtained with encrypt function that is also in the executable)
Code: Select all
puVar1 = (undefined4 *)malloc(param_2 + 0x10);
*puVar1 = 0x45444f43;
*(undefined2 *)((int)puVar1 + 6) = 0x7755;
*(undefined2 *)(puVar1 + 1) = 0x100;
__dest = puVar1 + 4;
memcpy(__dest,param_1,param_2);
lVar2 = lrand48();
__aeabi_idivmod(lVar2,0xffff);