首页
社区
课程
招聘
[原创]一款单机斗地主手机游戏的分析
发表于: 2025-11-17 01:35 6387

[原创]一款单机斗地主手机游戏的分析

2025-11-17 01:35
6387

一款我很久之前玩过的单机斗地主游戏,但是玩的时间一久,系统发的牌就变得特别差,所以我就想分析分析一下这个App.
注意:这是一个32位的安装包,需要把IDA的32位调试服务器adb push到/data/local/tmp 中。

把apk放到GDA4.11中分析,如下图所示。

图片描述

图片描述

虽然它有显示疑似有腾讯的什么bugly打包服务,但它Java层代码是可以正常阅读的。

以及使用Android killer调用apktool给apk重新打包签名,也没有提示用户安装盗版应用的对话框。

大致看了下,有关发牌、金币结算的逻辑并不在Java层里,因此我考虑到它应该是用了一些游戏引擎的,关键的逻辑应该在so层中。

使用frida脚本来监视App加载了哪些文件,如下图所示。
图片描述

frida的JS脚本如下:

执行的命令行为:frida -U -f com.june.game.doudizhu.g.baidu -l file.js

可以看到有许许多多的luac文件,特别是在进入房间的时候有一些疑似关键的luac被加载起来了。

比如,libsendCard2_2.lua 或者libsendcard2_2.luac ,猜想它是负责发牌的一段脚本。再看看apk中包含的文件,so文件又包含libcocos2dcpp.so 文件,assets 下也有许多luac文件。如下图所示。
图片描述
图片描述
那么根据网上的资料来看,这个游戏极可能是基于cocos2dx来开发的。
在使用frida hook open函数的时候,虽然它有感知到有lua文件的打开,但在目标目录下并没有找到解密的lua文件。

所以此时的逻辑就是应当解密关键的luac 文件了,luac 的形式如下图所示。
图片描述
除了最开始的bianfengqipai (边锋棋牌,和游戏的产商名是对的上的),剩余的内容都是加密的。一般来讲,程序是先检查头部的数据再去解密的。不过为了防止开发者做字符串加密,还是hook系统的open函数、过滤参数并跟踪它的调用堆栈就能找它解密的地方了。

此时的hook脚本如下:

输出如下图所示。

图片描述

都是一样的,以及大致看了一下这些调用堆栈,并没有发现特殊的,负责解密的代码段。于是修改脚本hooklua_pcall ,脚本如下。

此时的打印如下,在IDA中回看这些堆栈,依旧没有看到有用的解密代码。

图片描述

但是仍没有看出调用堆栈能解密的代码。这时我选择了直接hook fread函数,并对比头部的字节,如果是bianfengqipai 则打印出调用堆栈。但在实际操作过程中,它只打印了一行fread 没有打印出更上一级的调用堆栈了,所以我修改了一下frida的脚本,如下。(另外写的一个脚本)

打印的调用堆栈如下所示:

当然了,这里最好搜搜网上资料了,下图是cocos2dx_lua_loader 的伪代码。

图片描述

函数cocos2d::LuaStack::luaLoadBuffer 就包含了真正解密的函数,如下图所示。
图片描述

其源码所在的链接为:4a8K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6U0L8$3y4G2M7K6u0V1i4K6u0r3j5$3!0U0L8%4x3J5k6q4)9J5k6s2S2Q4x3V1k6T1L8r3!0T1i4K6u0r3N6U0c8Q4x3V1k6U0L8$3y4G2M7#2)9J5c8Y4y4U0M7X3W2H3N6r3W2F1k6#2)9J5c8X3I4#2j5g2)9J5k6r3u0A6L8X3c8A6L8X3N6K6i4K6u0r3L8h3q4F1N6h3q4D9i4K6u0r3b7@1y4x3N6h3q4e0N6r3q4U0K9#2)9J5k6h3y4H3M7l9`.`.

如下图所示。好在这个so没有去掉符号。
图片描述

这里再给两个参考链接:

https://bbs.kanxue.com/thread-268574-1.htm

