【手游】魔灵幻想 美术资源加密分析

这个游戏是用Unity3D引擎开发的,加密方式是先对AssetBundle加密,然后在libmono.so中对Assembly-CSharp.dll进行加密


0x00 在ida中先找到加密dll的地方


从上面的伪代码可以看出只是进行了简单的异或处理,在看看反汇编代码


这个int EncryptNum[61]里面存放的就是加解密的key



0x01 分析完可以先对Assembly-CSharp.dll进行解密了

//Assembly-CSharp.dll解密算法
private byte[] DecryptDll (byte[] bytes)
{
    byte[] encryptNum = new byte[] { 
                        0xA8, 0xBB, 0xCD, 0xDF, 0xE2, 0xF4, 0x86, 0x98, 0xB7, 0xCA, 0xDC, 0xEE,
                        0xF1, 0x83, 0x95, 0xA7, 0xC6, 0xD9, 0xEB, 0xFD, 0x8F, 0x92, 0xA4, 0xB6,
                        0xD5, 0xE8, 0xFA, 0x8C, 0x9E, 0xA1, 0xB3, 0xC5, 0xE4, 0xF7, 0x89, 0x9B,
                        0xAD, 0xBF, 0xC2, 0xD4, 0xF3, 0x86, 0x98, 0xAA, 0xBC, 0xCE, 0xD1, 0xE3,
                        0x82, 0x95, 0xA7, 0xB9, 0xCB, 0xDD, 0xEF, 0xF2, 0x91, 0xA4, 0xB6, 0xC8, 0xDA};

    for (int i = 0; i < bytes.Length; i++)
    {
        bytes[i] ^= encryptNum[i % encryptNum.Length];
    }

    return bytes;
}

0x02 用.NET Reflector反编译Assembly-CSharp.dll找到对AssetBundle加密的地方(搜索WWW和AssetBundle等相关API去定位)


加密方式和对dll一样也是异或加密,密钥在GameConfig.EncryptNum里



0x03 对AssetBundle进行解密

//AssetBundle解密算法
private byte[] DecryptAssetBundle(byte[] array)
{
    byte[] encryptNum = new byte[] { 
                        0xA8, 0xBB, 0xCD, 0xDF, 0xE2, 0xF4, 0x86, 0x98, 0xB7, 0xCA, 220, 0xEE, 0xF1, 0x83, 0x95, 0xA7, 
                        0xC6, 0xD9, 0xEB, 0xFD, 0x8F, 0x92, 0xA4, 0xB6, 0xD5, 0xE8, 250, 140, 0x9E, 0xA1, 0xB3, 0xC5, 
                        0xE4, 0xF7, 0x89, 0x9B, 0xAD, 0xBF, 0xC2, 0xD4, 0xF3, 0x86, 0x98, 170, 0xBC, 0xCE, 0xD1, 0xE3, 
                        130, 0x95, 0xA7, 0xB9, 0xCB, 0xDD, 0xEF, 0xF2, 0x91, 0xA4, 0xB6, 200, 0xDA, 0, 0, 0};

    for (int i = 0; (i < array.Length) && (i < encryptNum.Length); i++)
    {
        array[i] ^= encryptNum[i % encryptNum.Length];
    }
    return array;
}

0x04 解密完后,用提取工具进行提取会发现一直在报错用WinHex看了一下,这些AssetBundle还被压缩了


压缩算法使用的是LZMA,这里我用了开源的LZMA库(支持C/C++,C#,Java) http://www.7-zip.org/sdk.html

//解压LZMA文件
private void DecompressFileLZMA(byte[] bytes, string outFile)
{
    Decoder coder = new Decoder();
    MemoryStream input = new MemoryStream(bytes);
    FileStream output = new FileStream(outFile, FileMode.OpenOrCreate, FileAccess.ReadWrite);

    // Read the decoder properties
    byte[] properties = new byte[5];
    input.Read(properties, 0, 5);

    // Read in the decompress file size.
    byte[] fileLengthBytes = new byte[8];
    input.Read(fileLengthBytes, 0, 8);
    long fileLength = BitConverter.ToInt64(fileLengthBytes, 0);

    // Decompress the file.
    coder.SetDecoderProperties(properties);
    coder.Code(input, output, input.Length, fileLength, null);
    output.Flush();
    output.Close();
    input.Close();
}

资源提取源码

链接:http://pan.baidu.com/s/1mipYkOC 密码:cyo3

`useMemoizedFn` 和 `useCallback` 都是优化 React 组件性能的钩子。它们的作用是缓存函数的结果,以避免在每次渲染时重新创建函数。 `useMemoizedFn` 是一个自定义的 hook,它接受一个函数作为参数,并返回一个被缓存的函数。这个缓存的函数会在每次渲染时保持不变,直到依赖项发生变化。当依赖项发生变化时,`useMemoizedFn` 会重新计算并返回一个新的缓存函数。 ```javascript import { useMemo } from 'react'; function useMemoizedFn(fn, deps) { const memoizedFn = useMemo(() => fn, deps); return memoizedFn; } ``` `useCallback` 也是一个 React 提供的 hook,它接受一个函数和一个依赖项数组作为参数,并返回一个被缓存的回调函数。与 `useMemoizedFn` 类似,这个缓存的回调函数会在每次渲染时保持不变,直到依赖项发生变化。当依赖项发生变化时,`useCallback` 会重新计算并返回一个新的缓存回调函数。 ```javascript import { useCallback } from 'react'; function MyComponent() { const handleClick = useCallback(() => { // 处理点击事件 }, []); return ( <button onClick={handleClick}>Click me</button> ); } ``` 在使用 `useMemoizedFn` 和 `useCallback` 时,需要注意以下几点: - 如果函数依赖的变量不在依赖项数组中,那么每次渲染时都会创建一个新的函数。因此,确保将所有函数依赖的变量都包含在依赖项数组中。 - 通常情况下,应该优先使用 `useCallback` 来缓存回调函数,因为大多数情况下我们更经常需要缓存回调函数而不是普通函数。 - 只有在性能优化的情况下才需要使用这两个 hook。在大多数情况下,React 会自动处理函数的性能优化,因此不需要手动使用这些 hook。 希望能解答你的问题!如果还有其他问题,请继续提问。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值