首页 > 其他分享 >R3下用ZwQueryObject/ZwDuplicateObject关闭互斥体和解除文件占用

R3下用ZwQueryObject/ZwDuplicateObject关闭互斥体和解除文件占用

时间:2022-11-07 20:37:41浏览次数:33  
标签:INFORMATION typedef HANDLE R3 句柄 下用 互斥 DWORD NULL


    不少程序在运行时会创建/打开全局Mutex,来限制用户多开。百度上搜一圈下来,他们的实现基本是这样:

int main(int argc, char* argv[])
{
HANDLE hMtx = CreateMutex(NULL,false,"process"); //创建一个有名对象,可以在其他进程中访问
if(GetLastError() == ERROR_ALREADY_EXISTS) //除了创建该对象的进程能进到else分支,其他进程都进入if分支,然后退出
{
ExitProcess(0);
}
else
//do something
return 0;
}

    饶过这类限制,我能想到2种方法:

    1).修改跳转发生时的条件,不管什么情况都进入else分支

     2).删除全局Mutex变量
先简单说下方式1)

if(GetLastError() == ERROR_ALREADY_EXISTS)
00DC13D9 mov esi,esp
00DC13DB call dword ptr [__imp__GetLastError@0 (0DC81F0h)]
00DC13E1 cmp esi,esp
00DC13E3 call @ILT+295(__RTC_CheckEsp) (0DC112Ch)
00DC13E8 cmp eax,0B7h //比较-判断发生在这,只要修改eax的值或者EFlags的值即可
00DC13ED jne main+7Dh (0DC141Dh)

再看下方式2),a.主要思路是查找目标进程,b.找到后枚举进程所有打开的句柄,c.用ZwDuplicateObject复制句柄,能复制成功的基本是可用的句柄,d.先关闭上次调用ZwDuplicateObject时复制的句柄,然后已DUPLICATE_CLOSE_SOURCE的方式(复制后关闭原句柄)再次复制/关闭句柄。以此关闭限制多开的Mutex。

    罗列代码前,先看下这种方式是否具有可行性。首先不杀Mutex句柄,运行同一个进程两次的结果如下

R3下用ZwQueryObject/ZwDuplicateObject关闭互斥体和解除文件占用_#include

用xuetr关闭进程中的mutex句柄,再次打开:

R3下用ZwQueryObject/ZwDuplicateObject关闭互斥体和解除文件占用_#define_02

R3下用ZwQueryObject/ZwDuplicateObject关闭互斥体和解除文件占用_句柄_03

R3下用ZwQueryObject/ZwDuplicateObject关闭互斥体和解除文件占用_#include_04

由上面几张图可知,杀掉全局Mutext后,可以实现进程多开。有了理论的支持,下面开始实际编码。

1.准备工作。代码进行枚举并打开进程,打开进程需要提权,使进程本身具有SE_PRIVILEGE_ENABLED权限。然后导出一堆Zw*函数用于枚举系统和进程的UnDocument API。读者可能知道进程EPROCESS结构中有进程句柄表,记录了进程打开的句柄信息,但是有了SE_PRIVILEGE_ENABLED权限和UnDocument API就能获得目标进程的句柄表了么?貌似也不是~那这不就无解了?在R3下的确很直白的方法,但是,有个很重要的事:进程句柄从4开始计数,每次往上加4,这个可以通过ARK工具查看验证。知道这个事就好办了,大不了在循环中慢慢找过去呗~虽然挨个找过去是一个办法,但是未必每个值为4N的整数就是进程内有效句柄啊~因此,需要用另一个UnDocument API----ZwDuplicateObject加以验证,如果调用这个函数成功就是一个可用的句柄,然后对这个句柄经行下一步处理。

2.摸排信息。光有进程句柄,同时知道这是一个有效的句柄作用不大。想想GUI进程一堆窗口,每个窗口一个句柄,不可能直接一棍打死吧?所以得摸清楚这个句柄的背景信息,通过调用ZwQueryObject可以获得该句柄的类型名和句柄名。通过字符串比较(wcsstr)找到Mutext对应的句柄。

3.暗杀。以DUPLICATE_CLOSE_SOURCE方式调用DuplicateHandle,意思很直白了,复制的时候把源句柄关闭。谁是源句柄?当然是创建Mutext的进程中的Mutext句柄。读者可以在调试时单步运行到DuplicateHandle,然后观察xuetr中句柄占用情况:调用前,创建这个Mutext的进程对这个句柄的引用数>0,调用成功后引用数=0,因此在创建进程中再也找不到这个句柄的信息了。最后在代码中调用CloseHandle,关闭复制到本进程中的Mutext句柄。当系统发现这个句柄的引用数==0,于是会删除对应的内核对象。当下次运行同一个进程并创建互斥体时,发现系统中并没有这个互斥体,于是顺利的通过if语句往下执行。

