之所以产生这个想法,是在删除文件的时候有时会提示文件被占用了,然后让我们先关闭之后在来删除,但是我怎么知道哪个进程打开了我的文件?

于是就去网上了找了一份代码然后改了改,接着来说说是怎么实现功能的。首先我们需要遍历所有的进程,所有要找到遍历进程的代码。

//遍历进程WCHAR * szServerName = NULL;HANDLE WtsServerHandle = WTSOpenServer(szServerName);PWTS_PROCESS_INFO pWtspi;DWORD dwCount;if (!WTSEnumerateProcesses(WtsServerHandle, 0, 1, &pWtspi, &dwCount))return 0;//printf("tottal process %d", dwCount);for (DWORD i = 0; i < dwCount; i++){printf("ProcessID: %d (%ls)\n", pWtspi[i].ProcessId,pWtspi[i].pProcessName);}

现在我们能够拿到每一个进程的id,那么怎么通过进程的id拿到它的句柄信息呢?这里可以通过ZwQueryInformationProcess未文档话的函数,得到它的句柄总数,具体的参数如下

typedef NTSTATUS (WINAPI *ZWQUERYINFORMATIONPROCESS)(HANDLE  ProcessHandle, PROCESSINFOCLASS ProcessInformationClass, PVOID  ProcessInformation, ULONG  ProcessInformationLength, PULONG ReturnLength);

其中它的第二个参数表示的是我们需要查询的内容,第三个是返回的内容,第四个是长度,最后一个不管。

通过上述函数我们能拿到当前进程的所有句柄的总数,但是需要注意的是句柄的后两位代表的是句柄的属性,也就是说,所有的句柄都是4的倍数,举一个例子,后两位不能使用了是吧,那么现在最小能使用的就是4=100二进制数,每增加一个就是增加4的倍数8=1000。拿pchunter随便看一个进程的伪句柄表,直观的看到句柄都是4的倍数。

我们现在有了每一个进程的进程句柄总数,那么只需要枚举所有的句柄不就行了吗,但是问题又来了,我们现在拿到的是伪句柄不能正常的使用,需要使用DuplicateHandle复制句柄。之后又会用到例外一个未文档话的函数ZwQueryObject,它可以查看得到当前的对象的类型和对象的名字,

typedef NTSTATUS (WINAPI *ZWQUERYOBJECT)(HANDLE OPTIONAL, OBJECT_INFORMATION_CLASS, PVOID OPTIONAL, ULONG, PULONG OPTIONAL);

最后还有一个问题,就算我们知道了每一个进程的句柄种类和名字,我们又怎么修改它呢?DuplicateHandle不仅可以复制句柄,还可以关闭源句柄。

DUPLICATE_CLOSE_SOURCE(0x00000001) Closes the source handle. This occurs regardless of any error status returned.DUPLICATE_SAME_ACCESS(0x00000002) Ignores the dwDesiredAccess parameter. The duplicate handle has the same access as the source handle.

那么最后重新整理一下思路,首先是遍历所有进程得到每一个进程的伪句柄,之后使用DuplicateHandlede复制真实的句柄,判断句柄的属性,判断句柄名字是不是我们想要的,是在干掉句柄。这里有一个小细节需要说一下就是在使用ZwQueryObject的查询句柄名字的时候会出现卡死的情况。这时我们需要创建一个新的线程来执行这个操作,避免在主线程中一直卡住。最后整理的代码如下。

