redistemplate注入为null_Windows DLL 注入技术

Windows DLL 注入技术

本文主要介绍四种常见的 Windows DLL 注入技术。

分别为全局钩子、远线程钩子、突破 SESSION 0 隔离的远线程注入和 APC 注入。

全局钩子注入

Windows 中大部分应用是基于 Windows 的消息机制,Windows提供截获这些消息的钩子函数。

根据钩子作用的不同范围,钩子可以被分为全局和局部钩子。局部钩子是针对某个线程的,全局钩子是只要有使用消息机制的应用。接下来我们主要来看下利用SetWindowsHookEx实现全局钩子。

SetWindowsHookEx函数介绍

HHOOK SetWindowsHookEx(int       idHook, // 要安装的钩子程序的类型
  HOOKPROC  lpfn, // 指向钩子函数的指针
  HINSTANCE hmod, // 指向钩子过程的DLL的句柄
  DWORD     dwThreadId // 与钩子进程关联的线程标识符
);
// 具体详细介绍可以查阅msdn文档,里面有更为详细的介绍

实现过程

首先我们需要创建一个windowsHookDll DLL,这个DLL在安装全局钩子后只要系统中的其他进程接收到可以发出钩子的消息,这个DLL就会被加载到此进程的地址空间中。这样便实现了Dll注入。

具体实现代码如下:

#include "Hook.h"
#include 

extern HMODULE g_hDllModule;
// 共享内存
#pragma data_seg("mydata") 
HHOOK g_hHook = NULL;
#pragma data_seg()
#pragma comment(linker, "/SECTION:mydata,RWS")


// 钩子回调函数
LRESULT GetMsgProc(int code,
 WPARAM wParam,
 LPARAM lParam){
 return ::CallNextHookEx(g_hHook, code, wParam, lParam);
}

// 注册钩子
BOOL SetHook(){
 g_hHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)GetMsgProc, g_hDllModule, 0); //使用WH_GETMESSAGE 确保DLL能够注入所有的进程
 if (g_hHook)
 {
  return TRUE;
 }
 return FALSE;
}

// 卸载钩子
BOOL UnSetHook(){
 if (g_hHook)
 {
  UnhookWindowsHookEx(g_hHook);
 }
 return TRUE;
}

试验结果

新建TestHook.exe程序,首先加载windowsHookDll.dll导出函数SetHook并调用。在注册成功后用ProcessExplorer.exe察看explorer.exe中的加载情况。

20f0d07b5a9cc298f66ea84f5ad32028.png
windowsHookDll成功被explorer.exe加载

如果想要卸载可调用函数UnSetHook,执行完成后再观察explorer.exe是否加载了windowsHookDll.dll。

远线程注入

远线程注入是指一个进程在另一个进程中创建线程的技术。主要是利用LoadLibrary在所有进程空间中的地址是一样,进程在另一个进程中创建线程时传入LoadLibrary的地址和我们要注入的DLL的路径,这样在另一个进程中就能通过LoadLibray加载DLL到进程空间中。

说起来简单,但是实现起来却有几大难点需要注意的。

  • 如何将DLL路径传给另一个进程,因为进程之间是相互隔离的,所以我们要在我们要注入的进程中申请内存。
  • 需要对打开的进程进行提权,这就要求我们的注入程序要有管理员以上的权限。

函数介绍

HANDLE OpenProcess(
  DWORD dwDesiredAccess, // 进程访问权限
  BOOL  bInheritHandle, // 子进程是否继承此句柄
  DWORD dwProcessId // 要打开的进程id
);

// 分配指定进程的内存
LPVOID VirtualAllocEx(
  HANDLE hProcess, // 进程句柄
  LPVOID lpAddress, // 要分配内存起始地址,为NULL则自动分配
  SIZE_T dwSize, // 要分配的内存大小
  DWORD  flAllocationType,// 内存分配类型
  DWORD  flProtect // 内存保护(读写)
);