#include <stdio.h>
#include <string.h>
#include <windows.h>
#include <winternl.h>

#define STATUS_SUCCESS 0x00UL
#define STATUS_INFO_LENGTH_MISMATCH 0xC0000004

#define SystemHandleInformation 16
#define SE_DEBUG_PRIVILEGE 0x14

typedef enum _OBJECT_INFORMATION_CLASSEX {
ObjBasicInformation = 0,
ObjNameInformation,
ObjTypeInformation,
} OBJECT_INFORMATION_CLASSEX;

typedef enum _PROCESSINFOCLASSEX
{
ProcessHandleInformation=20,
}PROCESSINFOCLASSEX;

typedef struct _SYSTEM_HANDLE
{
ULONG ProcessId;
BYTE ObjectTypeNumber;
BYTE Flags;
USHORT Handle;
PVOID Object;
ACCESS_MASK GrantAccess;
}SYSTEM_HANDLE;

typedef struct _SYSTEM_HANDLE_INFORMATION
{
DWORD HandleCount;
SYSTEM_HANDLE Handles[1];
}SYSTEM_HANDLE_INFORMATION;

typedef struct _OBJECT_NAME_INFORMATION
{
UNICODE_STRING ObjectName;
}OBJECT_NAME_INFORMATION;

typedef NTSTATUS (WINAPI *ZwQueryInformationProcessProc)(HANDLE,PROCESSINFOCLASSEX,LPVOID,DWORD,PDWORD);
ZwQueryInformationProcessProc ZwQueryInformationProcess;

typedef NTSTATUS (WINAPI *ZwQuerySystemInformationProc)(DWORD,PVOID,DWORD,DWORD*);
ZwQuerySystemInformationProc ZwQuerySystemInformation;

typedef NTSTATUS (WINAPI *ZwQueryObjectProc)(HANDLE,OBJECT_INFORMATION_CLASSEX,PVOID,ULONG,PULONG);
ZwQueryObjectProc ZwQueryObject;

typedef NTSTATUS (WINAPI *RtlAdjustPrivilegeProc)(DWORD,BOOL,BOOL,PDWORD);
RtlAdjustPrivilegeProc RtlAdjustPrivilege;

typedef DWORD (WINAPI *ZwSuspendProcessProc)(HANDLE);
ZwSuspendProcessProc ZwSuspendProcess;

typedef DWORD (WINAPI *ZwResumeProcessProc)(HANDLE);
ZwResumeProcessProc ZwResumeProcess;
//进程提权
BOOL ElevatePrivileges()
{
HANDLE hToken;
TOKEN_PRIVILEGES tkp;
tkp.PrivilegeCount = 1;
if(!OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,&hToken))
return FALSE;
LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&tkp.Privileges[0].Luid);
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if(!AdjustTokenPrivileges(hToken,FALSE,&tkp,sizeof(TOKEN_PRIVILEGES),NULL,NULL))
{
return FALSE;
}

return TRUE;
}
//获得未导出api的地址
BOOL GetUnDocumentAPI()
{
ZwSuspendProcess = (ZwSuspendProcessProc)
GetProcAddress(GetModuleHandle(L"ntdll.dll"),"ZwSuspendProcess");

ZwQuerySystemInformation = (ZwQuerySystemInformationProc)
GetProcAddress(GetModuleHandle(L"ntdll.dll"),"ZwQuerySystemInformation");

ZwQueryObject = (ZwQueryObjectProc)
GetProcAddress(GetModuleHandle(L"ntdll.dll"),"ZwQueryObject");

ZwResumeProcess = (ZwResumeProcessProc)
GetProcAddress(GetModuleHandle(L"ntdll.dll"),"ZwResumeProcess");

ZwQueryInformationProcess = (ZwQueryInformationProcessProc)
GetProcAddress(GetModuleHandle(L"ntdll.dll"),"ZwQueryInformationProcess");

if((ZwSuspendProcess==NULL)||\
(ZwQuerySystemInformation==NULL)||\
(ZwQueryObject==NULL)||\
(ZwResumeProcess==NULL)||\
(ZwQueryInformationProcess==NULL))
return FALSE;

return TRUE;
}

