首页
社区
课程
招聘
[原创]Windows平台反调试技术学习
发表于: 2024-1-3 18:25 15912

[原创]Windows平台反调试技术学习

2024-1-3 18:25
15912

前俩天的学习记录Windows上面的反调试学习,主要是参考《恶意代码实战分析》和《加密与解密》里面的,给每个小技术都写了程序示例,自己编译反调试了一遍。对于加解密一书是还有很多不理解的地方的,目前只能记录到这了,后面继续慢慢学吧,欢迎一起交流讨论,谢谢师傅。

IsDebuggerPresent函数通过获取进程环境块(PEB)中的BeingDebugged标志来检测进程是否处于调试状态。

其实现代码:

BeingDebugged是PEB中的一个标志。每个运行中的进程都有一个PEB结构,其0x02偏移处就是BeingDebugged标志,如果程序处于调试状态,该标志的值会被设置为非零值。相关Windows API就是通过访问该值来进行反调试操作。

如何访问PEB?PEB的地址储存在另一个名为线程环境块(TEB)中。

Windows在调入进程、创建线程时,操作系统会为每个线程分配TEB,而且FS段寄存器总是被设置成使得FS:[0]指向当前线程的TEB数据。而TEB结构中的0x30偏移处正是PEB的地址。

Windows一般通过TEB间接获取PEB的地址:

TIB+18h处为Self。它是TIB的自身指针,指向TEB的首地址。因此也可以省略它直接使用fs:[30h]得到自己进程的PEB。

如何过掉IsDebuggerPresent?

示例程序:

第一种方法是IDA中在修改恶意代码(例如jz跳转将其改为jnz,或者修改cmp)

image-20231231133724934

第二种使用xdbg,在数据窗口使用“Ctrl+G”搜索fs:[30]+2

image-20231231133408373

这里的01就是BeingDebugged标志,将其用“Ctrl+E”修改成00,运行程序,程序输出:
image-20231231133520252

CheckRemoteDebuggerPresent不仅可以探测进程自身是否被调试,也可以探测系统其他进程是否被调试。函数接收两个参数进程句柄和一个指向布尔值的指针。如果指定的进程正在被调试,则函数会把指向布尔值的指针设为 TRUE,否则设为FALSE。

函数原型:

程序示例:

在《恶意代码分析实战》一书中提到该函数也检测了BeingDebugged标志,而在《加密与解密》中说该函数并没有使用BeingDebugged标志,按照过IsDebuggerPresent的方式再试一次,发现CheckRemoteDebuggerPresent的确没有用到BeingDebugged标志。

image-20231231134736373

将BeingDebugged标志位设为00后,程序依旧处于反调试状态。

那么CheckRemoteDebuggerPresent是通过什么来进行反调试的?

在xdbg中Ctrl+G搜索CheckRemoteDebuggerPresent,可以找到该函数的汇编代码

其中唯一关键处就是在17行调用了NtQueryInformationProcess函数。

该函数原型:

该函数会根据不同的 ProcessInformationClass 查询有关一个进程对象的信息,在文档中列举了一些情况:

回到上述汇编,正是查询了7号信息ProcessDebugPort

image-20231231141616259

所以说,CheckRemoteDebuggerPresent实际上是调用了NtQueryInformationProcess函数,查询了某个进程的ProcessDebugPort,这个值是系统用来与调试器通信的端口句柄。

如何过掉CheckRemoteDebuggerPresent?

在IDA中和IsDebuggerPresent都可以采取修改恶意代码的形式,使用xdbg的话,将传给NtQueryInformationProcess的参数7修改掉就可以了。

image-20231231142550898

在本例中修改了参数7可以过掉(可能只是个例,因为修改成0,也就是获取进程的基本信息,应该不一定会成功)。还是应该要修改后面的eax寄存器中的值。将01修改成00。

image-20231231165315883

OutPutDebugString函数的作用是在调试器中显示一个字符串。

函数原型:

如何用它来检测调试状态?

可以配合SetLastErrorGetLastError 函数,这俩个函数前者将当前的错误码设置成一个任意值,后者是获取当前的错误码。如果进程没有被调试器附加,那么调用OutPutDebugString函数就会失败,错误码会被重新设置,因此再使用GetLastError函数获取的错误码应该就不是我们设置的值。若进程被调试器附加并调用了OutPutDebugString函数,那么该函数会调用成功,GetLastError函数获取的也就是我们设置的值。

