吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 6762|回复: 3
收起左侧

[其他原创] 简单加壳程序编写以及加壳思路的分享

  [复制链接]
镇北看雪 发表于 2020-10-4 22:33
本帖最后由 镇北看雪 于 2020-10-4 22:56 编辑

写在前面


编写加壳软件对于熟悉PE文件格式是再好不过的了方法了,这两天脱了一些简单地壳所以想尝试自己写一个加壳程序。此加壳程序非常简单,就是对代码段压缩,修改输入表和重定位表。反调试,反dump等等都没加,但有了基本思路后后面就可以进一步任意发挥了。在写此加壳程序时有一些小麻烦和一些值得思考的地方,在这里把这些问题和加壳思路与大家分享一下。

加壳程序编写思路


此加壳程序通过为PE文件添加一个新的区块,将PE文件的代码段入口点指向此区块。并在此区块中进行代码段的解压缩和输入表的还原以及重定位数据的处理,当然我们如果想加入反调试等技巧也可以在此区块中完成。最后我们在跳回原代码段的入口点处。

(0YNMP5)Q%}3C046NV3~7H4.png

下面我们就需要程序来实现将上图左边的PE文件变为上图右边的PE文件,这样就实现了文件的加壳。程序编写的基本思路如下:

  1. 将待加壳的PE文件的PE头和各个区段一起读入内存
  2. 将待加壳的PE文件的附加数据读入内存
  3. 修改待加壳的PE文件的输入表
  4. 修改待加壳的PE文件的重定位表
  5. 压缩待加壳的PE文件的代码段
  6. 构建新区块
  7. 将内存中修改后PE文件以及构建的新区块和附加数据按照PE文件的格式合并成新的PE文件
  8. 创建文件并写入文件,PE文件加壳完成

下面就是各个步骤中需要注意的一些问题。

将待加壳的PE文件的PE头和各个区段一起读入内存

因为将PE文件加载到内存中我们需要处理他们,而PE文件中的一些数据大部分都是以RVA的方式查询的。为了在后面的操作中能够方便处理,我们需要以windows加载器的形式将PE文件读入一块连续的内存中(对齐粒度为0x1000)并将读入内存的基地址保存以供后续操作。
在编码时还要注意有的区块在文件中大小为0,只有在PE文件映射到内存中时才会分配内存。还要注意未初始化数据所在的区块,此区块一般在实际大小大于在PE文件中对其后的大小。

将待加壳的PE文件的附加数据读入内存

附加数据在PE文件的尾部,即最后一个区块的后面。因为PE文件在映射到内存中时并不会将附加数据映射到内存,而有的程序又会在程序中使用附加数据。所以我们需要将附加数据读到内存中,在重组PE文件时将附加数据放在PE文件的尾部,即新加区块的后面。

修改待加壳的PE文件的输入表

为了防止破解者在脱壳时轻易得到输入表我们需要将输入表破坏,并将输入表的信息保存到新区块中以供外壳代码恢复IAT表。
原输入表的IID的结构如下:

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics;           
        DWORD   OriginalFirstThunk;         
      } DUMMYUNIONNAME;
    DWORD   TimeDateStamp;                                      
    DWORD   ForwarderChain;                  
    DWORD   Name;
    DWORD   FirstThunk;                    
} IMAGE_IMPORT_DESCRIPTOR;

此结构中TimeDateStamp与ForwarderChain一般是不使用的,所以我们将IID破坏后将其余三个字段保存形成自己的IID结构。重组PE文件时把我们保存的自己的IID结构放在新区块中指定的位置供新区块修复IAT表使用,当然你也可以形成自己的IID结构。

//新IID结构
typedef struct NEW_IID
{
        DWORD        FirstThunk;
        DWORD        Name;
        DWORD        OriginalFirstThunk;
};