int main(int argc, char* argv[])
{
HANDLE duplicateHnd;
HANDLE sourceHnd;
DWORD procHndNum;
ULONG pid;
FILE* fp = fopen("./pid.txt","r+");
fscanf(fp,"%d",&pid);
fclose(fp);
SYSTEM_HANDLE* currnetHnd;
DWORD buffLen = 0x1000;
NTSTATUS status;
SYSTEM_HANDLE_INFORMATION* buff = (SYSTEM_HANDLE_INFORMATION*)malloc(buffLen);

if((ElevatePrivileges()==FALSE)||(GetUnDocumentAPI()==FALSE))
ExitProcess(0);

do
{
//初次运行时,不知道缓存区要多大,就试探性的调用该函数。函数返回<span style="font-family: Arial, Helvetica, sans-serif;">STATUS_INFO_LENGTH_MISMATCH</span>
<span style="font-family:Arial, Helvetica, sans-serif;">                                //意为缓存区不够大,然后在下次循环前扩大缓存空间再次调用函数
</span> status = ZwQuerySystemInformation(SystemHandleInformation,buff,buffLen,&buffLen);
if(status == STATUS_INFO_LENGTH_MISMATCH)
{
free(buff);
buff = (SYSTEM_HANDLE_INFORMATION*)malloc(buffLen);
}
else
break;

}while(1);

OBJECT_NAME_INFORMATION* objNameInfo = (OBJECT_NAME_INFORMATION*)malloc(0x1000);
OBJECT_NAME_INFORMATION* objTypeInfo = (OBJECT_NAME_INFORMATION*)malloc(0x1000);

for(int idx=0;idx<buff->HandleCount;idx++)
{
currnetHnd = &(buff->Handles[idx]);

if(currnetHnd->ProcessId == pid)
{
sourceHnd = OpenProcess(PROCESS_ALL_ACCESS|PROCESS_DUP_HANDLE|PROCESS_SUSPEND_RESUME,FALSE,pid);
(ZwSuspendProcess)(sourceHnd);
(ZwQueryInformationProcess)(sourceHnd,ProcessHandleInformation,&procHndNum,sizeof(DWORD),NULL);
//进程有效句柄从4开始,每次以4递增
unsigned short hndNum=4;
for(int idx=0;idx<procHndNum;hndNum+=4)
{
//判断是否为有效句柄,返回TRUE,就是有效句柄
if(!DuplicateHandle(sourceHnd,
(HANDLE)hndNum,
GetCurrentProcess(),
&duplicateHnd,0,FALSE,DUPLICATE_SAME_ACCESS))
{
continue;
}
else
{
memset(objNameInfo,0,0x1000);
memset(objTypeInfo,0,0x1000);
//查询句柄类型名和句柄名
ZwQueryObject((HANDLE)duplicateHnd,ObjNameInformation,objNameInfo,0x1000,NULL);
ZwQueryObject((HANDLE)duplicateHnd,ObjTypeInformation,objTypeInfo,0x1000,NULL);

//找到互斥体 比较名字
if(wcsicmp(objTypeInfo->ObjectName.Buffer,L"mutant") == 0)
{
if(wcsstr(objNameInfo->ObjectName.Buffer,L"process") != 0)
{
CloseHandle(duplicateHnd);
/*
DUPLICATE_CLOSE_SOURCE
Closes the source handle. This occurs regardless of any error status returned.
DUPLICATE_SAME_ACCESS
Ignores the dwDesiredAccess parameter. The duplicate handle has the same access as the source handle.

也就是说当我们选择DUPLICATE_CLOSE_SOURCE时,源句柄就会自动关闭了
*/
if(DuplicateHandle(sourceHnd,
(HANDLE)hndNum,
GetCurrentProcess(),
&duplicateHnd,0,FALSE,DUPLICATE_CLOSE_SOURCE))
{
CloseHandle(duplicateHnd);
ExitProcess(0);
}
}
}

wprintf(L"idx:%03x\nType:%ls\nName:%ls\n",
(idx+1)*4,
objTypeInfo->ObjectName.Buffer,
objNameInfo->ObjectName.Buffer);

wprintf(L"\n\n");
CloseHandle(duplicateHnd);
idx++;
}
}



}
}

(ZwResumeProcess)(sourceHnd);
return 0;
}


这段代码做个变体,可以查找进程打开的文件句柄,然后关闭同时删除文件,unlocker的作者大熊猫候佩就是在R3下这么实现结束进程对文件的占用。具体可以搜索他的博客,我就简单罗列一下按他的思路实现的代码

