码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • 驱动开发:取进程模块的函数地址


    合集 - Windows 内核安全编程技术实践(97)
    1.驱动开发:运用VAD隐藏R3内存思路2021-07-162.驱动开发:应用DeviceIoContro模板精讲06-29
    3.驱动开发:取进程模块的函数地址06-28
    4.驱动开发:内核读写内存多级偏移06-275.驱动开发:内核物理内存寻址读写06-266.驱动开发:内核远程线程实现DLL注入06-257.驱动开发:摘除InlineHook内核钩子06-248.驱动开发:内核中进程与句柄互转06-239.驱动开发:内核注册表增删改查06-2110.驱动开发:基于事件同步的反向通信06-2011.驱动开发:文件微过滤驱动入门06-1912.驱动开发:内核RIP劫持实现DLL注入06-1613.驱动开发:内核解锁与强删文件06-1514.驱动开发:内核ShellCode线程注入06-1415.驱动开发:内核LoadLibrary实现DLL注入06-1316.驱动开发:内核遍历文件或目录06-1217.驱动开发:内核文件读写系列函数06-0918.驱动开发:内核封装WFP防火墙入门06-0819.驱动开发:PE导出函数与RVA转换06-0720.驱动开发:内核扫描SSDT挂钩状态06-0621.驱动开发:内核实现SSDT挂钩与摘钩06-0522.驱动开发:内核PE结构VA与FOA转换06-0223.驱动开发:内核解析PE结构节表06-0124.驱动开发:内核解析PE结构导出表05-3125.驱动开发:内核读写内存浮点数05-3026.驱动开发:内核解析内存四级页表05-2927.驱动开发:内核实现进程汇编与反汇编05-2328.驱动开发:通过应用堆实现多次通信05-1929.驱动开发:内核远程堆分配与销毁05-1530.驱动开发:通过MDL映射实现多次通信04-2931.驱动开发:内核使用IO/DPC定时器04-0432.驱动开发:探索DRIVER_OBJECT驱动对象04-0333.驱动开发:配置Visual Studio驱动开发环境03-1334.驱动开发:内核封装TDI网络通信接口2022-11-0335.驱动开发:内核封装WSK网络通信接口2022-11-0336.驱动开发:内核层InlineHook挂钩函数2022-10-3137.驱动开发:内核LDE64引擎计算汇编长度2022-10-3138.驱动开发:内核强制结束进程运行2022-10-2939.驱动开发:内核监控FileObject文件回调2022-10-2840.驱动开发:内核监控Register注册表回调2022-10-2741.驱动开发:内核运用LoadImage屏蔽驱动2022-10-2642.驱动开发:内核监视LoadImage映像回调2022-10-2543.驱动开发:内核无痕隐藏自身分析2022-10-2444.驱动开发:内核注册并监控对象回调2022-10-2445.驱动开发:内核监控进程与线程回调2022-10-2346.驱动开发:内核测试模式过DSE签名2022-10-2247.驱动开发:内核枚举进程与线程ObCall回调2022-10-2248.驱动开发:内核枚举Registry注册表回调2022-10-2149.驱动开发:内核枚举LoadImage映像回调2022-10-2050.驱动开发:内核枚举ShadowSSDT基址2022-10-2051.驱动开发:Win10枚举完整SSDT地址表2022-10-1952.驱动开发:Win10内核枚举SSDT表基址2022-10-1953.驱动开发:内核特征码扫描PE代码段2022-10-1854.驱动开发:内核枚举Minifilter微过滤驱动2022-10-1855.驱动开发:内核特征码搜索函数封装2022-10-1756.驱动开发:内核枚举驱动内线程(答疑篇)2022-10-1657.驱动开发:内核枚举PspCidTable句柄表2022-10-1658.驱动开发:内核枚举DpcTimer定时器2022-10-1659.驱动开发:如何枚举所有SSDT表地址2022-10-1460.驱动开发:内核枚举IoTimer定时器2022-10-1461.驱动开发:内核遍历进程VAD结构体2022-10-1362.驱动开发:内核中实现Dump进程转储2022-10-1163.驱动开发:内核R3与R0内存映射拷贝2022-10-1164.驱动开发:内核通过PEB得到进程参数2022-10-1065.驱动开发:内核取应用层模块基地址2022-10-0966.驱动开发:内核取ntoskrnl模块基地址2022-10-0967.驱动开发:判断自身是否加载成功2022-10-0868.驱动开发:应用DeviceIoContro开发模板2022-10-0369.驱动开发:通过Async反向与内核通信2022-10-0370.驱动开发:通过PIPE管道与内核层通信2022-10-0171.驱动通信:通过PIPE管道与内核层通信2022-10-0172.驱动开发:通过ReadFile与内核层通信2022-09-3073.驱动开发:内核字符串拷贝与比较2022-09-2974.驱动开发:内核字符串转换方法2022-09-2875.驱动开发:内核中的自旋锁结构2022-09-2876.驱动开发:内核CR3切换读写内存2022-09-2577.驱动开发:内核中的链表与结构体2022-09-2378.驱动开发:摘链DKOM进程隐藏2022-08-3179.驱动开发:WinDBG 枚举SSDT以及SSSDT地址2022-04-2880.驱动开发:实现驱动加载卸载工具2021-07-1681.驱动开发:断链隐藏驱动程序自身2021-07-1682.驱动开发:通过内存拷贝读写内存2021-07-1283.驱动开发:内核MDL读写进程内存2021-07-0584.驱动开发:内核监控进程与线程创建2021-05-0685.驱动开发:监控进程与线程对象操作2020-06-1486.驱动开发:WinDBG 常用调试命令总结2020-06-1087.驱动开发:通过SystemBuf与内核层通信2020-04-1388.驱动开发:对象回调监控文件访问2019-11-0189.驱动开发:内核中枚举进线程与模块2019-10-2190.驱动开发:DKOM 实现进程隐藏2019-10-1191.驱动开发:内核读取SSDT表基址2019-10-0992.驱动开发:驱动与应用的简单通信2019-09-2393.驱动开发:恢复SSDT内核钩子2019-09-2194.驱动开发:挂接SSDT内核钩子2019-09-2095.驱动开发:WinDBG 配置内核双机调试2019-09-1996.VS2013+WDK8.1 驱动开发环境配置2019-09-1897.驱动开发:派遣函数与设备对象2019-09-14
    收起

    在笔者上一篇文章《驱动开发:内核取应用层模块基地址》中简单为大家介绍了如何通过遍历PLIST_ENTRY32链表的方式获取到32位应用程序中特定模块的基地址,由于是入门系列所以并没有封装实现太过于通用的获取函数,本章将继续延申这个话题,并依次实现通用版GetUserModuleBaseAddress()取远程进程中指定模块的基址和GetModuleExportAddress()取远程进程中特定模块中的函数地址,此类功能也是各类安全工具中常用的代码片段。

    首先封装一个lyshark.h头文件,此类头文件中的定义都是微软官方定义好的规范,如果您想获取该结构的详细说明文档请参阅微软官方,此处不做过多的介绍。

    // 署名权
    // right to sign one's name on a piece of work
    // PowerBy: LyShark
    // Email: me@lyshark.com
    
    #include 
    #include 
    #include 
    
    // 导出未导出函数
    NTKERNELAPI PPEB NTAPI PsGetProcessPeb(IN PEPROCESS Process);
    NTKERNELAPI PVOID NTAPI PsGetProcessWow64Process(IN PEPROCESS Process);
    
    typedef struct _PEB32
    {
      UCHAR InheritedAddressSpace;
      UCHAR ReadImageFileExecOptions;
      UCHAR BeingDebugged;
      UCHAR BitField;
      ULONG Mutant;
      ULONG ImageBaseAddress;
      ULONG Ldr;
      ULONG ProcessParameters;
      ULONG SubSystemData;
      ULONG ProcessHeap;
      ULONG FastPebLock;
      ULONG AtlThunkSListPtr;
      ULONG IFEOKey;
      ULONG CrossProcessFlags;
      ULONG UserSharedInfoPtr;
      ULONG SystemReserved;
      ULONG AtlThunkSListPtr32;
      ULONG ApiSetMap;
    } PEB32, *PPEB32;
    
    typedef struct _PEB_LDR_DATA
    {
      ULONG Length;
      UCHAR Initialized;
      PVOID SsHandle;
      LIST_ENTRY InLoadOrderModuleList;
      LIST_ENTRY InMemoryOrderModuleList;
      LIST_ENTRY InInitializationOrderModuleList;
    } PEB_LDR_DATA, *PPEB_LDR_DATA;
    
    typedef struct _PEB
    {
      UCHAR InheritedAddressSpace;
      UCHAR ReadImageFileExecOptions;
      UCHAR BeingDebugged;
      UCHAR BitField;
      PVOID Mutant;
      PVOID ImageBaseAddress;
      PPEB_LDR_DATA Ldr;
      PVOID ProcessParameters;
      PVOID SubSystemData;
      PVOID ProcessHeap;
      PVOID FastPebLock;
      PVOID AtlThunkSListPtr;
      PVOID IFEOKey;
      PVOID CrossProcessFlags;
      PVOID KernelCallbackTable;
      ULONG SystemReserved;
      ULONG AtlThunkSListPtr32;
      PVOID ApiSetMap;
    } PEB, *PPEB;
    
    typedef struct _PEB_LDR_DATA32
    {
      ULONG Length;
      UCHAR Initialized;
      ULONG SsHandle;
      LIST_ENTRY32 InLoadOrderModuleList;
      LIST_ENTRY32 InMemoryOrderModuleList;
      LIST_ENTRY32 InInitializationOrderModuleList;
    } PEB_LDR_DATA32, *PPEB_LDR_DATA32;
    
    typedef struct _LDR_DATA_TABLE_ENTRY32
    {
      LIST_ENTRY32 InLoadOrderLinks;
      LIST_ENTRY32 InMemoryOrderLinks;
      LIST_ENTRY32 InInitializationOrderLinks;
      ULONG DllBase;
      ULONG EntryPoint;
      ULONG SizeOfImage;
      UNICODE_STRING32 FullDllName;
      UNICODE_STRING32 BaseDllName;
      ULONG Flags;
      USHORT LoadCount;
      USHORT TlsIndex;
      LIST_ENTRY32 HashLinks;
      ULONG TimeDateStamp;
    } LDR_DATA_TABLE_ENTRY32, *PLDR_DATA_TABLE_ENTRY32;
    
    typedef struct _LDR_DATA_TABLE_ENTRY
    {
      LIST_ENTRY InLoadOrderLinks;
      LIST_ENTRY InMemoryOrderLinks;
      LIST_ENTRY InInitializationOrderLinks;
      PVOID DllBase;
      PVOID EntryPoint;
      ULONG SizeOfImage;
      UNICODE_STRING FullDllName;
      UNICODE_STRING BaseDllName;
      ULONG Flags;
      USHORT LoadCount;
      USHORT TlsIndex;
      LIST_ENTRY HashLinks;
      ULONG TimeDateStamp;
    } LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
    

    GetUserModuleBaseAddress(): 实现取进程中模块基址,该功能在《驱动开发:内核取应用层模块基地址》中详细介绍过原理,这段代码核心原理如下所示,此处最需要注意的是如果是32位进程则我们需要得到PPEB32 Peb32结构体,该结构体通常可以直接使用PsGetProcessWow64Process()这个内核函数获取到,而如果是64位进程则需要将寻找PEB的函数替换为PsGetProcessPeb(),其他的枚举细节与上一篇文章中的方法一致。

    // 署名权
    // right to sign one's name on a piece of work
    // PowerBy: LyShark
    // Email: me@lyshark.com
    
    #include 
    #include 
    #include "lyshark.h"
    
    // 获取特定进程内特定模块的基址
    PVOID GetUserModuleBaseAddress(IN PEPROCESS EProcess, IN PUNICODE_STRING ModuleName, IN BOOLEAN IsWow64)
    {
    	if (EProcess == NULL)
    		return NULL;
    	__try
    	{
    		// 设置延迟时间为250毫秒
    		LARGE_INTEGER Time = { 0 };
    		Time.QuadPart = -250ll * 10 * 1000;
    
    		// 如果是32位则执行如下代码
    		if (IsWow64)
    		{
    			// 得到PEB进程信息
    			PPEB32 Peb32 = (PPEB32)PsGetProcessWow64Process(EProcess);
    			if (Peb32 == NULL)
    			{
    				return NULL;
    			}
    
    			// 延迟加载等待时间
    			for (INT i = 0; !Peb32->Ldr && i < 10; i++)
    			{
    				KeDelayExecutionThread(KernelMode, TRUE, &Time);
    			}
    
    			// 没有PEB加载超时
    			if (!Peb32->Ldr)
    			{
    				return NULL;
    			}
    
    			// 搜索模块 InLoadOrderModuleList
    			for (PLIST_ENTRY32 ListEntry = (PLIST_ENTRY32)((PPEB_LDR_DATA32)Peb32->Ldr)->InLoadOrderModuleList.Flink; ListEntry != &((PPEB_LDR_DATA32)Peb32->Ldr)->InLoadOrderModuleList; ListEntry = (PLIST_ENTRY32)ListEntry->Flink)
    			{
    				UNICODE_STRING UnicodeString;
    				PLDR_DATA_TABLE_ENTRY32 LdrDataTableEntry32 = CONTAINING_RECORD(ListEntry, LDR_DATA_TABLE_ENTRY32, InLoadOrderLinks);
    				RtlUnicodeStringInit(&UnicodeString, (PWCH)LdrDataTableEntry32->BaseDllName.Buffer);
    
    				// 找到了返回模块基址
    				if (RtlCompareUnicodeString(&UnicodeString, ModuleName, TRUE) == 0)
    				{
    					return (PVOID)LdrDataTableEntry32->DllBase;
    				}
    			}
    		}
    		// 如果是64位则执行如下代码
    		else
    		{
    			// 同理,先找64位PEB
    			PPEB Peb = PsGetProcessPeb(EProcess);
    			if (!Peb)
    			{
    				return NULL;
    			}
    
    			// 延迟加载
    			for (INT i = 0; !Peb->Ldr && i < 10; i++)
    			{
    				KeDelayExecutionThread(KernelMode, TRUE, &Time);
    			}
    
    			// 找不到PEB直接返回
    			if (!Peb->Ldr)
    			{
    				return NULL;
    			}
    
    			// 遍历链表
    			for (PLIST_ENTRY ListEntry = Peb->Ldr->InLoadOrderModuleList.Flink; ListEntry != &Peb->Ldr->InLoadOrderModuleList; ListEntry = ListEntry->Flink)
    			{
    				// 将特定链表转换为PLDR_DATA_TABLE_ENTRY格式
    				PLDR_DATA_TABLE_ENTRY LdrDataTableEntry = CONTAINING_RECORD(ListEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
    
    				// 找到了则返回地址
    				if (RtlCompareUnicodeString(&LdrDataTableEntry->BaseDllName, ModuleName, TRUE) == 0)
    				{
    					return LdrDataTableEntry->DllBase;
    				}
    			}
    		}
    	}
    	__except (EXCEPTION_EXECUTE_HANDLER)
    	{
    		return NULL;
    	}
    	return NULL;
    }
    

    那么该函数该如何调用传递参数呢,如下代码是DriverEntry入口处的调用方法,首先要想得到特定进程的特定模块地址则第一步就是需要PsLookupProcessByProcessId找到模块的EProcess结构,接着通过PsGetProcessWow64Process得到当前被操作进程是32位还是64位,通过调用KeStackAttachProcess附加到进程内存中,然后调用GetUserModuleBaseAddress并传入需要获取模块的名字得到数据后返回给NtdllAddress变量,最后调用KeUnstackDetachProcess取消附加即可。

    // 署名权
    // right to sign one's name on a piece of work
    // PowerBy: LyShark
    // Email: me@lyshark.com
    
    NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
    {
    	HANDLE ProcessID = (HANDLE)7924;
    
    	PEPROCESS EProcess = NULL;
    	NTSTATUS Status = STATUS_SUCCESS;
    	KAPC_STATE ApcState;
    
    	DbgPrint("Hello LyShark.com \n");
    
    	// 根据PID得到进程EProcess结构
    	Status = PsLookupProcessByProcessId(ProcessID, &EProcess);
    	if (Status != STATUS_SUCCESS)
    	{
    		DbgPrint("获取EProcessID失败 \n");
    		return Status;
    	}
    
    	// 判断目标进程是32位还是64位
    	BOOLEAN IsWow64 = (PsGetProcessWow64Process(EProcess) != NULL) ? TRUE : FALSE;
    
    	// 验证地址是否可读
    	if (!MmIsAddressValid(EProcess))
    	{
    		DbgPrint("地址不可读 \n");
    		Driver->DriverUnload = UnDriver;
    		return STATUS_SUCCESS;
    	}
    
    	// 将当前线程连接到目标进程的地址空间(附加进程)
    	KeStackAttachProcess((PRKPROCESS)EProcess, &ApcState);
    
    	__try
    	{
    		UNICODE_STRING NtdllUnicodeString = { 0 };
    		PVOID NtdllAddress = NULL;
    
    		// 得到进程内ntdll.dll模块基地址
    		RtlInitUnicodeString(&NtdllUnicodeString, L"Ntdll.dll");
    		NtdllAddress = GetUserModuleBaseAddress(EProcess, &NtdllUnicodeString, IsWow64);
    		if (!NtdllAddress)
    		{
    			DbgPrint("没有找到基址 \n");
    			Driver->DriverUnload = UnDriver;
    			return STATUS_SUCCESS;
    		}
    
    		DbgPrint("[*] 模块ntdll.dll基址: %p \n", NtdllAddress);
    	}
    	__except (EXCEPTION_EXECUTE_HANDLER)
    	{
    	}
    
    	// 取消附加
    	KeUnstackDetachProcess(&ApcState);
    
    	Driver->DriverUnload = UnDriver;
    	return STATUS_SUCCESS;
    }
    

    替换DriverEntry入口函数处的ProcessID并替换为当前需要获取的应用层进程PID,运行驱动程序即可得到该进程内Ntdll.dll的模块基址,输出效果如下;

    GetModuleExportAddress(): 实现获取特定模块中特定函数的基地址,通常我们通过GetUserModuleBaseAddress()可得到进程内特定模块的基址,然后则可继续通过GetModuleExportAddress()获取到该模块内特定导出函数的内存地址,至于获取导出表中特定函数的地址则可通过如下方式循环遍历导出表函数获取。

    // 署名权
    // right to sign one's name on a piece of work
    // PowerBy: LyShark
    // Email: me@lyshark.com
    
    // 获取特定模块下的导出函数地址
    PVOID GetModuleExportAddress(IN PVOID ModuleBase, IN PCCHAR FunctionName, IN PEPROCESS EProcess)
    {
    	PIMAGE_DOS_HEADER ImageDosHeader = (PIMAGE_DOS_HEADER)ModuleBase;
    	PIMAGE_NT_HEADERS32 ImageNtHeaders32 = NULL;
    	PIMAGE_NT_HEADERS64 ImageNtHeaders64 = NULL;
    	PIMAGE_EXPORT_DIRECTORY ImageExportDirectory = NULL;
    	ULONG ExportDirectorySize = 0;
    	ULONG_PTR FunctionAddress = 0;
    
    	// 为空则返回
    	if (ModuleBase == NULL)
    	{
    		return NULL;
    	}
    
    	// 是不是PE文件
    	if (ImageDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
    	{
    		return NULL;
    	}
    
    	// 获取NT头
    	ImageNtHeaders32 = (PIMAGE_NT_HEADERS32)((PUCHAR)ModuleBase + ImageDosHeader->e_lfanew);
    	ImageNtHeaders64 = (PIMAGE_NT_HEADERS64)((PUCHAR)ModuleBase + ImageDosHeader->e_lfanew);
    
    	// 是64位则执行
    	if (ImageNtHeaders64->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)
    	{
    		ImageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)(ImageNtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress + (ULONG_PTR)ModuleBase);
    		ExportDirectorySize = ImageNtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;
    	}
    	// 是32位则执行
    	else
    	{
    		ImageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)(ImageNtHeaders32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress + (ULONG_PTR)ModuleBase);
    		ExportDirectorySize = ImageNtHeaders32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;
    	}
    
    	// 得到导出表地址偏移和名字
    	PUSHORT pAddressOfOrds = (PUSHORT)(ImageExportDirectory->AddressOfNameOrdinals + (ULONG_PTR)ModuleBase);
    	PULONG  pAddressOfNames = (PULONG)(ImageExportDirectory->AddressOfNames + (ULONG_PTR)ModuleBase);
    	PULONG  pAddressOfFuncs = (PULONG)(ImageExportDirectory->AddressOfFunctions + (ULONG_PTR)ModuleBase);
    
    	// 循环搜索导出表
    	for (ULONG i = 0; i < ImageExportDirectory->NumberOfFunctions; ++i)
    	{
    		USHORT OrdIndex = 0xFFFF;
    		PCHAR  pName = NULL;
    
    		// 搜索导出表下标索引
    		if ((ULONG_PTR)FunctionName <= 0xFFFF)
    		{
    			OrdIndex = (USHORT)i;
    		}
    		// 搜索导出表名字
    		else if ((ULONG_PTR)FunctionName > 0xFFFF && i < ImageExportDirectory->NumberOfNames)
    		{
    			pName = (PCHAR)(pAddressOfNames[i] + (ULONG_PTR)ModuleBase);
    			OrdIndex = pAddressOfOrds[i];
    		}
    		else
    		{
    			return NULL;
    		}
    
    		// 找到设置返回值并跳出
    		if (((ULONG_PTR)FunctionName <= 0xFFFF && (USHORT)((ULONG_PTR)FunctionName) == OrdIndex + ImageExportDirectory->Base) || ((ULONG_PTR)FunctionName > 0xFFFF && strcmp(pName, FunctionName) == 0))
    		{
    			FunctionAddress = pAddressOfFuncs[OrdIndex] + (ULONG_PTR)ModuleBase;
    			break;
    		}
    	}
    	return (PVOID)FunctionAddress;
    }
    

    如何调用此方法,首先将ProcessID设置为需要读取的进程PID,然后将上图中所输出的0x00007FF9553C0000赋值给BaseAddress接着调用GetModuleExportAddress()并传入BaseAddress模块基址,需要读取的LdrLoadDll函数名,以及当前进程的EProcess结构。

    NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
    {
    	HANDLE ProcessID = (HANDLE)4144;
    	PEPROCESS EProcess = NULL;
    	NTSTATUS Status = STATUS_SUCCESS;
    
    	// 根据PID得到进程EProcess结构
    	Status = PsLookupProcessByProcessId(ProcessID, &EProcess);
    	if (Status != STATUS_SUCCESS)
    	{
    		DbgPrint("获取EProcessID失败 \n");
    		return Status;
    	}
    
    	PVOID BaseAddress = (PVOID)0x00007FF9553C0000;
    	PVOID RefAddress = 0;
    
    	// 传入Ntdll.dll基址 + 函数名 得到该函数地址
    	RefAddress = GetModuleExportAddress(BaseAddress, "LdrLoadDll", EProcess);
    	DbgPrint("[*] 函数地址: %p \n", RefAddress);
    
    	Driver->DriverUnload = UnDriver;
    	return STATUS_SUCCESS;
    }
    

    运行这段程序,即可输出如下信息,此时也就得到了x64.exe进程内ntdll.dll模块里面的LdrLoadDll函数的内存地址,如下所示;

  • 相关阅读:
    知识管理系统如何提升企业核心竞争力
    Centos7安装Nginx
    虚拟机开机字体变大,进入系统后字体模糊
    分页操作系统
    Docker的网络模式
    【C++入门基础】
    「React」RSC 服务端组件
    使用HXT和Haskell编写的程序
    php如何处理高并发请求
    SpringMVC 项目中 创建SpringBoot,使用Hibernate和JPA
  • 原文地址:https://www.cnblogs.com/LyShark/p/17152894.html
  • 最新文章
  • 攻防演习之三天拿下官网站群
    数据安全治理学习——前期安全规划和安全管理体系建设
    企业安全 | 企业内一次钓鱼演练准备过程
    内网渗透测试 | Kerberos协议及其部分攻击手法
    0day的产生 | 不懂代码的"代码审计"
    安装scrcpy-client模块av模块异常,环境问题解决方案
    leetcode hot100【LeetCode 279. 完全平方数】java实现
    OpenWrt下安装Mosquitto
    AnatoMask论文汇总
    【AI日记】24.11.01 LangChain、openai api和github copilot
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
正则表达式工具 cron表达式工具 密码生成工具

京公网安备 11010502049817号