// 写入内存数据
BOOL WriteProcessMemory(
  HANDLE  hProcess, // 进程句柄
  LPVOID  lpBaseAddress, // 目标进程缓冲区地址
  LPCVOID lpBuffer, // 要写入数据的地址
  SIZE_T  nSize, // 数据大小
  SIZE_T  *lpNumberOfBytesWritten // 写入数据的返回大小, 为NULL则忽略此参数
);

// 在另一个进程中创建线程
HANDLE CreateRemoteThread(
  HANDLE                 hProcess, // 进程句柄
  LPSECURITY_ATTRIBUTES  lpThreadAttributes, // 线程安全描述符
  SIZE_T                 dwStackSize, // 堆栈的初始大小
  LPTHREAD_START_ROUTINE lpStartAddress, // 函数地址(存在于另一个进程中)
  LPVOID                 lpParameter, // 函数的参数
  DWORD                  dwCreationFlags, // 线程创建标志
  LPDWORD                lpThreadId // 线程标识符
);

// 详细信息可参阅msdn

代码实现


/*dwProcessId: 目标进程的pid
**pszDllFileName: 要注入DLL的路径
*/
BOOL CreateRemoteThreadInjectDll(DWORD dwProcessId, char *pszDllFileName){
 HANDLE hProcess = NULL;
 SIZE_T dwSize = 0;
 LPVOID pDllAddr = NULL;
 FARPROC pFuncProcAddr = NULL;

 // 打开注入进程,获取进程句柄
 hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
 if (NULL == hProcess)
 {
  EP_ShowError("OpenProcess");
  return FALSE;
 }
 // 在注入进程中申请内存
 dwSize = 1 + ::lstrlenA(pszDllFileName);
 pDllAddr = ::VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
 if (NULL == pDllAddr)
 {
  EP_ShowError("VirtualAllocEx");
  return FALSE;
 }
 // 向申请的内存中写入数据
 if (FALSE == ::WriteProcessMemory(hProcess, pDllAddr, pszDllFileName, dwSize, NULL))
 {
  EP_ShowError("WriteProcessMemory");
  return FALSE;
 }
 // 获取LoadLibraryA函数地址
 pFuncProcAddr = ::GetProcAddress(::GetModuleHandleA("kernel32.dll"), "LoadLibraryA");
 if (NULL == pFuncProcAddr)
 {
  EP_ShowError("GetProcAddress_LoadLibraryA");
  return FALSE;
 }
 // 使用 CreateRemoteThread 创建远线程, 实现 DLL 注入
 HANDLE hRemoteThread = ::CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pFuncProcAddr, pDllAddr, 0, NULL);
 if (NULL == hRemoteThread)
 {
  EP_ShowError("CreateRemoteThread");
  return FALSE;
 }
 // 关闭句柄
 ::CloseHandle(hProcess);

 return TRUE;
}

试验结果

bfc1e06b9919c00b60c984d1186d7f2a.png
注入成功后效果图
ab2f4f854f0abc8252c54b931a55fa1e.png
在explorer.exe中RemoteThreadDll.dll被正确加载

远线程注册进阶 突破SESSION 0隔离

在系统普通进程中可以使用远线程注入DLL,但如果要注入系统服务则不行,因为有SESSION 0隔离。想要突破隔离需要使用ZwCreateThreadEx函数。这也是和远线程注入的区别。

ZwCreateThreadEx 是未公开的函数,在ntdll.dll中,它的函数声明如下:

#ifdef _WIN64
 typedef DWORD(WINAPI *typedef_ZwCreateThreadEx)(
  PHANDLE ThreadHandle,
  ACCESS_MASK DesiredAccess,
  LPVOID ObjectAttributes,
  HANDLE ProcessHandle,
  LPTHREAD_START_ROUTINE lpStartAddress,
  LPVOID lpParameter,
  ULONG CreateThreadFlags,
  SIZE_T ZeroBits,
  SIZE_T StackSize,
  SIZE_T MaximumStackSize,
  LPVOID pUnkown);
