Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # 在不运行游戏的情况下用python调用c语言的方式调用unity il2cpp游戏函数的尝试
- # 如果调用的函数内部用了unity提供的函数,由于unity的函数都是重定向回unityplayer.dll,必须加载unitymain进行注入,其实这种情况已经和运行游戏没什么区别
- # 没有用到的话静态函数可以il2cpp的api直接调用,其他理论可行但未测试
- # 如果你不是闲的找抽,使用任何一种hook框架或者直接写c++在运行时注入调用或者自己重新实现想要的方法都会简单方便许多
- # https://www.perfare.net/archives/1741
- # https://github.com/BepInEx/BepInEx
- # https://github.com/vfsfitvnm/frida-il2cpp-bridge
- # https://github.com/in1nit1t/uniref
- # 等等......
- import time
- import ctypes
- import os
- import threading
- import faulthandler
- from cdef.il2cpp_class_internal import *
- from cdef.il2cpp_object_internal import *
- from cdef.il2cpp_api import register_function
- 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
- from loguru import logger
- faulthandler.enable()
- def castToAny(obj):
- return cast(obj, c_void_p)
- # init
- unityPlayer = ctypes.CDLL(os.path.abspath(r'UnityPlayer.dll'))
- game_assembly = ctypes.CDLL(os.path.abspath(r'GameAssembly.dll'))
- register_function(game_assembly)
- game_assembly.il2cpp_set_data_dir(br"GC_Data\il2cpp_data")
- game_assembly.il2cpp_init(b"IL2CPP Root Domain")
- domain = Il2CppDomain()
- size = c_size_t()
- assemblies = game_assembly.il2cpp_domain_get_assemblies(domain, byref(size))
- for i in range(size.value):
- assembly = assemblies[i]
- aname = cast(assembly.contents.aname.name, c_char_p).value.decode()
- match aname:
- case "Assembly-CSharp":
- GameCore = game_assembly.il2cpp_assembly_get_image(assembly)
- logger.info("loaded Assembly-CSharp")
- case "UnityEngine.CoreModule":
- UnityCore = game_assembly.il2cpp_assembly_get_image(assembly)
- logger.info("loaded UnityEngine.CoreModule")
- # csharp class define
- mscorlib = game_assembly.il2cpp_get_corlib()
- systemByte = game_assembly.il2cpp_class_from_name(mscorlib, b"System", b"Byte")
- MovieCrypt = game_assembly.il2cpp_class_from_name(GameCore, b'DMM.OLG.Unity.Engine', b'MovieCrypt')
- TryDecrypt = game_assembly.il2cpp_class_get_method_from_name(MovieCrypt, b"TryDecrypt", 4)
- def getIl2cppArray(length, data: bytes = None):
- array = game_assembly.il2cpp_array_new(systemByte, c_ulonglong(length))
- if data:
- vector_ptr = ctypes.addressof(array.contents) + ctypes.sizeof(Il2CppArray)
- ctypes.memmove(vector_ptr, data, length)
- return array
- def unpackIl2cppArray(array):
- vector_ptr = ctypes.addressof(array.contents) + ctypes.sizeof(Il2CppArray)
- length = array.contents.max_length
- data = (c_ubyte * length).from_address(vector_ptr)
- return bytes(data)
- def getIl2cppString(string):
- return game_assembly.il2cpp_string_new(string)
- def unpackIl2cppString(string):
- length = string.contents.length
- s = string.contents.chars
- chars = cast(s, POINTER((c_int16 * length)))
- s = bytes(chars.contents)
- return s.decode("utf-16")
- def getExceptionMessage(exception):
- errorMessage = exception.contents.message
- return unpackIl2cppString(errorMessage)
- def call_unity():
- '''
- int UnityMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPWSTR /*lpCmdLine*/, int /*nShowCmd*/)
- Unity的函数在UnityPlayer.dll中,通过il2cpp的add_internal_call注入,resolve_icall调用
- 如果调用的函数需要用到Unity提供的函数或需要调用Unity函数,则必须要让unityplayer初始化并注入
- 又或者是想让游戏自动初始化其他内容(大致相当于运行时hook)
- 否则无需调用此函数
- 需要nuitka编译成与游戏同名的exe并覆盖(注意备份)
- '''
- unityPlayer.UnityMain.argtypes = [wintypes.HINSTANCE, wintypes.HINSTANCE, wintypes.LPWSTR, ctypes.c_int]
- unityPlayer.UnityMain.restype = ctypes.c_int
- hInstance = None
- hPrevInstance = None
- lpCmdLine = ctypes.create_unicode_buffer('-batchmode -nographics')
- nShowCmd = 1
- game_assembly.il2cpp_thread_attach(domain)
- unityPlayer.UnityMain(hInstance, hPrevInstance, lpCmdLine, nShowCmd)
- def decryptData(assetname: bytes, data: bytes):
- dataLength = len(data)
- cassetname = castToAny(getIl2cppString(assetname))
- defBuff = castToAny(getIl2cppArray(dataLength, data))
- outbyte = POINTER(Il2CppArray)()
- coutByte = pointer(outbyte)
- coutByte = cast(coutByte, c_void_p)
- offseto = c_int32(0)
- offset = castToAny(pointer(offseto))
- exception = POINTER(Il2CppException)()
- # public static bool TryDecrypt(string assetName, byte[] data, out byte[] result, out int offset)
- params = (c_void_p * 4)(cassetname, defBuff, coutByte, offset)
- result = game_assembly.il2cpp_runtime_invoke(TryDecrypt, c_void_p(0), cast(params, POINTER(c_void_p)), byref(exception))
- if exception:
- logger.info(f"decryptData {assetname.decode()} exception:{getExceptionMessage(exception)} ")
- else:
- logger.info(f"decryptData {assetname.decode()} result:{cast(result,POINTER(c_bool)).contents}")
- return unpackIl2cppArray(outbyte)
- def decryptFile(assetname: bytes, src: str, dst: str):
- with open(src, 'rb') as f:
- data = decryptData(assetname, f.read())
- assert data[12:16] == b'CRID'
- with open(dst, 'wb') as f:
- f.write(data[12:])
- if __name__ == '__main__':
- thread = threading.Thread(target=call_unity)
- thread.start() # 把unityplayer丢到子线程执行
- time.sleep(5) # 确保unityplayer执行完初始化操作,否则unity的random可能无法initstate
- decryptFile(b'emm_40112023_10.usm', 'emm_40112023_10.usm', 'emm_40112023_10_dec.usm') # 示例操作
Advertisement
Add Comment
Please, Sign In to add comment