碧蓝航线1.5相关

2017-12-20 6,838 ℃

Salt.dll解密

关键函数libmono.somono_set_data,此函数已加密,直接选择附加调试,F5
照抄一份c#

var bytes = File.ReadAllBytes("Salt.dll");
bytes[0] = 0x4D;
bytes[1] = 0x5A;
bytes[2] = 0x90;
bytes[3] = 0x00;
var result = 4;
var v4 = 12312;
do
{
    var v5 = bytes[result];
    bytes[result++] = (byte)(v5 ^ (v4 >> 8));
    v4 = -12691 * (v5 + v4) + 11719;
}
while (result != bytes.Length);
File.WriteAllBytes("Salt.dll", bytes);

用dnspy打开解密后的dll,发现已混淆,不过有个熟悉的老朋友

使用工具NoFuserEx

再使用de4dot进行重命名,导出Salt类代码

scripts加解密

直接调用函数
解密

Salt.Make(File.ReadAllBytes("scripts"), false);

加密

Salt.Make(File.ReadAllBytes("scripts"), true);

LuaJIT opcode加解密

libtolua.so下两个关键函数lj_bclock用于加密,lj_bcunlock用于解密,这两个函数也被加密了,依旧直接附加调试,F5
lj_bclock

lj_bcunlock

传入参数1是instructions段,参数2是段内opcode的数量

两个key长度都为256字节

参考ljd源码写一个简单的程序批量加解密

    static class Program
    {
        static byte[] lockkey;//自行从libtolua.so中取出
        static byte[] unlockkey;//自行从libtolua.so中取出

        static void Main(string[] args)
        {
            var bytes = File.ReadAllBytes(args[0]);
            var reader = new BinaryReader(new MemoryStream(bytes));
            //_read_header
            var magic = reader.ReadBytes(3);
            var version = reader.ReadByte();
            var bits = reader.ReadUleb128();
            var is_stripped = ((bits & 2u) != 0u);
            if (!is_stripped)
            {
                var length = reader.ReadUleb128();
                var name = Encoding.UTF8.GetString(reader.ReadBytes((int)length));
            }
            //_read_prototypes
            while (reader.BaseStream.Position < reader.BaseStream.Length)
            {
                var size = reader.ReadUleb128();
                if (size == 0)
                    break;
                var next = reader.BaseStream.Position + size;
                bits = reader.ReadByte();//_read_flags
                var arguments_count = reader.ReadByte();//_read_counts_and_sizes
                var framesize = reader.ReadByte();
                var upvalues_count = reader.ReadByte();
                var complex_constants_count = reader.ReadUleb128();
                var numeric_constants_count = reader.ReadUleb128();
                var instructions_count = reader.ReadUleb128();
                var start = (int)reader.BaseStream.Position;
                //加密
                /*bytes[3] = 0x80;
                bytes = lj_bclock(start, bytes, (int)instructions_count);*/
                //解密
                bytes[3] = 2;
                bytes = lj_bcunlock(start, bytes, (int)instructions_count);
                //
                reader.BaseStream.Position = next;
            }
            File.WriteAllBytes(args[0], bytes);
        }

        static byte[] lj_bclock(int start, byte[] bytes, int count)
        {
            var result = start;
            result += 4;
            var v2 = 0;
            do
            {
                var v3 = bytes[result - 4];
                result += 4;
                var v4 = bytes[result - 7] ^ v2++;
                bytes[result - 8] = (byte)(lockkey[v3] ^ v4);
            }
            while (v2 != count);
            return bytes;
        }

        static byte[] lj_bcunlock(int start, byte[] bytes, int count)
        {
            var result = start;
            result += 4;
            var v2 = 0;
            do
            {
                var v3 = bytes[result - 4];
                result += 4;
                var v4 = bytes[result - 7] ^ v3 ^ (v2++ & 0xFF);
                bytes[result - 8] = unlockkey[v4];
            }
            while (v2 != count);
            return bytes;
        }

        static public uint ReadUleb128(this BinaryReader reader)
        {
            uint value = reader.ReadByte();
            if (value >= 0x80)
            {
                var bitshift = 0;
                value &= 0x7f;
                while (true)
                {
                    var b = reader.ReadByte();
                    bitshift += 7;
                    value |= (uint)((b & 0x7f) << bitshift);
                    if (b < 0x80)
                        break;
                }
            }
            return value;
        }
    }

