michalss Posted November 7, 2023 Posted November 7, 2023 We will look at the game Valkyrie Crusade. ID com.nubee.valkyriecrusade we look at the games files and they are encrypted. 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. /* 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. pvVar1 = (void *)Coder::decode(__ptr,(uint)local_50,(uint *)&local_50); So in our decompiler lets look at Coder::decode function. /* 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 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. 43 4F 44 45 00 01 Then the next few variables are defined __size = param_2 - 0x10; - sets __size to file size - 16 local_2c = (int *)malloc(__size); - creates a buffer with the size of __size __src = (uint *)((int)param_1 + 0x10); sets __src to the input file starting at offset 16 memcpy(local_2c,__src,__size); copies __src into local_2c buffer iVar2 = *(int *)((int)param_1 + 0xc); read an int from offset 0xC in the file We can skip all the checks in the middle for file size and just go do the actual decryption portion. 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 Quote *piVar4 = (*__src ^ 0x45af6e5d) - iVar2; which is basically buffer[i] = (buffer[i] ^ secretKey) - seed; working bms script to show the code in action. set MEMORY_FILE10 string " typedef unsigned char u8; typedef unsigned int u32; static u32 secretKey = 0x45AF6E5D; void Decrypt(u8* data, int data_size, int seed) { u32* buffer = (u32*)(data); u32 xored_size = data_size / 4; if (xored_size > 0) { for (int i = 0; i < xored_size; i++) { buffer[i] = (buffer[i] ^ secretKey) - seed; } } } " idstring "\x43\x4F\x44\x45\x00\x01\x55\x77" get CRC32 long get SEED long get SIZE asize math SIZE - 16 get NAME basename string NAME + .png log MEMORY_FILE 16 SIZE calldll MEMORY_FILE10 "Decrypt" "tcc" RET MEMORY_FILE SIZE SEED log NAME 0 SIZE MEMORY_FILE 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) 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); 19975_18.zip 1 1
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now