#include <stdio.h>
#include <string.h>
#include <windows.h>
//#include <winternl.h>

#define STATUS_SUCCESS 0x00UL
#define STATUS_INFO_LENGTH_MISMATCH 0xC0000004
#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)

#define SystemHandleInformation 16
#define SE_DEBUG_PRIVILEGE 0x14

typedef struct _IO_STATUS_BLOCK
{
union
{
NTSTATUS Status;
PVOID Pointer;
};
ULONG_PTR Information;
}IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;

typedef enum _FILE_INFORMATION_CLASS
{
FileDirectoryInformation=1,
FileFullDirectoryInformation, // 2
FileBothDirectoryInformation, // 3
FileBasicInformation, // 4 wdm
FileStandardInformation, // 5 wdm
FileInternalInformation, // 6
FileEaInformation, // 7
FileAccessInformation, // 8
FileNameInformation, // 9
FileRenameInformation, // 10
FileLinkInformation, // 11
FileNamesInformation, // 12
FileDispositionInformation, // 13
FilePositionInformation, // 14 wdm
FileFullEaInformation, // 15
FileModeInformation, // 16
FileAlignmentInformation, // 17
FileAllInformation, // 18
FileAllocationInformation, // 19
FileEndOfFileInformation, // 20 wdm
FileAlternateNameInformation, // 21
FileStreamInformation, // 22
FilePipeInformation, // 23
FilePipeLocalInformation, // 24
FilePipeRemoteInformation, // 25
FileMailslotQueryInformation, // 26
FileMailslotSetInformation, // 27
FileCompressionInformation, // 28
FileObjectIdInformation, // 29
FileCompletionInformation, // 30
FileMoveClusterInformation, // 31
FileQuotaInformation, // 32
FileReparsePointInformation, // 33
FileNetworkOpenInformation, // 34
FileAttributeTagInformation, // 35
FileTrackingInformation, // 36
FileIdBothDirectoryInformation, // 37
FileIdFullDirectoryInformation, // 38
FileValidDataLengthInformation, // 39
FileShortNameInformation, // 40
FileMaximumInformation,
}FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;

typedef struct _UNICODE_STRING
{
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
}UNICODE_STRING, *PUNICODE_STRING;

typedef struct _OBJECT_NAME_INFORMATION
{
UNICODE_STRING Name;
}OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION;

typedef enum _OBJECT_INFORMATION_CLASSEX {
ObjBasicInformation = 0,
ObjNameInformation,
ObjTypeInformation,
} OBJECT_INFORMATION_CLASSEX;

typedef enum _PROCESSINFOCLASSEX
{
ProcessHandleInformation=20,
}PROCESSINFOCLASSEX;

typedef struct _SYSTEM_HANDLE
{
ULONG ProcessId;
BYTE ObjectTypeNumber;
BYTE Flags;
USHORT Handle;
PVOID Object;
ACCESS_MASK GrantAccess;
}SYSTEM_HANDLE;

typedef struct _SYSTEM_HANDLE_INFORMATION
{
DWORD HandleCount;
SYSTEM_HANDLE Handles[1];
}SYSTEM_HANDLE_INFORMATION;

typedef NTSTATUS (NTAPI *ZwQueryInformationFileProc)(HANDLE,PIO_STATUS_BLOCK,PVOID,DWORD,FILE_INFORMATION_CLASS);
ZwQueryInformationFileProc ZwQueryInformationFile;

typedef NTSTATUS (WINAPI *ZwQueryInformationProcessProc)(HANDLE,PROCESSINFOCLASSEX,LPVOID,DWORD,PDWORD);
ZwQueryInformationProcessProc ZwQueryInformationProcess;

typedef NTSTATUS (WINAPI *ZwQuerySystemInformationProc)(DWORD,PVOID,DWORD,DWORD*);
ZwQuerySystemInformationProc ZwQuerySystemInformation;

typedef NTSTATUS (WINAPI *ZwQueryObjectProc)(HANDLE,OBJECT_INFORMATION_CLASSEX,PVOID,ULONG,PULONG);
ZwQueryObjectProc ZwQueryObject;

typedef NTSTATUS (WINAPI *RtlAdjustPrivilegeProc)(DWORD,BOOL,BOOL,PDWORD);
RtlAdjustPrivilegeProc RtlAdjustPrivilege;

typedef DWORD (WINAPI *ZwSuspendProcessProc)(HANDLE);
ZwSuspendProcessProc ZwSuspendProcess;

