吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2161|回复: 12
收起左侧

[Android 脱壳] 整体加固Demo及加壳工具的编写

  [复制链接]
soma20 发表于 2026-2-15 08:52
本帖最后由 soma20 于 2026-4-4 21:12 编辑

整体加固Demo及加壳工具的编写

一、实验环境准备

​        首先来准备一下demo,这里写一个小程序,能够体现成功运行的效果即可。

​        首先创建一个Empty Views Activity的项目
image-20260212174314427.png

​        接着画一下界面,这里就使用两个TextView组件,一个里面写的是MyApplication is not Loaded!!,另外一个是MainActivity is not Loaded。展现效果就是启动app之后,这两个textViewnot Loaded变成loaded

image-20260212121612807.png

​        接下来是代码的编写

  • MyApplication
public class MyApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d("[+]","MyApplication's OnCreate is calling...");
    }

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        Log.d("[+]","MyApplication's attachBaseContext is calling...");
    }
}
  • MainActivity
public class MainActivity extends AppCompatActivity {

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EdgeToEdge.enable(this);
        setContentView(R.layout.activity_main);
        TextView tv_application = (TextView) findViewById(R.id.text_Application);
        TextView tv_activity = (TextView) findViewById(R.id.text_Activity);
        if(getApplication() instanceof MyApplication){
            tv_application.setText("MyApplication is loaded");
        }
        tv_activity.setText("MainActivity is loaded!!");
    }
}

​        MainActivity当中通过getApplication获取全局唯一的一个Application实例对象,来判断该对象是不是属于MyApplication这个类。最后启动之后的效果如下(未加壳状态):
image-20260212121518289.png

二、加壳代码的编写

1. 提取相关dex

​        首先通过bandzip将对应的dex文件提取出来

image-20260212123303523.png

​        这里是classes3,只把这个留下来即可,接下来将这个dex文件放到项目当中的assests文件夹当中,准备编写壳程序,对于整体加固来说分成落地加固不落地加固这里使用落地加固,及直接加载assests目录当中的dex文件。

​        

2. 编写壳程序

1. 壳代码思路

​        在之前的源码分析中,我们知道了在handleBindApplication当中会创建一个LoadedApk,接着会使用makeApplicationInner来获取一个Application

image-20260213120007019.png

     后续使用到的类加载器通过`getClassLoader`获取,而这个获取的就是`LoadedApk`当中的`mClassLoader`。

​        由于app启动时执行的是壳代码的dex的类加载器,想要应用正常执行,就需要将类加载器也就是mClassLoader换成源程序的dex的加载器才能正常执行。可以通过源码查看一下mClassLoaderLoadedApk当中的属性:

image-20260212133151552.png

​        可以看到这一个私有非静态成员,那么需要获取LoadedApk才能够访问到这个成员,而且由于是私有成员,那么需要通过反射来在外部访问或者修改这个成员。通过将这个mClassLoader修改成原来的ClassLoader就能够正常往下执行了。

​        在源码分析当中,简单的分析到app的执行流程如下:attachBaseContext --> ContentProvider.onCreate --> Application.OnCreate --> Activity.OnCreate。这里的attachBaseContext是先执行的,那我们的壳代码就可以放在这里,然后完成对环境的恢复。

  • 操作如下:

​        首先从assets目录中读取加密的dex文件进行解密操作,接着将解密后的dex存放到私有目录当中,接着动态加载解密后的dex文件,将得到的ClassLoader替换掉mClassLoader完成环境的复原。

2. 获取LoadedApk

​        那么有了思路,就需要来想想该如何获取这个LoadedApk了。有几种方法:

  • 通过Context获取:
    image-20260213131219430.png

​        在ContextImpl当中有一个mPackageInfo的成员,类型是LoadedApkattachBaseContext传入的参数就是ContextImpl,这里就可以使用反射来获取这个mPackageInfo,代码如下:

