VS2015项目中,MFC内存中调用DLL函数(VC6生成的示例DLL)

本例主要讲一下,用VC6如何生成DLL,用工具WinHex取得DLL全部内容,VC2015项目加载内存中的DLL函数,并调用函数的示例。        

本例中的示例代码下载,点击可以下载

一、VC++6.0生成示例DLL项目

1.新建项目,这里选择Win32 Dynamic-link Library,如下图:

 2.选择“A simple DLL project”,然后点击完成,如下:

  3.生成示例项目后,如下图:

4.添加自己的示例函数,这里以简单的求和函数为例:

/*
程序功能:DLL生成项目,生成测试的DLL文件,
作者:依星
QQ:34596561,312337667
日期:2023/8/15
*/
#include "stdafx.h"

extern "C" __declspec(dllexport) int sum(int a,int b);

BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
					 )
{
    return TRUE;
}

extern "C" __declspec(dllexport) int sum(int a,int b)
{
	return a+b;
}

5.设置项目为release,开始编译构建DLL文件,按“F7”。

二、用WinHex复制取得DLL文件的全部数据

这里使用WinHex来取得DLL的全部数据,具体使用流程如下:

1.打开WinHex,然后将DLL文件拖入到主界面中,如下图:

 2.选择“编辑”---“复制所有”---“C源码”,这里实际是复制了所有的数据,如下图:

3.新建一个记事本,命名为“dll.h” ,将上面复制的数据粘贴到记事本中,然后将此文件复制到VC2015项目中。

 三、建立VC2015示例工程,调用CMemLoadDll类源码

1. 这里先用VC2015建立一个标准的MFC工程项目,新建---项目,如下:

2. 选择MFC--MFC应用程序,然后选择保存的目录,并命名项目名称,如下图:

 3.下一步,然后再选择“基于对话框”,点击“在静态库中使用MFC”,如下图:

 4.至此没有特别要设置的了,点击下一步,直到完成。

5.项目创建后,默认为Unicode字符集,这里改为多字节;(PS:其实改不改都行,主要是项目中都是使用的多字节,习惯了)如下图:

6.将上一步生成的Dll.h文件添加到解决方案的头文件中,然后再新建一个头文件和源文件,用于把网上的CMemLoadDll类源码复制过来,源码将在后面贴出来,咱们先说流程。添加好之后,如下图工程目录:

7.在主对话框中,加一个按钮,用于调用示例:

8.双击按钮,显示点击方法(MemRunDllDlg.cpp): 

/*
调用CMemLoadDll,
加载内存中的DLL,
并运行DLL中的函数
//
dllData:为生成的测试DLL数据文件dll.h;(注:此处也可以把这个DLL文件加到资源中加载,或者以文件形式读取到内存中,都是可以的)
为安全起见,可以把DLL的数据加密存储到DLL.H中,本例不再展示。
*/
void CMemRunDllDlg::OnBnClickedButton1()
{
	// TODO: 在此添加控件通知处理程序代码
	CMemLoadDll *pMemLoadDll = new CMemLoadDll();
	if (!pMemLoadDll->MemLoadLibrary(&dllData, sizeof(dllData))) //加载dll到当前进程的地址空间
	{
		AfxMessageBox("Load DLL error!");
		return ;
	}

	addNumberProc addNumber = (addNumberProc)pMemLoadDll->MemGetProcAddress("sum");
	if (addNumber == NULL)
	{
		AfxMessageBox("Find Add function failed!");
		return ;
	}
	int c = addNumber(1, 2);

	char itc[10];
	sprintf(itc, "%d", c);
	AfxMessageBox(itc);

}

9 .在此文件头部(MemRunDllDlg.cpp),加入文件引用及DLL函数声明,如下:

#include "dll.h"
#include "MemLoadDll.h"

typedef int(*addNumberProc)(int, int);

10.生成EXE并运行,正常运行,如下:

 四、CMemLoadDll类源码,网上搜索的,作者不详

1.MemLoadDll.h头文件

#pragma once  

typedef   BOOL(__stdcall *ProcDllMain)(HINSTANCE, DWORD, LPVOID);