我们将原IID的关键信息保存后,需要将原IID破坏。


        pNewIID = (NEW_IID* )stImportTable.dwAddress;                                   //指向新IID结构
        while (pOldImportTable->Name != 0)
        {
                pNewIID->FirstThunk = pOldImportTable->FirstThunk;                      //创建新的IID结构
                pNewIID->Name = pOldImportTable->Name;
                pNewIID->OriginalFirstThunk = pOldImportTable->OriginalFirstThunk;

                pOldImportTable->FirstThunk = 0;                                        //破坏旧的IID结构
                pOldImportTable->OriginalFirstThunk = 0;
                pOldImportTable->Name = 0;
                pOldImportTable->ForwarderChain = 0;
                pOldImportTable->TimeDateStamp = 0;

                pNewIID = (NEW_IID*)((BYTE*)pNewIID + sizeof(NEW_IID));
                pOldImportTable = (PIMAGE_IMPORT_DESCRIPTOR)((BYTE*)pOldImportTable + sizeof(IMAGE_IMPORT_DESCRIPTOR));
        }

修改待加壳的PE文件的重定位表

无论是EXE文件还是DLL文件都可能会重定位信息,我们和处理输入表一样将原重定位表破坏,然后将重定位信息在重组PE文件时将其保存到新区块中以供外壳代码修复重定位数据。
我的做法是将所有需要重定位地址的RVA以DWORD保存,然后将原重定位表清空。这里要注意一定不要忘记将PE文件头中数据目录表关于重定位表的信息清空,如果不清空的话加壳后的PE在被windows加载器映射到内存中时会根据此项进行重定位,有可能会破坏程序中的某些数据。

        //指向重定位表
        dwRelocateAddress = (PIMAGE_BASE_RELOCATION)(dwAddress + ((PIMAGE_DATA_DIRECTORY)(((PIMAGE_NT_HEADERS)((dwAddress + ((PIMAGE_DOS_HEADER)dwAddress)->e_lfanew)))->OptionalHeader.DataDirectory))[5].VirtualAddress);
        while (dwRelocateAddress->SizeOfBlock != 0)                                             //循环遍历重定位表的各个块
        {
                for (int i = 0; i < dwRelocateAddress->SizeOfBlock - 8; i = i + 2)
                {
                        if (0 != *((WORD*)((BYTE*)dwRelocateAddress + 8 + i)))                  //如果是重定位数据,而不是为了对齐填充的数据就将其保存
                        {
                                (*(DWORD*)dwFindAddress) = *(DWORD*)dwRelocateAddress + WORD(WORD((*((WORD*)((BYTE*)dwRelocateAddress + 8 + i))) << 4) >> 4);                                                                        //写入新的重定位表
                                *((WORD*)((BYTE*)dwRelocateAddress + 8 + i)) = 0;               //清除原重定位表
                                dwFindAddress = (DWORD)((BYTE*)dwFindAddress + 4);
                        }
                }
                dwRelocateAddress = (PIMAGE_BASE_RELOCATION)((BYTE*)dwRelocateAddress + dwRelocateAddress->SizeOfBlock);
        }

压缩待加壳的PE文件的代码段

除了压缩代码段还可以将其他区块压缩,但注意有的区块的数据不能压缩(资源块)。压缩是用的APLIB库,通过函数aP_pack()压缩代码段。保存压缩前代码段的大小和压缩后代码段的大小,在重组PE文件时保存到最后一个区块中以供外壳代码解压缩使用。

构建新区块

将上面保存的一些数据(新的IID表和重定位信息等)和外壳代码组成一个新的区块。
这里说一下外壳代码,外壳代码我使用c++裸函数实现的。也就是说我们需要将函数_ShellProc()中的数据保存到新区块中。

#pragma comment(linker, "/INCREMENTAL:NO")        
#pragma auto_inline(off)
__declspec(naked)
void _stdcall _ShellProc()
{
                                        //外壳代码
}

void _stdcall _ShellProcEnd()
{

}
#pragma auto_inline(on)

我构建的新区块的组成结构如下图所示,当然也可以采用其他结构。注意新区块的数据部分和代码部分都是以0x200为对齐粒度。