public Object LoadedApk = null;
protected void attachBaseContext(Context base) {
    super.attachBaseContext(base);
    try{
        Context contextImpl = base;
        while(contextImpl instanceof ContextWrapper){
            contextImpl = ((ContextWrapper) contextImpl).getBaseContext();
        }
        Class<?> contextImplClass = contextImpl.getClass();
        Field mPackageInfoField = contextImplClass.getDeclaredField("mPackageInfo");
        mPackageInfoField.setAccessible(true);

        LoadedApk = mPackageInfoField.get(contextImpl);
    } catch (Exception e){
        e.printStackTrace();
    }
    // ...
}
  • 通过ActivityThread获取

image-20260213142052406.png

​        在ActivityThread的源码当中可以看到,ActivityThread有一个mPackages的成员,是一个map,键是包名(String),值是LoadedApk,这意味着我们可以通过获取这个mPackages来获取LoadedApk。这里获取方法也是反射

​        获取ActivityThread可以使用静态方法currentActivityThreadMethod,代码如下:

protected void attachBaseContext(Context base) {
    super.attachBaseContext(base);

    try{
        Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
        Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");
        currentActivityThreadMethod.setAccessible(true);
        Object currentActivityThread = currentActivityThreadMethod.invoke(null);
        Field mPackagesField = activityThreadClass.getDeclaredField("mPackages");
        mPackagesField.setAccessible(true);
        Map<String,WeakReference<?>> mPackages = (Map<String, WeakReference<?>>) mPackagesField.get(currentActivityThread);
        String packageName = base.getPackageName();
        WeakReference<?> wr = mPackages.get(packageName);
        if(wr != null){
            LoadedApk = wr.get();
        }else{
            Log.e("[+]","获取LoadedApk失败");
        }

    }catch (Exception e){
        Log.e("[+]","获取LoadedApk失败");
        e.printStackTrace();
    }
// ...
}
3. 封装反射

​        由于比较多地方使用到反射,如果一直都要写一遍反射的话会比较麻烦,这里直接封装一下,定义为Ref.java