typedef DWORD (WINAPI *ZwResumeProcessProc)(HANDLE);
ZwResumeProcessProc ZwResumeProcess;

BOOL ElevatePrivileges()
{
HANDLE hToken;
TOKEN_PRIVILEGES tkp;
tkp.PrivilegeCount = 1;
if(!OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,&hToken))
return FALSE;
LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&tkp.Privileges[0].Luid);
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if(!AdjustTokenPrivileges(hToken,FALSE,&tkp,sizeof(TOKEN_PRIVILEGES),NULL,NULL))
{
return FALSE;
}

return TRUE;
}

BOOL GetUnDocumentAPI()
{
ZwSuspendProcess = (ZwSuspendProcessProc)
GetProcAddress(GetModuleHandle(L"ntdll.dll"),"ZwSuspendProcess");

ZwQueryInformationFile = (ZwQueryInformationFileProc)
GetProcAddress(GetModuleHandle(L"ntdll.dll"),"ZwQueryInformationFile");

ZwQuerySystemInformation = (ZwQuerySystemInformationProc)
GetProcAddress(GetModuleHandle(L"ntdll.dll"),"ZwQuerySystemInformation");

ZwQueryObject = (ZwQueryObjectProc)
GetProcAddress(GetModuleHandle(L"ntdll.dll"),"ZwQueryObject");

ZwResumeProcess = (ZwResumeProcessProc)
GetProcAddress(GetModuleHandle(L"ntdll.dll"),"ZwResumeProcess");

ZwQueryInformationProcess = (ZwQueryInformationProcessProc)
GetProcAddress(GetModuleHandle(L"ntdll.dll"),"ZwQueryInformationProcess");

if((ZwSuspendProcess==NULL)||\
(ZwQuerySystemInformation==NULL)||\
(ZwQueryObject==NULL)||\
(ZwResumeProcess==NULL)||\
(ZwQueryInformationProcess==NULL))
return FALSE;

return TRUE;
}

int _tmain(int argc, _TCHAR* argv[])
{
_tsetlocale(0, _T("chs"));
HANDLE duplicateHnd;
HANDLE sourceHnd;
DWORD procHndNum;
ULONG pid;
FILE* fp = _wfopen(L"./pid.txt",L"r+");
fwscanf(fp,L"%d",&pid);
fclose(fp);
SYSTEM_HANDLE* currnetHnd;
DWORD buffLen = 0x1000;
NTSTATUS status;
SYSTEM_HANDLE_INFORMATION* buff = (SYSTEM_HANDLE_INFORMATION*)malloc(buffLen);

IO_STATUS_BLOCK IoStatus ={0};
POBJECT_NAME_INFORMATION objName = NULL;
DWORD allocSize = (DWORD)(sizeof(OBJECT_NAME_INFORMATION) + MAX_PATH*sizeof(WCHAR));
objName = (POBJECT_NAME_INFORMATION)malloc(allocSize);
memset(objName,0,allocSize);

if((ElevatePrivileges()==FALSE)||(GetUnDocumentAPI()==FALSE))
ExitProcess(0);

do
{
status = ZwQuerySystemInformation(SystemHandleInformation,buff,buffLen,&buffLen);
if(status == STATUS_INFO_LENGTH_MISMATCH)
{
free(buff);
buff = (SYSTEM_HANDLE_INFORMATION*)malloc(buffLen);
}
else
break;

}while(1);

OBJECT_NAME_INFORMATION* objNameInfo = (OBJECT_NAME_INFORMATION*)malloc(0x1000);
OBJECT_NAME_INFORMATION* objTypeInfo = (OBJECT_NAME_INFORMATION*)malloc(0x1000);

for(int idx=0;idx<buff->HandleCount;idx++)
{
currnetHnd = &(buff->Handles[idx]);

if(currnetHnd->ProcessId == pid)
{
sourceHnd = OpenProcess(PROCESS_ALL_ACCESS|PROCESS_DUP_HANDLE|PROCESS_SUSPEND_RESUME,FALSE,pid);
//(ZwSuspendProcess)(sourceHnd);
(ZwQueryInformationProcess)(sourceHnd,ProcessHandleInformation,&procHndNum,sizeof(DWORD),NULL);
//进程有效句柄从4开始,每次以4递增
unsigned short hndNum=4;
for(int idx=0;idx<procHndNum;hndNum+=4)
{
//判断是否为有效句柄,返回TRUE,就是有效句柄
if(!DuplicateHandle(sourceHnd,
(HANDLE)hndNum,
GetCurrentProcess(),
&duplicateHnd,0,FALSE,DUPLICATE_SAME_ACCESS))
{
continue;
}
else
{

memset(objNameInfo,0,0x1000);
memset(objTypeInfo,0,0x1000);

ZwQueryObject((HANDLE)duplicateHnd,ObjNameInformation,objNameInfo,0x1000,NULL);
ZwQueryObject((HANDLE)duplicateHnd,ObjTypeInformation,objTypeInfo,0x1000,NULL);

//找到互斥体 比较名字
if(wcsicmp(objTypeInfo->Name.Buffer,L"file") == 0)
{
NTSTATUS status =-1;
memset(objName,0,allocSize);
status = ZwQueryInformationFile((HANDLE)duplicateHnd,&IoStatus,objName,allocSize,FileNameInformation);
if(NT_SUCCESS(status))
{
if(wcsstr((wchar_t*)&(objName->Name.Buffer), L"unlock")!=0)
{
if(DuplicateHandle(sourceHnd,
(HANDLE)hndNum,
GetCurrentProcess(),
&duplicateHnd,0,FALSE,DUPLICATE_CLOSE_SOURCE))
{
CloseHandle(duplicateHnd);
//DeleteFile();
ExitProcess(0);
}
}
wprintf(L"%ls\n",&(objName->Name.Buffer));
}
}

idx++;
}
}
}
}
//(ZwResumeProcess)(sourceHnd);
return 0;
}