#else
 typedef DWORD(WINAPI *typedef_ZwCreateThreadEx)(
  PHANDLE ThreadHandle,
  ACCESS_MASK DesiredAccess,
  LPVOID ObjectAttributes,
  HANDLE ProcessHandle,
  LPTHREAD_START_ROUTINE lpStartAddress,
  LPVOID lpParameter,
  BOOL CreateSuspended,
  DWORD dwStackSize,
  DWORD dw1,
  DWORD dw2,
  LPVOID pUnkown);
#endif

具体实现代码:

// 使用 ZwCreateThreadEx 实现远线程注入
BOOL ZwCreateThreadExInjectDll(DWORD dwProcessId, char *pszDllFileName){
 HANDLE hProcess = NULL;
 SIZE_T dwSize = 0;
 LPVOID pDllAddr = NULL;
 FARPROC pFuncProcAddr = NULL;
 HANDLE hRemoteThread = NULL;
 DWORD dwStatus = 0;

 // 打开注入进程,获取进程句柄
 hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
 if (NULL == hProcess)
 {
  EP_ShowError("OpenProcess");
  return FALSE;
 }
 // 在注入进程中申请内存
 dwSize = 1 + ::lstrlenA(pszDllFileName);
 pDllAddr = ::VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
 if (NULL == pDllAddr)
 {
  EP_ShowError("VirtualAllocEx");
  return FALSE;
 }
 // 向申请的内存中写入数据
 if (FALSE == ::WriteProcessMemory(hProcess, pDllAddr, pszDllFileName, dwSize, NULL))
 {
  EP_ShowError("WriteProcessMemory");
  return FALSE;
 }
 // 加载 ntdll.dll
 HMODULE hNtdllDll = ::LoadLibraryA("ntdll.dll");
 if (NULL == hNtdllDll)
 {
  EP_ShowError("LoadLirbary");
  return FALSE;
 }
 // 获取LoadLibraryA函数地址
 pFuncProcAddr = ::GetProcAddress(::GetModuleHandleA("Kernel32.dll"), "LoadLibraryA");
 if (NULL == pFuncProcAddr)
 {
  EP_ShowError("GetProcAddress_LoadLibraryA");
  return FALSE;
 }
 // 获取ZwCreateThread函数地址
#ifdef _WIN64
 typedef DWORD(WINAPI *typedef_ZwCreateThreadEx)(
  PHANDLE ThreadHandle,
  ACCESS_MASK DesiredAccess,
  LPVOID ObjectAttributes,
  HANDLE ProcessHandle,
  LPTHREAD_START_ROUTINE lpStartAddress,
  LPVOID lpParameter,
  ULONG CreateThreadFlags,
  SIZE_T ZeroBits,
  SIZE_T StackSize,
  SIZE_T MaximumStackSize,
  LPVOID pUnkown);
#else
 typedef DWORD(WINAPI *typedef_ZwCreateThreadEx)(
  PHANDLE ThreadHandle,
  ACCESS_MASK DesiredAccess,
  LPVOID ObjectAttributes,
  HANDLE ProcessHandle,
  LPTHREAD_START_ROUTINE lpStartAddress,
  LPVOID lpParameter,
  BOOL CreateSuspended,
  DWORD dwStackSize,
  DWORD dw1,
  DWORD dw2,
  LPVOID pUnkown);
#endif
 typedef_ZwCreateThreadEx ZwCreateThreadEx = (typedef_ZwCreateThreadEx)::GetProcAddress(hNtdllDll, "ZwCreateThreadEx");
 if (NULL == ZwCreateThreadEx)
 {
  EP_ShowError("GetProcAddress_ZwCreateThread");
  return FALSE;
 }
 // 使用 ZwCreateThreadEx 创建远线程, 实现 DLL 注入
 dwStatus = ZwCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL, hProcess, (LPTHREAD_START_ROUTINE)pFuncProcAddr, pDllAddr, 0, 0, 0, 0, NULL);
 if (NULL == hRemoteThread)
 {
  EP_ShowError("ZwCreateThreadEx");
  return FALSE;
 }
 // 关闭句柄
 ::CloseHandle(hProcess);
 ::FreeLibrary(hNtdllDll);

 return TRUE;
}

