Windows核心编程 跨进程操作

目录

进程A拿到进程B句柄是否能用

句柄的权限

关于句柄表

跨进程使用句柄-继承

CreateProcess:bInheritHandles 

OpenProcess

FindWinodw

GetCurrentProcess

跨进程使用句柄-拷贝

跨进程操作内存

WriteProcessMemory

VirtualProtectEx

ReadProcessMemory


进程A拿到进程B句柄是否能用

创建两个基于对话框的MFC分别为A,B

MFC A

ZeroMemory(&si, sizeof(si)); 是一段用于清空内存的代码,它使用了Windows操作系统提供的ZeroMemory函数。

该函数接受两个参数:第一个参数是指向要清空的内存块的指针,第二个参数是要清空的内存块的大小(以字节为单位)。

在这段代码中,&si 是指向变量 si 的指针,sizeof(si) 则是获取变量 si 所占用的内存块的大小。ZeroMemory函数将会将此内存块中的所有字节都设置为0。

void CADlg::OnBnClickedButton1()
{
	STARTUPINFO si;
	PROCESS_INFORMATION pi;

	ZeroMemory(&si, sizeof(si));
	si.cb = sizeof(si);
	ZeroMemory(&pi, sizeof(pi));

	// Start the child process. 
	if (!CreateProcess(NULL, // No module name (use command line). 
		"B.exe", // Command line. 
		NULL,             // Process handle not inheritable. 
		NULL,             // Thread handle not inheritable. 
		FALSE,            // Set handle inheritance to FALSE. 
		0,                // No creation flags. 
		NULL,             // Use parent's environment block. 
		NULL,             // Use parent's starting directory. 
		&si,              // Pointer to STARTUPINFO structure.
		&pi)             // Pointer to PROCESS_INFORMATION structure.
		)
	{
		AfxMessageBox("CreateProcess failed.");
	}
	//显示句柄值
	SetDlgItemInt(EDT_HANDLE, (UINT)pi.hProcess);
}

MFC B

void CBDlg::OnBnClickedButton1()
{

	HANDLE hProc = (HANDLE)GetDlgItemInt(EDT_HANDLE);
	::TerminateProcess(hProc, 0);
	
}

修改输出目录

输出目录如下: 

经过实验后:进程B是无法结束进程A的

剖析:一个进程,它所打开的句柄(或者说它所获得的句柄),进程都会把句柄存起来,这样就会形成一个表记录进程拿到哪些句柄;这个表叫做句柄表;

在B进程的句柄表中,并没有自己的进程的句柄,所以就算手动把A进程的句柄给B进程;让B进程结束——B进程表示在自己的句柄表中找不到自己的进程的句柄,所以报错,传入的句柄是无效句柄;

句柄的权限

句柄的权限是指操作系统对于句柄所代表的对象所授予的操作权限。不同类型的对象(如文件、进程、线程等)具有不同的权限集合。以下是一些常见的句柄权限:

  1. 文件权限:
  • FILE_READ_DATA:允许读取文件内容。
  • FILE_WRITE_DATA:允许写入文件内容。
  • FILE_APPEND_DATA:允许在文件末尾追加数据。
  • FILE_EXECUTE:允许执行文件。
  • FILE_DELETE:允许删除文件。
  • FILE_READ_ATTRIBUTES:允许读取文件属性。
  • FILE_WRITE_ATTRIBUTES:允许修改文件属性。
  1. 进程和线程权限:
  • PROCESS_CREATE_PROCESS:允许创建子进程。
  • PROCESS_TERMINATE:允许终止进程。
  • PROCESS_QUERY_INFORMATION:允许查询进程信息。
  • THREAD_CREATE_THREAD:允许创建线程。
  • THREAD_TERMINATE:允许终止线程。
  • THREAD_QUERY_INFORMATION:允许查询线程信息。
  1. 窗口权限:
  • GWL_STYLE:允许设置窗口样式。
  • GWL_EXSTYLE:允许设置窗口扩展样式。
  • WM_CLOSE:允许关闭窗口。
  • WM_DESTROY:允许销毁窗口。

这些仅是一些常见的句柄权限示例,实际上句柄的权限取决于所代表对象的类型和操作系统的安全策略。在使用句柄进行操作时,需要根据具体的需求和操作对象的类型来确定所需的权限,以确保在合法范围内进行操作。