class CMemLoadDll
{
public:
	CMemLoadDll();
	~CMemLoadDll();
	BOOL    MemLoadLibrary(void *lpFileData, int DataLength);  // Dll file data buffer  
	FARPROC MemGetProcAddress(LPCSTR lpProcName);
private:
	BOOL isLoadOk;
	BOOL CheckDataValide(void *lpFileData, int DataLength);
	int  CalcTotalImageSize();
	void CopyDllDatas(void *pDest, void *pSrc);
	BOOL FillRavAddress(void *pBase);
	void DoRelocation(void *pNewBase);
	int  GetAlignedSize(int Origin, int Alignment);
private:
	ProcDllMain pDllMain;

private:
	DWORD  pImageBase;
	PIMAGE_DOS_HEADER pDosHeader;
	PIMAGE_NT_HEADERS pNTHeader;
	PIMAGE_SECTION_HEADER pSectionHeader;

};

2.MemLoadDll.cpp源文件

#include "stdafx.h"
#include <windows.h>  
#include <assert.h>  
#include "MemLoadDll.h"  


CMemLoadDll::CMemLoadDll()
{
	isLoadOk = FALSE;
	pImageBase = NULL;
	pDllMain = NULL;
}

CMemLoadDll::~CMemLoadDll()
{
	if (isLoadOk)
	{
		assert(pImageBase != NULL);
		assert(pDllMain != NULL);
		//脱钩,准备卸载dll  
		pDllMain((HINSTANCE)pImageBase, DLL_PROCESS_DETACH, 0);
		VirtualFree((LPVOID)pImageBase, 0, MEM_RELEASE);
	}
}

