Some days ago I was dealing with an Android malware, it hides strings and other things inside an arsc file. The resource item is identified by an hex value. The value contains three informations: Package id, Type id and Entry index. The problem is: can I easily recognize the resource item during a static analysis of the malware?
I didn’t search throught the net, I simply decided to write a 010 Editor template file able to parse .arsc files. I did test it over two files only (I don’t have more samples right now…), so it’s not perfect and it surely needs some more adjustments. It works fine on the samples btw, so I decided to put it online anyway. Feel free to add/remove/adjust/modify/etcetc…
//-------------------------------------- // File: ARSCTemplate.bt // Author: ZaiRoN (@_zairon_ www.zairon.wordpress.com) // File mask: *.arsc // Purpose: to parse Android arsc resource files #define RES_STRING_POOL_TYPE 0x0001 #define RES_TABLE_TYPE 0x0002 #define RES_TABLE_PACKAGE_TYPE 0x0200 #define RES_TABLE_TYPE_TYPE 0x0201 #define RES_TABLE_TYPE_SPEC_TYPE 0x0202 typedef struct { ushort type; // Type identifier of the chunk ushort headerSize; // Size of the chunk header uint size; // Size of the entire chunk } RESCHUNK_HEADER; typedef struct { RESCHUNK_HEADER header; uint packageCount; // Number of ResTable structures } RESTABLE_HEADER; typedef struct (int _len) { char _string[_len]; } stringPool; typedef struct (short _len) { wchar_t _string[ReadWStringLength(FTell(), 2*_len)]; } w_stringPool; typedef struct { local int offsetStruct = FTell(); RESCHUNK_HEADER header; uint stringCount; // Number of string uint indices uint styleCount; // Number of span uint indices uint flags; uint stringsStart; // Index of the string data uint stylesStart; // Index of the style data if (resStringPool_header.stringCount > 0) { local int utf8_flag = 0; if (((flags & 0x100) >> 8) == 1) utf8_flag = 1; // UTF8_FLAG set uint stringOffset[resStringPool_header.stringCount]; /* Read the strings */ local int offsetStart = resStringPool_header.stringsStart + offsetStruct; local int startStringOffset; local int endStringOffset; local int i; for(i = 0; i < resStringPool_header.stringCount;i++) { startStringOffset = offsetStart + stringOffset[i]; if (i == (resStringPool_header.stringCount-1)) endStringOffset = offsetStruct + resStringPool_header.header.size; else endStringOffset = offsetStart + stringOffset[i+1]; if (utf8_flag) stringPool currentString(endStringOffset - startStringOffset); else w_stringPool currentString(endStringOffset - startStringOffset); } /* Skip padding bytes */ while((FTell() % 4) != 0) FSkip(1); } } RESSTRINGPOOL_HEADER; typedef struct { uint32 size; // Number of bytes in this structure uint16 mcc; uint16 mnc; char language[2]; char country[2]; ubyte orientation; ubyte touchscreen; uint16 density; ubyte keyboard; ubyte navigation; ubyte inputFlags; ubyte inputPad0; uint16 screenWidth; uint16 screenHeight; uint16 sdkVersion; uint16 minorVersion; ubyte screenLayout; ubyte uiMode; uint16 smallestScreenWidthDp; uint16 screenWidthDp; uint16 screenHeightDp; }RESTABLE_CONFIG; typedef struct { RESCHUNK_HEADER header; ubyte id; // The type identifier, 0 is invalid ubyte res0; // 0 uint16 res1; // 0 uint32 entryCount; // Number of uint32_t entry configuration masks if (resTable_TypeSpec.entryCount > 0) uint entryFlag[resTable_TypeSpec.entryCount]; }RESTABLE_TYPESPEC; typedef struct { uint16 size; // Number of bytes in this structure uint16 flags; uint32 key; }RESTABLE_ENTRY; typedef struct (int len) { uint16 size; // Number of bytes in this structure ubyte res0; // 0 ubyte dataType; // Type of the data value ubyte data[len]; // The data }RES_VALUE; typedef struct { local int offsetStruct = FTell(); RESCHUNK_HEADER header; ubyte id; ubyte res0; uint16 res1; uint32 entryCount; // Number of uint32_t entry indices that follow uint32 entriesStart; // Offset from header where ResTable_entry data starts RESTABLE_CONFIG resTable_config; uint entryOffset[resTable_Type.entryCount]; local uint startRes = FTell(); local uint endOffset; local uint32 j; local uint32 i = 0; while (i < resTable_Type.entryCount) { if (entryOffset[i] != -1) { RESTABLE_ENTRY restableEntry; j = i+1; if (i == resTable_Type.entryCount - 1) endOffset = offsetStruct + resTable_Type.header.size; else { while ((j < resTable_Type.entryCount) && (entryOffset[j] == -1)) j++; if (j == resTable_Type.entryCount) endOffset = offsetStruct + resTable_Type.header.size; else endOffset = startRes + entryOffset[j]; } RES_VALUE resValue(endOffset - (startRes + entryOffset[i]) - 12 ); } i++; } }RESTABLE_TYPE; typedef struct { RESCHUNK_HEADER header; uint32 id; // Package ID wchar_t name[128]; // Package name uint typeStrings; // Offset to a ResStringPool_header defining the resource type symbol table uint lastPublicType; // Last index into typeStrings uint keyStrings; // Offset to a ResStringPool_header defining the resource key symbol table uint lastPublicKey; // Last index into keyStrings }RESTABLE_PACKAGE; /* ----------------------------------------------------------------------------- */ LittleEndian(); local ushort type; while (!FEof()) { type = ReadShort(FTell()); if (type == RES_TABLE_TYPE) RESTABLE_HEADER resTable_header; else if (type == RES_STRING_POOL_TYPE) RESSTRINGPOOL_HEADER resStringPool_header; else if (type == RES_TABLE_PACKAGE_TYPE) RESTABLE_PACKAGE resTable_package; else if (type == RES_TABLE_TYPE_SPEC_TYPE) RESTABLE_TYPESPEC resTable_TypeSpec; else if (type == RES_TABLE_TYPE_TYPE) RESTABLE_TYPE resTable_Type; else return -1; }
Reference:
http://justanapplication.wordpress.com/category/android/android-resources/
OAT AXML ARSC Template for 010Editor
https://github.com/tomken/010_template_for_android