关于句柄表

Windows操作系统中的句柄表通常被划分为以下三层:

  1. 用户层:用户层是最高层,包含了应用程序和操作系统之间的交互接口。在用户层,开发人员可以使用操作系统提供的API函数来创建和操作各种内核对象,如文件、进程、线程、窗口、消息等。
  2. 内核层:内核层是操作系统的核心,包括了内核、设备驱动程序等。在内核层,操作系统可以直接访问硬件资源,提供更加底层的操作接口。
  3. 硬件层:硬件层包含了操作系统所运行的计算机的物理硬件设备,如CPU、内存、硬盘、网络设备等。

句柄表通常被放置在内核层,用于管理应用程序和内核对象之间的关系。操作系统为每个进程维护一个独立的句柄表,该句柄表包含了该进程所拥有的句柄。在内核层,操作系统使用句柄来标识和访问内核对象。句柄通常被视为指向内核对象的指针,开发人员可以使用操作系统提供的API函数来获取、创建、操作句柄,并使用句柄来操作内核对象。

父进程和子进程:(A创建的进程都是A的子进程)
父进程和子进程从使用的角度讲,没有什么非常特殊操作的关系,是两个单独的进程,各有4g内存,各自有各自的线程,堆和栈,是独立的;使用的过程中,特别:进程句柄的时候用到父子进程,让项目的结构方便一点,
子进程有父进程的进程id,系统管理进程是由结构体管理的,由表管理,在内核里面,子进程的内核存有父进程的id信息;

跨进程使用句柄-继承

继承父进程的条件:

句柄本身可以被继承,CreateProcess的bInHeritHandle为TRUE。

子进程只能继承在自身被创建之前父进程打开的句柄,自身创建后父进程打开的句柄无法继承。

也就是说,CreateProcess创建进程B,进程B不能继承自身的句柄,自身句柄创建完之后才能拿到。解决办法是:

  1. 获取自己进程的句柄:GetCurrentProcess。返回值为-1,是个伪句柄,该句柄用于操作自身。
  2. 句柄本身也是带有私有公有属性的,和C++的继承很像,所以句柄都有一个是否可被继承的属性,这个属性由OpenProcess函数决定

CreateProcess:bInheritHandles 

新进程是否继承来自父进程的句柄。TRUE则继承。

子进程继承父进程已经打开了的句柄,只能在父子进程之间使用,CreateProcess中的第五个参数可以设置继承关系,TRUE就是可以被子进程继承,FALSE就不会被继承:

CreateProcess:参数三四安全属性指明创建出的子进程的进程句柄和线程句柄能否被继承

安全描述符是一个结构体SECURITY_ATTRIBUTES;定义如下

第一个参数是长度大小,第二个参数一般填NULL,第三个成员决定是否被继承

OpenProcess

OpenProcess函数将打开指定PID的进程,并返回一个与该进程关联的句柄。如果成功,返回的句柄可用于后续的操作,如读取或写入进程的内存、终止进程等。如果操作失败,返回NULL或INVALID_HANDLE_VALUE。

1 // 作用:打开一个存在的进程对象。(获取进程句柄)
2 // 返回值:成功返回进程句柄,失败返回NULL。
3 HANDLE OpenProcess(
4  DWORD dwDesiredAccess, 	// 权限标志,一般填PROCESS_ALL_ACCESS通杀
5  BOOL bInheritHandle, 	// OpenProcess打开的句柄能否被子进程继承
6  DWORD dwProcessId 		// 进程ID
7  );