//MemLoadLibrary函数从内存缓冲区数据中加载一个dll到当前进程的地址空间,缺省位置0x10000000  
//返回值: 成功返回TRUE , 失败返回FALSE  
//lpFileData: 存放dll文件数据的缓冲区  
//DataLength: 缓冲区中数据的总长度  
BOOL CMemLoadDll::MemLoadLibrary(void *lpFileData, int DataLength)
{
	if (pImageBase != NULL)
	{
		return FALSE;  //已经加载一个dll,还没有释放,不能加载新的dll  
	}

	//检查数据有效性,并初始化  
	if (!CheckDataValide(lpFileData, DataLength))
	{
		return FALSE;
	}

	//计算所需的加载空间  
	int ImageSize = CalcTotalImageSize();
	if (ImageSize == 0)
	{
		return FALSE;
	}

	// 分配虚拟内存  
	void *pMemoryAddress = VirtualAlloc((LPVOID)NULL, ImageSize,
		MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
	if (pMemoryAddress == NULL)
	{
		return FALSE;
	}
	else
	{
		CopyDllDatas(pMemoryAddress, lpFileData); //复制dll数据,并对齐每个段  

												  //重定位信息  
		if (pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress > 0
			&& pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size > 0)
		{
			DoRelocation(pMemoryAddress);
		}

		//填充引入地址表  
		if (!FillRavAddress(pMemoryAddress))  //修正引入地址表失败  
		{
			VirtualFree(pMemoryAddress, 0, MEM_RELEASE);
			return FALSE;
		}

		//修改页属性。应该根据每个页的属性单独设置其对应内存页的属性。这里简化一下。  
		//统一设置成一个属性PAGE_EXECUTE_READWRITE  
		unsigned long old;
		VirtualProtect(pMemoryAddress, ImageSize, PAGE_EXECUTE_READWRITE, &old);
	}

	//修正基地址  
	pNTHeader->OptionalHeader.ImageBase = (DWORD)pMemoryAddress;

	//接下来要调用一下dll的入口函数,做初始化工作。  
	pDllMain = (ProcDllMain)(pNTHeader->OptionalHeader.AddressOfEntryPoint + (DWORD)pMemoryAddress);

	BOOL InitResult = pDllMain((HINSTANCE)pMemoryAddress, DLL_PROCESS_ATTACH, 0);
	if (!InitResult)  //初始化失败  
	{
		pDllMain((HINSTANCE)pMemoryAddress, DLL_PROCESS_DETACH, 0);
		VirtualFree(pMemoryAddress, 0, MEM_RELEASE);
		pDllMain = NULL;
		return FALSE;
	}

	isLoadOk = TRUE;
	pImageBase = (DWORD)pMemoryAddress;
	return TRUE;
}

//MemGetProcAddress函数从dll中获取指定函数的地址  
//返回值: 成功返回函数地址 , 失败返回NULL  
//lpProcName: 要查找函数的名字或者序号  
FARPROC  CMemLoadDll::MemGetProcAddress(LPCSTR lpProcName)
{
	if (pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress == 0 ||
		pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size == 0)
	{
		return NULL;
	}

	if (!isLoadOk)
	{
		return NULL;
	}

	DWORD OffsetStart = pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
	DWORD Size = pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;

	PIMAGE_EXPORT_DIRECTORY pExport = (PIMAGE_EXPORT_DIRECTORY)((DWORD)pImageBase + pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
	int iBase = pExport->Base;
	int iNumberOfFunctions = pExport->NumberOfFunctions;
	int iNumberOfNames = pExport->NumberOfNames; //<= iNumberOfFunctions  
	LPDWORD pAddressOfFunctions = (LPDWORD)(pExport->AddressOfFunctions + pImageBase);
	LPWORD  pAddressOfOrdinals = (LPWORD)(pExport->AddressOfNameOrdinals + pImageBase);
	LPDWORD pAddressOfNames = (LPDWORD)(pExport->AddressOfNames + pImageBase);

	int iOrdinal = -1;

	if (((DWORD)lpProcName & 0xFFFF0000) == 0)  //IT IS A ORDINAL!  
	{
		iOrdinal = (DWORD)lpProcName & 0x0000FFFF - iBase;
	}
	else     //use name  
	{
		int iFound = -1;

		for (int i = 0; i < iNumberOfNames; i++)
		{
			char *pName = (char *)(pAddressOfNames[i] + pImageBase);
			if (strcmp(pName, lpProcName) == 0)
			{
				iFound = i;
				break;
			}
		}
		if (iFound >= 0)
		{
			iOrdinal = (int)(pAddressOfOrdinals[iFound]);
		}
	}

	if (iOrdinal < 0 || iOrdinal >= iNumberOfFunctions)
	{
		return NULL;
	}
	else
	{
		DWORD pFunctionOffset = pAddressOfFunctions[iOrdinal];
		if (pFunctionOffset > OffsetStart && pFunctionOffset < (OffsetStart + Size)) //maybe Export Forwarding  
		{
			return NULL;
		}
		else
		{
			return (FARPROC)(pFunctionOffset + pImageBase);
		}
	}

}

// 重定向PE用到的地址  
void CMemLoadDll::DoRelocation(void *NewBase)
{
	/* 重定位表的结构:
	// DWORD sectionAddress, DWORD size (包括本节需要重定位的数据)
	// 例如 1000节需要修正5个重定位数据的话,重定位表的数据是
	// 00 10 00 00   14 00 00 00      xxxx xxxx xxxx xxxx xxxx 0000
	// -----------   -----------      ----
	// 给出节的偏移  总尺寸=8+6*2     需要修正的地址           用于对齐4字节
	// 重定位表是若干个相连,如果address 和 size都是0 表示结束
	// 需要修正的地址是12位的,高4位是形态字,intel cpu下是3
	*/
	//假设NewBase是0x600000,而文件中设置的缺省ImageBase是0x400000,则修正偏移量就是0x200000  
	DWORD Delta = (DWORD)NewBase - pNTHeader->OptionalHeader.ImageBase;

	//注意重定位表的位置可能和硬盘文件中的偏移地址不同,应该使用加载后的地址  
	PIMAGE_BASE_RELOCATION pLoc = (PIMAGE_BASE_RELOCATION)((unsigned long)NewBase
		+ pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
	while ((pLoc->VirtualAddress + pLoc->SizeOfBlock) != 0)  //开始扫描重定位表  
	{
		WORD *pLocData = (WORD *)((int)pLoc + sizeof(IMAGE_BASE_RELOCATION));
		//计算本节需要修正的重定位项(地址)的数目  
		int NumberOfReloc = (pLoc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
		for (int i = 0; i < NumberOfReloc; i++)
		{
			if ((DWORD)(pLocData[i] & 0xF000) == 0x00003000)  //这是一个需要修正的地址  
			{
				// 举例:  
				// pLoc->VirtualAddress = 0x1000;  
				// pLocData[i] = 0x313E; 表示本节偏移地址0x13E处需要修正  
				// 因此 pAddress = 基地址 + 0x113E  
				// 里面的内容是 A1 ( 0c d4 02 10)  汇编代码是: mov eax , [1002d40c]  
				// 需要修正1002d40c这个地址  
				DWORD *pAddress = (DWORD *)((unsigned long)NewBase + pLoc->VirtualAddress + (pLocData[i] & 0x0FFF));
				*pAddress += Delta;
			}
		}
		//转移到下一个节进行处理  
		pLoc = (PIMAGE_BASE_RELOCATION)((DWORD)pLoc + pLoc->SizeOfBlock);
	}
}

//填充引入地址表  
BOOL CMemLoadDll::FillRavAddress(void *pImageBase)
{
	// 引入表实际上是一个 IMAGE_IMPORT_DESCRIPTOR 结构数组,全部是0表示结束  
	// 数组定义如下:  
	//  
	// DWORD   OriginalFirstThunk;         // 0表示结束,否则指向未绑定的IAT结构数组  
	// DWORD   TimeDateStamp;  
	// DWORD   ForwarderChain;             // -1 if no forwarders  
	// DWORD   Name;                       // 给出dll的名字  
	// DWORD   FirstThunk;                 // 指向IAT结构数组的地址(绑定后,这些IAT里面就是实际的函数地址)  

	int i;

	unsigned long Offset = pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
	if (Offset == 0)
	{
		return TRUE;    //No Import Table  
	}
	PIMAGE_IMPORT_DESCRIPTOR pID = (PIMAGE_IMPORT_DESCRIPTOR)((unsigned long)pImageBase + Offset);
	while (pID->Characteristics != 0)
	{
		PIMAGE_THUNK_DATA pRealIAT = (PIMAGE_THUNK_DATA)((unsigned long)pImageBase + pID->FirstThunk);
		PIMAGE_THUNK_DATA pOriginalIAT = (PIMAGE_THUNK_DATA)((unsigned long)pImageBase + pID->OriginalFirstThunk);
		//获取dll的名字  
		TCHAR buf[256]; //dll name;  
		BYTE *pName = (BYTE *)((unsigned long)pImageBase + pID->Name);
		for (i = 0; i < 256; i++)
		{
			if (pName[i] == 0)
			{
				break;
			}
			buf[i] = pName[i];
		}
		if (i >= 256)
		{
			return FALSE;    // bad dll name  
		}
		else
		{
			buf[i] = 0;
		}
		HMODULE hDll = GetModuleHandle(buf);
		if (hDll == NULL)
		{
			hDll = LoadLibrary(buf);
		}
		if (hDll == NULL)
		{
			return FALSE;    //NOT FOUND DLL  
		}
		//获取DLL中每个导出函数的地址,填入IAT  
		//每个IAT结构是 :  
		// union { PBYTE  ForwarderString;  
		//   PDWORD Function;  
		//   DWORD Ordinal;  
		//   PIMAGE_IMPORT_BY_NAME  AddressOfData;  
		// } u1;  
		// 长度是一个DWORD ,正好容纳一个地址。  
		for (i = 0; ; i++)
		{
			if (pOriginalIAT[i].u1.Function == 0)
			{
				break;
			}
			FARPROC lpFunction = NULL;
			if (pOriginalIAT[i].u1.Ordinal & IMAGE_ORDINAL_FLAG)  //这里的值给出的是导出序号  
			{
				lpFunction = GetProcAddress(hDll, (LPCSTR)(pOriginalIAT[i].u1.Ordinal & 0x0000FFFF));
			}
			else     //按照名字导入  
			{
				//获取此IAT项所描述的函数名称  
				PIMAGE_IMPORT_BY_NAME pByName = (PIMAGE_IMPORT_BY_NAME)
					((DWORD)pImageBase + (DWORD)(pOriginalIAT[i].u1.AddressOfData));
				//    if(pByName->Hint !=0)  
				//     lpFunction = GetProcAddress(hDll, (LPCSTR)pByName->Hint);  
				//    else  
				lpFunction = GetProcAddress(hDll, (char *)pByName->Name);
			}

			if (lpFunction != NULL)  //找到了!  
			{
				pRealIAT[i].u1.Function = (DWORD)lpFunction;//(PDWORD) lpFunction;  
			}
			else
			{
				return FALSE;
			}
		}

		//move to next  
		pID = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pID + sizeof(IMAGE_IMPORT_DESCRIPTOR));
	}

	return TRUE;
}

//CheckDataValide函数用于检查缓冲区中的数据是否有效的dll文件  
//返回值: 是一个可执行的dll则返回TRUE,否则返回FALSE。  
//lpFileData: 存放dll数据的内存缓冲区  
//DataLength: dll文件的长度  
BOOL CMemLoadDll::CheckDataValide(void *lpFileData, int DataLength)
{
	//检查长度  
	if (DataLength < sizeof(IMAGE_DOS_HEADER))
	{
		return FALSE;
	}
	pDosHeader = (PIMAGE_DOS_HEADER)lpFileData;  // DOS头  
												 //检查dos头的标记  
	if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
	{
		return FALSE;    //0x5A4D : MZ  
	}

	//检查长度  
	if ((DWORD)DataLength < (pDosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS)))
	{
		return FALSE;
	}
	//取得pe头  
	pNTHeader = (PIMAGE_NT_HEADERS)((unsigned long)lpFileData + pDosHeader->e_lfanew); // PE头  
																					   //检查pe头的合法性  
	if (pNTHeader->Signature != IMAGE_NT_SIGNATURE)
	{
		return FALSE;    //0x00004550 : PE00  
	}
	if ((pNTHeader->FileHeader.Characteristics & IMAGE_FILE_DLL) == 0) //0x2000  : File is a DLL  
	{
		return FALSE;
	}
	if ((pNTHeader->FileHeader.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE) == 0) //0x0002 : 指出文件可以运行  
	{
		return FALSE;
	}
	if (pNTHeader->FileHeader.SizeOfOptionalHeader != sizeof(IMAGE_OPTIONAL_HEADER))
	{
		return FALSE;
	}

	//取得节表(段表)  
	pSectionHeader = (PIMAGE_SECTION_HEADER)((int)pNTHeader + sizeof(IMAGE_NT_HEADERS));
	//验证每个节表的空间  
	for (int i = 0; i < pNTHeader->FileHeader.NumberOfSections; i++)
	{
		if ((pSectionHeader[i].PointerToRawData + pSectionHeader[i].SizeOfRawData) >(DWORD)DataLength)
		{
			return FALSE;
		}
	}
	return TRUE;
}

//计算对齐边界  
int CMemLoadDll::GetAlignedSize(int Origin, int Alignment)
{
	return (Origin + Alignment - 1) / Alignment * Alignment;
}

//计算整个dll映像文件的尺寸  
int CMemLoadDll::CalcTotalImageSize()
{
	int Size;
	if (pNTHeader == NULL)
	{
		return 0;
	}
	int nAlign = pNTHeader->OptionalHeader.SectionAlignment; //段对齐字节数  

															 // 计算所有头的尺寸。包括dos, coff, pe头 和 段表的大小  
	Size = GetAlignedSize(pNTHeader->OptionalHeader.SizeOfHeaders, nAlign);
	// 计算所有节的大小  
	for (int i = 0; i < pNTHeader->FileHeader.NumberOfSections; ++i)
	{
		//得到该节的大小  
		int CodeSize = pSectionHeader[i].Misc.VirtualSize;
		int LoadSize = pSectionHeader[i].SizeOfRawData;
		int MaxSize = (LoadSize > CodeSize) ? (LoadSize) : (CodeSize);

		int SectionSize = GetAlignedSize(pSectionHeader[i].VirtualAddress + MaxSize, nAlign);
		if (Size < SectionSize)
		{
			Size = SectionSize;    //Use the Max;  
		}
	}
	return Size;
}
//CopyDllDatas函数将dll数据复制到指定内存区域,并对齐所有节  
//pSrc: 存放dll数据的原始缓冲区  
//pDest:目标内存地址  
void CMemLoadDll::CopyDllDatas(void *pDest, void *pSrc)
{
	// 计算需要复制的PE头+段表字节数  
	int  HeaderSize = pNTHeader->OptionalHeader.SizeOfHeaders;
	int  SectionSize = pNTHeader->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER);
	int  MoveSize = HeaderSize + SectionSize;
	//复制头和段信息  
	memmove(pDest, pSrc, MoveSize);

	//复制每个节  
	for (int i = 0; i < pNTHeader->FileHeader.NumberOfSections; ++i)
	{
		if (pSectionHeader[i].VirtualAddress == 0 || pSectionHeader[i].SizeOfRawData == 0)
		{
			continue;
		}
		// 定位该节在内存中的位置  
		void *pSectionAddress = (void *)((unsigned long)pDest + pSectionHeader[i].VirtualAddress);
		// 复制段数据到虚拟内存  
		memmove((void *)pSectionAddress,
			(void *)((DWORD)pSrc + pSectionHeader[i].PointerToRawData),
			pSectionHeader[i].SizeOfRawData);
	}

	//修正指针,指向新分配的内存  
	//新的dos头  
	pDosHeader = (PIMAGE_DOS_HEADER)pDest;
	//新的pe头地址  
	pNTHeader = (PIMAGE_NT_HEADERS)((int)pDest + (pDosHeader->e_lfanew));
	//新的节表地址  
	pSectionHeader = (PIMAGE_SECTION_HEADER)((int)pNTHeader + sizeof(IMAGE_NT_HEADERS));
	return;
}

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

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

相关文章

爬虫逆向实战(十四)--某培训平台登录

一、数据接口分析 主页地址&#xff1a;某培训平台 1、抓包 通过抓包可以发现登录是表单提交到j_spring_security_check 2、判断是否有加密参数 请求参数是否加密&#xff1f; 通过查看“载荷”模块可以发现有一个j_password加密参数 请求头是否加密&#xff1f; 无响应是…

SAP MM学习笔记23-购买发注的账户分配类型(勘定Category)

SAP中控制财务凭证过账科目的是 账号分配类型&#xff08;勘定Category&#xff09;栏目。 ・账号分配类型&#xff08;勘定Category&#xff09;有&#xff1a; 1&#xff0c;K 原价Center&#xff08;成本中心。用于消耗物料采购 的过账&#xff09; 2&#xff0c;E 得意先…

TPAMI, 2023 | 用压缩隐逆向神经网络进行高精度稀疏雷达成像

CoIR: Compressive Implicit Radar | IEEE TPAMI, 2023 | 用压缩隐逆向神经网络进行高精度稀疏雷达成像 注1:本文系“无线感知论文速递”系列之一,致力于简洁清晰完整地介绍、解读无线感知领域最新的顶会/顶刊论文(包括但不限于Nature/Science及其子刊;MobiCom, Sigcom, MobiSy…

C++之string类的使用

C之string类的使用 1.为什么要学string类2.标准库中的string类3.string类的接口的使用3.1默认成员函数3.2容量操作3.3访问操作3.4遍历操作3.5修改操作3.6字符串操作3.7非成员函数 1.为什么要学string类 我们所学的字符串是以’\0’结尾的一些字符的集合&#xff0c;为了操作方…

工业视觉相机镜头选型方法

一、相机选型 1、首先&#xff0c;根据检测需求确定选用黑白/彩色、面阵/线阵相机&#xff0c;接口类型一般选择GigE 2、确定检测精度要求&#xff08;最小特征尺寸mm&#xff09;、视野范围&#xff0c;一个测量精度对应几个像素数&#xff08;一般取3-5&#xff09; 3、计…

uni-app根据经纬度逆解析详细地址

uni-app中的getLocation()方法可以获取到用户当前的地理位置&#xff08;经纬度&#xff09;、速度。 但是返回参数中的address在app中才会显示&#xff0c;小程序中不会显示&#xff0c;所以我们需要进行逆解析其地址&#xff0c;解析出它的地址信息。 1.首先要在腾讯位置服务…

【Redis从头学-4】Redis中的String数据类型实战应用场景之验证码、浏览量、点赞量、Json格式存储

&#x1f9d1;‍&#x1f4bb;作者名称&#xff1a;DaenCode &#x1f3a4;作者简介&#xff1a;啥技术都喜欢捣鼓捣鼓&#xff0c;喜欢分享技术、经验、生活。 &#x1f60e;人生感悟&#xff1a;尝尽人生百味&#xff0c;方知世间冷暖。 &#x1f4d6;所属专栏&#xff1a;Re…

Intelij IDEA 配置Tomcat解决Application Server不显示的问题

今天搭建war工程时部署项目发现&#xff0c;IDEA的控制台没有Application Servers&#xff0c;在网上查了一下&#xff0c;总结几个比较好的解决方法&#xff0c;为了方便自己和其他人以后碰到相同的问题&#xff0c;不再浪费时间再次寻找解决办法。 Intelij IDEA 配置Tomcat时…

如何做好科技文献资料的翻译!

我们知道&#xff0c;科技文献是工程技术人员的重要参考文献&#xff0c;翻译科技文献资料有助于促进国内外科技知识和技术的传播。那么&#xff0c;如何做好科技文献资料的翻译&#xff0c;专业科技文献翻译哪家好&#xff1f; 据了解&#xff0c;科技文献翻译是一种以应用为主…

Nginx虚拟主机(server块)部署Vue项目

需求 配置虚拟主机&#xff0c;实现一个Nginx运行多个服务。 实现 使用Server块。不同的端口号&#xff0c;表示不同的服务&#xff1b;同时在配置中指定&#xff0c;Vue安装包所在的位置。 配置 Vue项目&#xff0c;放在 html/test 目录下。 config中的配置如下&#xf…

接口测试,负载测试,并发测试,压力测试区别

接口测试 1.定义&#xff1a;接口测试是测试系统组件间接口的一种测试。接口测试主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点。测试的重点是要检查数据的交换&#xff0c;传递和控制管理过程&#xff0c;以及系统间的相互逻辑依赖关系等。 2.目的&#xf…

Matlab中图例的位置(图例放在图的上方、下方、左方、右方、图外面)等

一、图例默认位置 默认的位置在NorthEast r 10; a 0; b 0; t0:0.1:2.1*pi; xar*cos(t); ybr*sin(t); A1plot(x,y,r,linewidth,4);%圆 hold on axis equal A2plot([0 0],[1 10],b,linewidth,4);%直线 legend([A1,A2],圆形,line)二、通过Location对legend的位置进行改变 变…

无涯教程-Perl - sysread函数

描述 该函数等效于C /操作系统函数read(),因为它绕过了诸如print,read和seek之类的函数所采用的缓冲系统,它仅应与相应的syswrite和sysseek函数一起使用。 它从FILEHANDLE中读取LENGTH个字节,并将输出放入SCALAR中。如果指定了OFFSET,则将数据从OFFSET字节写入SCALAR,从而有效…

小航助学GESP_C++一级模拟测试卷第2套(含题库答题软件账号)

需要在线模拟训练的题库账号请点击 小航助学编程在线模拟试卷系统&#xff08;含题库答题软件账号&#xff09;_程序猿下山的博客-CSDN博客 需要在线模拟训练的题库账号请点击 小航助学编程在线模拟试卷系统&#xff08;含题库答题软件账号&#xff09;_程序猿下山的博客-CSD…

【C++笔记】C++之类与对象(中)

【C笔记】C之类与对象&#xff08;中&#xff09; 1、类的构造函数1.1、构造函数的基本用法1.2、构造函数的7个特性 2、类的析构函数2.1、析构函数的基本用法2.2、析构函数的6个特性 3、类的拷贝构造函数3.1、拷贝构造的基本用法3.2、拷贝构造的“无限套娃”陷阱3.3、深拷贝与浅…

通过css设置filter 属性,使整个页面呈现灰度效果,让整个网页变灰

通过css设置filter 属性设置页面整体置灰 效果图: 通过设置 filter 属性为 grayscale(100%)&#xff0c;页面中的所有元素都会被应用灰色滤镜效果&#xff0c;使整个页面呈现灰度效果。 <style type"text/css"> html { filter: grayscale(100%); -webkit-f…

《Linux从练气到飞升》No.15 Linux 环境变量

&#x1f57a;作者&#xff1a; 主页 我的专栏C语言从0到1探秘C数据结构从0到1探秘Linux菜鸟刷题集 &#x1f618;欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收藏✍️留言 &#x1f3c7;码字不易&#xff0c;你的&#x1f44d;点赞&#x1f64c;收藏❤️关注对我真的…

C++函数模板和类模板

C另一种编程思想称为泛型编程&#xff0c;主要利用的技术是模板 C提供两种模板机制&#xff1a;函数模板和类模板 C提供了模板(template)编程的概念。所谓模板&#xff0c;实际上是建立一个通用函数或类&#xff0c; 其类内部的类型和函数的形参类型不具体指定&#xff0c; 用…

SpringBoot代理访问本地静态资源400 404

SpringBoot代理访问静态资源400 404 背景&#xff1a;pdf文件上传到linux服务器上&#xff0c;使用SpringBoot代理访问问题&#xff1a;访问过程中可能会出现400、404问题 前提&#xff1a;保证有文件&#xff0c;并且文件路径正确 SpringBoot如何配置静态资源代理&#xff0…

网络综合布线实训室建设方案

一、网络综合布线系统概述 网络综合布线系统是为了满足数据通信需求而设计和建立的一套基础设施。它提供了数据传输、信号传输和电力供应的基础结构&#xff0c;支持各种网络设备和终端设备之间的连接。 网络综合布线系统通常包括以下组成部分&#xff1a; 1&#xff09; 数据…