试验结果

同远线程注入一致,只是在服务程序中不能弹窗。

APC注入

APC(Asynchronous Procedure Call)为异步过程调用,APC注入是指利用线程本身的APC队列进行DLL注入。

函数介绍

// 将异步调用函数添加到指定线程的APC队列中
DWORD QueueUserAPC(
  PAPCFUNC  pfnAPC, // 函数指针
  HANDLE    hThread, // 线程句柄
  ULONG_PTR dwData // 函数参数
);

// 详细信息可参阅msdn

实现原理

APC队列中的函数需要等待线程挂起时才会被执行,所以要保证我们注入的程序能被执行需要将我们的函数插入到进程的所有线程中。具体代码实现如下:

// apc.h
#include 
#include 


// 根据进程名称获取PID
DWORD GetProcessIdByProcessName(char *pszProcessName);

// 根据PID获取所有的相应线程ID
BOOL GetAllThreadIdByProcessId(DWORD dwProcessId, DWORD **ppThreadId, DWORD *dwThreadIdLength);

// APC注入
BOOL ApcInjectDll(char *pszProcessName, char *pszDllName);


//apc.cpp
#include "APC.h"


void ShowError(char *pszText){
 char szErr[MAX_PATH] = { 0 };
 ::wsprintf(szErr, "%s Error[%d]\n", pszText);
 ::MessageBox(NULL, szErr, "ERROR", MB_OK | MB_ICONERROR);
}


// 根据进程名称获取PID
DWORD GetProcessIdByProcessName(char *pszProcessName){
 DWORD dwProcessId = 0;
 PROCESSENTRY32 pe32 = { 0 };
 HANDLE hSnapshot = NULL;
 BOOL bRet = FALSE;
 ::RtlZeroMemory(&pe32, sizeof(pe32));
 pe32.dwSize = sizeof(pe32);

 // 获取进程快照
 hSnapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
 if (NULL == hSnapshot)
 {
  ShowError("CreateToolhelp32Snapshot");
  return dwProcessId;
 }

 // 获取第一条进程快照信息
 bRet = ::Process32First(hSnapshot, &pe32);
 while (bRet)
 {
  // 获取快照信息
  if (0 == ::lstrcmpi(pe32.szExeFile, pszProcessName))
  {
   dwProcessId = pe32.th32ProcessID;
   break;
  }

  // 遍历下一个进程快照信息
  bRet = ::Process32Next(hSnapshot, &pe32);
 }

 return dwProcessId;
}


// 根据PID获取所有的相应线程ID
BOOL GetAllThreadIdByProcessId(DWORD dwProcessId, DWORD **ppThreadId, DWORD *pdwThreadIdLength){
 DWORD *pThreadId = NULL;
 DWORD dwThreadIdLength = 0;
 DWORD dwBufferLength = 1000;
 THREADENTRY32 te32 = { 0 };
 HANDLE hSnapshot = NULL;
 BOOL bRet = TRUE;

 do
 {
  // 申请内存
  pThreadId = new DWORD[dwBufferLength];
  if (NULL == pThreadId)
  {
   ShowError("new");
   bRet = FALSE;
   break;
  }
  ::RtlZeroMemory(pThreadId, (dwBufferLength * sizeof(DWORD)));

  // 获取线程快照
  ::RtlZeroMemory(&te32, sizeof(te32));
  te32.dwSize = sizeof(te32);
  hSnapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
  if (NULL == hSnapshot)
  {
   ShowError("CreateToolhelp32Snapshot");
   bRet = FALSE;
   break;
  }

  // 获取第一条线程快照信息
  bRet = ::Thread32First(hSnapshot, &te32);
  while (bRet)
  {
   // 获取进程对应的线程ID
   if (te32.th32OwnerProcessID == dwProcessId)
   {
    pThreadId[dwThreadIdLength] = te32.th32ThreadID;
    dwThreadIdLength++;
   }

   // 遍历下一个线程快照信息
   bRet = ::Thread32Next(hSnapshot, &te32);
  }

  // 返回
  *ppThreadId = pThreadId;
  *pdwThreadIdLength = dwThreadIdLength;
  bRet = TRUE;

 } while (FALSE);

 if (FALSE == bRet)
 {
  if (pThreadId)
  {
   delete[]pThreadId;
   pThreadId = NULL;
  }
 }

 return bRet;
}