OpenProcess函数是Windows操作系统提供的函数之一,用于打开一个已存在的进程并返回一个与该进程关联的句柄。它的参数如下:

  1. dwDesiredAccess:指定打开进程的访问权限,即访问级别。可以使用以下常量进行设置:

    • PROCESS_ALL_ACCESS:具有完全访问权限的句柄,可以执行任意操作。
    • PROCESS_CREATE_PROCESS:允许创建进程。
    • PROCESS_CREATE_THREAD:允许创建线程。
    • PROCESS_DUP_HANDLE:允许复制句柄。
    • PROCESS_QUERY_INFORMATION:允许查询进程信息。
    • PROCESS_QUERY_LIMITED_INFORMATION:允许有限查询进程信息。
    • PROCESS_SET_INFORMATION:允许设置进程信息。
    • PROCESS_SET_QUOTA:允许设置进程配额。
    • PROCESS_SUSPEND_RESUME:允许挂起或恢复进程。
    • PROCESS_TERMINATE:允许终止进程。
    • PROCESS_VM_OPERATION:允许对进程进行虚拟内存操作。
    • PROCESS_VM_READ:允许读取进程的虚拟内存。
    • PROCESS_VM_WRITE:允许写入进程的虚拟内存。
  2. bInheritHandle:指定打开的句柄是否可被子进程继承。如果为TRUE,则可被子进程继承;如果为FALSE,则不能被子进程继承。

  3. dwProcessId:要打开的进程的标识符(PID)。可以通过其他函数(如EnumProcesses)获取进程的PID,或者使用特定的值来表示特定的进程,如GetCurrentProcessId()表示当前进程的PID。

FindWinodw

FindWindow函数是Windows操作系统提供的函数之一,用于查找具有指定类名和窗口名的顶级窗口。它的参数如下:

  1. lpClassName:指定要查找的窗口类名。可以是一个字符串指针,指向类名的字符串,也可以是预定义的常量,如"Button"、"Edit"等。如果想要查找所有窗口,请将该参数设置为NULL。

  2. lpWindowName:指定要查找的窗口名。可以是一个字符串指针,指向窗口名的字符串。如果想要查找具有指定类名但没有特定窗口名的窗口,请将该参数设置为NULL。

FindWindow函数将根据提供的类名和窗口名在系统中查找匹配的顶级窗口。如果找到匹配的窗口,将返回该窗口的句柄(HWND)。否则,返回NULL。

// 作用:获取窗口句柄。
// 返回值:成功返回窗口句柄,失败返回NULL。
// 备注:参数二填一即可,另一个写NULL。
HWND FindWindow(
   LPCTSTR lpClassName, // 类名
   LPCTSTR lpWindowName // 窗口名
   );

GetCurrentProcess

// 作用:获取自身进程句柄。
// 返回值:恒为‐1。对任何进程而言,‐1代表自身进程的句柄,‐1是个伪句柄。
HANDLE GetCurrentProcess(void);

测试代码

一个设置句柄权限包括是否可被继承,一个设置可被子进程继承。

//使用继承跨进程使用句柄
void CADlg::OnBnClickedButton1()
{
	STARTUPINFO si;
	PROCESS_INFORMATION pi;

	ZeroMemory(&si, sizeof(si));
	si.cb = sizeof(si);
	ZeroMemory(&pi, sizeof(pi));

	SECURITY_ATTRIBUTES sa = {};
	sa.nLength = sizeof(sa); 
	sa.bInheritHandle = TRUE; //允许进程句柄被子进程继承(赋予保护/公有属性)
	sa.lpSecurityDescriptor = NULL;

	// Start the child process. 
	if (!CreateProcess(NULL, // No module name (use command line). 
		"B.exe", // Command line. 
		&sa,             // 允许此句柄被继承
		NULL,             // Thread handle not inheritable. 
		TRUE,            // 允许子进程继承句柄
		0,                // No creation flags. 
		NULL,             // Use parent's environment block. 
		NULL,             // Use parent's starting directory. 
		&si,              // Pointer to STARTUPINFO structure.
		&pi)             // Pointer to PROCESS_INFORMATION structure.
		)
	{
		AfxMessageBox("CreateProcess failed.");
	}
	//显示句柄值
	
	SetDlgItemInt(EDT_HANDLE, (UINT)pi.hProcess);


}

跨进程使用句柄-拷贝

把句柄拷贝到别的进程,有没有父子关系都无所谓,从自己的句柄表里拷贝到对方的句柄表里,注意是拷贝一个句柄而不是一个句柄表,继承才是把句柄表打包一份。说是拷贝实际上对方是重新获取了。

拷贝函数DuplicateHandle