程序示例:

上述程序是由《恶意代码实战分析》的代码清单扩展来的,但实际发现,没有附加调试器的情况下,也会报**“[-] 检测到调试器”**。原因应该是因为:程序没有调用OutPutDebugString函数也会改变错误码。

一种调试器攻击。

ZwSetInformationThread函数,用于设置线程的优先级。示例如下:

这个函数可以设置一个与线程相关的信息。查看ThreadInformationClass列表:
image-20231231170307307

可以看到ThreadHideFromDebugger,关于它的定义:
这个信息类只能被设置。它禁用了线程的调试事件生成。这个信息类不需要数据,因此 ThreadInformation 可以是一个空指针。ThreadInformationLength 应该是零。

通过为线程设置ThreadHideFromDebugger,可以禁止某个线程产生调试事件。

测试示例:

我用单步测试的,在打印“当前线程已尝试隐藏自调试器.”之后延迟一会程序就会退出调试状态。

**关于怎么干掉这个反调试。(待学)**https://bbs.kanxue.com/thread-249689.html

在一些情况下,程序中可能没有使用Windows API进行反调试,所以需要我们手动检查数据结构,关注一些会暴露调试器的数据结构(PEB)。

BeingDebugged如上文所说,位于PEB中的0x2偏移处

image-20231231160314219

过掉这种调试的方法就是在执行跳转是,手动修改零标志(使其强制跳转或不跳转);或者修改跳转指令;手动设置BeingDebugged属性值为0;

在PEB结构的Reserved4数组中有一个未公开的位置叫做ProcessHeap,它被设置为加载器为进程分配的第一个堆的位置。ProcessHeap位于PEB结构的0x18偏移处。第一个堆头部有一个属性字段,它告诉内核这个堆是否在调试器中创建。这些属性叫做ForceFlagsFlags

在正常情况下,系统在为进程创建第一个堆时,会将它的Flags和ForceFlags分别设为2和0,而在调试状态下,这俩个标志通常会被设为50000062h(取决于NtGlobalFlag)和40000060h。

同时,Flags位于ProcessHeap的0x0c偏移处ForceFlags位于ProcessHeap的0x10偏移处

因此可以写出这样一段检测代码:

对付这种反调试的方法之一就是手动修改ProcessHeap标志。

NtGlobalFlag位于PEB结构的0x68偏移处。因为在调试器中启动进程和正常模式下启动进程时它们创建内存堆的方式不同。如果进程是由调试器创建的,那么该标志的值会被设置成0x70

检测代码:

image-20231231163106456

过掉反调试的方法都差不多。

在逆向工程中,进行代码分析时,可以用调试器设置断点,或者单步执行一个进程。当调试器在执行这些操作时,它们会修改进程中的代码。因此,恶意代码常使用探测INT扫描完整性校验,以及时钟检测等几种类型的调试器行为。

原理:调试器在设置断点时的一般采用的是软件断点(INT 3),打下INT 3后调试器会临时替换运行程序中的一条指令,当程序运行到这里时,调用调试异常处理例程

INT 3 的机器码是0xCC,所以若是在关键位置检测到该指令,就可以判断进程处于调试状态。。

常用的反调试方法,扫描0xCC。

这段先执行了一个函数调用,随后用pop指令将eip寄存器的值存入edi,然后将edi设置为代码的开始。接下来扫描这段代码的0xCC字节,如果发现0xCC则证明存在调试器。

对抗这种反调试技术的方法就是使用硬件断点

示例程序:

用xdbg调试,Ctrl+G搜索MessageBoxA。在函数处下断点,之后一直单步就可以发现程序输出了检测到调试。

查看汇编代码:

image-20240101102602420

在调用完函数后,调试器扫描了esp-10处开始的字节,查找0xCC。

对抗:

在xdbg右键断点设置硬件断点,

image-20240101102927639