#include<windows.h>
#include<stdio.h>
#include <Wtsapi32.h>
#pragma  comment (lib,"Wtsapi32.lib")#define STATUS_INFO_LENGTH_MISMATCH      ((NTSTATUS)0xC0000004L)
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
typedef enum _POOL_TYPE {NonPagedPool,NonPagedPoolExecute,PagedPool,NonPagedPoolMustSucceed,DontUseThisType,NonPagedPoolCacheAligned,PagedPoolCacheAligned,NonPagedPoolCacheAlignedMustS,MaxPoolType,NonPagedPoolBase,NonPagedPoolBaseMustSucceed,NonPagedPoolBaseCacheAligned,NonPagedPoolBaseCacheAlignedMustS,NonPagedPoolSession,PagedPoolSession,NonPagedPoolMustSucceedSession,DontUseThisTypeSession,NonPagedPoolCacheAlignedSession,PagedPoolCacheAlignedSession,NonPagedPoolCacheAlignedMustSSession,NonPagedPoolNx,NonPagedPoolNxCacheAligned,NonPagedPoolSessionNx
} POOL_TYPE;
typedef enum _OBJECT_INFORMATION_CLASS {ObjectBasicInformation,ObjectNameInformation,ObjectTypeInformation,ObjectAllInformation,ObjectDataInformation
} OBJECT_INFORMATION_CLASS, *POBJECT_INFORMATION_CLASS;
typedef struct
{USHORT Length;    //当前名称长度USHORT MaxLen;    //缓冲区最大长度USHORT *Buffer;    //Unicode 名称指针
}UNICODE_STRING, *PUNICODE_STRING;
typedef struct _OBJECT_NAME_INFORMATION {UNICODE_STRING          Name;WCHAR                   NameBuffer[0];
} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION;
typedef struct _OBJECT_TYPE_INFORMATION {UNICODE_STRING          TypeName;ULONG                   TotalNumberOfHandles;ULONG                   TotalNumberOfObjects;WCHAR                   Unused1[8];ULONG                   HighWaterNumberOfHandles;ULONG                   HighWaterNumberOfObjects;WCHAR                   Unused2[8];ACCESS_MASK             InvalidAttributes;GENERIC_MAPPING         GenericMapping;ACCESS_MASK             ValidAttributes;BOOLEAN                 SecurityRequired;BOOLEAN                 MaintainHandleCount;USHORT                  MaintainTypeList;POOL_TYPE               PoolType;ULONG                   DefaultPagedPoolCharge;ULONG                   DefaultNonPagedPoolCharge;
} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION;
typedef enum _SYSTEM_INFORMATION_CLASS {SystemBasicInformation,              // 0        Y        NSystemProcessorInformation,          // 1        Y        NSystemPerformanceInformation,        // 2        Y        NSystemTimeOfDayInformation,          // 3        Y        NSystemNotImplemented1,               // 4        Y        NSystemProcessesAndThreadsInformation, // 5       Y        NSystemCallCounts,                    // 6        Y        NSystemConfigurationInformation,      // 7        Y        NSystemProcessorTimes,                // 8        Y        NSystemGlobalFlag,                    // 9        Y        YSystemNotImplemented2,               // 10       Y        NSystemModuleInformation,             // 11       Y        NSystemLockInformation,               // 12       Y        NSystemNotImplemented3,               // 13       Y        NSystemNotImplemented4,               // 14       Y        NSystemNotImplemented5,               // 15       Y        NSystemHandleInformation,             // 16       Y        NSystemObjectInformation,             // 17       Y        NSystemPagefileInformation,           // 18       Y        NSystemInstructionEmulationCounts,    // 19       Y        NSystemInvalidInfoClass1,             // 20SystemCacheInformation,              // 21       Y        YSystemPoolTagInformation,            // 22       Y        NSystemProcessorStatistics,           // 23       Y        NSystemDpcInformation,                // 24       Y        YSystemNotImplemented6,               // 25       Y        NSystemLoadImage,                     // 26       N        YSystemUnloadImage,                   // 27       N        YSystemTimeAdjustment,                // 28       Y        YSystemNotImplemented7,               // 29       Y        NSystemNotImplemented8,               // 30       Y        NSystemNotImplemented9,               // 31       Y        NSystemCrashDumpInformation,          // 32       Y        NSystemExceptionInformation,          // 33       Y        NSystemCrashDumpStateInformation,     // 34       Y        Y/NSystemKernelDebuggerInformation,     // 35       Y        NSystemContextSwitchInformation,      // 36       Y        NSystemRegistryQuotaInformation,      // 37       Y        YSystemLoadAndCallImage,              // 38       N        YSystemPrioritySeparation,            // 39       N        YSystemNotImplemented10,              // 40       Y        NSystemNotImplemented11,              // 41       Y        NSystemInvalidInfoClass2,             // 42SystemInvalidInfoClass3,             // 43SystemTimeZoneInformation,           // 44       Y        NSystemLookasideInformation,          // 45       Y        NSystemSetTimeSlipEvent,              // 46       N        YSystemCreateSession,                 // 47       N        YSystemDeleteSession,                 // 48       N        YSystemInvalidInfoClass4,             // 49SystemRangeStartInformation,         // 50       Y        NSystemVerifierInformation,           // 51       Y        YSystemAddVerifier,                   // 52       N        YSystemSessionProcessesInformation    // 53       Y        N
} SYSTEM_INFORMATION_CLASS;
typedef enum _PROCESSINFOCLASS
{ProcessBasicInformation = 0, // 0, q: PROCESS_BASIC_INFORMATION, PROCESS_EXTENDED_BASIC_INFORMATIONProcessQuotaLimits, // qs: QUOTA_LIMITS, QUOTA_LIMITS_EXProcessIoCounters, // q: IO_COUNTERSProcessVmCounters, // q: VM_COUNTERS, VM_COUNTERS_EXProcessTimes, // q: KERNEL_USER_TIMESProcessBasePriority, // s: KPRIORITYProcessRaisePriority, // s: ULONGProcessDebugPort, // q: HANDLEProcessExceptionPort, // s: HANDLEProcessAccessToken, // s: PROCESS_ACCESS_TOKENProcessLdtInformation, // 10ProcessLdtSize,ProcessDefaultHardErrorMode, // qs: ULONGProcessIoPortHandlers, // (kernel-mode only)ProcessPooledUsageAndLimits, // q: POOLED_USAGE_AND_LIMITSProcessWorkingSetWatch, // q: PROCESS_WS_WATCH_INFORMATION[]; s: voidProcessUserModeIOPL,ProcessEnableAlignmentFaultFixup, // s: BOOLEANProcessPriorityClass, // qs: PROCESS_PRIORITY_CLASSProcessWx86Information,ProcessHandleCount, // 20, q: ULONG, PROCESS_HANDLE_INFORMATIONProcessAffinityMask, // s: KAFFINITYProcessPriorityBoost, // qs: ULONGProcessDeviceMap, // qs: PROCESS_DEVICEMAP_INFORMATION, PROCESS_DEVICEMAP_INFORMATION_EXProcessSessionInformation, // q: PROCESS_SESSION_INFORMATIONProcessForegroundInformation, // s: PROCESS_FOREGROUND_BACKGROUNDProcessWow64Information, // q: ULONG_PTRProcessImageFileName, // q: UNICODE_STRINGProcessLUIDDeviceMapsEnabled, // q: ULONGProcessBreakOnTermination, // qs: ULONGProcessDebugObjectHandle, // 30, q: HANDLEProcessDebugFlags, // qs: ULONGProcessHandleTracing, // q: PROCESS_HANDLE_TRACING_QUERY; s: size 0 disables, otherwise enablesProcessIoPriority, // qs: ULONGProcessExecuteFlags, // qs: ULONGProcessResourceManagement,ProcessCookie, // q: ULONGProcessImageInformation, // q: SECTION_IMAGE_INFORMATIONProcessCycleTime, // q: PROCESS_CYCLE_TIME_INFORMATIONProcessPagePriority, // q: ULONGProcessInstrumentationCallback, // 40ProcessThreadStackAllocation, // s: PROCESS_STACK_ALLOCATION_INFORMATION, PROCESS_STACK_ALLOCATION_INFORMATION_EXProcessWorkingSetWatchEx, // q: PROCESS_WS_WATCH_INFORMATION_EX[]ProcessImageFileNameWin32, // q: UNICODE_STRINGProcessImageFileMapping, // q: HANDLE (input)ProcessAffinityUpdateMode, // qs: PROCESS_AFFINITY_UPDATE_MODEProcessMemoryAllocationMode, // qs: PROCESS_MEMORY_ALLOCATION_MODEProcessGroupInformation, // q: USHORT[]ProcessTokenVirtualizationEnabled, // s: ULONGProcessConsoleHostProcess, // q: ULONG_PTRProcessWindowInformation, // 50, q: PROCESS_WINDOW_INFORMATIONProcessHandleInformation, // q: PROCESS_HANDLE_SNAPSHOT_INFORMATION // since WIN8ProcessMitigationPolicy, // s: PROCESS_MITIGATION_POLICY_INFORMATIONProcessDynamicFunctionTableInformation,ProcessHandleCheckingMode,ProcessKeepAliveCount, // q: PROCESS_KEEPALIVE_COUNT_INFORMATIONProcessRevokeFileHandles, // s: PROCESS_REVOKE_FILE_HANDLES_INFORMATIONMaxProcessInfoClass
}PROCESSINFOCLASS;
//上面为需要用到的结构体和枚举//定义函数指针
typedef NTSTATUS (NTAPI *ZWQUERYSYSTEMINFORMATION)( SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation,  ULONG SystemInformationLength, PULONG ReturnLength OPTIONAL);
ZWQUERYSYSTEMINFORMATION ZwQuerySystemInformation = NULL;typedef NTSTATUS (WINAPI *ZWQUERYOBJECT)(HANDLE OPTIONAL, OBJECT_INFORMATION_CLASS, PVOID OPTIONAL, ULONG, PULONG OPTIONAL);
ZWQUERYOBJECT ZwQueryObject = NULL;typedef NTSTATUS (WINAPI *ZWQUERYINFORMATIONPROCESS)(HANDLE  ProcessHandle, PROCESSINFOCLASS ProcessInformationClass, PVOID  ProcessInformation, ULONG  ProcessInformationLength, PULONG ReturnLength);
ZWQUERYINFORMATIONPROCESS ZwQueryInformationProcess = NULL;//初始化未文档化函数
BOOL InitUnDocumentProc()
{HMODULE hNtdll = GetModuleHandle("Ntdll.dll");if (hNtdll == NULL)    return FALSE;ZwQuerySystemInformation = (ZWQUERYSYSTEMINFORMATION)GetProcAddress(hNtdll, "NtQuerySystemInformation");ZwQueryObject = (ZWQUERYOBJECT)GetProcAddress(hNtdll, "NtQueryObject");ZwQueryInformationProcess = (ZWQUERYINFORMATIONPROCESS)GetProcAddress(hNtdll, "NtQueryInformationProcess");if ((ZwQuerySystemInformation == NULL) || (ZwQueryObject == NULL) || (ZwQueryInformationProcess == NULL))return FALSE;return TRUE;
}//象征性的提权,其实用处不大
BOOL EnableDebugPrivilege()
{HANDLE hToken;LUID sedebugnameValue;TOKEN_PRIVILEGES tkp;if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)){return   FALSE;}if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &sedebugnameValue)){CloseHandle(hToken);return FALSE;}tkp.PrivilegeCount = 1;tkp.Privileges[0].Luid = sedebugnameValue;tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(tkp), NULL, NULL)){CloseHandle(hToken);return FALSE;}return TRUE;
}//查询名字的时候会卡死
HANDLE hDuplicate = NULL;
OBJECT_NAME_INFORMATION *ObjectName;
char BufferForObjectName[1024];UINT WINAPI ZwThreadProc(PVOID lpParma){ZwQueryObject((HANDLE)hDuplicate, ObjectNameInformation, BufferForObjectName, sizeof(BufferForObjectName), NULL);ObjectName = (POBJECT_NAME_INFORMATION)BufferForObjectName;//MessageBoxA(0,0,0,0);
}int main()
{EnableDebugPrivilege();InitUnDocumentProc();NTSTATUS Status;HANDLE hSource = NULL;DWORD HandleCount;OBJECT_TYPE_INFORMATION *ObjectType;char BufferForObjectType[1024];//遍历进程WCHAR * szServerName = NULL;HANDLE WtsServerHandle = WTSOpenServer(szServerName);PWTS_PROCESS_INFO pWtspi;DWORD dwCount;if (!WTSEnumerateProcesses(WtsServerHandle, 0, 1, &pWtspi, &dwCount))return 0;//printf("tottal process %d", dwCount);/*for (DWORD i = 0; i < dwCount; i++){printf("ProcessID: %d (%ls)\n", pWtspi[i].ProcessId,pWtspi[i].pProcessName);}*/for (DWORD j = 0; j < dwCount; j++) {hSource = OpenProcess(PROCESS_ALL_ACCESS | PROCESS_DUP_HANDLE | PROCESS_SUSPEND_RESUME, FALSE, pWtspi[j].ProcessId);if (hSource != NULL) {DWORD dwHandle;Status = ZwQueryInformationProcess(hSource, ProcessHandleCount, &HandleCount, sizeof(HandleCount), NULL);//printf("total size %d  \n", HandleCount);for (DWORD i = 1; i <= HandleCount; i++) {//穷举句柄dwHandle = i * 4;//复制一个句柄对象 && 判断此句柄是否有效if (DuplicateHandle(hSource, (HANDLE)dwHandle, GetCurrentProcess(), &hDuplicate, 0, FALSE, DUPLICATE_SAME_ACCESS)) {ZeroMemory(BufferForObjectName, 1024);ZeroMemory(BufferForObjectType, 1024);//获取句柄类型Status = ZwQueryObject(hDuplicate, ObjectTypeInformation, BufferForObjectType, sizeof(BufferForObjectType), NULL);ObjectType = (OBJECT_TYPE_INFORMATION*)BufferForObjectType;if (Status == STATUS_INFO_LENGTH_MISMATCH || !NT_SUCCESS(Status))continue;//获取句柄名,会卡死,所以这里只有开一个线程//Status = ZwQueryObject((HANDLE)hDuplicate, ObjectNameInformation, BufferForObjectName, sizeof(BufferForObjectName), NULL);//ObjectName = (POBJECT_NAME_INFORMATION)BufferForObjectName;//if (Status == STATUS_INFO_LENGTH_MISMATCH || !NT_SUCCESS(Status))//continue;HANDLE hThread = CreateThread(NULL, 0, ZwThreadProc, NULL, 0, NULL);DWORD dwSatu = WaitForSingleObject(hThread, 53); // 等待,直到线程被激发if (dwSatu == WAIT_TIMEOUT) {hThread = OpenThread(THREAD_TERMINATE, FALSE, hThread);if (!TerminateThread(hThread, 0)) {CloseHandle(hThread);}CloseHandle(hThread);//MessageBoxA(0, 0, 0, 0);continue;}//关闭复制的句柄CloseHandle(hDuplicate);//printf("Type:%S\tName:%S\tHandle:%X\n", ObjectType->TypeName.Buffer,ObjectName->Name.Buffer, hDuplicate);if (ObjectName->Name.Buffer!=NULL&&!strcmp(ObjectType->TypeName.Buffer, L"File")&& wcsstr(ObjectName->Name.Buffer,L"msyh.ttc")) {//判断是不是文件类型,判断关闭文件对不对printf("pid %d \tName:%S\tHandle:%X\n", pWtspi[j].ProcessId, ObjectName->Name.Buffer, hDuplicate);//printf("pid %d \tName:%S\n", pWtspi[j].ProcessId, ObjectType->TypeName.Buffer);//关闭符合的句柄DuplicateHandle(hSource, (HANDLE)dwHandle, GetCurrentProcess(), &hDuplicate,0,FALSE,DUPLICATE_CLOSE_SOURCE);CloseHandle(hDuplicate);}}}CloseHandle(hSource);}}system("pause");return 0;
}