BOOL DuplicateHandle(
  HANDLE   hSourceProcessHandle,  // 源进程句柄
  HANDLE   hSourceHandle,         // 源句柄
  HANDLE   hTargetProcessHandle,  // 目标进程句柄
  LPHANDLE lpTargetHandle,        // 目标句柄
  DWORD    dwDesiredAccess,       // 访问权限
  BOOL     bInheritHandle,        // 是否可被继承
  DWORD    dwOptions              // 选项
);

参数说明: 

  • hSourceProcessHandle:源进程的句柄,即拥有要复制句柄的进程。
  • hSourceHandle:要复制的句柄,即源句柄。
  • hTargetProcessHandle:目标进程的句柄,即要将复制的句柄关联到的进程。
  • lpTargetHandle:指向目标句柄的指针,用于接收复制后的句柄。

当调用DuplicateHandle函数时,有三个参数需要指定具体的取值:

  1. dwDesiredAccess:表示复制后句柄的访问权限。可以使用以下访问权限常量进行设置,也可以通过逻辑或运算符(|)组合多个权限:
  • GENERIC_READ:允许对对象进行读取操作。
  • GENERIC_WRITE:允许对对象进行写入操作。
  • GENERIC_EXECUTE:允许对对象进行执行操作。
  • GENERIC_ALL:允许对对象进行所有操作。

此外,还可以使用特定对象类型的访问权限常量,例如FILE_READ_DATA、FILE_WRITE_DATA等。具体取值取决于复制的句柄所代表的对象类型。

  1. bInheritHandle:表示目标句柄是否可被子进程继承。如果值为TRUE,则子进程可以继承目标句柄;如果值为FALSE,则子进程不会继承目标句柄。

  2. dwOptions:表示一些额外的选项。可以使用以下常量进行设置:

  • 0:没有额外的选项。
  • DUPLICATE_SAME_ACCESS:复制后的句柄将具有与源句柄相同的访问权限。

使用DUPLICATE_SAME_ACCESS选项时,dwDesiredAccess参数中的访问权限可以省略,复制后的句柄将具有与源句柄相同的访问权限。

适用场景:跨权限操作一些东西。比如有system权限的进程拿到句柄给管理员权限的进程用。

伪句柄:把进程自身-1的句柄值拷贝给自己,可以获取进程自身真正的句柄值。

此时 -1 对应的就是当前获取的句柄,它操作的是它自己,这就是伪句柄。

void CADlg::OnBnClickedButton1()
{
	STARTUPINFO si;
	PROCESS_INFORMATION pi;

	ZeroMemory(&si, sizeof(si));
	si.cb = sizeof(si);
	ZeroMemory(&pi, sizeof(pi));



	// Start the child process. 
	if (!CreateProcess(NULL, // No module name (use command line). 
		"B.exe", // Command line. 
		NULL,             // 不允许此句柄被继承
		NULL,             // Thread handle not inheritable. 
		TRUE,             // 不允许子进程继承句柄
		0,                // No creation flags. 
		NULL,             // Use parent's environment block. 
		NULL,             // Use parent's starting directory. 
		&si,              // Pointer to STARTUPINFO structure.
		&pi)              // Pointer to PROCESS_INFORMATION structure.
		)
	{
		AfxMessageBox("CreateProcess failed.");
	}
	HANDLE hProcInDst = NULL;
	BOOL fSuccess = DuplicateHandle(
		GetCurrentProcess(), 
		pi.hProcess, //被拷贝的句柄
		pi.hProcess, //拷贝到B进程
		&hProcInDst,
		0,
		FALSE,
		DUPLICATE_SAME_ACCESS
	);
	if (!fSuccess)
		AfxMessageBox("DuplicateHandle failed");


	//显示句柄值

	SetDlgItemInt(EDT_HANDLE, (UINT)hProcInDst);


}

句柄表里没有自身进程的记录

跨进程操作内存

跨进程读写内存的办法

1. Winhex手动修改进程内存:open Memory - 目标进程 - 修改完保存(写入进程内存)。

2. API修改:跨进程写内存 - WriteProcessMemory。内存属性不可写会写入失败。

3. 跨进程读内存 - ReadProcessMemory。

内存属性

1. 内存访问属性:R读,W写,X执行,C写时拷贝。修改内存访问属性:VirtualProtectEx。

2. ProcessHacker看内存属性:exe双击 - Memory - Protection。

3. WriteProcessMemory不用判断内存属性,每次写之前修改内存属性,写完后还原内存属性。