完工~

少女前线Live2D解密

上周在CP22看到这个游戏两周年了,打算回坑玩一阵,不过上一个号是买的初始号账号密码都忘记了,所以只好开了个新号。然后新皮肤倒是抽到了,不过枪都没有怎...

阅读全文

简单绕过BanG Dream校验

2.2.0 上周去参加CP22,炸梦演出时的人比想象中的要多,给我一种国服能很火的感觉,于是我决定下回日服顺带摸鱼一篇文章 游戏不懂什么版本开始加入了一点校验...

阅读全文

浅谈某NetHTProtect

声明,本文只稍微提一下思路,不提供任何代码~ 这次是第二次写它了,上篇文章讲的也是这玩意,不过这次它加了个新东西,直接暴力dump dll的话会发现method co...

阅读全文

44 条评论

  1. 大佬,在dnspy修改方法后编译出现的一堆the name Debugger&AssetBundle%time does not exist,该怎么解决?

  2. 大佬,改完后怎么打包回去啊?US解压出来的ship_data_xxxxx.lua什么的在UABE里根本搜索不到。求助。

  3. Perfare大大您好,
    听说您以前有制作Last Period的数据库,
    想请教您,
    有没有办法取得Last Period全部人物立绘的下载地址呢?
    因为Last Period的人物立绘后面都跟有一串随机数数字码,
    没有办法直接推测下载,
    想请问大佬有没有办法?
    至少可以知道所有人物立绘后面的随机数数字码,
    问题就可以基本解决了~
    如果有相关方法,
    还麻烦大佬教教小弟~~

    拜托您了QwQ

    1. Last Period你直接翻DLL的代码就好了,请求Asset返回的AssetInfoResponse里有你要的信息

      1. 大佬您好。感谢您的解答
        小弟以前没有解析过dll档案,
        能请教大佬有推荐的解析软件吗?
        感谢您~

  4. 大佬好
    ljd_bclock和ljd_bcunclock如果直接f5附加调试的话,出现的是这个..

    void __noreturn lj_bclock()
    {
    __asm { STC2 p5, c8, [R0,#0x258] }
    if ( !_CF )
    __asm { LDCCCL p1, c3, [R8,#0x3E4]! }
    JUMPOUT(0x122C020u);
    }

    这是加密的原因吗?

  5. 大佬你好,
    static byte[] unlockkey;//自行从libtolua.so中取出
    我把这里的unlockkey替换成解出来的0xB2,0x9D,那段256字节的key并运行后,结果显示输入的标记“0xb2”无效
    学着动态调试里那样加{}也不行,
    请问大佬我解出来的KEY应该写在代码的什么地方,有什么格式要求没,还是说我用的编程软件有问题?

  6. 问一下大佬用哪个版本的IDA,我用6.6的老是弹出错误提示框:bogus or irresponseble remote server,重新转发android_server也不行,求助

        1. 大佬,lua解密始终不是明文,卡这块几天了,方不方便加我企鹅号帮我看看代码。。
          企鹅号447986571 :!:

          1. 你该不会以为用我这代码能解出明文lua?不存在的。也别问我解出来的文件是什么,自己研究

            1. 原来是我搞错了。。闹笑话了
              看了大佬这回复再回去仔细看了看正文,果断明白了
              现在已经成功解密lua了,感谢大佬

  7. P大你好JIT已经按你的方法解开了,可是我scripts是按照冰大的方法提取出来的,所以能问问你这个Salt.dll是在哪吗?

  8. 大佬,有些腳本似乎是多個prototypes組起來的,例如AddShipCommand.lua,執行的結果好像不如預期

欢迎留言

4 + 8 =