Guest User

GC.py

a guest
Jun 23rd, 2024
1,008
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 5.88 KB | None | 0 0
  1. # 在不运行游戏的情况下用python调用c语言的方式调用unity il2cpp游戏函数的尝试
  2. # 如果调用的函数内部用了unity提供的函数,由于unity的函数都是重定向回unityplayer.dll,必须加载unitymain进行注入,其实这种情况已经和运行游戏没什么区别
  3. # 没有用到的话静态函数可以il2cpp的api直接调用,其他理论可行但未测试
  4. # 如果你不是闲的找抽,使用任何一种hook框架或者直接写c++在运行时注入调用或者自己重新实现想要的方法都会简单方便许多
  5. # https://www.perfare.net/archives/1741
  6. # https://github.com/BepInEx/BepInEx
  7. # https://github.com/vfsfitvnm/frida-il2cpp-bridge
  8. # https://github.com/in1nit1t/uniref
  9. # 等等......
  10. import time
  11. import ctypes
  12. import os
  13. import threading
  14. import faulthandler
  15. from cdef.il2cpp_class_internal import *
  16. from cdef.il2cpp_object_internal import *
  17. from cdef.il2cpp_api import register_function
  18. from ctypes import POINTER, c_bool, c_int16, c_int32, c_size_t, c_void_p, c_char_p, cast, byref, c_ulonglong, pointer, c_ubyte, wintypes
  19. from loguru import logger
  20.  
  21. faulthandler.enable()
  22.  
  23.  
  24. def castToAny(obj):
  25.     return cast(obj, c_void_p)
  26.  
  27.  
  28. # init
  29. unityPlayer = ctypes.CDLL(os.path.abspath(r'UnityPlayer.dll'))
  30. game_assembly = ctypes.CDLL(os.path.abspath(r'GameAssembly.dll'))
  31.  
  32. register_function(game_assembly)
  33.  
  34. game_assembly.il2cpp_set_data_dir(br"GC_Data\il2cpp_data")
  35. game_assembly.il2cpp_init(b"IL2CPP Root Domain")
  36.  
  37. domain = Il2CppDomain()
  38. size = c_size_t()
  39. assemblies = game_assembly.il2cpp_domain_get_assemblies(domain, byref(size))
  40.  
  41. for i in range(size.value):
  42.     assembly = assemblies[i]
  43.     aname = cast(assembly.contents.aname.name, c_char_p).value.decode()
  44.     match aname:
  45.         case "Assembly-CSharp":
  46.             GameCore = game_assembly.il2cpp_assembly_get_image(assembly)
  47.             logger.info("loaded Assembly-CSharp")
  48.         case "UnityEngine.CoreModule":
  49.             UnityCore = game_assembly.il2cpp_assembly_get_image(assembly)
  50.             logger.info("loaded UnityEngine.CoreModule")
  51.  
  52.  
  53. # csharp class define
  54. mscorlib = game_assembly.il2cpp_get_corlib()
  55. systemByte = game_assembly.il2cpp_class_from_name(mscorlib, b"System", b"Byte")
  56. MovieCrypt = game_assembly.il2cpp_class_from_name(GameCore, b'DMM.OLG.Unity.Engine', b'MovieCrypt')
  57.  
  58. TryDecrypt = game_assembly.il2cpp_class_get_method_from_name(MovieCrypt, b"TryDecrypt", 4)
  59.  
  60.  
  61. def getIl2cppArray(length, data: bytes = None):
  62.     array = game_assembly.il2cpp_array_new(systemByte, c_ulonglong(length))
  63.     if data:
  64.         vector_ptr = ctypes.addressof(array.contents) + ctypes.sizeof(Il2CppArray)
  65.         ctypes.memmove(vector_ptr, data, length)
  66.     return array
  67.  
  68.  
  69. def unpackIl2cppArray(array):
  70.     vector_ptr = ctypes.addressof(array.contents) + ctypes.sizeof(Il2CppArray)
  71.     length = array.contents.max_length
  72.     data = (c_ubyte * length).from_address(vector_ptr)
  73.     return bytes(data)
  74.  
  75.  
  76. def getIl2cppString(string):
  77.     return game_assembly.il2cpp_string_new(string)
  78.  
  79.  
  80. def unpackIl2cppString(string):
  81.     length = string.contents.length
  82.     s = string.contents.chars
  83.     chars = cast(s, POINTER((c_int16 * length)))
  84.     s = bytes(chars.contents)
  85.     return s.decode("utf-16")
  86.  
  87.  
  88. def getExceptionMessage(exception):
  89.     errorMessage = exception.contents.message
  90.     return unpackIl2cppString(errorMessage)
  91.  
  92.  
  93. def call_unity():
  94.     '''
  95.    int UnityMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPWSTR /*lpCmdLine*/, int /*nShowCmd*/)
  96.    Unity的函数在UnityPlayer.dll中,通过il2cpp的add_internal_call注入,resolve_icall调用
  97.    如果调用的函数需要用到Unity提供的函数或需要调用Unity函数,则必须要让unityplayer初始化并注入
  98.    又或者是想让游戏自动初始化其他内容(大致相当于运行时hook)
  99.    否则无需调用此函数
  100.    需要nuitka编译成与游戏同名的exe并覆盖(注意备份)
  101.    '''
  102.     unityPlayer.UnityMain.argtypes = [wintypes.HINSTANCE, wintypes.HINSTANCE, wintypes.LPWSTR, ctypes.c_int]
  103.     unityPlayer.UnityMain.restype = ctypes.c_int
  104.     hInstance = None
  105.     hPrevInstance = None
  106.     lpCmdLine = ctypes.create_unicode_buffer('-batchmode -nographics')
  107.     nShowCmd = 1
  108.     game_assembly.il2cpp_thread_attach(domain)
  109.     unityPlayer.UnityMain(hInstance, hPrevInstance, lpCmdLine, nShowCmd)
  110.  
  111.  
  112. def decryptData(assetname: bytes, data: bytes):
  113.     dataLength = len(data)
  114.     cassetname = castToAny(getIl2cppString(assetname))
  115.     defBuff = castToAny(getIl2cppArray(dataLength, data))
  116.     outbyte = POINTER(Il2CppArray)()
  117.     coutByte = pointer(outbyte)
  118.     coutByte = cast(coutByte, c_void_p)
  119.     offseto = c_int32(0)
  120.     offset = castToAny(pointer(offseto))
  121.     exception = POINTER(Il2CppException)()
  122.     # public static bool TryDecrypt(string assetName, byte[] data, out byte[] result, out int offset)
  123.     params = (c_void_p * 4)(cassetname, defBuff, coutByte, offset)
  124.  
  125.     result = game_assembly.il2cpp_runtime_invoke(TryDecrypt, c_void_p(0), cast(params, POINTER(c_void_p)), byref(exception))
  126.     if exception:
  127.         logger.info(f"decryptData {assetname.decode()} exception:{getExceptionMessage(exception)} ")
  128.     else:
  129.         logger.info(f"decryptData {assetname.decode()} result:{cast(result,POINTER(c_bool)).contents}")
  130.     return unpackIl2cppArray(outbyte)
  131.  
  132.  
  133. def decryptFile(assetname: bytes, src: str, dst: str):
  134.     with open(src, 'rb') as f:
  135.         data = decryptData(assetname, f.read())
  136.     assert data[12:16] == b'CRID'
  137.     with open(dst, 'wb') as f:
  138.         f.write(data[12:])
  139.  
  140.  
  141. if __name__ == '__main__':
  142.     thread = threading.Thread(target=call_unity)
  143.     thread.start()  # 把unityplayer丢到子线程执行
  144.     time.sleep(5)  # 确保unityplayer执行完初始化操作,否则unity的random可能无法initstate
  145.     decryptFile(b'emm_40112023_10.usm', 'emm_40112023_10.usm', 'emm_40112023_10_dec.usm')  # 示例操作
  146.  
Advertisement
Add Comment
Please, Sign In to add comment