4. 写完数据不还原属性:会被检测(向只读内存写入数据,看是否触发异常,触发则表明正常)

PS:内存的基本单位4k【一个分页0x1000】,所以当修改0x1225的内存属性实则修改了0x0 ~ 0x2048的内存属性

内存分页

1. 内存分页:大小0x1000(4096),4kb。系统管理内存的基本单位。属性改一字节影响一个分页。

2. 申请内存时,系统至少分配一个分页,一个字节也分配一个分页。

3. new和malloc是在系统分配的基础上再次分配,在系统分配的分页中再次分配需要的字节。

再次以内存属性来看常量区等可读不可写区域

操作系统喜欢将统一权限属性的放在一起的原因是方便管理,且不浪费内存,也更容易维护。

WriteProcessMemory

1 // 作用:将数据写入指定进程内存。
2 BOOL WriteProcessMemory(
3  HANDLE hProcess, // 目标进程句柄
4  LPVOID lpBaseAddress, // 需要修改目标进程的地址
5  LPCVOID lpBuffer, // 写入数据缓冲区
6  SIZE_T nSize, // 写入数据缓冲区的大小
7  SIZE_T * lpNumberOfBytesWritten // 传出参数(可选),写入成功的数据大小,不
需要可以填NULL
8  );

VirtualProtectEx

 // 作用:修改内存的访问属性。
BOOL VirtualProtectEx(
 HANDLE hProcess, // 目标进程句柄
 LPVOID lpAddress, // 修改属性的内存地址
 SIZE_T dwSize, // 修改属性的内存大小
 DWORD flNewProtect, // 修改后的内存访问属性
 PDWORD lpflOldProtect // 传出参数,修改前的内存访问属性,填NULL会调用失败
);

参数3:修改属性的内存大小
修改内存属性会影响到这一段地址空间涉及到的所有内存分页。
跨越页面边界的2字节范围会导致两个页面的保护属性都被更改。

参数4:内存属性
PAGE_READONLY 只读
PAGE_READWRITE 可读可写
PAGE_WRITECOPY 写时拷贝
PAGE_EXECUTE 可执行
PAGE_EXECUTE_READ 可执行可读
PAGE_EXECUTE_READWRITE 可执行可读写
PAGE_EXECUTE_WRITECOPY 可执行可写时拷贝

ReadProcessMemory

BOOL ReadProcessMemory(  
  HANDLE hProcess,              // handle to the process
  LPCVOID lpBaseAddress,        // base of memory area
  LPVOID lpBuffer,              // data buffer
  SIZE_T nSize,                 // number of bytes to read
  SIZE_T * lpNumberOfBytesRead  // number of bytes read);

飞机躲子弹-无敌模式

1.编写飞机躲子弹工具

0x00406D6C   4   nplanX,  当前飞机位置
0x00406D70   4   nplanY,
0x00406E10   4   子弹数组首地址
0x00406DA8   4   当前子弹的个数
0x00406D80   4   死亡标志
0x004020F5   1   速度
0x00403616   0xeb  __asm jmp    无敌模式
             0x74  __asm je     普通模式
0x0040469E   4    初始子弹个数

      nBulletX >>= 6;
      nBulletX -= 4;
      nBulletY >>= 6;
      nBulletY -= 4;

+=0xF

子弹:
x坐标 = ary[i * 0xf +0] >>= 6 -= 4
y坐标 = ary[i * 0xf =4] >>= 6 -= 4

代码如下


void CGameAssistDlg::Wudi(BYTE bt)
{
    //获取窗口句柄
    HWND hWndGame = ::FindWindow(NULL, "摿孭");
    if (hWndGame == NULL)
    {
        AfxMessageBox("获取窗口句柄失败");
        return;
    }

    //获取进程ID
    DWORD dwProId = 0;
    DWORD dwThreadId = GetWindowThreadProcessId(hWndGame, &dwProId);
    if (dwThreadId == 0)
    {
        AfxMessageBox("获取进程id失败");
        return;
    }
    //获取进程句柄
    HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProId);
    if (hProc == NULL)
    {
        AfxMessageBox("获取进程句柄失败");
        return;
    }

    //修改内存属性
    LPVOID pAddrGod = (LPVOID)0x00403616;
    //BYTE bt = 0xeb;
    DWORD dwOldProc = 0;
    BOOL bRet = VirtualProtectEx(hProc, pAddrGod, sizeof(bt), PAGE_READWRITE, &dwOldProc);
    if (!bRet)
    {
        AfxMessageBox("修改内存属性失败");
        return ;
    }

    //修改内存
    bRet = WriteProcessMemory(hProc, pAddrGod, &bt, sizeof(bt), NULL);
    if (!bRet)
    {
        AfxMessageBox("无敌失败");
    }

    //修改完后还原内存属
    VirtualProtectEx(hProc, pAddrGod, sizeof(bt), dwOldProc, &dwOldProc);

    //释放进程句柄
    CloseHandle(hProc);
}

 修改前0x400000是可读的

