<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    Il2Cpp恢復符號過程分析

    VSole2022-06-28 16:49:39

    Il2Cpp介紹

    unity作為兩大游戲引擎之一,其安全性也值得被關注。在早期unity的腳本都是采用C#編寫的,直接編譯成C#模塊,所以直接使用C#反編譯器即可非常完整的獲得游戲的源碼,此時的Unity腳本后處理引擎為Mono。

    而由于C#的效率問題和安全性問題,Unity推出了新的腳本后處理引擎Il2Cpp,該引擎分為兩個部分,一個是AOT靜態編譯引擎,一個是libil2cpp運行時庫。

    前者通過將C# IL編譯成C++代碼,從而交由不同平臺編譯器編譯,后者則實現了諸如垃圾回收、線程/文件獲取、內部調用直接修改托管數據結構的原生代碼的服務與抽象。

    Il2Cpp編譯的游戲,往往有兩個重要的文件,一個是GameAssembly.dll,該文件是由C++代碼編譯而成的程序,游戲重要的邏輯都在該文件內,其中包含了il2cpp的運行時庫。另一個文件是global-metadata.dat,該文件保存了一些重要的字符串信息和一些元數據,用于il2cpp的動態特性,例如反射等。

    這里著重分析如果通過解析GameAssembly.dll和global-metadata.dat來恢復其中包含的類名,方法名,field偏移。

    解析global-metadata.dat

    首先得弄明白該文件保存了哪些內容,可以找到libil2cpp的源碼。直接搜索global-metadata.dat即可找到相關代碼。該函數位于vm/GlobalMetadata.cpp下。

    可以看到代碼直接將文件內容讀入后,轉化成了一個結構體,Il2CppGlobalMetadataHeader。該結構體定義在了GlobalMetadataFileInternals.h里,其中包含了一些重要的信息。

    1)獲取所有Image

    首先得知道其中包含的各個Image信息。直接定位到結構體中的這兩個域,對應著Il2CppImageDefinition數組的偏移,該結構保存了Image的信息。imagesSize記錄了Il2CppImageDefinition數組的大小。

    可以通過如下方法獲得Il2CppImageDefinition數組,并且進行遍歷。

    Il2CppGlobalMetadataHeader *header=(Il2CppGlobalMetadataHeader*)ptr;  if(header->sanity!=0xFAB11BAF || header->stringLiteralOffset!=sizeof(Il2CppGlobalMetadataHeader))  {      printf("invalid file..");      return 0;  }  int image_count=header->imagesSize/sizeof(Il2CppImageDefinition);   for(int i=0;i  {      const Il2CppImageDefinition *image=&image_arr[i];  }
    

    接著來查看Il2CppImageDefinition結構體,可以發現里面包含了Image的名字相關信息StringIndex nameIndex。

    StringIndex是一個整型,是一個索引,global-metadata.dat中存在一個字符串表,所有metadata相關的字符串都放在了一起,通過這個索引進行引用,這個字符串表通過Il2CppGlobalMetadataHeader下的偏移stringOffset計算得到。

    可以通過這樣的函數獲取StringIndex對應的字符串,ptr是Il2CppGlobalMetadataHeader的地址。

    static const char* GetStringFromIndex(StringIndex index){  return (const char*)(((Il2CppGlobalMetadataHeader*)ptr)->stringOffset+ptr+index);}
    

    2)獲取Image下的類

    如何獲取該image所有的類呢,這就需要獲取對應的Il2CppTypeDefinition,在Il2CppGlobalMetadataHeader結構體下存在typeDefinitionsOffset,而由于Il2CppImageDefinition存在一個TypeDefinitionIndex typeStart的域,所以可以類比StringIndex。

    類比StringIndex寫出如下代碼。

    static const Il2CppTypeDefinition* GetTypeDefinitionFromIndex(TypeDefinitionIndex index){  return (const Il2CppTypeDefinition*)(ptr+((Il2CppGlobalMetadataHeader*)ptr)->typeDefinitionsOffset)+index;}
    

    然后便可以獲得Image下的所有Il2CppTypeDefinition了。也就是獲取所有類的元數據。

    const Il2CppImageDefinition *image=&image_arr[i];  printf("image: %s",GetStringFromIndex(image->nameIndex));  for(int j=0;jtypeCount;j++)  {      const Il2CppTypeDefinition *type=GetTypeDefinitionFromIndex(image->typeStart+j);      printf("class: %s:%s",GetStringFromIndex(type->namespaceIndex),GetStringFromIndex(type->nameIndex));  }
    

    這樣就能解析出了各種Image下的類。

    3)獲取類下的方法名和Field

    此時需要我們進一步的分析類下面的方法,此時查看Il2CppTypeDefinition結構體可以發現其下有兩個域值得注意。

    同樣的在Il2CppGlobalMetadataHeader由兩個域正好對應的上。

    類比上文的方法,可以寫出如下代碼,來獲取類對應的方法和field的信息。

    static const Il2CppMethodDefinition* GetMethodDefinitionFromIndex(MethodIndex index){  return (const Il2CppMethodDefinition*)(((Il2CppGlobalMetadataHeader*)ptr)->methodsOffset+ptr)+index;}static const Il2CppFieldDefinition* GetFieldDefinitionFromIndex(FieldIndex index){  return (const Il2CppFieldDefinition*)(ptr+((Il2CppGlobalMetadataHeader*)ptr)->fieldsOffset)+index;}
    

    同樣的,獲得的結構體中都保存了名字。

    效果如下:

    恢復方法符號

    由于此時僅僅獲得了方法的名稱,還無法和Gameassembly.dll中的函數對應起來,得繼續查看il2cpp的源碼。

    從il2cpp的api入手,其中有個函數名稱為il2cpp_runtime_invoke。

    點進其中所調用的函數,可以發現有一個InvokeWithThrow,不難猜到這應該就是調用method的函數。

    繼續分析InvokeWithThrow,可以發現里面調用了method->invoker_method,并且其第一個參數method->methodPointer就是方法的指針。

    繼續搜索對method->methodPointer的修改,在Class.cpp文件中的Class::SetupMethodsLocked(Il2CppClass *klass, const il2cpp::os::FastAutoLock& lock)方法下成功找到了賦值語句。該函數的作用即通過metadata構造類的所有MethodInfo,而MethodInfo對象則包含了方法函數指針。

    可以看到MetadataCache::GetMethodPointer通過image對象找到了codeGenModule,再定位到了其下的methodPointers數組,再根據token取出對應的函數指針。

    剛好一個方法的token在Il2CppMethodDefinition中存在,就差怎么定位codeGenModule->methodPointers了。

    在源代碼中搜索codeGenModule的類型Il2CppCodeGenModule。

    得到了一個結構體Il2CppCodeRegistration。

    然后在libil2cpp中就沒法找到相關的定義了,可能是由il2cpp aot編譯器生成的,然后注入到il2cpp runtime里去,所以去看看GameAssembly.dll看看能否定位到Il2CppCodeRegistration這個結構體。

    由于Il2CppCodeRegistration中的codeGenModules的Il2CppCodeGenModule最大的特征就是moduleName,試試字符串搜索。

    合理猜測這個字符串就是Il2CppCodeGenModule的moduleName,交叉引用,猜測這個就是Il2CppCodeGenModule。

    繼續交叉引用,不出所料應該就得到了一個數組,這個數組應該就是Il2CppCodeRegistration->codeGenModules指向的數組。

    再次交叉引用找到Il2CppCodeRegistration變量。

    所以只需要定位到Il2CppCodeRegistration codeRegistration這個全局變量即可,然后遍歷codeGenModules找到方法對應的Image,然后通過方法的token(Il2CppMethodDefinition下有)去codeGenModules->methodPointers取出函數指針,即可將方法和函數指針對應上。

    具體代碼如下:

    uint32_t GetMethodPointer(const Il2CppImageDefinition *image,uint32_t token){  for(int i=0;icodeGenModulesCount;i++)  {      const Il2CppCodeGenModule *module=CodeRegistration->codeGenModules[i];      if(!strcmp(module->moduleName,GetStringFromIndex(image->nameIndex)))      {          return module->methodPointers[GetTokenRowId(token)-1];      }  }  printf("invalid!");  return 0;}
    

    獲得field的偏移地址

    同樣的從il2cpp的api出發,可以發現該函數直接調用了Class::GetFieldFromName,直接去查看該函數。

    找到Class::GetFieldFromName,它通過GetFields獲得所有的FieldInfo,然后返回對應的FieldInfo。

    看看GetFields函數,其中調用了SetupFieldsLocked。

    繼續找到SetupFieldsLocked,該函數遞歸初始化了類和父類的所有FieldInfo,使用SetupFieldsFromDefinitionLocked進行初始化。

    找到SetupFieldsFromDefinitionLocked,在這個函數中就能找到具體的初始化過程了,可以看到其offset是通過MetadataCache::GetFieldOffsetFromIndexLocked計算而出的。

    繼續找到MetadataCache::GetFieldOffsetFromIndexLocked,該函數進一步調用了 GlobalMetadata::GetFieldOffset。

    最后一步,GlobalMetadata::GetFieldOffset。該函數通過typeIndex和fieldIndexInType進行查表。typeIndex可以通過查當前type的Il2CppTypeDefinition在global-metadata.dat的序號獲得,而fieldIndexInType在遍歷type的field可以直接獲得。

    獲取typeIndex代碼如下:

    static TypeDefinitionIndex GetIndexForTypeDefinitionInternal(const Il2CppTypeDefinition* typeDefinition){  const Il2CppTypeDefinition* typeDefinitions=(const Il2CppTypeDefinition*)(ptr+((Il2CppGlobalMetadataHeader*)ptr)->typeDefinitionsOffset);  ptrdiff_t index=typeDefinition-typeDefinitions;  return (TypeDefinitionIndex)index;}
    

    索引信息都能獲取了,現在需要找到的是s_Il2CppMetadataRegistration->fieldOffsets,也就是要定位到s_Il2CppMetadataRegistration。該變量的類型為Il2CppMetadataRegistration。

    該變量存在于GameAssemly.dll,也是由il2cpp aot編譯生成注冊到il2cpp runtime的,而剛好之前發現codeRegistration注冊時一并注冊了metadataRegistration。

    于是我們根據上文找到的codeRegistration進行交叉引用,成功定位到metadataRegistration。

    所以只需要根據這個變量直接用typeIndex和fieldIndexInType查表即可。

    uint32_t GetFieldOffset(TypeDefinitionIndex typeIndex,uint32_t index){  return MetadataRegistration->fieldOffsets[typeIndex][index];   }
    
    符號函數符號計算
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    iddm帶你讀論文——SymQEMU:Compilation-based symbolic execution for binaries 本篇文章收錄于2021年網絡安全頂會NDSS,介紹了最新的符號執行技術,并且清晰地比較了當前流行的各種符號執行的引擎的優劣勢,可以比較系統的了解符號執行技術的相關知識
    得益于Unicorn的強大的指令trace能力,可以很容易實現對cpu執行的每一條匯編指令的跟蹤,進而對ollvm保護的函數進行剪枝,去掉虛假塊,大大提高逆向分析效率。
    Flutter APP逆向實踐
    2022-07-21 16:20:09
    分析flutter的時候,沒有交叉引用,那真是想它他拖進回收站。最近又遇到了幾個flutter開發的app,對它進行了一些研究,總結了以下的分析流程。本文以iOS app為例子作為講解,Android 的flutter app和iOS 的flutter app分析方法類似。
    Kernel從0開始
    2021-12-10 13:42:20
    網上一大堆教編譯內核的,但很多教程看得特別迷糊。第一次編譯內核時,沒設置好參數,直接把虛擬機編譯炸開了。所以就想著能不能先做個一鍵獲取內核源碼和相關vmlinux以及bzImage的腳本,先試試題,后期再深入探究編譯內核,加入debug符號,所以就有了這個一鍵腳本。
    1、so地址頁對其&第一個PT_LOAD和第二個PT_LOAD之間有多余占位數據2、實現libart.so中的inlinehook3、hook dex加載函數更改只讀權限
    今年初,Akamai 的研究人員發現了一個新型惡意僵尸網絡,它以 Realtek SDK、華為路由器和 Hadoop YARN 服務器為目標,將設備引入到 DDoS 群中,有可能進行大規模攻擊。這個新型僵尸網絡是研究人員在自己的 HTTP 和 SSH 蜜罐上發現的,該僵尸網絡利用了 CVE-2014-8361 和 CVE-2017-17215 等漏洞。
    Android 應用gl,使用了加固i,老版本的應用gl是js源碼,新版本更新后,剛開始以為是加密了源碼,分析后才知道是使用了Hermes優化引擎。Hermes優化的優化效果如下:優化前:優化后:二、前期準備2.1 繞過root檢測應用gl使用了加固i,在被root 的手機上運行,閃退。繞過方法也很簡單,在magisk的設置中,開啟遵守排除列表,然后在配置排除列表中選擇應用gl。
    前言最近一段時間在研究Android加殼和脫殼技術,其中涉及到了一些hook技術,于是將自己學習的一些hook技術進行了一下梳理,以便后面回顧和大家學習。主要是進行文本替換、宏展開、刪除注釋這類簡單工作。所以動態鏈接是將鏈接過程推遲到了運行時才進行。
    文中使用的示例代碼可以從 這里 獲取。的功能是在終端打印出hello這6個字符(包括結尾的?編譯它們分別生成libtest.so和?存在嚴重的內存泄露問題,每調用一次say_hello函數,就會泄露1024字節的內存。
    DLL劫持的防御策略
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类