D4PKFE0C[@@N2IEEG5[AN.png

NEW_DATA是我们自己定义的一个结构。

typedef struct NEW_DATA{
        TCHAR        szSectionName[0x10];                   //新区块的名称

        TCHAR        szDllName[0x10];                       //IID指向的DLL的名称
        IMAGE_IMPORT_DESCRIPTOR                 stIID[2];   //加壳程序的IID
        IMAGE_THUNK_DATA                        stINT[3];   //加壳程序的INT
        BYTE         bProcName1[0x10];                      //GetProcAddress
        BYTE         bProcName2[0x10];                      //LoadLibrary
        IMAGE_THUNK_DATA                        stIAT[3];   //加壳程序的IAT

        DWORD        dwIIDAddress;                          //新IID相对于最后一个区块的偏移
        DWORD        dwIIDSize;                             //新IID的大小
        DWORD        dwRelocationAddress;                   //新重定位表相对于最后一个区块的偏移
        DWORD        dwRelocationSize;                      //新重定位表的大小
        DWORD        dwOldEP;                               //原程序旧的入口点
        DWORD        dwImageBase;                           //默认加载基址
        DWORD        dwPackCodeSize;                        //压缩后代码段的大小
};

此结构是方便我们外壳代码还原原程序以及构建新的输入表使用的,因为我们在外壳代码中需要使用最基本的两个函数为GetProcAddress()和LoadLibreryA()以此来调用其他API函数,所以我么要构建新的输入表就是包含这两个函数。

RtlZeroMemory(&stNewData, sizeof(NEW_DATA));
        lstrcpy(stNewData.szSectionName, TEXT(".pack"));                           //新区块名为.pack
        lstrcpy(stNewData.szDllName, TEXT("kernel32.dll"));                        //新IID的DLL名称
        stNewData.stIID[0].OriginalFirstThunk = (*((DWORD*)((dwAddress + *((DWORD*)(dwAddress + 0x3c))) + 0x50))) + 0x20 + sizeof(IMAGE_IMPORT_DESCRIPTOR) * 2;
        stNewData.stIID[0].TimeDateStamp = 0;
        stNewData.stIID[0].ForwarderChain = 0;
        stNewData.stIID[0].Name = (*((DWORD*)((dwAddress + *((DWORD*)(dwAddress + 0x3c))) + 0x50))) + 0x10;
        stNewData.stIID[0].FirstThunk = (*((DWORD*)((dwAddress + *((DWORD*)(dwAddress + 0x3c))) + 0x50))) + 0x40 + sizeof(IMAGE_IMPORT_DESCRIPTOR) * 2 + sizeof(IMAGE_THUNK_DATA) * 3;
        stNewData.stINT[0].u1.AddressOfData = (*((DWORD*)((dwAddress + *((DWORD*)(dwAddress + 0x3c))) + 0x50))) + 0x20 + sizeof(IMAGE_IMPORT_DESCRIPTOR) * 2 + sizeof(IMAGE_THUNK_DATA) * 3;
        stNewData.stINT[1].u1.AddressOfData = (*((DWORD*)((dwAddress + *((DWORD*)(dwAddress + 0x3c))) + 0x50))) + 0x30 + sizeof(IMAGE_IMPORT_DESCRIPTOR) * 2 + sizeof(IMAGE_THUNK_DATA) * 3;
        lstrcpy((LPSTR)(stNewData.bProcName1 + 2), TEXT("GetProcAddress"));
        lstrcpy((LPSTR)(stNewData.bProcName2 + 2), TEXT("LoadLibraryA"));
        stNewData.stIAT[0].u1.AddressOfData = (*((DWORD*)((dwAddress + *((DWORD*)(dwAddress + 0x3c))) + 0x50))) + 0x20 + sizeof(IMAGE_IMPORT_DESCRIPTOR) * 2 + sizeof(IMAGE_THUNK_DATA) * 3;
        stNewData.stIAT[1].u1.AddressOfData = (*((DWORD*)((dwAddress + *((DWORD*)(dwAddress + 0x3c))) + 0x50))) + 0x30 + sizeof(IMAGE_IMPORT_DESCRIPTOR) * 2 + sizeof(IMAGE_THUNK_DATA) * 3;
        stNewData.dwIIDAddress = sizeof(NEW_DATA) + 4;
        stNewData.dwIIDSize = stImportTable.dwSize;
        stNewData.dwRelocationAddress = stImportTable.dwSize + sizeof(NEW_DATA) + 8;
        stNewData.dwRelocationSize = stRelocation.dwSize;
        stNewData.dwOldEP = *((DWORD*)((dwAddress + *((DWORD*)(dwAddress + 0x3c))) + 0x28));
        stNewData.dwImageBase = *((DWORD*)((dwAddress + *((DWORD*)(dwAddress + 0x3c))) + 0x34));
        stNewData.dwPackCodeSize = dwPackCodeSize;

将修改后的PE文件和新区块和附加数据合并成新的PE文件并写入文件

我们需要将PE文件头部的一些信息修改一下。需要新增一个区块,修改程序代码入口点为外壳代码入口点,修改数据目录表中输入表和输入地址表的地址和大小,还要将数据目录表中对应的重定位表的地质和大小置零,最后修改PE文件的映射大小。

    //修改PE文件头信息
        lstrcpy((LPSTR)pSection->Name, ((NEW_DATA*)stSection.dwAddress)->szSectionName);
        pSection->Misc.VirtualSize = stSection.dwSize;
        pSection->VirtualAddress = (*((DWORD*)((dwAddress + *((DWORD*)(dwAddress + 0x3c))) + 0x50)));
        pSection->SizeOfRawData = stSection.dwSize;
        pSection->PointerToRawData = dwMaxSectionAddress;
        pSection->Characteristics = 0xE0000020;                                
        *((WORD*)((dwAddress + *((DWORD*)(dwAddress + 0x3c))) + 0x6)) = *((WORD*)((dwAddress + *((DWORD*)(dwAddress + 0x3c))) + 0x6)) + 1;                                          //增加区块的数量
        (*(DWORD*)(dwAddress + *((DWORD*)(dwAddress + 0x3c)) + 0x28)) = pSection->VirtualAddress + stSection.dwDataSize;                                                                                //修改代码入口点
        ((PIMAGE_NT_HEADERS)(dwAddress + *((DWORD*)(dwAddress + 0x3c))))->OptionalHeader.DataDirectory[1].VirtualAddress = (*((DWORD*)((dwAddress + *((DWORD*)(dwAddress + 0x3c))) + 0x50))) + 0x20;                 //修改输入表的地址
        ((PIMAGE_NT_HEADERS)(dwAddress + *((DWORD*)(dwAddress + 0x3c))))->OptionalHeader.DataDirectory[1].Size = sizeof(IMAGE_IMPORT_DESCRIPTOR);                                                                                                                        //修改输入表的大小
        ((PIMAGE_NT_HEADERS)(dwAddress + *((DWORD*)(dwAddress + 0x3c))))->OptionalHeader.DataDirectory[12].VirtualAddress = (*((DWORD*)((dwAddress + *((DWORD*)(dwAddress + 0x3c))) + 0x50))) + 0x40 + sizeof(IMAGE_IMPORT_DESCRIPTOR) * 2 + sizeof(IMAGE_THUNK_DATA) * 3;                //修改输入地址表的地址
        ((PIMAGE_NT_HEADERS)(dwAddress + *((DWORD*)(dwAddress + 0x3c))))->OptionalHeader.DataDirectory[12].Size = sizeof(IMAGE_THUNK_DATA) * 3;                                                                                                                                                                                                                                                                        //修改输入地址表的大小
        ((PIMAGE_NT_HEADERS)(dwAddress + *((DWORD*)(dwAddress + 0x3c))))->OptionalHeader.DataDirectory[5].Size = 0;                                                                                         //修改重定位表的地址为0
        ((PIMAGE_NT_HEADERS)(dwAddress + *((DWORD*)(dwAddress + 0x3c))))->OptionalHeader.DataDirectory[5].VirtualAddress = 0;                                                                   //修改重定位表的大小为0

        if (pSection->SizeOfRawData % 0x1000 != 0)
                (*((DWORD*)((dwAddress + *((DWORD*)(dwAddress + 0x3c))) + 0x50))) = pSection->VirtualAddress + ((pSection->SizeOfRawData / 0x1000 + 1) * 0x1000);   //修改映射大小
        else
                (*((DWORD*)((dwAddress + *((DWORD*)(dwAddress + 0x3c))) + 0x50))) = pSection->VirtualAddress + ((pSection->SizeOfRawData / 0x1000) * 0x1000);

外壳代码的编写


一般外壳代码编写有一下两种方法

  • 外壳代码编写可以用纯汇编编写,
  • 也可以用C++利用内嵌汇编然后编写DLL,之后将DLL中的代码段和数据段一起保存到新区块中

用汇编写外壳代码虽然灵活但是麻烦,用第二种也不是很简单。我用的是裸函数结合c++内嵌汇编的特点,其实相当于第一种方法但是编写的时候可以用c++写,而编译器帮我们将其变为了对应的汇编指令,但是注意因为是裸函数所以我们需要自己来构建函数的栈空间和局部变量。不能采用字符串常量和全局变量以及任何不存在栈中的数据。而且对于DLL的加壳和EXE的加壳外壳代码会有一些不同,原因是DLL的入口点是会被多次调用的而EXE的入口点只会被调用一次。

EXE加壳的外壳代码

外壳代码一开始要做一些初始化工作,保存环境并且构建一个足够大的工作栈空间

_asm{
start:          push eax                                                           //为了方便跳到程序入口点
                pushad                                                             //保存环境
                mov ebp, esp
                sub esp, 0x400;                                                    //创建工作栈空间
        }

结合需要创建一些需要用到的一些局部变量并将其初始化。注意那些字符串变量不能用字符串常量初始化,因为字符串常量在静态存储区不在栈中,我们可以用字符一个一个初始化。

         TCHAR                                szVirtualFree[0x10];
        szVirtualFree[0] = 'V';
        szVirtualFree[1] = 'i';
        szVirtualFree[2] = 'r';
        szVirtualFree[3] = 't';
        szVirtualFree[4] = 'u';
        szVirtualFree[5] = 'a';
        szVirtualFree[6] = 'l';
        szVirtualFree[7] = 'F';
        szVirtualFree[8] = 'r';
        szVirtualFree[9] = 'e';
        szVirtualFree[10] = 'e';
        szVirtualFree[11] = 0;

        DWORD                                dwImageBaseAddress;              //被加壳程序加载基地址
        DWORD                                dwPackCodeAddress;               //压缩代码段数据的地址
        DWORD                                dwPackCodeSize;                  //压缩代码段数据的大小
        DWORD                                dwUpackCodeAddress;              //解压缩代码数据的地址
        DWORD                                dwUpackCodeSize;                 //解压缩代码数据的大小
        DWORD                                dwOldVirtualProtect;             //代码段内存旧的属性
        DWORD                                dwNewIID;                        //指向新的IID表
        DWORD                                dwDllName;                       //指向IID的DLL名称
        DWORD                                dwDllINT;                        //指向INT表
        DWORD                                dwProcName;                      //指向INT的函数名称
        DWORD                                dwProcAddress;                   //函数的地址
        DWORD                                dwRelocAddress;                  //指向新重定位表的地址
        DWORD                                dwDefultImageAddress;            //默认的加载基址
        DWORD                                dwIAT;                           //指向IAT表
        DWORD                                dwProtect;                       //用来保存IAT表原来的内存属性
        DWORD                                dwOldEntry;                      //程序真正的入口点
        DWORD                                dwLoadDllAddress;                //加载DLL的基地址
        DWORD                                dwProcNum;                       //导入函数的序数值
        DWORD                                dwProcIndex;                     //函数在DLL中的索引

        PIMAGE_SECTION_HEADER pSection;                                       //区块表指针

        //各个需要用到的函数的指针
        pfnMyGetProcAddress                  MyGetProcAddress;
        pfnMyLoadLibraryA                    MyLoadLibraryA;
        pfnMyVirtualAlloc                    MyVirtualAlloc;
        pfnMyVirtualProtect                  MyVirtualProtect;
        pfnMylstrcmpA                        MylstrcmpA;
        pfnMymemset                          Mymemset;
        pfnMymemcpy                          Mymemcpy;
        pfnMyVirtualFree                     MyVirtualFree;

我们需要获得程序加载的基地址,然后进一步获得GetProcAddress()和LoadLibraryA()函数的地址。对于EXE程序来说我们可以通过PEB结构的ImageBaseAddress字段获得。

_asm
        {
                mov eax,fs:[0x30]                                         //PEB地址
                mov eax,dword ptr [eax + 0x8]                    //获得程序加载基地址
                mov dwImageBaseAddress,eax

        }

然后对代码段解压缩,接着修正重定位信息和填充IAT表。其中代码段解压的函数需要将aP_depack()函数的汇编代码通过内嵌汇编保存到外壳代码中。做完这些我们就可以跳转到原程序的OEP中了。

        //平衡堆栈,跳到OEP
        _asm
        {
                mov esp,ebp
                mov eax,dwOldEntry                                //dwOldEntry为原代码入口点
                mov dword ptr[esp + 0x20],eax
                popad
                pop eax
                jmp eax
        }

DLL加壳的外壳代码

DLL加壳的外壳代码大部分都和EXE一样。只不过其需要解决入口点需要被多次调用的问题,以及其获得程序代码入口点的方法和EXE不同。
解决其入口点被多次调用,一般DLL入口点的函数为BOOL APIENTRY DllMain( HMODULE hModule,  DWORD  ul_reason_for_call,  LPVOID lpReserved ),当入口点被第一次调用时ul_reason_for_call值为1,所以我们可以在入口处加上判断ul_reason_for_call参数值的代码。

          _asm
        {
                mov eax,[esp + 4]
                add ebx,[eax + 0x3c]
                add ebx,eax
                mov ebx,[ebx + 0x28]               
                add eax,ebx                                                //获得原程序的入口点代码
                mov ebx,[esp + 8]
                cmp ebx,1                                                  //判断ul_reason_for_call值是否为1
                je start
                jmp eax                                                    //直接跳到原程序入口点

start:          push eax                                                   //为了方便跳到程序入口点
                pushad                                                     //保存环境
                mov ebp, esp
                sub esp, 0x400;                                            //创建工作栈空间
        }

在第一调用外壳代码最后需要将PE中的程序入口RVA还原,使得在以后调用外壳代码时上方代码可以得到原程序入口点

        MyVirtualProtect((LPVOID)((dwImageBaseAddress + *((DWORD*)(dwImageBaseAddress + 0x3c))) + 0x28), 4, PAGE_READWRITE, &dwProtect);                        //修改属性
        (*((DWORD*)((dwImageBaseAddress + *((DWORD*)(dwImageBaseAddress + 0x3c))) + 0x28))) = dwOldEntry - dwImageBaseAddress;                                          //对于DLL而言修改入口点
        MyVirtualProtect((LPVOID)((dwImageBaseAddress + *((DWORD*)(dwImageBaseAddress + 0x3c))) + 0x28), 4, dwProtect, &dwProtect);

DLL外壳代码不能通过PEB获得程序加载基址,因为PEB的ImageBaseAddress字段是其exe对应的加载基地址。DLL通过入口函数BOOL APIENTRY DllMain( HMODULE hModule,  DWORD  ul_reason_for_call,  LPVOID lpReserved )的参数hModule就是其加载基地址。

总结


此加壳程序没有什么高端技术,但是可以在此基础上添加其他代码,这些就是我编写此加壳器所遇到的一些问题和思路。写一个加壳器对PE文件的格式也能有进一步的认识,而且对于脱壳也能有很大的帮助,进一步了解壳的结构和基本工作原理。附上源代码供大家学习

Pack.zip

22.55 KB, 下载次数: 170, 下载积分: 吾爱币 -1 CB

免费评分

参与人数 11威望 +1 吾爱币 +36 热心值 +10 收起 理由
xingdongpai + 1 + 1 用心讨论,共获提升!
大黑屋 + 1 大佬啊 dll也是一样的吗? dll应该问题多吧
7fMeteor + 1 + 1 用心讨论,共获提升!
boyving + 3 + 1 我很赞同!
iyzyi + 1 + 1 谢谢@Thanks!
FleTime + 3 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
Nghitsong + 1 + 1 鼓励转贴优秀软件安全工具和文档!
小朋友呢 + 2 + 1 热心回复!
2kkkk + 1 我很赞同!
苏紫方璇 + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
Milk-Tea + 3 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!

查看全部评分

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

boyving 发表于 2020-10-5 08:28
非常好的帖子。
刀大喵 发表于 2020-10-5 08:50
LilyGor 发表于 2021-8-3 16:33
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

快速回复 收藏帖子 返回列表 搜索

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

GMT+8, 2024-8-9 04:06

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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