2dbK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3f1#2x3Y4m8G2K9X3W2W2i4K6u0W2j5$3&6Q4x3V1k6@1K9s2u0W2j5h3c8Q4x3X3b7I4y4K6R3H3y4U0t1$3i4K6u0V1x3g2)9J5k6o6q4Q4x3X3g2Z5N6r3#2D9

那么现在我们就搜索字符串bianfengqipai ,有且仅有一个结果。找到它的交叉引用,找到int __fastcall AppDelegate::applicationDidFinishLaunching(AppDelegate *this)

图片描述

再看到伪码。
图片描述

我们寻找这个函数,显然它是一个虚函数,间接调用的形式。所以要找this指针v34 .

图片描述

图片描述

图片描述

以下就是v34+84h 偏移所包含的函数了。

图片描述

再来看看LuaStack 的源码。是密钥在前,签名在后。

图片描述

图片描述

那么我们就能解密luac文件了,密钥是03f0fdcbf5215b45fc790aaf2b965237 ,签名是bianfengqipai 。算法是XXTEA ,我们找到apk解压路径\assets\src\game\libcard ,这个目录下足足有100多个luac文件,而且命名都是差不多的。如下图所示。

图片描述

随便拿出一个用吾爱破解论坛上的逆向密码学工具来解密即可。一定要先去掉头部的签名,否则会解不出来。

图片描述

为了不影响原有的文件,我特地将原文件备份了一份,去掉了头部字节。放入解密工具,参数设置如下图所示。
图片描述

解出来发现是一个超大的数组,如下图所示。

图片描述

最后返回

图片描述

例如我拿出最后一个数组。

想必应该能猜出来,这是斗地主三个玩家的牌,baseCards 变量应该指的就是底牌了。如此说来,牌型的组合是相对固定的,只是App随机给你分配一个了。

我们已经知道\assets\src\game\libcard 中的文件都是定义牌型组合的脚本。那么它的数值是怎么和牌的点数对应上的呢?

一副扑克牌有大王、小王,剩下的就是4种花色的A~K了,等于13 * 4 + 2 = 54张牌。

游戏的逻辑很显然在libcocos2dcpp.so 里了,那么有很大概率lua会申请一段内存,存放每个玩家的牌,数值就像上文中提到的{53,41,28,15,2,42,29,16,48,47,46,45,31,43,37,9,7}

由于malloc 的调用次数会非常多,所以建议在开始游戏之前附加frida脚本,点击之后再看输出。脚本如下。当参数等于17时打印调用堆栈,因为在叫地主之前,大家都只有17张牌。

先执行命令行frida -U 单机斗地主 -l malloc.js 再开始游戏,如下图所示。

图片描述

屏幕有一大堆输出,找到有用的调用堆栈如下。

在IDA中跳转至lua_RunRule_RunRule_findCards ,这个函数能一下子看见许多疑似的牌点、数值互转的代码段。如下图所示。

图片描述

我们再看到堆栈中的_ZN8bianfeng7RunRule15findCardsByNumsERKSt6vectorIhSaIhEES5_S5_RS3_ ,其实是函数名int __fastcall bianfeng::RunRule::findCardsByNums(int, _DWORD *, _DWORD *, _DWORD *, _DWORD *); 如下图所示

图片描述

CardNum = bianfeng::CardFunc::getCardNum((bianfeng::CardFunc *)*v11++, v9); 就很像牌点和数值之间的转换了。

图片描述

在这个函数下断,最好是某一局有炸弹的,即存在不同花色的同一点数的牌。我这里选中了方片9,数值是9.函数也断下了。
图片描述

再选中梅花9,如下图所示。数值是22

图片描述

选中红心9,如下图所示。数值是35.

图片描述

最后选中黑桃9,如下图所示。数值是48。
图片描述

其它牌的点数就能一步一步调出来了。方块花色的数值是最小的,黑桃是最大的。以及A代表1,J、Q、K分别是11,12,13。小王是53,大王是54.所以就有以下牌点和数值的对应关系了。

十进制

纵向:同一花色,相邻点数牌相差1。

横向:同一点数,相邻花色牌相差13。