// APC注入
BOOL ApcInjectDll(char *pszProcessName, char *pszDllName){
 BOOL bRet = FALSE;
 DWORD dwProcessId = 0;
 DWORD *pThreadId = NULL;
 DWORD dwThreadIdLength = 0;
 HANDLE hProcess = NULL, hThread = NULL;
 PVOID pBaseAddress = NULL;
 PVOID pLoadLibraryAFunc = NULL;
 SIZE_T dwRet = 0, dwDllPathLen = 1 + ::lstrlen(pszDllName);
 DWORD i = 0;

 do
 {
  // 根据进程名称获取PID
  dwProcessId = GetProcessIdByProcessName(pszProcessName);
  if (0 >= dwProcessId)
  {
   bRet = FALSE;
   break;
  }

  // 根据PID获取所有的相应线程ID
  bRet = GetAllThreadIdByProcessId(dwProcessId, &pThreadId, &dwThreadIdLength);
  if (FALSE == bRet)
  {
   bRet = FALSE;
   break;
  }

  // 打开注入进程
  hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
  if (NULL == hProcess)
  {
   ShowError("OpenProcess");
   bRet = FALSE;
   break;
  }

  // 在注入进程空间申请内存
  pBaseAddress = ::VirtualAllocEx(hProcess, NULL, dwDllPathLen, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
  if (NULL == pBaseAddress)
  {
   ShowError("VirtualAllocEx");
   bRet = FALSE;
   break;
  }
  // 向申请的空间中写入DLL路径数据 
  ::WriteProcessMemory(hProcess, pBaseAddress, pszDllName, dwDllPathLen, &dwRet);
  if (dwRet != dwDllPathLen)
  {
   ShowError("WriteProcessMemory");
   bRet = FALSE;
   break;
  }

  // 获取 LoadLibrary 地址
  pLoadLibraryAFunc = ::GetProcAddress(::GetModuleHandle("kernel32.dll"), "LoadLibraryA");
  if (NULL == pLoadLibraryAFunc)
  {
   ShowError("GetProcessAddress");
   bRet = FALSE;
   break;
  }

  // 遍历线程, 插入APC
  i = dwThreadIdLength;
  for (; i > 0; --i)
  {
   // 打开线程
   hThread = ::OpenThread(THREAD_ALL_ACCESS, FALSE, pThreadId[i]);
   if (hThread)
   {
    // 插入APC
    ::QueueUserAPC((PAPCFUNC)pLoadLibraryAFunc, hThread, (ULONG_PTR)pBaseAddress);
    // 关闭线程句柄
    ::CloseHandle(hThread);
    hThread = NULL;
   }
  }

  bRet = TRUE;

 } while (FALSE);

 // 释放内存
 if (hProcess)
 {
  ::CloseHandle(hProcess);
  hProcess = NULL;
 }
 if (pThreadId)
 {
  delete[]pThreadId;
  pThreadId = NULL;
 }

 return bRet;
}

总结

Windows注入技术可以方便我们对目标进程作修改,但是也可能使目标进程崩溃,在使用的时候需要小心谨慎。

694e0ee754b576785ad2967fd75a5308.png
长按图片自动识别~~~关注我哦~~~~
- END -

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

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

相关文章

nginx location 正则表达式匹配多个地址_就是要让你搞懂Nginx,这篇就够了!

Nginx 是一个高性能的 HTTP 和反向代理服务器,特点是占用内存少,并发能力强,事实上 Nginx 的并发能力确实在同类型的网页服务器中表现较好。Nginx 专为性能优化而开发,性能是其最重要的要求,十分注重效率,有…

mysql查询网址_bootstrap+flask+mysql实现网站查询

之前那篇文章是flaskredis的,如果用flaskmysql怎么实现呢?创建数据库:CREATE DATABASE web12306 DEFAULT CHARACTER SET utf8;创建表:CREATE TABLE web12306 (user_email varchar(100) NOT NULL DEFAULT ,user_pass varchar(100)…

dubbo官方文档_不可忽视的Dubbo线程池

问题描述线上突然出现Dubbo超时调用,时间刚好为Consumer端设置的超时时间。有好几个不同的接口都报超时了第1次调用超时,第2次(或第3次)重试调用非常快(正常水平)Dubbo调用超时的情况集中出现了3次&#xf…

python中比较重要的几个函数_Python 几个重要的内置函数 python中的内置函数和关键字需要背过吗...

python重要的几个内置函数用法python内置函数什么用忘不掉的是回忆,继续的是生活,错过的,就当是路过吧。来来往往身边出现很多人,总有一个位置,一直没有变。看看温暖的阳光,偶尔还是会想一想。Python内置函…

netty worker线程数量_Dubbo线程模型

Dubbo中线程池的应用还是比较广泛的,按照consumer端到provider的RPC的方向来看,consumer端的应用业务线程到netty线程、consuemr端dubbo业务线程池,到provider端的netty boss线程、worker线程和dubbo业务线程池等。这些线程各司其职相互配合&…

function click_click事件的累加问题解决

click事件的 累加问题解决:$判断是否隐藏:hidden.c>span 只包含儿子.c span 包含儿子和孙子data-*嵌入自定义数据 .data(to)获取数据remove是移除标签 delete删除数组元素each()函数$(".layui-table-total .layui-table-cell").each(functi…

知道python语言应用2020答案_热点:大学moocPython语言基础与应用答案

2020年智慧树网课答案为您详细解读azMisb热点:大学moocPython语言基础与应用答案的详情,题主的教授应该是想要同学们找出一个值得研究和讨论的theory,简单来说就是你论文探讨的中心。然后需要你们定topic,然后搜索大量靠谱的资料,…

timestamp 转换 date mysql_MySQL时间函数 | 时间戳和日期之间得转换

一、时间戳转日期select FROM_UNIXTIME(1606028010, %Y-%m-%d %H:%i:%s);二、日期转时间戳select unix_timestamp(2018-01-15 09:45:16);三、时间戳格式化十位时间戳转为固定格式(yyyy-MM-dd HH:mm:ss)日期1.格式规定%M 月名字(January……December)%W 星期名字(Sunday……Satu…

python是一种动态语言这意味着_Python如何能成为全球最受欢迎的编程语言?该不该学Python?...

全文共3304字,预计学习时长10分钟 图源:(Python logo courtesy of https 有一种语言在过去十年受喜爱度一路飙升,成为最受欢迎的一种编程语言,它是谁? 千呼万唤始出来,没错,它就是我们的老弟Pyt…

python处理csv文件案例_让繁琐的工作自动化——python处理CSV文件

让繁琐的工作自动化——python处理CSV文件CSV:CSV文件是一种简化的电子表格,不同于Excle(二进制文件),CSV是纯文本文件。1.环境python3.8pycharm2020.12.读取本期实例数据haha,18,10.0jiji,16,12.1lala,17,11.9papa,11,13.3首先导入csv模块&a…

python实现坐标求取_根据相机位姿求指定点的世界坐标及其python实现

Authorshaniadolphin求解目的本文将展示位姿估计的一种应用,即通过单目相机对环境进行测量。简单来说,本文的工作就是利用下面的两幅图,在已知P1、P2、P3、P4四点世界坐标的情况下,计算出其它点的世界坐标。如图所示,一…

c# treeview查找并选中节点_最通俗易懂的二叉查找树(BST)详解

原来来自 呆萌数据结构-06二叉查找树​imoegirl.com二叉查找树(Binary Search Tree),简写BST,是满足某些条件的特殊二叉树。任何一个节点的左子树上的点,都必须小于当前节点。任何一个节点的右子树上的点,都…

服务器损坏mysql修复_云服务器mysql数据库损坏修复mysql

有的时候因为各种原因导致mysql数据库损坏,我们可以使用mysql自带的mysqlcheck命令来快速修复所有的数据库或者特定的数据库,检查优化并修复所有的数据库.1.先在运行中输入CMD,启动命令行.2.进入Mysql的Bin目录:D:\VHostData\MySQL5.1\bin,(这个路径在数据库的安装目…

bootstrap jquery alert_bootstrap第七课

bootstrap 模态框bootstrap是一个非常酷的前端开发框架,它可以大大的简化我们日常开发当中的功能与样式。它有非常漂亮的css组件和非常实用的控件供我们使用。接下来我们来看看bootstrap的内容吧!首先大家要引入bootstrap的css和js可以在这里下载&#x…

java 闭包_公司新来的女实习生问我什么是闭包?

作者:霍语佳来源:前端食堂观感度:?????口味:冰镇西瓜烹饪时间:20min撩妹守则第一条,女孩子都喜欢童话故事。那就先来讲一个童话故事~// 有一个公主// 她生活在一个充满冒险的奇妙世界里// 她遇见了她的…

java org.apache.http_org.apache.http jar包下载-org.apache.http.jar包下载 --pc6下载站

org.apache.http.jar包是一款十分常用的jar包如果没有org.apache.http.jar包Apache与http的链接将会出现错误等现象马上下载org.apache.http.jar包。。相关软件软件大小版本说明下载地址org.apache.http.jar包是一款十分常用的jar包,如果没有org.apache.http.jar包,Apache与htt…

网络连接异常、网站服务器失去响应_网站常见故障解决办法

网站在运行过程中,常常遇到各种服务器问题,虽然有服务器厂商的维护,但是往往耗时耗工小编对常见的服务器问题,进行了归纳整理,下面跟各位分享一下。常见故障分析一、恶意攻击在我平时管理网站时,可能会遭到…

python3 sleep 并发_python异步编程之asyncio(百万并发)

点击上方蓝字关注我们目录[python 异步编程之 asyncio(百万并发)]一、asyncio二、aiohttp前言:python 由于 GIL(全局锁)的存在,不能发挥多核的优势,其性能一直饱受诟病。然而在 IO 密集型的网络编程里,异步处理比同步处理能提升成…

【Spring实战】02 配置多数据源

文章目录 1. 配置数据源信息2. 创建第一个数据源3. 创建第二个数据源4. 创建启动类及查询方法5. 启动服务6. 创建表及做数据7. 查询验证8. 详细代码总结 通过上一节的介绍,我们已经知道了如何使用 Spring 进行数据源的配置以及应用。在一些复杂的应用中,…

windows查看usb信息命令_【VPS】Linux VPS查看系统信息命令大全

本文转自老左笔记,自用mark系统# uname -a # 查看内核/操作系统/CPU信息 # head -n 1 /etc/issue # 查看操作系统版本 # cat /proc/cpuinfo # 查看CPU信息 # hostname # 查看计算机名 # lspci -tv # 列出所有PCI设备 # lsusb -tv # 列出所有USB设备 # lsmod # 列出加…