R3下,遍历所有进程的伪句柄表,关闭指定句柄相关推荐

  1. wsappx关不掉_win10系统下wsappx.exe进程占用内存大能关闭吗

    有不少win10系统用户在查看任务管理器的时候,发现有一个wsappx.exe进程占用内存大,但是又不知道wsappx.exe是什么进程,可以关闭吗?针对这个问题,小编这就给大家讲解一下win10系统 ...

  2. Windows句柄表学习笔记 —— 句柄表全局句柄表

    Windows句柄表学习笔记 -- 句柄表&全局句柄表 句柄表 实验一:在WinDbg中查看句柄表 第一步:打开一个Win32窗口程序 第二步:编译并运行以下代码 第三步:查看运行结果 第四步 ...

  3. EXCEL批量删除当前目录下所有工作薄的所有工作表的指定行

    本代码示例用于批量删除第6, 7, 9, 10, 12, 13, 15, 16行的代码,使用这个批量删除行的代码要注意以下几点: 1)该代码只用于批量删除代码所在文件目录下(不含子目录)与 " ...

  4. 【超详细】遍历Windows进程模块

    直奔主题 摘要 上一篇文章详细介绍了如何 遍历Windows进程.这篇文章主要是对获取的系统进程作进一步处理,依次遍历每一个进程中使用的模块!主要是用到了Module32First以及Module32 ...

  5. (64)句柄表,遍历所有进程的句柄表实现反调试

    一.内核对象,句柄 这次课讨论的内核对象是指创建时需要指定 LPSECURITY_ATTRIBUTES 参数的对象,例如 Mutex, Thread. 调用 CreateThread 等函数会返回一个 ...

  6. (66)全局句柄表,遍历全局句柄表

    一.回顾 前面的课程我们学习了进程的句柄表,全局句柄表和进程句柄表非常像,只有一些小区别. 这节课的课后作业我先给出来: 编写程序,通过全局句柄表PsdCidTable,遍历所有进程(包括隐藏进程). ...

  7. (65)如何根据句柄从二级、三级结构句柄表中找到内核对象

    一.回顾 上一篇博客介绍了如何遍历一级句柄表.一级句柄表非常简单,就是一个4KB页,最多存储512个句柄表项.如果句柄数量在 512 - 1024*512 之间,句柄表就是二级结构:如果句柄数量大于 ...

  8. 私有句柄表(内核对象,并非用户对象),全局句柄表

    文章目录 私有句柄表 1.什么是句柄(内核对象) 2.为什么要有句柄? Windows设计理念: 3.句柄表在哪? 全局句柄表 注意 私有句柄表 1.什么是句柄(内核对象) 当一个进程创建或者打开一个 ...

  9. Linux上怎样停止服务(jar包)、查看进程路径、关闭指定端口

    场景 在linux上部署的jar包服务需要停止该服务并关闭防火墙对应的端口. 注: 博客: https://blog.csdn.net/badao_liumang_qizhi 关注公众号 霸道的程序猿 ...