十六进制

Tips:这里代码的定位也可以直接搜索含有card*** 关键字的函数,看到类似getCardNum 等函数就去下断尝试,速度会更快。***

这一小节会令你十分兴奋,因为不用再忍受差牌了,在内存中改牌相当于你出了老千,可以纵横赌场和赌神媲美了哈哈。

在函数插入断点,如下图所示。
图片描述

进入房间。断下。
图片描述

此时还卡在发牌阶段。

图片描述

R1 = 36h,表示牌数值。往回跟。

图片描述

退出房间,再进入。R1 = 36h,还仍然是其中的一张牌。再往回跟。

图片描述

R1来源于R2,R2又来源于R4.
图片描述

看看R4存放了什么值,如下图所示。

图片描述

图片描述

此时没有叫地主,所以只有17张牌。36 35 29 1C 0F 02 21 14 07 1E 11 10 25 12 1F 06 1D,和拿到的牌是一致的。

跟踪到了lua_RunRule_RunRule_sortByWeight(lua_State *)

R1的值来自于R6,如下图所示。
图片描述

其中的函数luaval_to_cards(lua_State *, int, std::vector<unsigned char> *, char const*)

R2 = R6

R6是局部变量,luaval_to_cards(lua_State *, int, std::vector<unsigned char> *, char const*) 访问了R6,所以我们接下来重点看这个luaval_to_cards 函数。

图片描述

往下看,发现了一个重要的函数。所在指令是: 图片描述

图片描述

std::vector<uchar>::emplace_back<uchar>(uchar &&) GPT解释如下:

std::vector

在这个例子中:
std::move(value) 将 value 转换为右值引用,以便 emplace_back 使用它来构造一个新的 uchar 元素。
emplace_back 会直接在 vec 的末尾构造 uchar 类型的元素,而不进行额外的拷贝或移动操作。
总结
std::vector

那么也许这个就是动态添加牌数值的函数。

插入断点调试如下:

图片描述

多按几次F9,直至R4 = 0x11时,再F8 step over 这个函数。