修改后

还原内存属性

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

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

相关文章

情感对话机器人的任务体系

人类在处理对话中的情感时,需要先根据对话场景中的蛛丝马迹判断出对方的情感,继而根据对话的主题等信息思考自身用什么情感进行回复,最后结合推理出的情感形成恰当的回复。受人类处理情感对话的启发,情感对话机器人需要完成以下几…

npm pnpm yarn(包管理器)的安装及镜像切换

安装Node.js 要安装npm,你需要先安装Node.js。 从Node.js官方网站(https://nodejs.org)下载并安装Node.js。 根据你的需要选择相应的版本。 一路Next,直到Finish 打开CMD,输入命令来检查Node.js和npm是否成功安装 nod…

授时小课堂——北斗卫星信号和GPS卫星信号谁更强?

北斗卫星信号好还是GPS信号更胜一筹呢?下面小编带大家一起来比较一下看看吧。 1. 系统覆盖范围 北斗卫星导航系统是中国自主研发的授时定位系统,其覆盖范围包括全球各个地区。但在海外地区,主要还是东南亚、南亚、中亚等地区,北斗…

精通Nginx(18)-FastCGI/SCGI/uWSGI支持

最初用浏览器浏览的网页只能是静态html页面。随着社会发展,动态获取数据、操作数据需要变得日益强烈,CGI应运而生。CGI(Common Gateway Interface)公共网关接口,是外部扩展应用程序与静态Web服务器交互的一个标准接口。它可以使外部程序处理浏览器送来的表单数据并对此作出…

NX二次开发UF_CURVE_ask_curve_struct_data 函数介绍

文章作者:里海 来源网站:https://blog.csdn.net/WangPaiFeiXingYuan UF_CURVE_ask_curve_struct_data Defined in: uf_curve.h int UF_CURVE_ask_curve_struct_data(UF_CURVE_struct_p_t curve_struct, int * type, double * * curve_data ) overview…

数据结构与算法编程题20

统计二叉树的叶结点个数。 #define _CRT_SECURE_NO_WARNINGS#include <iostream> using namespace std;typedef char ElemType; #define ERROR 0 #define OK 1 typedef struct BiNode {ElemType data;BiNode* lchild, * rchild; }BiNode,*BiTree;bool Create_tree(BiTre…

JWT和Token之间的区别

✅作者简介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Leo的博客 &#x1f49e;当前专栏&#xff1a;每天一个知识点 ✨特色专栏&#xff1a…

【点云surface】 凹包重构

1 处理过程可视化 原始数据 直通滤波过滤后 pcl::ProjectInliers结果 pcl::ExtractIndices结果 凹包结果 凸包结果 2 处理过程分析&#xff1a; 原始点云 ---> 直通滤波 --> pcl::SACSegmentation分割出平面 -->pcl::ProjectInliers投影 --> pcl::ConcaveHull凹包…

模板初阶(1):函数模板,类模板

一、函数模板 1.1 概念 函数模板代表了一个函数家族&#xff0c;该函数模板与类型无关&#xff0c;在使用时被参数化&#xff0c;根据实参类型产生函数的特定类型版本。 格式&#xff1a; template <typename T>或template <class T> template <class T>…

一起学docker系列之九docker运行mysql 碰到的各种坑及解决方法

目录 前言1 Docker 运行mysql命令2 坑一&#xff1a;无法读取/etc/mysql/conf.d目录的问题3 坑二&#xff1a;/tmp/ibnr0mis 文件无法创建/写入的问题4 坑三&#xff1a;Navicat 连接错误&#xff08;1045-access denied&#xff09;5 坑四&#xff1a;MySQL 登录失败问题结语 …

micro_ros

原文链接Supported Hardware | micro-ROS Supported Hardware The main targets of micro-ROS are mid-range 32-bits microcontroller families. Usually, the minimum requirements for running micro-ROS in an embedded platform are memory constraints. Since memory u…

Spring Boot - 瘦身大作战:优雅应对Spring Boot Fat Jar

文章目录 Fat Jar瘦身pom修改copy lib启动 -Dloader.path验证 源码分析前置阅读spring-boot-loader 依赖类继承关系PropertiesLauncher属性配置 附 pom.xml Fat Jar 【pom.xml】 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"ht…

支持Arm CCA的TF-A威胁模型

目录 一、简介 二、评估目标 2.1 假定 2.2 数据流图 三、威胁分析 3.1 威胁评估 3.1.1 针对所有固件镜像的一般威胁 3.1.2 引导固件可以缓解的威胁 3.1.3 运行时EL3固件可缓解的威胁 一、简介 本文针对支持Arm Realm Management Extension (RME)、实现Arm Confidentia…

【Amazon】安装卸载AWS CLI操作流程(Windows 、Linux系统)

AWS 命令行界面&#xff08;AWS CLI&#xff09;是用于管理 AWS 产品的统一工具。只需要下载和配置一个工具&#xff0c;您就可以使用命令行控制多个 AWS 产品并利用脚本来自动执行这些服务。 AWS CLI v2 提供了多项新功能&#xff0c;包括改进的安装程序、新的配置选项&#…

(Matalb分类预测)GA-BP遗传算法优化BP神经网络的多维分类预测

目录 一、程序及算法内容介绍&#xff1a; 基本内容&#xff1a; 亮点与优势&#xff1a; 二、实际运行效果&#xff1a; 三、部分代码&#xff1a; 四、本文代码数据说明手册分享 一、程序及算法内容介绍&#xff1a; 基本内容&#xff1a; 本代码基于Matalb平台编译&am…

NX二次开发UF_CURVE_ask_curve_inflections 函数介绍

文章作者&#xff1a;里海 来源网站&#xff1a;https://blog.csdn.net/WangPaiFeiXingYuan UF_CURVE_ask_curve_inflections Defined in: uf_curve.h int UF_CURVE_ask_curve_inflections(tag_t curve_eid, double proj_matrx [ 9 ] , double range [ 2 ] , int * num_infpt…

如何处理git多分支

本篇文章主要处理以下两种多分支问题 如何将自己在本地的修改上传到一个新的Git分支&#xff08;比如用于测试&#xff0c;不合并进main分支&#xff09;&#xff1f;如何在一个新的本地仓库拉取一个项目的非main分支&#xff0c;并处理他们关联关系&#xff1f; 1. 将自己在…

如何用低代码的思路设计文字描边渐变组件

前言 文字特效设计一直是困扰 Web 前端 Css 世界多年的问题, 比如如何用纯 Css 实现文字描边, 渐变, 阴影等, 由于受限于浏览器兼容性的问题, 我们不得不使用其他替代方案来实现. 平时工作中我们使用 PS 等设计工具能很容易的实现文字渐变等特效, 但是随着可视化技术的成熟, 我…

C语言进阶之路-基本数据小怪篇

目录 一、学习目标&#xff1a; 二、数据基本类型 整型 浮点型 / 实型 字符 字符串 布尔型数据 三、重要的杂七杂八知识点 常量与变量 标准输入 sizeof运算符&#xff1a; 类型转换 数据类型的本质 整型数据尺寸 可移植性整型 拿下第一个C语言程序 总结 一、学…

6 个有效且可用的顶级 Android 数据恢复工具

经过测试 42 种数据恢复软件产品&#xff0c;发现奇客数据恢复安卓版是 Android 设备的最佳选择。 过去几十年来&#xff0c;我一直在科技行业工作&#xff0c;经常帮助人们应对计算机灾难&#xff0c;包括丢失数据。 Android 数据恢复应用程序不在您的设备上运行&#xff0c…