范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

DLL劫持目标应用软件实现注入

  原理:
  DLL注入技术,一般来讲是向一个 正在运行的进程 插入/注入代码 的过程。我们注入的代码以 动态链接库(DLL) 的形式存在。
  dll注入即是让程序A强行加载程序B给定的a.dll,并执行程序B给定的a.dll里面的代码。注意,程序B所给定的a.dll原先并不会被程序A主动加载,但是当程序B通过某种手段让程序A"加载"a.dll后,程序A将会执行a.dll里的代码,此时,a.dll就进入了程序A的地址空间,而a.dll模块的程序逻辑由程序B的开发者设计,因此程序B的开发者可以对程序A为所欲为。
  实现:
  加载Dll的API就是LoadLibrary,它的参数是保存要加载的DLL的路径的地址。所以DLL注入的核心就是把要注入的DLL的路径写到目标进程中,然后在目标进程中调用LoadLibrary函数,并且指定参数为保存了DLL路径的地址。
  LoadLibrary函数  HMODULE WINAPI LoadLibrary(__in  LPCTSTR lpFileName);
  这个函数同样也只需要一个参数,这个参数是一个地址,而这个地址中保存的是我们要加载的DLL的名称的字符串
  跟exe有个main或者WinMain入口函数一样,DLL也有一个入口函数,就是DllMain。当使用LoadLibrary函数加载DLL时,系统会调用DLL的入口点函数
  // dllmain.cpp : 定义 DLL 应用程序的入口点。 #include "pch.h"  BOOL APIENTRY DllMain( HMODULE hModule,                        DWORD  ul_reason_for_call,                        LPVOID lpReserved                      ) {     switch (ul_reason_for_call)     {     case DLL_PROCESS_ATTACH:     case DLL_THREAD_ATTACH:     case DLL_THREAD_DETACH:     case DLL_PROCESS_DETACH:         break;     }     return TRUE; }
  会有如下文件,当DLL的状态发生变化的时候,就会调用DllMain函数。而传递的ul_reason_for_call这个参数代表了4种不同的状态变化的情况,我们就可以根据这四种不同的状态根据需要来写出相应的代码,就会让注入的DLL执行我们需要的功能
  ul_reason_for_call的值
  代表的状态
  DLL_PROCESS_ATTACH
  Dll刚刚映射到进程空间中
  DLL_THREAD_ATTACH
  进程中有新线程创建
  DLL_THREAD_DETACH
  进程中有新线程销毁
  DLL_PROCESS_DETACH
  Dll从进程空间中接触映射
  代码框架:#include  #include  #include  #define PROCESS_NAME "Taskmgr.exe"    //要注入的进程名,这个是任务管理器的进程名 #define DLL_NAME "InjectDll.dll"  //要注入的DLL的名称   BOOL InjectDll(DWORD dwPid, CHAR szDllName[]);  //注入DLL DWORD GetPID(PCHAR pProName); //根据进程名获取PID VOID ShowError(PCHAR msg);    //打印错误信息 BOOL EnbalePrivileges(HANDLE hProcess, char *pszPrivilegesName);    //提升进程权限   int main() {     CHAR szDllPath[MAX_PATH] = { 0 };  //保存要注入的DLL的路径     DWORD dwPID = 0;                 //保存要注入的进程的PID       // 提升当前进程令牌权限     if (!EnbalePrivileges(GetCurrentProcess(), SE_DEBUG_NAME))     {         printf("权限提升失败 ");     }       dwPID = GetPID(PROCESS_NAME);     if (dwPID == 0)     {         printf("没有找到要注入的进程 ");         goto exit;     }           GetCurrentDirectory(MAX_PATH, szDllPath);  //获取程序的目录     strcat(szDllPath, "");     strcat(szDllPath, DLL_NAME);               //与DLL名字拼接得到DLL的完整路径     printf("要注入的进程名:%s PID:%d ", PROCESS_NAME, dwPID);     printf("要注入的DLL的完整路径%s ", szDllPath);       if (InjectDll(dwPID, szDllPath))     {         printf("Dll注入成功 ");     } exit:     system("pause");       return 0; }   BOOL InjectDll(DWORD dwPid, CHAR szDllName[]) {     BOOL bRet = TRUE;       return bRet; }   DWORD GetPID(PCHAR pProName) {     PROCESSENTRY32 pe32 = { 0 };     HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);     BOOL bRet = FALSE;     DWORD dwPID = 0;       if (hSnap == INVALID_HANDLE_VALUE)     {         printf("CreateToolhelp32Snapshot process %d ", GetLastError());         goto exit;     }       pe32.dwSize = sizeof(pe32);     bRet = Process32First(hSnap, &pe32);     while (bRet)     {         if (lstrcmp(pe32.szExeFile, pProName) == 0)         {             dwPID = pe32.th32ProcessID;             break;         }         bRet = Process32Next(hSnap, &pe32);     }       CloseHandle(hSnap); exit:     return dwPID; }   VOID ShowError(PCHAR msg) {     printf("%s Error %d ", msg, GetLastError()); }   BOOL EnbalePrivileges(HANDLE hProcess, char *pszPrivilegesName) {     HANDLE hToken = NULL;     LUID luidValue = { 0 };     TOKEN_PRIVILEGES tokenPrivileges = { 0 };     BOOL bRet = FALSE;     DWORD dwRet = 0;         // 打开进程令牌并获取具有 TOKEN_ADJUST_PRIVILEGES 权限的进程令牌句柄     if (!OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES, &hToken))     {         ShowError("OpenProcessToken");         goto exit;     }       // 获取本地系统的 pszPrivilegesName 特权的LUID值     if (!LookupPrivilegeValue(NULL, pszPrivilegesName, &luidValue))     {         ShowError("LookupPrivilegeValue");         goto exit;     }       // 设置提升权限信息     tokenPrivileges.PrivilegeCount = 1;     tokenPrivileges.Privileges[0].Luid = luidValue;     tokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;     // 提升进程令牌访问权限     if (!AdjustTokenPrivileges(hToken, FALSE, &tokenPrivileges, 0, NULL, NULL))     {         ShowError("AdjustTokenPrivileges");         goto exit;     }     else     {         // 根据错误码判断是否特权都设置成功         dwRet = ::GetLastError();         if (ERROR_SUCCESS == dwRet)         {             bRet = TRUE;             goto exit;         }         else if (ERROR_NOT_ALL_ASSIGNED == dwRet)         {             ShowError("ERROR_NOT_ALL_ASSIGNED");             goto exit;         }     } exit:     return bRet; }
  远程线程注入:
  远程线程注入简单的说,就是调用CreateRemoteThread(),在其他的进程中创建一条线程,执行LoadLibrary(),将特定的DLL加载到进程中间.
  由于我们不能轻易的控制别人进程中的线程,因此这种方法要求我们在目标进程中创建一个线程并在线程中执行LoadLibrary函数加载我们要注入的dll。幸运的是Windows为我们提供了CreateRemoteThread函数,它使得在另一个进程中创建一个线程变得非常容易。
  HANDLE WINAPI CreateRemoteThread(   _In_  HANDLE                 hProcess, /要创建线程的进程句柄   _In_  LPSECURITY_ATTRIBUTES  lpThreadAttributes,/新线程的安全描述符   _In_  SIZE_T                 dwStackSize, /堆栈起始大小,为0表示默认大小   _In_  LPTHREAD_START_ROUTINE lpStartAddress,/表示要运行线程的起始地址   _In_  LPVOID                 lpParameter,/保存要传递给线程参数的地址   _In_  DWORD                  dwCreationFlags,/控制线程创建的标志,为0表示创建后立即执行   _Out_ LPDWORD                lpThreadId/指向接收线程标识符变量的指针。为NULL表示不返回线程标识符 );
  hProcess用来指定在哪个进程中创建新线程  lpStartAddress用来指定将进程中的哪个地址开始作为新线程运行的起始地址  lpParameter保存的也是一个地址,这个地址中保存的就是新线程要用到的参数
  只要我们可以获取新进程中的LoadLibrary函数的地址以及包含有要加载的DLL的字符串的地址就可以通过CreateRemoteThread函数来成功开起一个线程执行LoadLibrary函数来加载我们的DLL。
  那么现在的问题就是如何获得LoadLibrary函数的地址以及保存有要加载的DLL路径的字符串的地址。
  对于LoadLibrary函数,由于它是在常用的系统DLL,也就是KERNEL32.dll中,所以这个DLL是可以按照它的ImageBase成功装载到每个进程的空间中。这样的话Kernel32.dll在每个进程中的起始地址是一样的,那么LoadLibrary函数的地址也就会一样。那么我们就可以在本进程中查找LoadLibrary函数的地址,并且完全可以相信,在要注入DLL的进程中LoadLibrary的地址也是这个。
  对于LoadLibrary函数,由于它是在常用的系统DLL,也就是KERNEL32.dll中,所以这个DLL是可以按照它的ImageBase成功装载到每个进程的空间中。这样的话Kernel32.dll在每个进程中的起始地址是一样的,那么LoadLibrary函数的地址也就会一样。那么我们就可以在本进程中查找LoadLibrary函数的地址,并且完全可以相信,在要注入DLL的进程中LoadLibrary的地址也是这个。
  至于DLL名称的字符串,我们可以通过在进程中申请一块可以将DLL完整路径写入的内存,并在这个内存中将DLL的完整路径写入,将写入到注入进程DLL完整路径的内存地址作为参数就可以实现进程的注入
  注意:  在开始注入前,还需要确认一件事,就是目标进程使用的字符编码方式。因为我们所调用的LoadLibrary函数在底层实际调用有两种可能:
  如果目标程序使用的是ANSI编码方式,LoadLibrary实际调用的是LoadLibraryA,其参数字符串应当是ANSI编码;
  如果目标程序使用的是Unicode编码方式,LoadLibrary实际调用的是LoadLibraryW,其参数字符串应当是Unicode编码。
  这使得注入过程变得很麻烦,为了减少复杂性,不妨直接使用LoadLibraryA或LoadLibraryW而不是用LoadLibrary函数来避免这一麻烦。另外,即使使用的是LoadLibraryA,LoadLibraryA也会将传入的ANSI编码的字符串参数转换成Unicode编码后再调用LoadLibraryW。综上,不妨一致使用LoadLibraryW函数,并且字符串用Unicode编码即可。
  最后,我们可能会为获得目标进程中LoadLibraryW函数的起始地址而头疼,但其实这个问题也很简单,因为目标进程中函数LoadLibraryW的起始地址和我们的进程中的LoadLibraryW函数的起始地址是一样的。因此我们只需要用GetProcAddress即可获得LoadLibraryW函数的起始地址。
  步骤:
  (1).用VirtualAllocEx函数在目标进程的地址空间中分配一块足够大的内存用于保存被注入的dll的路径。
  (2).用WriteProcessMemory函数把本进程中保存dll路径的内存中的数据拷贝到第(1)步得到的目标进程的内存中。
  (3).用GetProcAddress函数获得LoadLibraryW函数的起始地址。LoadLibraryW函数位于Kernel32.dll中。
  (4).用CreateRemoteThread函数让目标进程执行LoadLibraryW来加载被注入的dll。函数结束将返回载入dll后的模块句柄。
  (5).用VirtualFreeEx释放第(1)步开辟的内存。
  在需要卸载dll时我们可以在上述第(5)步的基础上继续执行以下步骤:
  (6).用GetProcAddress函数获得FreeLibrary函数的起始地址。FreeLibrary函数位于Kernel32.dll中。
  (7).用CreateRemoteThread函数让目标进程执行FreeLibrary来卸载被注入的dll。(其参数是第(4)步返回的模块句柄)。  BOOL InjectDll(DWORD dwPid, CHAR szDllName[]) {     BOOL bRet = TRUE;     HANDLE hProcess = NULL, hRemoteThread = NULL;     HMODULE hKernel32 = NULL;     DWORD dwSize = 0;     LPVOID pDllPathAddr = NULL;     PVOID pLoadLibraryAddr = NULL;       // 打开注入进程,获取进程句柄     hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);     if (NULL == hProcess)     {         ShowError("OpenProcess");         bRet = FALSE;         goto exit;     }       // 在注入进程中申请可以容纳DLL完成路径名的内存空间     dwSize = 1 + strlen(szDllName);     pDllPathAddr = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);     if (!pDllPathAddr)     {         ShowError("VirtualAllocEx");         bRet = FALSE;         goto exit;     }       // 把DLL完整路径名写入进程中     if (!WriteProcessMemory(hProcess, pDllPathAddr, szDllName, dwSize, NULL))     {         ShowError("WriteProcessMemory");         bRet = FALSE;         goto exit;     }             hKernel32 = LoadLibrary("kernel32.dll");     if (hKernel32 == NULL)     {         ShowError("LoadLibrary");         bRet = FALSE;         goto exit;     }       // 获取LoadLibraryA函数地址     pLoadLibraryAddr = GetProcAddress(hKernel32, "LoadLibraryA");     if (pLoadLibraryAddr == NULL)     {         ShowError("GetProcAddress ");         bRet = FALSE;         goto exit;     }       //创建远程线程进行DLL注入     hRemoteThread = CreateRemoteThread(hProcess, NULL, 0,                        (LPTHREAD_START_ROUTINE)pLoadLibraryAddr,                        pDllPathAddr, 0, NULL);     if (hRemoteThread == NULL)     {         ShowError("CreateRemoteThread");         bRet = FALSE;         goto exit;     }       exit:     if (hKernel32) FreeLibrary(hKernel32);     if (hProcess) CloseHandle(hProcess);     if (hRemoteThread) CloseHandle(hRemoteThread);           return bRet; }
  补充:
  上面的方法虽然可以方便的注入DLL。但是在WIN7,WIN10系统上,会由于SESSION 0隔离机制从而导致只能成功注入普通的用户进程,如果注入系统进程就会导致失败。而经过逆向分析发现,使用Kernel32.dll中的CreateRemoteThread进行注入的时候,程序会走到ntdll.dll中的ZwCreateThreadEx函数进行执行。这是一个未导出的函数,所以需要手动获取函数地址来进行调用,相比于CreateRemoteThread更加底层。这个函数在64位和32位系统中的函数声明也不相同,在64位中的声明如下  typedef DWORD(WINAPI *pFnZwCreateThreadEx)(         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);
  它会导致线程创建的时候就被挂起,随后查看要运行的进程所在的会话层之后再决定是否要恢复线程的运行。所以要破解这种情况只需要将第7个参数设为0就可以,相应代码如下  typedef DWORD(WINAPI *pFnZwCreateThreadEx)(PHANDLE, ACCESS_MASK, LPVOID,                         HANDLE, LPTHREAD_START_ROUTINE,                        LPVOID, BOOL, DWORD, DWORD, DWORD, LPVOID);                       BOOL InjectDll(DWORD dwPid, CHAR szDllName[]) {     BOOL bRet = TRUE;     HANDLE hProcess = NULL, hRemoteThread = NULL;     HMODULE hKernel32 = NULL, hNtDll = NULL;     DWORD dwSize = 0;     LPVOID pDllPathAddr = NULL;     PVOID pLoadLibraryAddr = NULL;     pFnZwCreateThreadEx ZwCreateThreadEx = NULL;       // 打开注入进程,获取进程句柄     hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);     if (NULL == hProcess)     {         ShowError("OpenProcess");         bRet = FALSE;         goto exit;     }       // 在注入进程中申请可以容纳DLL完成路径名的内存空间     dwSize = 1 + strlen(szDllName);     pDllPathAddr = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);     if (!pDllPathAddr)     {         ShowError("VirtualAllocEx");         bRet = FALSE;         goto exit;     }       // 把DLL完成路径名写入进程中     if (!WriteProcessMemory(hProcess, pDllPathAddr, szDllName, dwSize, NULL))     {         ShowError("WriteProcessMemory");         bRet = FALSE;         goto exit;     }             hKernel32 = LoadLibrary("kernel32.dll");     if (hKernel32 == NULL)     {         ShowError("LoadLibrary kernel32");         bRet = FALSE;         goto exit;     }       // 获取LoadLibraryA函数地址     pLoadLibraryAddr = GetProcAddress(hKernel32, "LoadLibraryA");     if (pLoadLibraryAddr == NULL)     {         ShowError("GetProcAddress LoadLibraryA");         bRet = FALSE;         goto exit;     }       hNtDll = LoadLibrary("ntdll.dll");     if (hNtDll == NULL)     {         ShowError("LoadLibrary ntdll");         bRet = FALSE;         goto exit;     }           ZwCreateThreadEx = (pFnZwCreateThreadEx)GetProcAddress(hNtDll, "ZwCreateThreadEx");     if (!ZwCreateThreadEx)     {         ShowError("GetProcAddress ZwCreateThreadEx");         bRet = FALSE;         goto exit;     }       ZwCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL,                       hProcess, (LPTHREAD_START_ROUTINE)pLoadLibraryAddr,                      pDllPathAddr, 0, 0, 0, 0, NULL);     if (hRemoteThread == NULL)     {         ShowError("ZwCreateThreadEx");         bRet = FALSE;         goto exit;     } exit:     if (hKernel32) FreeLibrary(hKernel32);     if (hNtDll) FreeLibrary(hNtDll);     if (hProcess) CloseHandle(hProcess);     if (hRemoteThread) CloseHandle(hRemoteThread);     return bRet; }APC注入:
  APC 是一个简称,具体名字叫做异步过程调用,我们看下MSDN中的解释,异步过程调用,属于是同步对象中的函数,所以去同步对象中查看.
  异步函数调用的原理:
  异步过程调用是一种能在特定线程环境中异步执行的系统机制。 异步执行:
  A.所有异步程序的执行,都会在同步程序之后在执行
  B.异步程序自己之间的执行顺序,如果时间是相同的,那么就是按代码的先后顺序执 行,否则是时间短的会先执行。
  1,从一行代码开始执行程序
  2,同步程序正常执行
  3,如果发现是异步程序,暂时不执行,存储在异步池中,等待执行
  4,将程序中所有的同步程序执行完毕后
  5,开启异步池,执行异步程序,当设定的时间到达,就会执行对应的异步升序
  先到设定时间的异步程序先执行, 如果设定的时间相同,看异步程序的顺序,来执行
  回调函数:
  函数也可以作为函数的参数来传递
  你到一个商店买东西,刚好你要的东西没有货,于是你在店员那里留下了你的电话,过了几天店里有货了,店员就打了你的电话,然后你接到电话后就到店里去取了货。
  在这个例子里,你的电话号码就叫回调函数,你把电话留给店员就叫登记回调函数,店里后来有货了叫做 触发回调事件,店员给你打电话叫做 调用回调函数,你到店里去取货叫做 响应回调事件。程序的APC
  往线程APC队列添加APC,系统会产生一个软中断。在线程下一次被调度的时候,就会执行APC函数,APC有两种形式,由系统产生的APC称为内核模式APC,由应用程序产生的APC被称为用户模式APC
  那么使用APC场合的注入就有了,
  1.必须是多线程环境下
  2.注入的程序必须会调用上面的那些同步对象.
  那么我们可以注入APC,注意下条件,也不是所有都能注入的. 注入方法的原理:
  1.当对面程序执行到某一个上面的等待函数的时候,系统会产生一个中断
  2.当线程唤醒的时候,这个线程会优先去Apc队列中调用回调函数
  3.我们利用QueueUserApc,往这个队列中插入一个回调
  4.插入回调的时候,把插入的回调地址改为LoadLibrary,插入的参数我们使用VirtualAllocEx申请内存,并且写入进去
  使用方法:
  1.利用快照枚举所有的线程
  2.写入远程内存,写入的是Dll的路径
  3.插入我们的DLL即可
  要往APC队列中增加APC函数,需要通过QueueUserAPC函数来实现,这个函数在文档中的定义如下  DWORD WINAPI QueueUserAPC(   __in  PAPCFUNC pfnAPC, /当满足条件时,要执行的APC函数的地址   __in  HANDLE hThread, /指定增加APC函数的线程句柄   __in  ULONG_PTR dwData); /要执行的APC函数参数地址
  可以看到pfnAPC和dwData这两个参数和CreateRemoteThread中的lpStartAddress和lpParameter的作用是一样的。不过这里是对线程进行操作,一个进程有多个线程。所以为了确保程序正确运行,所以需要遍历所有线程,查看是否是要注入的进程的线程,依次获得句柄插入APC函数。具体代码如下  BOOL InjectDll(DWORD dwPid, CHAR szDllName[]) {     BOOL bRet = TRUE;     HANDLE hProcess = NULL, hThread = NULL, hSnap = NULL;     HMODULE hKernel32 = NULL;     DWORD dwSize = 0;     PVOID pDllPathAddr = NULL;     PVOID pLoadLibraryAddr = NULL;     THREADENTRY32 te32 = { 0 };       // 打开注入进程,获取进程句柄     hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);     if (NULL == hProcess)     {         ShowError("OpenProcess");         bRet = FALSE;         goto exit;     }       // 在注入进程中申请可以容纳DLL完成路径名的内存空间     dwSize = 1 + strlen(szDllName);     pDllPathAddr = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);     if (!pDllPathAddr)     {         ShowError("VirtualAllocEx");         bRet = FALSE;         goto exit;     }       // 把DLL完成路径名写入进程中     if (!WriteProcessMemory(hProcess, pDllPathAddr, szDllName, dwSize, NULL))     {         ShowError("WriteProcessMemory");         bRet = FALSE;         goto exit;     }         hKernel32 = LoadLibrary("kernel32.dll");     if (hKernel32 == NULL)     {         ShowError("LoadLibrary");         bRet = FALSE;         goto exit;     }       // 获取LoadLibraryA函数地址     pLoadLibraryAddr = GetProcAddress(hKernel32, "LoadLibraryA");     if (pLoadLibraryAddr == NULL)     {         ShowError("GetProcAddress");         bRet = FALSE;         goto exit;     }       //获得线程快照     hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);     if (!hSnap)     {         ShowError("CreateToolhelp32Snapshot");         bRet = FALSE;         goto exit;     }       //遍历线程     te32.dwSize = sizeof(te32);     if (Thread32First(hSnap, &te32))     {         do         {             //这个线程的进程ID是不是要注入的进程的PID             if (te32.th32OwnerProcessID == dwPid)             {                 hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, te32.th32ThreadID);                 if (hThread)                 {                     QueueUserAPC((PAPCFUNC)pLoadLibraryAddr, hThread, (ULONG_PTR)pDllPathAddr);                     CloseHandle(hThread);                     hThread = NULL;                 }                 else                 {                     ShowError("OpenThread");                     bRet = FALSE;                     goto exit;                 }             }         } while (Thread32Next(hSnap, &te32));     } exit:     if (hKernel32) FreeLibrary(hKernel32);     if (hProcess) CloseHandle(hProcess);     if (hThread) CloseHandle(hThread);     return bRet; }注册表注入:
  什么是注册表
  注册表是windows操作系统、硬件设备以及客户应用程序得以正常运行和保存设置的核心"数据库",也可以说是一个非常巨大的树状分层结构的数据库系统。 注入原理:
  在cmd输入 regedit打开注册表。定位 HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindows NTCurrentVersionWindows项
  是一种比较简单的注入,主要依赖于俩个表项,AppInit_Dlls和LoadAppInit_DLLs。AppInit_Dlls写入dll完整路径,LoadAppInit_DLLs写为1,重启后,指定DLL会注入到所有运行进程。
  AppInit_DLLs键的值可以是一个dll的文件名或一组dll的文件名(通过逗号或空格来分隔),由于空格是用来分隔文件名的,因此dll文件名不能含有空格。第一个dll的文件名可以包含路径,但其他的dll包含的路径将被忽略。
  LoadAppInit_DLLs键的值表示AppInit_DLLs键是否有效,为了让AppInit_DLLs键的值有效,需要将LoadAppInit_DLLs的值设置为1。
  这两个键值设定后,当应用程序启动并加载User32.dll时,会获得上述注册表键的值,并调用LoadLibrary来调用这些字符串中指定的每一个dll。这时每个被载入的dll可以完成相应的初始化工作。但是需要注意的是,由于被注入的dll是在进程生命期的早期被载入的,因此这些dll在调用函数时应慎重。调用Kernel32.dll中的函数应该没有问题,因为Kernel32.dll是在User32.dll载入前已被加载。但是调用其他的dll中的函数时应当注意,因为进程可能还未载入相应的dll,严重时可能会导致蓝屏。
  这种方法很简单,只需要在注册表中修改两个键的值即可,但是有如下  缺点  :
  1.只有调用了User32.dll的进程才会发生这种dll注入。也就是说某些CUI程序(控制台应用程序)可能无法完成dll注入,比如将dll注入到编译器或链接器中是不可行的。
  2.该方法会使得所有的调用了User32.dll的程序都被注入指定的dll,如果你仅仅想对某些程序注入dll,这样很多进程将成为无辜的被注入着,并且其他程序你可能并不了解,盲目的注入会使得其他程序发生崩溃的可能性增大。
  3.这种注入会使得在应用程序的整个生命周期内被注入的dll都不会被卸载。注入dll的原则是值在需要的时间才注入我们的dll,并在不需要时及时卸载。  注入流程
  第一步:打开注册表键值如下:
  HKEY_LOCAL_MACHINESoftWareMicroSoftWindows NTCurrentVersionWindows
  第二步:修改AppInit_Dlls
  在该键值中添加自己的DLL的全路径加dll名,多个DLL以逗号或者空格分开(因此在文件名中应该尽量不要存在空格),该键值只有第一个dll文件名可以包含路径,后面的都不能包含,因此我们最好将dll放在系统路径 下,这样就可以不用包含路径也能被加载了。
  第三步:修改LoadAppInit_DLLs
  在该注册表项中添加键值 LoadAppInit_DLLs ,类型为 DWORD,并将其值置为 1 .
  代码实现BOOL InjectDll(DWORD dwPid, CHAR szDllName[]) {     BOOL bRet = TRUE;     HKEY hKey = NULL;     CHAR szAppKeyName[] = { "AppInit_DLLs" };     CHAR szLoadAppKeyName[] = { "LoadAppInit_DLLs" };     DWORD dwLoadAppInit = 1; //设置LoadAppInit_DLLs的值       //打开相应注册表键     if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SoftwareMicrosoftWindows NTCurrentVersionWindows",         0, KEY_ALL_ACCESS, &hKey) != ERROR_SUCCESS)     {         ShowError("RegOpenKeyEx");         bRet = FALSE;         goto exit;     }       //设置AppInit_DLLs为相应的DLL路径     if (RegSetValueEx(hKey, szAppKeyName, 0, REG_SZ, (PBYTE)szDllName, strlen(szDllName) + 1) != ERROR_SUCCESS)     {         ShowError("RegSetValueEx");         bRet = FALSE;         goto exit;     }       //将LoadAppInit_DLLs的值设为1     if (RegSetValueEx(hKey, szLoadAppKeyName, 0, REG_DWORD, (PBYTE)&dwLoadAppInit, sizeof(dwLoadAppInit)) != ERROR_SUCCESS)     {         ShowError("RegSetValueEx");         bRet = FALSE;         goto exit;     } exit:     return bRet; }全局钩子注入:
  Windows系统中的大多数应用都是基于消息机制的,也就是说它们都有一个消息过程函数,可以根据收到的不同消息来执行不同的代码。基于这种消息机制,Windows维护了一个OS message queue以及为每个程序维护着一个application message queue。当发生各种事件的时候,比如敲击键盘,点击鼠标等等,操作系统会从OS message queue将消息取出给到相应的程序的application message queue。
  而OS message queue和application message queue的中间有一个称为钩链的结果如下
  在这个钩链中保存的就是设置的各种钩子函数,而这些钩子函数会比应用程序还早接收到消息并对消息进行处理。所以程序员可以通过在钩子中设置钩子函数,而要设置钩子函数就需要使用SetWindowHookEx来将钩子函数安装到钩链中,函数在文档中的定义如下
  HHOOK SetWindowsHookEx(int idHook, HOOKPROC lpfn, HINSTANCE hMod, DWORD dwThreadId);
  参数
  含义
  idHook
  要安装的钩子类型,为了挂全局钩子,这里选择WH_GETMESSAGE。表示的是安装一个挂钩过程,它监视发送到消息队列的消息
  lpfn
  表示的是钩子的回调函数。如果dwThreadId为0,则lpfn指向的钩子过程必须指向DLL中的钩子过程
  hMod
  包含由lpfn参数执行的钩子过程的DLL句柄
  dwThreadId
  与钩子过程关联的线程标识符,如果为0则表示与所有线程相关联。
  如果函数成功,则返回钩子过程的句柄,否则为NULL。
  根据上面的介绍可以得知,想要创建一个全局钩子,就必须在DLL文件中创建。这是因为进程的地址空间是独立的,发生对应事件的进程不能调用其他进程地址空间的钩子函数。如果钩子函数的实现代码在DLL中,则在对应事件发生时,系统会把这个DLL加载到发生事件的进程地址空间中,使它可以调用钩子函数进行处理。
  所以只要在系统中安装了全局钩子,那么只要进程接收到可以发出钩子的消息,全局钩子的DLL就会被系统自动或者强行加载到进程空间中,这就可以实现DLL注入。
  而这里之所以设置为WH_GETMESSAGE,是因为这种类型的钩子会监视消息队列,又因为Windows系统是基于消息驱动的,所以所有的进程都会有自己的一个消息队列,都会加载WH_GETMESSAGE类型的全局钩子。
  当idHook设置为WH_GETMESSAGE的时候,回调函数lpfn的定义如下  LRESULT CALLBACK GetMsgProc(int code,                             WPARAM wParam,                             LPARAM lParam);
  参数
  含义
  code
  指定钩子过程是否必须处理该消息。如果代码是HC_ACTION,则钩子过程必须处理该消息。如果代码小于零,则钩子过程必须将消息传递给CallNextHookEx函数而无需进一步处理,并且应该返回CallNextHookEx返回的值
  wParam  指定消息是否已从队列中删除。此参数可以是以下值之一。
  PM_NOREMOVE:指定消息尚未从队列中删除
  PM_REMOVE:指定消息已从队列中删除
  lParam
  指向包含消息详细信息的MSG结构体的指针
  当用户不需要再进行消息钩取时只需调用UnhookWindowsHookEx即可解除安装的消息钩子,函数的原型如下:  BOOL WINAPI UnhookWindowsHookEx(   _In_ HHOOK hhk );
  hhk参数是之前调用SetWindowsHookEx函数返回的HHOOK变量。这个函数调用成功后会使被注入过dll的锁计数器递减1,当锁计数器减到0时系统会卸载被注入的dll。  过程:
  1.调用SetWindowsHookEx设置钩子.
  2.在设置过程中.需要一个回调.所以我们填入一个回调. (个人tips:这个地方可以设置自写函数,自写功能)
  3.回调函数中调用CallNextHookEx函数. 如果不调用.那么相当于我们设置了不反悔.程序可能出现问题.当然是按需返回. (个人tips:如果不调用,这里可能就打乱程序原有的后续代码逻辑)
  4.取消HOOK设置. (个人tips:降低性能,所以需要取消)
  注意:钩子函数应当放在一个dll中,并且在你的进程中LoadLibrary这个dll。然后再调用SetWindowsHookEx函数对相应类型的消息安装钩子。
  由于设置全局钩子的代码需要在DLL文件中完成,所以首先需要新建一个dll文件。
  phc.h  #include "framework.h" extern "C" _declspec(dllexport) int SetGlobalHook(); extern "C" _declspec(dllexport) LRESULT GetMsgProc(int code, WPARAM wParam, LPARAM lParam); extern "C" _declspec(dllexport) BOOL UnsetGlobalHook();
  dllmain.cpp // dllmain.cpp : 定义 DLL 应用程序的入口点。 #include "pch.h"   HMODULE g_hDllModule = NULL;  BOOL APIENTRY DllMain( HMODULE hModule,                         DWORD  ul_reason_for_call,                         LPVOID lpReserved                      )   {     switch (ul_reason_for_call)        {     case DLL_PROCESS_ATTACH: {         g_hDllModule = hModule;         break;     }                case DLL_THREAD_ATTACH:     case DLL_THREAD_DETACH:     case DLL_PROCESS_DETACH:         break;     }      return TRUE; }
  pch.cpp#include "pch.h"  #include   #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 SetGlobalHook() {     		 g_hHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)GetMsgProc, g_hDllModule, 0);     		 if (NULL == g_hHook) {         return FALSE;     }     return TRUE; }  // 卸载钩子  	     BOOL UnsetGlobalHook() {     if (g_hHook) {         UnhookWindowsHookEx(g_hHook);     }      		 return TRUE; }
  SetGlobalHook():  设置全局钩子,WH_GETMESSAGE为监视发送到 消息队列的消息的钩子,第二个参数则为钩子的回调函数。
  GetMsgProc():  钩子的回调函数,CallNextHookEx表示将当前钩子传递给下一个钩子,若返回值为0,表示中断钩子传递,对钩子进行拦截。
  UnsetGlobalHook():  卸载钩子  共
  享内存:  由于全局钩子是以DLL形式加载到进程中,进程都是独立的,要将进程句柄传递给其他进程,可以使用共享内存突破进程独立性,使用"/SECTION:mydata,RWS"设置为可读可写可共享的数据段。
  创建c++空项目 编译下面代码,将hookDll.dll放在生成的exe下,运行 #include   #include    typedef BOOL(*PEN_HOOKSTART)();  typedef BOOL(*PEN_HOOKSTOP)();   int main() {     //加载dll      	HMODULE hDll = LoadLibrary(L"./hookDll.dll");       	if (NULL == hDll)     {          		printf("LoadLibrary Error[%d] ", ::GetLastError());         return 1;     }  	BOOL isHook = FALSE;      //导出函数地址      	PEN_HOOKSTART SetGlobalHook = (PEN_HOOKSTART)GetProcAddress(hDll, "SetGlobalHook"); 	if (NULL == SetGlobalHook)     {         printf("SetGlobalHook:GetProcAddress Error[%d] ", GetLastError());         	return 2;     }      	PEN_HOOKSTOP UnsetGlobalHook = (PEN_HOOKSTOP)GetProcAddress(hDll, "UnsetGlobalHook");       	if (NULL == UnsetGlobalHook)     {          		printf("UnsetGlobalHook:GetProcAddress Error[%d] ", GetLastError());          		return 3;     }       	isHook=SetGlobalHook();       	if (isHook) {         printf("Hook is ok! ");     }      	else {         printf("Hook is error[%d] ", GetLastError());     }       	system("pause");       	UnsetGlobalHook();       	FreeLibrary(hDll);       	return 0; }
  使用Process Explorer查看dll:
  被注入成功
  参考:
  https://bbs.pediy.com/thread-269910.htm#msg_header_h1_2
  https://www.cnblogs.com/wf751620780/p/10730013.html#autoid-4-5-0

限制激光雷达出口,是中国要卡欧美脖子,还是保成本优势?美媒称美国政府正在考虑切断美国供应商与中国华为公司之间的所有联系。已经在5G芯片领域,华为已经被美国卡了脖子,新款旗舰手机只能使用高通的4G芯片,而上面这条新禁令如若实施,高通和英中国芯片突围,被美国卡脖子,我们的中国芯该何去何从芯片在人类社会的应用中无处不在。一方面,现代军事领域上半导体芯片是不可缺少的零部件,另一方面,在民用经济领域芯片技术也是至关重要的一环。近几年来,芯片在国际中的影响力愈加升高,可以剧版三体大结局ETO组织被摧毁,叶文洁身死,汪淼崩溃!三体电视剧版大结局,古筝行动的成功,摧毁了地球三体组织,抓住了精神领袖统帅叶文洁,杀死了二号人物伊文斯,ETO组织实力大损,再也掀不起风浪。地球人终于知道了三体文明的存在,也知道了战士一人消灭56名越军,捡到一件战利品,为何因战利品牺牲自卫反击战打响不久,年轻的解放军战士岩龙在一次战斗中,凭一己之力击杀了56名越军,并攻下了那一座山头,捡到一件战利品。可是4天后,却因为那件战利品牺牲,这是怎么回事呢?确实,那天要广州出发惠州1天游,徒步咸台港海滩,听海拾贝,狮子岛赏日落今天给大家分享一条从广州出发,惠州1天旅游行程,徒步咸台港狮子岛,攀爬礁石,听海拾贝,狮子岛沙滩观看日落。梦里总有一片一望无际的海滩,碧蓝银白,纯粹朦胧,如果,你想寻觅梦境颀长的海广州自助游小记(十七)广州市博物馆看完五羊雕塑,接着来到位于越秀公园内部的广州市博物馆,广州市博物馆依托越秀公园内原有的几栋古建筑建设,时间关系我们只看了最有代表性的的镇海楼展馆。镇海楼始建于明朝,是广广州樱花,浪漫盛开随着广州温度逐渐回升,灿烂的阳光洒满大地,天气正好,春暖花开,趁着风和日丽的好天气,大批市民选择踏青赏花,不负晴日好时光。黄埔创业公园,樱花树下游客来往近日,广州市黄埔创业公园内,扫地出门!切尔西将出售7000万英格兰前锋,恩佐首秀获赞大师级头条创作挑战赛切尔西无疑成为整个冬窗期最出风头的球队,球队狂烧3。3亿欧元,甚至超过其他四大联赛球队引援花费的总和。但是在今天凌晨结束的英超第22轮焦点战中,切尔西主场00闷平富勒英格兰足球冠军联赛卡迪夫城01米尔沃尔登场于北京时间1月26日展开的英格兰足球冠军联赛火并战役,卡迪夫城主队前往英格兰冠军联赛米尔沃尔队展开紧张的较劲,最终这场刺激较量以01比分落幕,并且由米尔沃尔击败卡迪夫城来成为最超过恩佐?莫耶斯我认为赖斯若离队,肯定会打破英国记录西汉姆主教练大卫莫耶斯在11战平纽卡斯尔的赛后谈到了德克兰赖斯的话题。记者问赖斯离队是不是不可避免的事情?莫耶斯我希望不是,我希望他能在这里度过自己的时光。但是你看,有很多关于赖斯做时代领先人古语云,为天地立心,为生民立命,为往圣继绝学,为万世开太平。太多仁人志士在用自己的言行甚至生命在诠释着这句话彭士禄隐姓埋名潜心研究,填补了我国核潜艇发展的空白沙漠愚公皱纹堆累志留黑
A级通缉令恶性杀人逃犯张伟被依法判处死刑!警方披露案件侦破详情2021年12月30日,在内江市中级人民法院,随着审判长的判决,公安部A级通缉令恶性杀人犯背负三条人命的张伟被依法判处死刑。2022年6月27日四川省高级人民法院复核维持一审判决,别滑了!别滑了!警方回应齐鲁晚报综合1月15日,湖南衡阳南岳衡山景区看雾凇,下山的路成天然溜冰场,有一些游客不慎摔倒后向下滑行,速度很快,滑行一段距离后被其他游客帮忙减速扶起。游客滑倒后一路溜滑(视频截图跟我游德国之科隆科隆(德语Kln)是德国第四大城市,北莱茵威斯特法伦州最大的城市,亦是德国内陆最重要的港口之一,莱茵地区的经济文化和历史中心。它已有2000多年的历史,是德国最古老的城市之一。中世全国首个!广州出台生物多样性保护部门工作清单生物多样性保护日益受到关注,但生物多样性保护也面临着涉及部门众多职责边界不清晰的问题。近日,广州市生态环境保护委员会办公室印发广州市生物多样性保护部门工作清单(2022年版)(以下当心!这几种食物越新鲜越伤人,你一定要知道!在市场上挑选蔬菜水果,不少人都觉得是越新鲜越嫩越好。其实,并不是一切新鲜的蔬果都有利健康,有些食物趁鲜吃,不仅没营养,还可能伤害身体!这些食物越新鲜越伤人1hr新鲜木耳新鲜木耳中有当心!手机屏幕千万别随便共享!近年来随着网络视频会议广泛运用屏幕共享功能逐渐普及在给人们工作生活带来便利的同时也成为了不法分子利用的新型诈骗工具仅仅是屏幕共享自己账户里的钱怎么就不翼而飞了呢?什么是屏幕共享?屏筋膜枪怎么用?三大弊端隐患要当心前不久,一位外地的朋友来问我为什么他使用筋膜枪后没什么效果,反而出现了肌肉酸痛淤血的问题?经过深入地了解后,我发现他使用筋膜枪的方法存在很大的问题,其实他也不是个例,大部分人对于筋当心!这些东西可能会咬人什么?五个手指一下子卡住仨?注意!注意!孩子被卡并不是个例家长们稍不注意他们就可能会被千奇百怪的东西卡住网友只有你想不到没有熊孩子卡不到这不,宝贝们和它们来了次亲密接触按摩椅滑滑梯1岁女娃吃松子摔倒卡进气管!预防婴幼儿异物梗塞,5类食物要当心年节将至,家裡开始准备上瓜果类零食及点心,大人聚在一起开心聊天之馀有时也会随手喂孩子吃,但这对咀嚼功能仍未发育完全的孩子却并不安全,尤其1岁以上的孩子已经具备行走跑跳能力,如果在吃华润三九预计公司感冒药上半年会处于订单充裕状况华润三九视觉中国资料图华润三九披露其感冒药订单及库存情况,并表示已有感冒药产能提升的项目。1月9日,华润三九医药股份有限公司(华润三九,000999)公布的投资者关系活动记录表的显爱美人士必看!当心美丽背后的风险医美玻尿酸水光针微整形这些词语仿佛是美丽的代名词越来越多的爱美人士在追求美丽的道路上越走越远但是这些名词背后隐藏的风险你是否都了解?医疗美容不同于生活美容按照卫生行政管理有关规定医