要注意R0的值,LDR R0, [SP,#0x30+var_2C]

如下图所示。

图片描述

你会惊奇地发现R1是疑似容器指针,值CC14B180CC14B191 刚好相差0x11。

CC14B180 的内存空间如下图所示。

图片描述
尝试把这里的值改动一下(都是大王或小王),也许就能成功出千了哈哈(先忽略剩的3张底牌),改动如下图所示。
图片描述

取消断点,按下F9让程序继续运行,如下图所示。牌全部变成大王和小王了。

图片描述

这个时候我们是一定要抢地主的,因为已经是稳赢了。而且还能成功打出去,如下图所示。

图片描述

最终身为地主的我胜利了,如下图所示。

图片描述

在章节2.1.1 解密luac文件 我们可以得知,通知修改\assets\src\game\libcard 下的文件来实现静态改牌。要修改luac文件很简单,只需要把解密得到的lua文件使用工具加密,然后用010Editor在头部插入字节bianfengqipai 即可。

但是有一个问题,我修改了以上定义牌点的luac,重新打包、签名最后安装,但并没有生效。所以我又一次hook了文件打开函数。

frida hook的脚本如下。

脚本输出如下,发现App并没有按照我们给定的牌点去发牌,而是疑似用了另外的luac定义的牌组发牌。
图片描述

路径位于 /data/user/0/com.june.game.doudizhu.g.baidu/files/HotUpdateCacheDir/res/game/libcard/lib_sendcard1_6.lua ,然而手机文件浏览器并没有发现它的踪影。

这里尝试了很久很久,因为我对手机游戏开发并不熟。后来才发现在没网的情况下才会去使用本地的牌组定义。

那么最终找到了路径assets\src\bianfeng\hotupdate 中的HotUpdateManager.luac 文件。

做出了两处修改,不能再改多了,否则程序会卡在主界面。

图片描述

图片描述

添加这段代码的灵感来自于下图。
图片描述

这样就能禁用App的热更新了。

找到文件assets\src\game\logic\ShuffleLogic.luac ,改动如下图所示。好在注释里有手动添加牌点数的代码。

图片描述

注意这里不能像内存修改那样了全部都是大王、小王,程序会在你出牌后卡住。两张王,然后若干个炸弹足够我们赢了。

既然我们的胜利是板上钉钉的事情,那么我们的确该改改倍数,从而令自己赚更多钱。

改动的地方如下图所示。文件是src\game\rule\DDZRunRule.luac
图片描述

图片描述

实现了手动内存、静态改牌和倍数的修改。相关的luac文件修改完毕后重新打包签名即可。

下图分别是修改前、后。注意观察倍数和牌型。

修改前:低倍数,一般的牌。底牌是随机的,赢钱少则几百块,多的刚刚过万。想升级称号是很难的。

图片描述

修改后:高倍数,把把都是自己定义的好牌(出千了),赢钱一般都在千万级别。

图片描述

这里也能隐约看出另外两个人的牌特差哈哈,所以它们从主动不叫地主的。

图片描述

function my_hook_open() {
    Interceptor.attach(Process.getModuleByName('libc.so').getExportByName('open'),
        {
            onEnter: function (args) {
                var pathptr = args[0];
                if (pathptr !== undefined && pathptr != null) {
                    var path = ptr(pathptr).readCString();
             console.log("Load: " + path);     
                }
            },
            onLeave: function (retval) {
                
            }
        }
    );
}
setImmediate(my_hook_open);
// 简化打印的方法名,下同
function LOG(sth){
    console.log(sth);
}
function my_hook_open() {
    Interceptor.attach(Process.getModuleByName('libc.so').getExportByName('open'),
        {
            onEnter: function (args) {
                var pathptr = args[0];
                if (pathptr !== undefined && pathptr != null) {
                    var path = ptr(pathptr).readCString();
             console.log("Load: " + path);     
                }
            },
            onLeave: function (retval) {
                
            }
        }
    );
}
setImmediate(my_hook_open);
// 简化打印的方法名,下同
function LOG(sth){
    console.log(sth);
}
function my_hook_open() {
    Interceptor.attach(Process.getModuleByName('libc.so').getExportByName('open'),
        {
            onEnter: function (args) {
                var pathptr = args[0];
                if (pathptr !== undefined && pathptr != null) {
                var path = ptr(pathptr).readCString();
                    if(path.indexOf("libsendcard") >= 0)
                    {
                        // 此处添加自定义操作
                         console.log("Load: " + path);
                         // 打印调用堆栈
                         console.log('Call stack:\n' + Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n'));
                         console.log('\n');
                    }                  
                }
            },
            onLeave: function (retval) {
                
            }
        }
    );
}
setImmediate(my_hook_open);
function my_hook_open() {
    Interceptor.attach(Process.getModuleByName('libc.so').getExportByName('open'),
        {
            onEnter: function (args) {
                var pathptr = args[0];
                if (pathptr !== undefined && pathptr != null) {
                var path = ptr(pathptr).readCString();
                    if(path.indexOf("libsendcard") >= 0)
                    {
                        // 此处添加自定义操作
                         console.log("Load: " + path);
                         // 打印调用堆栈
                         console.log('Call stack:\n' + Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n'));
                         console.log('\n');
                    }                  
                }
            },
            onLeave: function (retval) {
                
            }
        }
    );
}
setImmediate(my_hook_open);
function my_hook_open() {
    Interceptor.attach(Process.getModuleByName('libc.so').getExportByName('open'),
        {
            onEnter: function (args) {
                var pathptr = args[0];
                if (pathptr !== undefined && pathptr != null) {
                var path = ptr(pathptr).readCString();
                    if(path.indexOf("sendcard") >= 0)
                    {
                        hookCocos();
                        // 此处添加自定义操作
                         console.log("Load: " + path);
                         // 打印调用堆栈
                         console.log('Call stack:\n' + Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n'));
                         console.log('\n');
                    }                  
                }
            },
            onLeave: function (retval) {
                
            }
        }
    );
}
 
function hookCocos()
{
        Interceptor.attach(Process.getModuleByName('libcocos2dcpp.so').getExportByName('lua_pcall'),
        {
            onEnter: function (args) {
                 // 打印调用堆栈
                 LOG("*********************************************");
                 console.log('Call stack:\n' + Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n'));
                 console.log('\n');    
                 LOG("*********************************************"); 
            },
            onLeave: function (retval) {
                
            }
        }
         
    );
}
setImmediate(my_hook_open);
// 简化打印的方法名,下同
function LOG(sth){
    console.log(sth);
}
function my_hook_open() {
    Interceptor.attach(Process.getModuleByName('libc.so').getExportByName('open'),
        {
            onEnter: function (args) {
                var pathptr = args[0];
                if (pathptr !== undefined && pathptr != null) {
                var path = ptr(pathptr).readCString();
                    if(path.indexOf("sendcard") >= 0)
                    {
                        hookCocos();
                        // 此处添加自定义操作
                         console.log("Load: " + path);
                         // 打印调用堆栈
                         console.log('Call stack:\n' + Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n'));
                         console.log('\n');
                    }                  
                }
            },
            onLeave: function (retval) {
                
            }
        }
    );
}
 
function hookCocos()
{
        Interceptor.attach(Process.getModuleByName('libcocos2dcpp.so').getExportByName('lua_pcall'),
        {
            onEnter: function (args) {
                 // 打印调用堆栈
                 LOG("*********************************************");
                 console.log('Call stack:\n' + Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n'));
                 console.log('\n');    
                 LOG("*********************************************"); 
            },
            onLeave: function (retval) {
                
            }
        }
         
    );
}
setImmediate(my_hook_open);
// 简化打印的方法名,下同
function LOG(sth){
    console.log(sth);
}
// hook_fread.js
 
// 要检测的目标字节序列
 
const targetBytes = [
    0x62, 0x69, 0x61, 0x6E, 0x66, 0x65, 0x6E, 0x67, 0x71, 0x69, 0x70, 0x61, 0x69
];
 
 
function bytesMatch(buffer, pattern) {
    for (let i = 0; i < buffer.length - pattern.length + 1; i++) {
        let match = true;
        for (let j = 0; j < pattern.length; j++) {
            if (buffer[i + j] !== pattern[j]) {
                match = false;
                break;
            }
        }
        if (match) return true;
    }
    return false;
}
 
const freadPtr = Module.findExportByName(null, "fread");
if (!freadPtr) {
    console.error("未找到 fread 符号");
} else {
    console.log("✅ Hook fread @ " + freadPtr);
 
    Interceptor.attach(freadPtr, {
        onEnter(args) {
            this.ptr = args[0];
            this.size = args[1].toInt32();
            this.nmemb = args[2].toInt32();
            this.totalSize = this.size * this.nmemb;
        },
        onLeave(retval) {
            const len = retval.toInt32();
            if (len > 0 && this.ptr && this.totalSize > 0) {
                try {
                    const buf = Memory.readByteArray(this.ptr, Math.min(this.totalSize, len * this.size));
                    const data = new Uint8Array(buf);
 
                    if (bytesMatch(data, targetBytes)) {
                        console.log("⚠️ 检测到目标字节序列!");
                        console.log("读取长度:", len, "字节");
 
                        // 打印十六进制数据
                       /* console.log(hexdump(this.ptr, {
                            offset: 0,
                            length: Math.min(64, len), // 打印前64字节
                            header: true,
                            ansi: true
                        }));*/
 
                        // 打印堆栈
                        console.log("堆栈回溯:\n" + Thread.backtrace(this.context, Backtracer.FUZZY)
                        .map(DebugSymbol.fromAddress).join("\n"));
                    }
                } catch (e) {
                    console.error("读取内存失败:", e);
                }
            }
        }
    });
}
 
// frida -U -f com.june.game.doudizhu.g.baidu -l fread.js
// frida -U 单机斗地主 -l fread.js
// hook_fread.js
 
// 要检测的目标字节序列
 
const targetBytes = [
    0x62, 0x69, 0x61, 0x6E, 0x66, 0x65, 0x6E, 0x67, 0x71, 0x69, 0x70, 0x61, 0x69
];
 
 
function bytesMatch(buffer, pattern) {
    for (let i = 0; i < buffer.length - pattern.length + 1; i++) {
        let match = true;
        for (let j = 0; j < pattern.length; j++) {
            if (buffer[i + j] !== pattern[j]) {
                match = false;
                break;
            }
        }
        if (match) return true;
    }
    return false;
}
 
const freadPtr = Module.findExportByName(null, "fread");
if (!freadPtr) {
    console.error("未找到 fread 符号");
} else {
    console.log("✅ Hook fread @ " + freadPtr);
 
    Interceptor.attach(freadPtr, {
        onEnter(args) {
            this.ptr = args[0];
            this.size = args[1].toInt32();
            this.nmemb = args[2].toInt32();
            this.totalSize = this.size * this.nmemb;
        },
        onLeave(retval) {
            const len = retval.toInt32();
            if (len > 0 && this.ptr && this.totalSize > 0) {
                try {
                    const buf = Memory.readByteArray(this.ptr, Math.min(this.totalSize, len * this.size));
                    const data = new Uint8Array(buf);
 
                    if (bytesMatch(data, targetBytes)) {
                        console.log("⚠️ 检测到目标字节序列!");
                        console.log("读取长度:", len, "字节");
 
                        // 打印十六进制数据
                       /* console.log(hexdump(this.ptr, {

传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2025-11-17 01:41 被taeyeon_ss编辑 ,原因:
收藏
免费 40
支持
分享
最新回复 (30)
雪    币: 10084
活跃值: (6012)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
学习了,非常精彩!
2025-11-17 09:32
0
雪    币: 6
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
3
感谢分享
2025-11-17 10:31
0
雪    币: 209
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
4
学习了
2025-11-17 10:45
0
雪    币: 7
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
5
感谢分享
2025-11-17 11:57
0
雪    币: 7
活跃值: (85)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
学了
2025-11-17 12:32
0
雪    币: 9
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
7
哥 能不能讲讲虚函数那个虚表怎么找的 求求了
2025-11-17 21:48
0
雪    币: 2496
活跃值: (3581)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
厉害,斗地主无敌
2025-11-18 08:54
1
雪    币: 385
活跃值: (320)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
mb_vhwdzyqo 哥 能不能讲讲虚函数那个虚表怎么找的 求求了
构造函数会初始化虚表,虚表在类成员之前。具体的话建议你参考一下钱林松的《C++反汇编与逆向分析技术揭秘(第2版)》哦
2025-11-18 09:06
0
雪    币: 2491
活跃值: (2842)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
麻烦请改好的apk发一下,让我也玩一下爽一下,谢谢~
2025-11-19 17:48
0
雪    币: 0
活跃值: (86)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
不错的文章
2025-11-19 18:09
0
雪    币: 3789
活跃值: (4090)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
6
2025-11-19 18:41
0
雪    币: 1420
活跃值: (1409)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
大佬,太强啦
2025-11-20 13:46
0
雪    币: 227
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
14
非常精彩!
2025-11-20 18:21
0
雪    币: 3359
活跃值: (4140)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
15
111
2025-11-20 19:33
0
雪    币: 59
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
16
妙啊
2025-11-21 18:47
0
雪    币: 21
活跃值: (265)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
666
2025-11-22 02:25
0
雪    币: 3266
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
18
666
2025-11-22 06:44
0
雪    币: 1851
活跃值: (2620)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
6666
2025-11-22 07:20
0
雪    币: 23
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
20
1
2025-11-22 08:40
0
雪    币: 5220
活跃值: (5911)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
感谢分享
2025-11-22 23:22
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
22
感谢分享
2025-11-25 22:30
0
雪    币: 200
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
23
1
2025-11-26 20:01
0
雪    币: 2553
活跃值: (3452)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
24
强啊,但是这样玩几局就感觉没有意思了。
2025-11-27 09:54
0
雪    币: 111
活跃值: (321)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
666
2025-11-27 13:48
0
游客
登录 | 注册 方可回帖
返回