最新文章

  1. 【项目实践】中英文文字检测与识别项目(CTPN+CRNN+CTC Loss原理讲解)
  2. PHPExcel 出现open_basedir restriction in effect的解决办法
  3. 一 SpringMVC源码之DispatcherServlet AbstractHanderMapping
  4. 11.JDK8内存模型、本地方法栈、虚拟机栈、栈帧结构(局部变量表、操作数栈、方法出口、虚拟机栈与本地方法栈的关系、寄存器、方法区、堆(Heap)、jvm中的常量池、Metaspace(元空间))
  5. latex表插入的位置不对_VSCode_LaTex_英文amp;中文配置
  6. 为什么自由职业受追捧?
  7. Chrome 73 稳定版正式发布,macOS 支持暗色主题
  8. Windows切换窗口
  9. Android 系统汉字转拼音 HanziToPinyin
  10. NetApp存储设置时间报“date: cannot set date when NTP is running.”处理
  11. html中版权号怎么打,网站底部版权符号怎么打出来
  12. 两台路由器的连接方法和无线路由桥接
  13. Adb文件及文件夹操作命令
  14. python 图像快速替换某种颜色
  15. Spring5春天还是配置地狱
  16. 【图解】九张图带你读懂大数据医疗
  17. 夜神模拟器 版本下载
  18. c语言如何不用数组排序,c语言实现数组排序.docx
  19. 武大计算机学院2017年博士分数线,武汉大学高等研究院2017年博士研究生综合考核录取工作通知...
  20. 印能捷服务器系统怎么装,Prinergy印能捷JTP全自动建立工具(32/64bit)

热门文章

  1. 世界Web设计最新潮流
  2. Redis 安装+四种启动设置(开机自启动)
  3. 翌加:抖音账号想要更多关注和粉丝要做好背景图
  4. 手撸架构,Docker 面试25问
  5. python 字典 遍历字典元素
  6. 【PyTorch】PyTorch基础知识——自动求导
  7. Excel打印如何让每一页都有相同的顶端标题和底端标题以方便阅读
  8. 从补贴大战到数字化博弈,美团与饿了么的无尽战火
  9. java签到 表设计_java springboot 7天签到功能设计 数据库表设计 加代码
  10. Windows Socket select函数使用