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

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

普通人买彩票真的可以脱离苦海吗?希望大吗?普通人想从购买彩票中脱离苦海,那是白日做梦!基本上是做梦。普通人买彩票不是脱离了苦海,而是掉进了苦海。普通人还是要脚踏实地的靠劳动挣钱靠谱,别幻想歪门邪道。天上不会掉馅饼的,醒醒吧DNF职业平衡剑宗整体比红眼加强了20会登上一线职业吗?要是我没误解的话,在国服宗妹一直是处于一个比较高的位置吧,男鬼剑中红眼和剑魂争雄,女鬼剑中暗帝削了一波至今元气大伤,剑魔剑帝至今也没有真正的崛起,说宗妹儿是一枝独秀也不为过吧,看看绑定银行卡后,如何用手机付款呢?在绑定银行卡后,需要将钱转入移动支付软件中进行付款。将银行卡内资金转入微信零钱1。进入微信APP,在右下角我界面选择点击钱包菜单。2。在我的钱包界面点击零钱菜单。3。在零钱界面点击漫山遍野的华为手机免费上网的广告骗局是个什么梗,目的是什么?我还真的安装了,这个软件安装之后,就会出现安装什么多,弹出来乱七八糟的东西,画面你还退不出,你要连接WIFI,对不起联不上,叫你在试试,这就是十足的流氓软件,你要问为什么不整治,这你的生活离得开手机吗?手机现在是真离不开了,不光是联系各个人要用,工作上也有很多群,许多事情都是在群里面解决的,买卖各种商品要用手机,特别是去年疫情防控期间到现在,去一些重要场所不光要戴口罩,还要查看健试管婴儿移植7次不着床,我该怎么办?不知道你是什么原因做试管的,我想说的是心态最重要,我做过两次试管都失败了,后来决定不做试管了,这辈子就这样了,然后放松心态,该玩玩,该吃吃,该养生养生,运动运动,居然自然怀孕了查种自己很看中一段感情,别人却不看重怎么办?幸福是自己的,与任何人无关ok你自己很看重一段感情,为啥要在意别人是否看重呢?你的感情是做给别人看的吗?很显然不是,幸福生活在你自己的手里掌握着。自己开心快乐比什么都好,也不是拍了剩女说,宁愿不嫁渣男,一个人过挺好,你怎么看?高质量的独处,比底质量的凑和好。年苓大一些的单身女都不愿意找渣男,在选择对象中,你不能总是碰到那些游手好娴好吃懒做的人吧,虽然年苓大些,只要你有正确的人生观,总归会遇到人品好的对象有哪些品质高,精美的武侠手游推荐吗?每个人心中都有一个江湖,是与三五好友提着一壶酒,坐在墙头,谈笑风生。是抽出鞘中剑,路斩不平,快意恩仇。是盛名之后,被人遗忘,无人再提及。可再多的不同,走到最后,都会指向一条道路。事周琦不打cba,你怎么看?今天对于中国篮球来说也算是个有好消息的日子,因为球迷非常关注的两大国家队中锋终于有了新的去处。在一开始,因为合同金额问题没能跟青岛队完成续约的刘传兴无法在新赛季的CBA联赛注册,于体彩足球真有大神级别么?快二十年了,胜平负足彩还是十三场的时候,和朋友一起研究出来的,最后由他定调,买了1024元。头奖498万,好像是十注10万二等。和朋友俩人税后一人分了两百三十多。其中一场当时如日中
海信牵头制定的又一项国际标准发布,填补国际空白海信主导制定的LED背光电气信号接口国际标准日前由国际电工委员会电子显示标准技术委员会(IECTC110)正式发布。该标准聚焦LED动态背光控制技术,内容涉及液晶电视的信号处理单元(体育)网球澳网王欣瑜不敌凯斯当日,在澳大利亚墨尔本进行的澳大利亚网球公开赛女单第二轮比赛中,中国选手王欣瑜以0比2不敌美国选手凯斯,无缘下一轮。1月18日,王欣瑜在比赛中。新华社发(胡泾辰摄)1月18日,王欣半月谈丨中安创谷育种育苗筑梦未来中安创谷育种育苗筑梦未来半月谈记者叶俊东杨玉华吴慧珺合肥西郊,一座200米的高楼巍然耸立,蓝色玻璃幕墙在阳光下折射出耀眼光芒,高楼周围是拥湖而建的现代楼群。这就是坐落于合肥综合性国谁还不服詹姆斯?只用32场,激活模板韦德的天才,6项数据创新高无论是否喜欢詹姆斯的打球方式,作为球迷,都必须要承认他的篮球实力。尤其在提升队友方面,现役球员当中,比他强的更是寥寥无几。最典型的例子,便是湖人队朗尼沃克。这位选秀模板韦德的天才锋中消协对46款充电数据线比较试验,仅14款安全中国消费者协会近期委托中国信息通信研究院中国泰尔实验室对市场销售的部分品牌充电数据线开展比较试验。试验结果显示,仅14款样品在全部8项测试中表现良好,近半数样品阻燃能力不佳,编织线2023年(1月)吸尘器推荐,怎么选?家用吸尘器选购指南(下)前言哈喽,大家好啊,我是東冬哥哥,一个推荐吸尘器的头条创作者。昨天给大家发布了吸尘器选购指南的上篇内容,为大家讲解了吸尘器的分类,适宜人群,工作原理和该如何选择吸尘器这方面的内容,真假朋友朋友分四类你身边的是哪一类?嘴上喊你哥,喊你姐的,不一定是朋友。需要的时候帮助你,遇到事情挺你的,这是朋友。记住了,朋友可以不要财大气粗的,但一定要真诚的,可以不要有权有势。但一定要心正的,相处多久无所谓。友更多国际大港在中国涌现助力中国制造出海远行更多国际大港在中国涌现傍晚时分,宁波舟山港灯火通明,巨轮停靠岸边,汽笛声阵阵,正在进行集装箱卸载作业。岸上,智能集卡装载着各色集装箱在巨大的桥吊下往返穿梭。近期,全国多地港口陆续公王思聪,撕开了当下社会病态的遮羞布文那年美美本文纯属原创,感谢评论与转发!美美谈情感,每天趣谈人物,社会,热点等情感新鲜事。在这里,有故事有观点,有逻辑有深度,期待您的关注!王思聪,求你揍我一顿。这句话,不是戏谑,烟火年年!这个春节来同里,有年味可寻有奖励可得新岁圆满,烟火年年。春节将至,苏州吴江同里镇通过线上云发布会的形式,发布了年味同里美好礼遇2023同来同里过大年新春系列活动。自1月14日至2月15日,活动跨度长达一月,涵盖水乡民一起来拍家乡的烟火年味大美广东新春征集人间烟火气,最抚凡人心春节脚步临近,年味越来越浓当街道恢复人来人往的热闹当家中厨房飘出你最熟悉的味道当久违谋面的亲人对你嘘寒问暖你的心是否也被渐渐填满?年味在烟火里流淌人情在烟火里