​​参考的文档打包连接​

标签:INFORMATION,typedef,HANDLE,R3,句柄,下用,互斥,DWORD,NULL
From: https://blog.51cto.com/u_13927568/5831307

相关文章

  • 多线程同步之互斥锁
    对于多线程程序来说,同步是指在一定的时间内只允许某一个线程访问某个资源。而在此时间内,不允许其他的线程访问该资源。同步资源的方式:互斥锁、条件变量、读写锁、信号......
  • JMeterPerfReporter3.0正式版本发布,速进围观
    Lemon-JMeterPerfReporter工具,是性能测试课程教研组根据JMeter性能测试报告的不足,定制开发的一个性能报告生成工具。2022年11月1日,正式发布了3.0版本。有需要的同学,可以通过......
  • 若依多模块版本,Linux下用Tomcat部署
    若依多模块版本简介下载地址https://gitee.com/y_project/RuoYi下载项目,打war包下载项目到Gitee下载项目用idea打开,会自动下载pom依赖修改项目配置修改......
  • 前端vue2+vue-router3+ElementUI+axios综合使用代码教程登录页案例
    为了测试vue2+vue-router3路由+ElementUI界面+axios网络HTTP请求的基本使用情况专门编写一个登录页面进行demo验证  依赖情况package.json{"name":"default",......
  • Windows 下用 MinGW-64 配置 VScode 的 C/C++ 环境
    蒟蒻第一次发博客,轻喷~我在安装VScode的时候可谓历经磨难,所以就萌生出写这篇文章的想法。Windows下用MinGW-64配置VScode的C/C++环境一、下载MinGW-w64并添......
  • vue2搭配vue-router3真正可用不报错的写法格式
    这里要吐槽下vue和vue-router的文档教程本身前端的版本就多,版本之间还各种不兼容,用法函数还多种多样,一会这个组件一会那里是按普通渲染,简直让人不知道按哪个才是对的。然......
  • Vector3 向量
    publicGameObjectHero;publicGameObjectMonster;privatefloatlength=10;privatefloatangle=120;voidisLookHero(){//求二者间的距离varhe......
  • 信号量与互斥锁之间的区别
    (1):互斥量用于线程的互斥,信号线用于线程的同步。这是互斥量和信号量的根本区别,也就是互斥和同步之间的区别。(2):互斥量值只能为0/1,信号量值可以为非负整数。也就是说,一个互......
  • DolphinScheduler3.0部署手册
    1前言1.1参考资料DolphinScheduler官网:https://dolphinscheduler.apache.org/zh-cn/index.html1.2架构说明本手册目的是在一台服务器上部署伪集群,系统配置如下:系......
  • AIR32F103(四) 27倍频216MHz,CoreMark跑分测试
    目录AIR32F103(一)合宙AIR32F103CBT6开发板上手报告AIR32F103(二)Linux环境和LibOpenCM3项目模板AIR32F103(三)Linux环境基于标准外设库的项目模板AIR32F103(四)2......