The important part of the way this works is the .so files. Mainly libmono.so which is located within the APK at:
/lib/x86/libmono.so
You are looking for the commonly used (due to the ability to encrypt the data) function:
mono_image_open_from_data_with_name
However, most games that make use of this encrypt the function to prevent people from easily decoding it. The way this is done is having a 'onload' function called when the module is first loaded (libmono.so) within the VM. Think of it like a TLS callback that is called immediately on load.
In your case, there is a function called:
_llooaa which is called by llooaa
This is where the decryption of mono_image_open_from_data_with_name takes place.
It is a simple xor encryption but the effect has its purpose to weed off people from just opening the .so in IDA and going right to the function needed.
The decryption of this function would be done, in the current APK you linked, like this:
Code: Select all
var fileDataOriginal = File.ReadAllBytes("data.bin");
var v26 = fileDataOriginal.Length - 1;
var v27 = 0;
var v28 = 0;
do
{
fileDataOriginal[v28] ^= (byte) 0x9a;
fileDataOriginal[v28] = (byte)~fileDataOriginal[v28];
v28 = ++v27;
} while (v27 != v26);
Where data.bin holds the function bytes of the mono_image_open_from_data_with_name function. You can see the libmono.so doing this here:
Code: Select all
int __usercall _llooaa@<eax>(int a1@<ebx>, int a2, char *s2)
{
int v3; // ebx@1
int result; // eax@1
int v5; // [sp+1Ch] [bp-2Ch]@1
int v6; // [sp+20h] [bp-28h]@2
char *v7; // [sp+24h] [bp-24h]@3
size_t len; // [sp+28h] [bp-20h]@2
void *addr; // [sp+2Ch] [bp-1Ch]@2
int v10; // [sp+30h] [bp-18h]@2
int (__cdecl *v11)(void *, size_t, int, int, int, char *); // [sp+34h] [bp-14h]@2
__int32 v12; // [sp+38h] [bp-10h]@2
unsigned int i; // [sp+3Ch] [bp-Ch]@2
_x86_get_pc_thunk_bx();
v3 = a1 + 2070368;
result = getTargetFuncInfo(a2, s2, (int)&v5);
if ( (_BYTE)result != -1 )
{
v12 = sysconf(40);
v11 = (int (__cdecl *)(void *, size_t, int, int, int, char *))(*(char **)((char *)&mono_image_open_from_data_with_name_ptr[-905985]
+ v3)
- 1);
v10 = v6;
addr = (void *)((unsigned int)v11 & -v12);
len = (char *)((char *)v11 + v6) - (char *)addr;
mprotect((void *)((unsigned int)v11 & -v12), len, 7);
for ( i = 0; i < v10 - 1; ++i )
{
v7 = (char *)v11 + i;
*((_BYTE *)v11 + i) ^= 0x9Au;
*v7 = ~*v7;
}
result = mprotect(addr, len, 5);
}
return result;
}
After you manually fix the function you can then decrypt the actual function that loads the modules, which looks like this now:
Code: Select all
int __usercall mono_image_open_from_data_with_name@<eax>(int a1@<ebx>, void *src, size_t n, int a4, int a5, int a6, char *haystack)
{
int v7; // ebx@1
char v8; // si@19
int v9; // eax@19
char v10; // si@38
int v11; // eax@38
int v12; // eax@43
char v14; // [sp+14h] [bp-144h]@18
char v15; // [sp+15h] [bp-143h]@18
char v16; // [sp+16h] [bp-142h]@18
char v17; // [sp+17h] [bp-141h]@18
char v18; // [sp+18h] [bp-140h]@18
char v19; // [sp+19h] [bp-13Fh]@18
char v20; // [sp+1Ah] [bp-13Eh]@18
char v21; // [sp+1Bh] [bp-13Dh]@18
char v22; // [sp+1Ch] [bp-13Ch]@18
char v23; // [sp+1Dh] [bp-13Bh]@18
char v24; // [sp+1Eh] [bp-13Ah]@18
char v25; // [sp+1Fh] [bp-139h]@18
char v26; // [sp+20h] [bp-138h]@18
char v27; // [sp+21h] [bp-137h]@18
char v28; // [sp+22h] [bp-136h]@18
char v29; // [sp+23h] [bp-135h]@18
char v30; // [sp+24h] [bp-134h]@18
char v31; // [sp+25h] [bp-133h]@18
char v32; // [sp+26h] [bp-132h]@18
char v33; // [sp+27h] [bp-131h]@18
char v34; // [sp+28h] [bp-130h]@18
char v35; // [sp+29h] [bp-12Fh]@18
char v36; // [sp+2Ah] [bp-12Eh]@18
char v37; // [sp+2Bh] [bp-12Dh]@18
char v38; // [sp+2Ch] [bp-12Ch]@18
char v39; // [sp+2Dh] [bp-12Bh]@18
char v40; // [sp+2Eh] [bp-12Ah]@18
char v41; // [sp+2Fh] [bp-129h]@18
char v42; // [sp+30h] [bp-128h]@18
char v43; // [sp+31h] [bp-127h]@18
char v44; // [sp+32h] [bp-126h]@18
char v45; // [sp+33h] [bp-125h]@18
char v46; // [sp+34h] [bp-124h]@18
char v47; // [sp+35h] [bp-123h]@18
char v48; // [sp+36h] [bp-122h]@18
char v49; // [sp+37h] [bp-121h]@18
char v50; // [sp+38h] [bp-120h]@18
char v51; // [sp+39h] [bp-11Fh]@18
char v52; // [sp+3Ah] [bp-11Eh]@18
char v53; // [sp+3Bh] [bp-11Dh]@18
char v54; // [sp+3Ch] [bp-11Ch]@18
char v55; // [sp+3Dh] [bp-11Bh]@18
char v56; // [sp+3Eh] [bp-11Ah]@18
char v57; // [sp+3Fh] [bp-119h]@18
char v58; // [sp+40h] [bp-118h]@18
char v59; // [sp+41h] [bp-117h]@18
char v60; // [sp+42h] [bp-116h]@18
char v61; // [sp+43h] [bp-115h]@18
char v62; // [sp+44h] [bp-114h]@18
char v63; // [sp+45h] [bp-113h]@18
char v64; // [sp+46h] [bp-112h]@18
char v65; // [sp+47h] [bp-111h]@18
char v66; // [sp+48h] [bp-110h]@18
char v67; // [sp+49h] [bp-10Fh]@18
char v68; // [sp+4Ah] [bp-10Eh]@18
char v69; // [sp+4Bh] [bp-10Dh]@18
char v70; // [sp+4Ch] [bp-10Ch]@18
char v71; // [sp+4Dh] [bp-10Bh]@18
char v72; // [sp+4Eh] [bp-10Ah]@18
char v73; // [sp+4Fh] [bp-109h]@18
char v74; // [sp+50h] [bp-108h]@18
char v75; // [sp+51h] [bp-107h]@18
char v76; // [sp+52h] [bp-106h]@18
char v77; // [sp+53h] [bp-105h]@18
char needle[4]; // [sp+54h] [bp-104h]@12
int v79; // [sp+58h] [bp-100h]@12
int v80; // [sp+5Ch] [bp-FCh]@12
int v81; // [sp+60h] [bp-F8h]@12
int v82; // [sp+64h] [bp-F4h]@12
char v83; // [sp+68h] [bp-F0h]@1
int v84; // [sp+E8h] [bp-70h]@45
int v85; // [sp+ECh] [bp-6Ch]@42
int v86; // [sp+F0h] [bp-68h]@37
signed __int32 v87; // [sp+F4h] [bp-64h]@37
signed __int32 v88; // [sp+F8h] [bp-60h]@32
char v89; // [sp+FFh] [bp-59h]@29
char *v90; // [sp+100h] [bp-58h]@27
signed __int32 v91; // [sp+104h] [bp-54h]@27
int v92; // [sp+108h] [bp-50h]@27
int v93; // [sp+10Ch] [bp-4Ch]@18
int v94; // [sp+110h] [bp-48h]@18
int v95; // [sp+114h] [bp-44h]@18
int v96; // [sp+118h] [bp-40h]@18
int v97; // [sp+11Ch] [bp-3Ch]@18
size_t v98; // [sp+120h] [bp-38h]@18
int v99; // [sp+124h] [bp-34h]@18
int v100; // [sp+128h] [bp-30h]@13
int v101; // [sp+12Ch] [bp-2Ch]@37
signed __int32 j; // [sp+130h] [bp-28h]@27
int i; // [sp+134h] [bp-24h]@18
int v104; // [sp+138h] [bp-20h]@18
void *dest; // [sp+13Ch] [bp-1Ch]@6
_x86_get_pc_thunk_bx();
v7 = a1 + 2061836;
qmemcpy(&v83, (char *)&unk_2B96C0 + v7 - 3623940, 0x80u);
if ( !src || !n )
{
if ( a5 )
*(_DWORD *)a5 = 3;
return mono_image_open_from_data_full(src, n, a4, a5, a6);
}
dest = src;
if ( a4 )
{
dest = (void *)g_malloc(n);
if ( !dest )
{
if ( a5 )
*(_DWORD *)a5 = 1;
return mono_image_open_from_data_full(src, n, a4, a5, a6);
}
memcpy(dest, src, n);
}
v82 = 0;
needle[0] = 65;
needle[1] = 116;
needle[2] = 115;
needle[3] = 101;
v79 = 2037146221;
v80 = 1750287149;
v81 = 779121249;
LOWORD(v82) = 27748;
BYTE2(v82) = 108;
if ( strstr(haystack, needle) )
{
v100 = getGlobalData();
if ( *(_BYTE *)v100 == 84 || *(_BYTE *)(v100 + 1) == 46 || *(_BYTE *)(v100 + 2) == 77 || *(_BYTE *)(v100 + 3) == 46 )
return mono_image_open_from_data_full(src, n, a4, a5, a6);
v99 = **(int **)((char *)&g_monoRelRotklog_ptr + v7 - 3623940);
v98 = v99 / 5;
v97 = v99 % 5;
v96 = ((signed int)&unk_6E147A - v99) / 4;
v95 = 136;
memcpy((char *)dest + 136, *(char **)((char *)&g_monoRelRotklog_ptr + v7 - 3623940) + 2 * v98 + 4, v98);
memcpy((char *)dest + v95 + v98, *(char **)((char *)&g_monoRelRotklog_ptr + v7 - 3623940) + v98 + 4, v98);
memcpy((char *)dest + 2 * v98 + v95, *(void ***)((char *)&g_monoRelRotklog_ptr + v7 - 3623940) + 1, v98);
memcpy(
(char *)dest + 3 * v98 + v95,
*(char **)((char *)&g_monoRelRotklog_ptr + v7 - 3623940) + 3 * v98 + v96 + 4,
2 * v98 + v97);
v14 = 60;
v15 = -90;
v16 = -72;
v17 = -25;
v18 = -102;
v19 = -52;
v20 = 20;
v21 = -123;
v22 = -105;
v23 = -96;
v24 = -58;
v25 = -72;
v26 = -29;
v27 = 71;
v28 = 94;
v29 = 96;
v30 = -55;
v31 = 42;
v32 = 23;
v33 = 69;
v34 = 40;
v35 = -74;
v36 = -46;
v37 = -112;
v38 = 24;
v39 = 41;
v40 = 60;
v41 = 70;
v42 = 92;
v43 = 106;
v44 = 123;
v45 = -114;
v46 = -99;
v47 = -95;
v48 = -76;
v49 = -60;
v50 = -41;
v51 = -31;
v52 = -24;
v53 = -4;
v54 = -8;
v55 = -103;
v56 = -56;
v57 = -41;
v58 = -24;
v59 = -75;
v60 = -90;
v61 = -3;
v62 = 3;
v63 = -74;
v64 = -105;
v65 = 8;
v66 = 62;
v67 = 106;
v68 = 123;
v69 = -87;
v70 = 75;
v71 = 94;
v72 = 108;
v73 = 125;
v74 = -120;
v75 = -112;
v76 = -109;
v77 = -78;
v104 = 0;
v94 = 64;
v93 = v99 + v95;
for ( i = v95; i < v93; ++i )
{
v8 = *((_BYTE *)dest + i);
v9 = v104++;
*((_BYTE *)dest + i) = v8 ^ *(&v14 + v9);
if ( v104 && !(v104 % v94) )
v104 = 0;
}
}
if ( *(_BYTE *)dest == 73
|| strstr(haystack, &aAssemblyCsharp[v7 - 3623940])
|| strstr(haystack, &aAssemblyCsha_0[v7 - 3623940]) )
{
*(_BYTE *)dest = 77;
v92 = 136;
v91 = (n - 136 + 3) >> 2;
v90 = (char *)dest + 136;
for ( j = 0; j < v91; ++j )
{
if ( j & 1 )
{
v89 = v90[j];
v90[j] = *(&v90[j] + v91);
*(&v90[j] + v91) = v89;
}
}
v88 = n - v92 + -3 * v91;
for ( j = 0; j < v88; ++j )
{
if ( !(j & 1) )
{
v89 = *(&v90[2 * v91] + j);
*(&v90[2 * v91] + j) = *(&v90[3 * v91] + j);
*(&v90[3 * v91] + j) = v89;
}
}
v87 = n - v92;
v101 = 0;
v86 = 128;
for ( j = 0; j < v87; ++j )
{
v10 = v90[j];
v11 = v101++;
v90[j] = v10 ^ *(&v83 + v11);
if ( !(v101 % v86) )
v101 = 0;
}
}
v85 = g_malloc0(872);
*(_DWORD *)(v85 + 8) = dest;
*(_DWORD *)(v85 + 12) = n;
*(_BYTE *)(v85 + 16) = 2 * (a4 & 1) | *(_BYTE *)(v85 + 16) & 0xFD;
if ( haystack )
v12 = g_strdup(haystack);
else
v12 = g_strdup_printf(&aDataP[v7 - 3623940], (char)dest);
*(_DWORD *)(v85 + 20) = v12;
v84 = g_malloc0(396);
*(_DWORD *)(v85 + 44) = v84;
*(_BYTE *)(v85 + 16) = 8 * (a6 & 1) | *(_BYTE *)(v85 + 16) & 0xF7;
*(_DWORD *)v85 = 1;
v85 = do_mono_image_load(v85, a5, 1, 1);
if ( v85 )
register_image(v85);
return mono_image_open_from_data_full(src, n, a4, a5, a6);
}
I don't have the time to work with the decrypted function at hte moment, have some real life stuff to attend to but this should be a good start for you to get the Assembly-CSharp.dll decoded now as what you need is here.
This function is replacing stolen data from the dll back into it starting from the IMAGE_NT_HEADERS Timestamp location. Which starts at file offset 0x88. It replaces the rest of the IMAGE_NT_HEADERS then the sections from data that is within the libmono.so file. If you fix the libmono.so file you can follow what its doing fairly easily to get it back to where it should be. When I get home later I can look into it more if someone else, or you, haven't done so already. But at this point its pretty straigt forward and easy to finish.