再次调试发现,变成了无调试。成功过掉。(

先了解一下什么是硬件断点,硬件断点和DRx寄存器有关,下图是Intel CPU体系架构里对DRx寄存器的介绍。

image-20240101113735452

硬件断点的原理是使用DR0~DR3设定地址,并使用DR7设定状态,因此最多设置4个断点。

怎么实现硬件断点反调试?

先获取硬件断点信息,利用函数GetThreadContext,它检索指定线程的上下文。

函数原型:

第一个参数是要检索其上下文线程的句柄,第二个参数指向CONTEXT结构。该结构是一个在Windows API中定义的结构体,它用于存储线程的上下文信息,包括寄存器和其他重要的状态信息。

image-20240101115917410

因此我们需要获取CONTEXT结构体中的DRx寄存器的信息,使用CONTEXT_DEBUG_REFGISTERS标志。

示例程序:

进行调试
image-20240101120236208

随便下个硬件断点,检测成功。

采用异常来进行硬件断点反调试(待学)https://www.cnblogs.com/Sna1lGo/p/15358626.html

恶意代码可以计算代码段的校验并实现与扫描中断相同的目的。与扫描0xCC不同,这种检查仅执行恶意代码中机器码的CRC(循环冗余校验)MD5校验和检查。

下面是一个简单的示例:

在这个程序中,checksum变量计算为代码段中所有字节的累加和,并对每个字节累加后进行一次ROL。然后将这个 checksum 和一个预设的校验和值比较。

关于绕过,可以修改检查函数或者校验和值。

程序调试时,进程的运行速度大大降低(单步调试)。

有如下俩种用时钟检测来探测调试器存在的方法:

rdtsc指令(操作码0x0F31)用于获取CPU自开机运行起的时钟周期数,并且将其作为一个64位的值存入edx和eax寄存器中。执行俩次rdstc指令,然后比较这俩次取值之间的差值

汇编:

俩次调用rdtsc,先检查了高位edx相不相同,相同的话再检查低位的差值是否大于0x20。

QueryPerformanceCounter函数检索性能计数器的当前值,这是一个高分辨率 (<1us) 时间戳,可用于时间间隔度量。

GetTickCount检索自系统启动以来经过的毫秒数。

这俩个函数都可以用于时钟检测。

下面是一个GetTickCount的示例:

过掉时钟检测比较好的办法是在检测指令之后下断点。例如:

image-20240101111332854

从理论上讲,一个程序被正常启动时,其父进程应该是Exploer.exe(资源管理器启动)、**cmd.exe(命令行启动)或者Services.exe(系统服务)**中的一个。如果某一个进程的父进程并非上述3个进程之一,一遍可以认为它被调试了(或者被内存补丁之类的Loader程序加载了)。

实现这种检测的方法:

Thread Local Storage(TLS),即线程本地存储,是Windows为解决一个进程中多个线程同时访问全局变量而提供的机制。

TLS回调函数就是在程序加载到调试器后,TLS回调会先于程序入口执行之前运行代码,这样就可以提前进行反调试操作或者修改代码。

在IDA中可以使用 Ctrl + E 查看二进制的入口点。

image-20240101121030613

在PEview等PE工具中也可以查看.tls段,因为通常情况下,正常程序不使用.tls段。

image-20240101121149354

(待学)。。。

调试器使用 INT 3设置软件断点,所以一种反调试技术就是在合法代码中插入0xCC来欺骗调试器,使其认为这些0xCC机器码是自己设置的段带你。

一些调试器用跟踪自身设置的断点的方法来避免这种反调试技术。

嗯。待学(INT 2D。ICE是什么。)
参考:
《加密与解密》第四版-第十八章反跟踪技术
《恶意代码分析实战》-第十六章反调试技术
MSDN文档 https://visualstudio.microsoft.com/zh-hans/msdn-platforms/
反调试技术总结 https://bbs.kanxue.com/thread-225740.htm#msg_header_h2_7
硬件断点 https://www.cnblogs.com/Sna1lGo/p/15358626.html
软件断点 https://blog.csdn.net/YANG12345_6/article/details/119904884

BOOL WINAPI IsDebuggerPresent(VOID)
{
    return NtCurrentPeb() -> BeingDebugged;
}
BOOL WINAPI IsDebuggerPresent(VOID)
{
    return NtCurrentPeb() -> BeingDebugged;
}
#include <stdio.h>
#include <windows.h>
 
BOOL CheckDebugger()
{
    // 直接调用IsDebuggerPresent函数,不需要定义额外的指针
    return IsDebuggerPresent();
}
 
int main(int argc, char *argv[])
{
    if (CheckDebugger())
    {
        printf("[-] 进程正在被调试 \n");
    }
    else
    {
        printf("[+] 进程没有被调试 \n");
    }
 
    system("pause");
    return 0;
}
#include <stdio.h>
#include <windows.h>
 
BOOL CheckDebugger()
{
    // 直接调用IsDebuggerPresent函数,不需要定义额外的指针
    return IsDebuggerPresent();
}
 
int main(int argc, char *argv[])
{
    if (CheckDebugger())
    {
        printf("[-] 进程正在被调试 \n");
    }
    else
    {
        printf("[+] 进程没有被调试 \n");
    }
 
    system("pause");
    return 0;
}
BOOL CheckRemoteDebuggerPresent{
    HANDLE hProcess,
    PBOOL pbDebuggerPresent
};
BOOL CheckRemoteDebuggerPresent{
    HANDLE hProcess,
    PBOOL pbDebuggerPresent
};
#include <stdio.h>
#include <windows.h>
 
// 定义指针
typedef BOOL(WINAPI *CHECK_REMOTE_DEBUG_PROCESS)(HANDLE, PBOOL);
 
BOOL CheckDebugger()
{
    BOOL bDebug = FALSE;
    CHECK_REMOTE_DEBUG_PROCESS CheckRemoteDebuggerPresent;
 
    HINSTANCE hModule = GetModuleHandle("kernel32");
    CheckRemoteDebuggerPresent = (CHECK_REMOTE_DEBUG_PROCESS)GetProcAddress(hModule, "CheckRemoteDebuggerPresent");
 
    HANDLE hProcess = GetCurrentProcess();
 
    CheckRemoteDebuggerPresent(hProcess, &bDebug);
    return bDebug;
}
 
int main(int argc, char *argv[])
{
    if (CheckDebugger())
    {
        printf("[-] 进程正在被调试 \n");
    }
    else
    {
        printf("[+] 进程没有被调试 \n");
    }
 
    system("pause");
    return 0;
}
#include <stdio.h>
#include <windows.h>
 
// 定义指针
typedef BOOL(WINAPI *CHECK_REMOTE_DEBUG_PROCESS)(HANDLE, PBOOL);
 
BOOL CheckDebugger()
{
    BOOL bDebug = FALSE;
    CHECK_REMOTE_DEBUG_PROCESS CheckRemoteDebuggerPresent;
 
    HINSTANCE hModule = GetModuleHandle("kernel32");
    CheckRemoteDebuggerPresent = (CHECK_REMOTE_DEBUG_PROCESS)GetProcAddress(hModule, "CheckRemoteDebuggerPresent");
 
    HANDLE hProcess = GetCurrentProcess();
 
    CheckRemoteDebuggerPresent(hProcess, &bDebug);
    return bDebug;
}
 
int main(int argc, char *argv[])
{
    if (CheckDebugger())
    {
        printf("[-] 进程正在被调试 \n");
    }
    else
    {
        printf("[+] 进程没有被调试 \n");
    }
 
    system("pause");
    return 0;
}
__kernel_entry NTSTATUS NtQueryInformationProcess(
  [in]            HANDLE           ProcessHandle,
  [in]            PROCESSINFOCLASS ProcessInformationClass,
  [out]           PVOID            ProcessInformation,
  [in]            ULONG            ProcessInformationLength,
  [out, optional] PULONG           ReturnLength
);
__kernel_entry NTSTATUS NtQueryInformationProcess(
  [in]            HANDLE           ProcessHandle,
  [in]            PROCESSINFOCLASS ProcessInformationClass,
  [out]           PVOID            ProcessInformation,
  [in]            ULONG            ProcessInformationLength,
  [out, optional] PULONG           ReturnLength
);
Value Meaning
ProcessInformationClass 0 搜索指向 PEB 结构的指示器,该结构可用于确定指定进程是否正在调试,以及系统用于标识指定进程的唯一值。使用CheckRemoteDebuggerPresentGetProcessId 函数获取此信息。
ProcessDebugPort 7 检索DWORD_PTR值,该值是进程的调试器的端口号。非零值表示进程正在环3调试器的控制下运行。使用CheckRemoteDebuggerPresentIsDebuggerPresent函数。
ProcessWow64Information 26 确定进程是否在 WOW64 环境中运行(WOW64 是允许基于 Win32 的应用程序在 64 位 Windows 上运行的 x86 模拟器)。使用IsWow64Process2函数获取此信息。
ProcessImageFileName 27 搜索包含进程图像文件名称的 **UNICODE_STRING值。**使用QueryFullProcessImageNameGetProcessImageFileName函数获取此信息。
ProcessBreakOnTermination 29 搜索ULONG值,该值指示进程是否被视为关键进程。注意 从 Windows XP SP3 开始可以使用该值。从 Windows 8.1 开始,应改用IsProcessCritical 。
**ProcessTelemetryIdInformation **64 检索包含有关进程的元数据的**PROCESS_TELEMETRY_ID_INFORMATION_TYPE值。**
ProcessSubsystemInformation 75 检索指示进程子系统类型的**SUBSYSTEM_INFORMATION_TYPE值。**ProcessInformation参数指向的蜡烛图应该足够大以容纳单个SUBSYSTEM_INFORMATION_TYPE枚举。
void OutputDebugStringW(
  [in, optional] LPCWSTR lpOutputString
);
void OutputDebugStringW(
  [in, optional] LPCWSTR lpOutputString
);
#include <windows.h>
#include <stdio.h>
 
void CheckDebugger()
{
    DWORD errorValue = 12345;
    SetLastError(errorValue);
 
    OutputDebugString("Test for Debugger");
 
    // 检查GetLastError返回的值是否改变
    if (GetLastError() == errorValue) {
        printf("[-] 检测到调试器\n");
        ExitProcess(1); // 结束进程
    } else {
        printf("[+] 未检测到调试器\n");
    }
}
 
int main()
{
    CheckDebugger();
    // 这里可以添加其他代码
 
    return 0;
}
#include <windows.h>
#include <stdio.h>
 
void CheckDebugger()
{
    DWORD errorValue = 12345;
    SetLastError(errorValue);
 
    OutputDebugString("Test for Debugger");
 
    // 检查GetLastError返回的值是否改变
    if (GetLastError() == errorValue) {
        printf("[-] 检测到调试器\n");
        ExitProcess(1); // 结束进程
    } else {
        printf("[+] 未检测到调试器\n");
    }
}
 
int main()
{
    CheckDebugger();
    // 这里可以添加其他代码
 
    return 0;
}
NTSYSAPI NTSTATUS ZwSetInformationThread(
  [in] HANDLE          ThreadHandle,
  [in] THREADINFOCLASS ThreadInformationClass,
  [in] PVOID           ThreadInformation,
  [in] ULONG           ThreadInformationLength
);
NTSYSAPI NTSTATUS ZwSetInformationThread(
  [in] HANDLE          ThreadHandle,
  [in] THREADINFOCLASS ThreadInformationClass,
  [in] PVOID           ThreadInformation,
  [in] ULONG           ThreadInformationLength
);
#include <windows.h>
// #include <winternl.h> // 包含 NTSTATUS
#include <stdio.h>
 
// 确保 NTSTATUS 已定义。如果没有,手动定义它。
#ifndef NTSTATUS
typedef LONG NTSTATUS;
#endif
 
// 定义 ZwSetInformationThread 函数类型
typedef NTSTATUS (WINAPI *ZW_SET_INFORMATION_THREAD)(HANDLE, DWORD, PVOID, ULONG);
#define ThreadHideFromDebugger 0x11 // 17 in decimal
 
// 函数来禁用调试事件
VOID DisableDebugEvent(VOID)
{
    HMODULE hModule;
    ZW_SET_INFORMATION_THREAD ZwSetInformationThread;
 
    // 获取 ntdll.dll 模块的句柄
    hModule = GetModuleHandleA("Ntdll.dll");
    if (hModule == NULL) {
        printf("无法获取 ntdll.dll 的句柄.\n");
        return;
    }
 
    // 获取 ZwSetInformationThread 函数的地址
    ZwSetInformationThread = (ZW_SET_INFORMATION_THREAD)GetProcAddress(hModule, "ZwSetInformationThread");
    if (ZwSetInformationThread == NULL) {
        printf("无法获取 ZwSetInformationThread 函数的地址.\n");
        return;
    }
 
    // 调用函数尝试隐藏当前线程
    NTSTATUS status = ZwSetInformationThread(GetCurrentThread(), ThreadHideFromDebugger, NULL, 0);
    if (status != 0) {
        printf("调用 ZwSetInformationThread 失败,状态码: 0x%X\n", status);
    } else {
        printf("当前线程已尝试隐藏自调试器.\n");
    }
}
 
int main()
{
    printf("测试程序开始...\n");
 
    // 尝试禁用调试事件
    DisableDebugEvent();
    printf(".....");
 
    return 0;
}
#include <windows.h>
// #include <winternl.h> // 包含 NTSTATUS
#include <stdio.h>

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2024-1-4 18:25 被kanxue编辑 ,原因:
收藏
免费 20
支持
分享
最新回复 (15)
雪    币: 10
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
图片有问题呦
2024-1-3 23:01
0
雪    币: 638
活跃值: (614)
能力值: ( LV4,RANK:43 )
在线值:
发帖
回帖
粉丝
3
mb_ldbucrik 图片有问题呦
更新了,应该可以了。
2024-1-3 23:45
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
4
图像仍然无法显示
2024-1-4 04:29
0
雪    币: 4822
活跃值: (3822)
能力值: ( LV12,RANK:230 )
在线值:
发帖
回帖
粉丝
5
给你推荐一篇老文章,成体系地总结了一堆相关技术,为防止链接失效,刚才测试了一下,还在

Anti-Unpacker Tricks - Peter Ferrie [2008-05-01]
https://pferrie.tripod.com/papers/unpackers.pdf
2024-1-4 08:35
1
雪    币: 10
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
6
还是不行呦,可以联系版主给看看呢
2024-1-4 10:13
0
雪    币: 3070
活跃值: (30876)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
图挂了
2024-1-4 13:43
1
雪    币: 638
活跃值: (614)
能力值: ( LV4,RANK:43 )
在线值:
发帖
回帖
粉丝
8
mb_ldbucrik 还是不行呦,可以联系版主给看看呢
啊..我再试试。
2024-1-4 15:11
0
雪    币: 638
活跃值: (614)
能力值: ( LV4,RANK:43 )
在线值:
发帖
回帖
粉丝
9

希望可以了,为什么会挂啊。我用的gitee,应该不是这个问题吧csdn那边都没问题,编辑页面的预览也是正常的。

最后于 2024-1-4 15:44 被Sciurdae.编辑 ,原因:
2024-1-4 15:28
0
雪    币: 638
活跃值: (614)
能力值: ( LV4,RANK:43 )
在线值:
发帖
回帖
粉丝
10
wx_Huber Barrientos 图像仍然无法显示
已更新
2024-1-4 15:29
0
雪    币: 638
活跃值: (614)
能力值: ( LV4,RANK:43 )
在线值:
发帖
回帖
粉丝
11
秋狝 图挂了
更新了
2024-1-4 15:29
0
雪    币: 638
活跃值: (614)
能力值: ( LV4,RANK:43 )
在线值:
发帖
回帖
粉丝
12
刚更新完。就挂了。。。
2024-1-4 15:29
0
雪    币: 26245
活跃值: (63297)
能力值: (RANK:135 )
在线值:
发帖
回帖
粉丝
13
稍等  我们先查下图片问题
2024-1-4 15:53
0
雪    币: 26245
活跃值: (63297)
能力值: (RANK:135 )
在线值:
发帖
回帖
粉丝
14
Sciurdae. 刚更新完。就挂了。。。
图片可以显示了,之前过滤严格了些
2024-1-4 18:25
0
雪    币: 638
活跃值: (614)
能力值: ( LV4,RANK:43 )
在线值:
发帖
回帖
粉丝
15
Editor 图片可以显示了,之前过滤严格了些
好的,谢谢了。
2024-1-4 19:11
0
雪    币: 1069
活跃值: (1010)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
很好的文章
2024-2-19 10:41
0
游客
登录 | 注册 方可回帖
返回
//