package com.example.protectdemo_1;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class Ref {
    public static Object invokeStaticMethod(String class_name,String method_name,Class[] pareType,Object[] pareValues){
        try{
            Class obj_class = Class.forName(class_name);
            Method method = obj_class.getMethod(method_name,pareType);
            return method.invoke(obj_class,pareValues);
        } catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    public static Object invokeMethod(String class_name, String method_name, Object obj,Class[] pareTyple, Object[] pareValues){
        try{
            Class obj_class = Class.forName(class_name);
            Method method = obj_class.getMethod(method_name, pareTyple);
            return method.invoke(obj,pareValues);
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    public static Object getFieldObject(String class_name, Object obj, String fileName){
        try{
            Class obj_class = Class.forName(class_name);
            Field field = obj_class.getDeclaredField(fileName);
            field.setAccessible(true);
            return field.get(obj);
        }catch(Exception e){
            e.printStackTrace();
        }
        return null;
    }

    public static Object getStaticFieldOjbect(String class_name, String filedName){
        try {
            Class obj_class = Class.forName(class_name);
            Field field = obj_class.getDeclaredField(filedName);
            field.setAccessible(true);
            return field.get(null);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;

    }
    public static void setFieldObject(String classname, String filename, Object obj, Object fileValue){
        try{
            Class obj_class = Class.forName(classname);
            Field field = obj_class.getDeclaredField(filename);
            field.setAccessible(true);
            field.set(obj,fileValue);
        }catch(Exception e){
            e.printStackTrace();
        }
    }

    public static void setStaticObject(String class_name, String filedName, Object filedValue){
        try{
            Class obj_class = Class.forName(class_name);
            Field field = obj_class.getDeclaredField(filedName);
            field.setAccessible(true);
            field.set(null,filedValue);
        }catch (Exception e) {
            e.printStackTrace();
        }
    }
}

​        使用的时候直接调用:Ref.xxx即可。

4. 壳代码编写

​        这里使用ActivityThread的方法来获取LoadedApk,并且从文件中加载dex,使用反射替换mClassLoader。来实现环境的修复,首先需要在main文件夹下创建一个assests的文件夹,然后将被保护的dex文件放入文件夹当中(这里实验使用落地加固),如下:
image-20260213225415575.png

​        实验代码如下:

 protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        Log.i("[+]","[StubApplication attachBaseContext] start...");
        String DexName = "classes.dex";
        String DexFilePath  = getDir("shell", MODE_PRIVATE).getAbsolutePath() + File.separator + DexName;
        try{
            InputStream ins = base.getAssets().open(DexName);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte[] bytes = new byte[1024];
            int index;
            while((index = ins.read(bytes)) != -1)
                baos.write(bytes,0,index);

            byte[] decDex = decrypt(baos.toByteArray());
            ins.close();
            baos.close();

            FileOutputStream fos = new FileOutputStream(new File(DexFilePath));
            fos.write(decDex);
            fos.close();
        } catch(IOException e){
            e.printStackTrace();
        }

        DexClassLoader dexClassLoader = new DexClassLoader(
            DexFilePath,
            base.getCacheDir().getAbsolutePath(),
            getApplicationInfo().nativeLibraryDir,
            getClassLoader()
        );

        currentActivityThread = Ref.invokeStaticMethod(
                "android.app.ActivityThread",
                "currentActivityThread",
                null,
                null
        );

        ArrayMap mPackages = (ArrayMap) Ref.getFieldObject(
                "android.app.ActivityThread",
                currentActivityThread,
                "mPackages"
        );

        WeakReference wr = (WeakReference) mPackages.get(getPackageName());
        LoadedApk = wr.get();
        Ref.setFieldObject(
                "android.app.LoadedApk",
                "mClassLoader",
                LoadedApk,
                dexClassLoader
        );
         }
    private byte[] decrypt(byte[] data){
        Log.d("[+]","开始解密...");
        /*
            解密操作
         */
        return data;
    }
}

​        这里为了方便,就没有写加解密逻辑,可以按照自身情况来编写。运行之后效果如下:

image-20260213230303584.png

可以发现MainActivity已经成功加载了,但是MyApplication并没有正常加载,接下来就需要处理一下这个问题了。

5. Application问题处理

​        出现这种问题的原因是通过我们此时加载的Application是壳的,不是源程序的Application。此时需要手动创建Application然后替换一些相关的东西。通过源代码可以看到ActivityThread创建Application的方法:

image-20260214105926111.png

​        这里调用的是makeApplicationInner,但是实际上调用的是makeApplication。还有就是需要将下面的mInitialApplication替换成新的Application。接着需要看看makeApplication当中有什么需要修改的:

image-20260214110127093.png

​        首先就是这个mApplication需要置零,不然就会直接返回现存的mApplication

image-20260214111136483.png

接着就需要修改mAppliactionInfo.className,这里是通过这个get..函数来获取mApplictionInfo当中的className成员。还有一个位置就是这里的add

image-20260214113202478.png

​        这里会创建的Application添加到mAllApplications当中,一开始添加的是壳的Application,所需要将其移除再添加新的Application。这个mAllApplicationArrayList的类型,可以直接使用remove将对应的项去除掉。

​        完成这些环境修复之后,需要主动调用OnCreate。这里调用makeApplication时需要注意传入的参数:

image-20260214115447321.png

image-20260214115503465.png

​        我们需要这两个条件当中的代码不执行,所以需要传入的forceDefaultAppClass = falseinstrumentation = null。综上所述,我们需要在OnCreate当中添加如下代码:

public void onCreate() {
    super.onCreate();
    // 替换className
    String className = "com.example.protectdemo_1.MyApplication";
    ApplicationInfo applicationInfo = (ApplicationInfo) Ref.getFieldObject(
            "android.app.LoadedApk",
            LoadedApk,
            "mApplicationInfo"
    );
    applicationInfo.className = className;

    // 清除mApplication
    Application oldApplication = (Application) Ref.getFieldObject(
            "android.app.LoadedApk",
            LoadedApk,
            "mApplication"
    );

    Ref.setFieldObject(
            "android.app.LoadedApk",
            "mApplication",
            LoadedApk,
            null
    );

    // 修改mAllApplication
    ArrayList mAllApplication = (ArrayList)Ref.getFieldObject(
            "android.app.ActivityThread",
            currentActivityThread,
            "mAllApplications"
    );

    mAllApplication.remove(oldApplication);
    Application realApp = (Application) Ref.invokeMethod(
            "android.app.LoadedApk",
            "makeApplication",
            LoadedApk,
            new Class[]{boolean.class, Instrumentation.class},
            new Object[]{false, null}
    );

    Ref.setFieldObject(
            "android.app.ActivityThread",
            "mInitialApplication",
            currentActivityThread,
            realApp
    );
    realApp.onCreate();
}

​        这样一来,程序就可以正常执行了:
image-20260214120513498.png

三、自动化加固

说明

​        这里的自动化加固是指在有壳代码的前提下,通过脚本辅助,使用工具对指定dex文件进行加固。这里使用apktool

1. 代码优化

  • className获取方法调整

​        在上面的Demo当中,className是写死的,如果要写自动化加固的话这样肯定不行,需要动态获取className。这里可以通过下面这个方式:

String className = null;
try{
    ApplicationInfo applicationInfo = getPackageManager().getApplicationInfo(getPackageName(), PackageManager.GET_META_DATA);
    Bundle bundle = applicationInfo.metaData;
    if(bundle != null && bundle.containsKey(appKey)){
        className = bundle.getString(appKey);
    }
}catch(PackageManager.NameNotFoundException e){
    Log.e("[+]","[Application OnCreate] NameNotFoundException!!");
    e.printStackTrace();
}

if(className == null){
    Log.e("[+]","[Application OnCreate] className is null!!");
    return;
}

​        这段代码的作用是从 Android 应用的 Manifest 文件中的 MetaData 读取一个配置的类名。使用这种方法需要在AndroidManifest.xml当中加入<meta-data />标签:

<application>
    <meta-data
        android:name="APPLICATION_CLASS_NAME"
        android:value="com.example.protectdemo_1.MyApplication"
</application>
  • 加密函数调整

​        这里使用一个简单的异或加密来对dex文件进行加密,对应的壳解密代码如下:

private byte[] decrypt(byte[] data){
    Log.d("[+]","开始解密...");
    /*
        解密操作
     */
    if(data != null){
        for(int i = 0; i < data.length; i++){
            data[i] ^= 0x53;
        }
    }
    return data;
}

2. 自动化加固思路

​        首先就是对目标apk进行解包,这里使用apktool的反编译功能,然后对目标dex进行加密,在解包后的文件夹下创建assests文件夹,将加密后的dex文件丢到assests文件夹当中,删除原来的dex文件,接着植入壳代码文件。最后使用apktool回编译,然后签名即可。

​        知道了思路,现在就可以来着手编写工具了。这里首先需要将壳代码(.dex从之前生成的apk当中取出来。这里还需要使用到tinyxml2.h的库

1. manifest_editor.hpp

​        通过该类的方法,实现对Mainfest当中的标签进行修改

#pragma once 
#include "tinyxml2.h"
#include <string>
#include <iostream>

using namespace tinyxml2;
class ManifestEditor
{
private:
        const char* PROXY_APP_NAME = "com.example.protectdemo_1.StubApplication";        // 注意:这里要与自己的包名匹配
        const char* META_KEY = "APPLICATION_CLASS_NAME";

public:
        void modify(const std::string& xmlPath)
        {
                XMLDocument doc;
                if (doc.LoadFile(xmlPath.c_str()) != XML_SUCCESS)
                {
                        std::cerr << "无法解析 AndroidManifest.xml" << std::endl;
                        return;
                }

                XMLElement* root = doc.RootElement();
                if (!root) return;

                XMLElement* appNode = root->FirstChildElement("application");
                if (!appNode)return;

                // 1. 获取原本的Application Name
                const char* oldAppName = appNode->Attribute("android:name");
                std::string originalAppClass = (oldAppName) ? oldAppName : "";

                // 2. 修改Application Name为壳的入口
                appNode->SetAttribute("android:name", PROXY_APP_NAME);

                // 3. 插入Meta-Data保存原来的Application
                if (!originalAppClass.empty())
                {
                        XMLElement* metaData = doc.NewElement("meta-data");
                        metaData->SetAttribute("android:name", META_KEY);
                        metaData->SetAttribute("android:value", originalAppClass.c_str());
                        if (appNode->FirstChild())
                                appNode->InsertFirstChild(metaData);
                        else
                                appNode->InsertEndChild(metaData);
                }

                // 4. 保存文件
                doc.SaveFile(xmlPath.c_str());
                std::cout << "[+] AndroidManifest.xml 修改完成" << std::endl;
        }
};
2. 工具类utils.hpp

​        该代码实现的是拷贝、加密等一系列操作

#pragma once

#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <filesystem>
#include <algorithm>

namespace fs = std::filesystem;

class Utils 
{
public:
        static bool runCommand(const std::string& cmd)
        {
                std::cout << "[CMD] 执行命令: " << cmd << std::endl;
                int ret = system(cmd.c_str());
                return ret == 0;
        }

        static std::vector<uint8_t> readFile(const std::string& filepath)
        {
                std::ifstream file(filepath, std::ios::binary);
                if(!file.is_open())
                {
                        std::cerr << "无法打开文件: " << filepath << std::endl;
                        return {};
                }
                return std::vector<uint8_t>((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
        }

        // 将字节数组写入文件
        static bool writeFile(const std::string& path, const std::vector<uint8_t>& data)
        {
                std::ofstream file(path, std::ios::binary);
                if (!file.is_open()) return false;
                file.write(reinterpret_cast<const char*>(data.data()), data.size());
                return true;
        }

        // 简单加密
        static std::vector<uint8_t> encrypt(std::vector<uint8_t> data)
        {
                const uint8_t key = 0x53;
                for (auto& byte : data)
                {
                        byte ^= key;
                }
                return data;
        }

        // 递归拷贝目录
        static void copyDir(const std::string& src, const std::string& dst)
        {
                try 
                {
                        fs::copy(src, dst, fs::copy_options::recursive | fs::copy_options::overwrite_existing);
                }
                catch (fs::filesystem_error& e) 
                {
                        std::cerr << "拷贝失败: " << e.what() << std::endl;        
                }
        }

        // 创建目录
        static void makeDir(const std::string& dirPath)
        {
                if (!fs::exists(dirPath))
                {
                        fs::create_directories(dirPath);
                }
        }

        // 删除文件或目录
        static void removePath(const std::string& path)
        {
                fs::remove_all(path);
        }
};
3. main.cpp
#include <iostream>
#include "utils.hpp"
#include "manifest_editor.hpp"

const std::string IN_APK = "test.apk";
const std::string OUT_APK = "appShell.apk";
const std::string TMP_DIR = "apkDecompile";
const std::string SHELL_DEX_SOURCE = "shell_files/classes.dex";
const std::string SHELL_LIBS = "shell_files/libs";

int main()
{
        // system("chcp 65001");
        std::cout << "==== Android APK 加壳工具 ====" << std::endl;
        Utils::removePath(TMP_DIR);
        Utils::removePath(OUT_APK);

        // 1. 反编译
        std::string cmdDecompile = "java -jar apktool.jar d -s " + IN_APK + " -o " + TMP_DIR + " -f";
        if (!Utils::runCommand(cmdDecompile))
        {
                std::cerr << "反编译失败, 请检查apktools是否存在!" << "\n";
                return -1;
        }

        // 2. 加密原始DEX并隐藏
        std::cout << " 正在加密原始Dex...\r\n";
        std::string srcDexPath = TMP_DIR + "/classes.dex";                                // 这里的dex是被保护app当中目标dex的名称
        std::string assetsDir = TMP_DIR + "/assets";
        std::string encDexPath = assetsDir + "/classes.dex";                        // 文件名是壳代码当中的DexName

        Utils::makeDir(assetsDir);
        std::vector<uint8_t> dexData = Utils::readFile(srcDexPath);
        if (dexData.empty())
        {
                std::cerr << "无法读取 classes.dex" << std::endl;
                return -1;
        }

        std::vector<uint8_t> encData = Utils::encrypt(dexData);
        Utils::writeFile(encDexPath, encData);

        // 删除原始classes.dex
        Utils::removePath(srcDexPath);

        // 3. 植入壳代码(class.dex)
        std::cout << "正在植入壳代码..." << std::endl;
        if (!fs::exists(SHELL_DEX_SOURCE))
        {
                std::cerr << "壳文件丢失: " << SHELL_DEX_SOURCE << std::endl;
                return -1;
        }

        fs::copy_file(SHELL_DEX_SOURCE, srcDexPath, fs::copy_options::overwrite_existing);
        if (fs::exists(SHELL_LIBS))
                Utils::copyDir(SHELL_LIBS, TMP_DIR + "/lib");

        // 4. 修改AndroidManifest.xml
        std::cout << " 正在修改 Manifest.xml.." << std::endl;
        ManifestEditor editor;
        editor.modify(TMP_DIR + "/AndroidManifest.xml");

        // 5. 回编译
        std::cout << "[+] 开始回编译..." << std::endl;
        std::string cmdBuild = "java -jar apktool.jar b " + TMP_DIR + " -o " + OUT_APK;
        if (!Utils::runCommand(cmdBuild))
        {
                std::cout << "[error] 回编译失败" << std::endl;
                return -1;
        }

        std::cout << "==== 加固完成! 输出文件: " << OUT_APK << " ====" << std::endl;
        return 0;
}

​        这里需要将壳代码放在当前目录当中的shell_files的文件夹当中,如果有so层的加密则存放到shell_files/lib当中。

4. 签名

​        成功生成appShell.apk之后需要先签名才能在手机上安装,这里签名使用keytoolapksigner来进行签名,大致命令如下:

# 生成签名文件
keytool -genkeypair -v -keystore my-release-key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias my-key-alias

# 签名
java -jar apksigner.jar sign --ks my-release-key.jks --out signed-app.apk appShell.apk

# 安装
adb install -t signed-app.apk

免费评分

参与人数 3吾爱币 +3 热心值 +2 收起 理由
jiyu0418 + 1 + 1 谢谢@Thanks!
jaffa + 1 谢谢@Thanks!
ytfh1131 + 1 + 1 谢谢@Thanks!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| soma20 发表于 2026-4-4 21:14
gxjkz 发表于 2026-4-3 16:17
大佬可以分享下工程文件吗

通过网盘分享的文件:01_自动加壳.zip
链接: https://pan.baidu.com/s/1CoUR_cwsoKzsxABXWGOltQ 提取码: u4k1
--来自百度网盘超级会员v5的分享
怜渠客 发表于 2026-2-15 11:20
hcsson 发表于 2026-2-15 13:36
 楼主| soma20 发表于 2026-2-15 16:12
怜渠客 发表于 2026-2-15 11:20
main.cpp和utils.hpp贴的代码重复了

感谢提醒
 楼主| soma20 发表于 2026-2-15 16:13
hcsson 发表于 2026-2-15 13:36
会不会触发安全机制

这里指的是什么安全机制?这边测试并没有什么问题
hcsson 发表于 2026-2-15 16:57
soma20 发表于 2026-2-15 16:13
这里指的是什么安全机制?这边测试并没有什么问题

类似电脑的防火墙
 楼主| soma20 发表于 2026-2-15 18:45
hcsson 发表于 2026-2-15 16:57
类似电脑的防火墙

这个没遇到过
辣条好像呀 发表于 2026-2-18 20:02
这个加固工程可以直接使用吗
 楼主| soma20 发表于 2026-2-19 08:57
辣条好像呀 发表于 2026-2-18 20:02
这个加固工程可以直接使用吗

自动化加固就只是用代码替换了手动的工作而已,复用的话需要根据自己的需求来修改
fzlte0 发表于 2026-3-18 21:18
加固过程讲解的很详细,有用收藏,谢谢lz。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-5-24 18:10

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表