1、闲着没事整理了一下DLL的N种注入方法闲着没事整理了一下DLL的N种注入方法,对学习外挂的朋友,应该有用!第一种方法:利用 CreateRemoteThread 远程建立线程的方式注入DLL首先,我们要提升自己的权限,因为远程注入必不可免的要访问到目标进程的内存空间,如果没有足够的系统权限,将无法作任何事下面是这个函数是用来提升我们想要的权限用的function EnableDebugPriv : Boolean;varhToken : THANDLE;tp : TTokenPrivileges;rl : Cardinal;beginresult := false;/打开进程令牌环OpenPr
2、ocessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, hToken);/获得进程本地唯一IDif LookupPrivilegeValue(nil, SeDebugPrivilege, tp.Privileges0.Luid) thenbegin tp.PrivilegeCount := 1; tp.Privileges0.Attributes := SE_PRIVILEGE_ENABLED; /调整权限 result := AdjustTokenPrivileges(hToken, False, tp,
3、 sizeof(tp), nil, rl);end;end;关于 OpenProcessToken() 和 AdjustTokenPrivileges() 两个 API 的简单介绍:OpenProcessToken():获得进程访问令牌的句柄function OpenProcessToken( ProcessHandle: THandle; /要修改访问权限的进程句柄 DesiredAccess: DWORD; /指定你要进行的操作类型 var TokenHandle: THandle): BOOL; /返回的访问令牌指针AdjustTokenPrivileges() :调整进程的权限func
4、tion AdjustTokenPrivileges( TokenHandle: THandle;/ 访问令牌的句柄 DisableAllPrivileges: BOOL; / 决定是进行权限修改还是除能(Disable)所有权限 const NewState: TTokenPrivileges;/ 指明要修改的权限,是一个指向TOKEN_PRIVILEGES结构的指针,该结构包含一个数组,数据组的每个项指明了权限的类型和要进行的操作; BufferLength: DWORD;/结构PreviousState的长度,如果PreviousState为空,该参数应为 0 var PreviousS
5、tate: TTokenPrivileges; / 指向TOKEN_PRIVILEGES结构的指针,存放修改前的访问权限的信息 var ReturnLength: DWORD /实际PreviousState结构返回的大小) : BOOL;远程注入DLL其实是通过 CreateRemoteThread 建立一个远程线程调用 LoadLibrary 函数来加载我们指定的DLL,可是如何能让远程线程知道我要加载DLL呢,要知道在Win32系统下,每个进程都拥有自己的4G虚拟地址空间,各个进程之间都是相互独立的。所我们需要在远程进程的内存空间里申请一块内存空间,写入我们的需要注入的 DLL 的路径
6、需要用到的 API 函数有:OpenProcess():打开目标进程,得到目标进程的操作权限,详细参看MSDNfunction OpenProcess( dwDesiredAccess: DWORD;/ 希望获得的访问权限 bInheritHandle: BOOL;/ 指明是否希望所获得的句柄可以继承 dwProcessId: DWORD / 要访问的进程ID): THandle; VirtualAllocEx():用于在目标进程内存空间中申请内存空间以写入DLL的文件名function VirtualAllocEx( hProcess: THandle;/ 申请内存所在的进程句柄 lpAdd
7、ress: Pointer;/ 保留页面的内存地址;一般用nil自动分配 dwSize,/ 欲分配的内存大小,字节单位;注意实际分 配的内存大小是页内存大小的整数倍 flAllocationType: DWORD; flProtect: DWORD): Pointer; WriteProcessMemory():往申请到的空间中写入DLL的文件名function WriteProcessMemory( hProcess: THandle;/要写入内存数据的目标进程句柄 const lpBaseAddress: Pointer; /要写入的目标进程的内存指针, 需以 VirtualAllocEx
8、() 来申请 lpBuffer: Pointer; /要写入的数据 nSize: DWORD; /写入数据的大小 var lpNumberOfBytesWritten: DWORD /实际写入的大小): BOOL; 然后就可以调用 CreateRemoteThread 建立远程线程调用 LoadLibrary 函数来加载我们指定的DLLCreateRemoteThread()/在一个远程进程中建立线程function CreateRemoteThread( hProcess: THandle;/远程进程的句柄 lpThreadAttributes: Pointer; /线程安全描述字,指向SE
9、CURITY_ATTRIBUTES结构的指针 dwStackSize: DWORD;/线程栈大小,以字节表示 lpStartAddress: TFNThreadStartRoutine;/ 一个TFNThreadStartRoutine类型的指针,指向在远程进程中执行的函数地址 lpParameter: Pointer; /传入参数的指针 dwCreationFlags: DWORD;/创建线程的其它标志 var lpThreadId: DWORD /线程身份标志,如果为0, 则不返回): THandle; 整个远程注入DLL的具体实现代码如下:function InjectDll(const
10、 DllFullPath : string; const dwRemoteProcessId : Cardinal): boolean;varhRemoteProcess, hRemoteThread: THANDLE;pszLibFileRemote : Pointer;pszLibAFilename: PwideChar;pfnStartAddr : TFNThreadStartRoutine;memSize, WriteSize, lpThreadId : Cardinal;beginresult := FALSE;/ 调整权限,使程序可以访问其他进程的内存空间if EnableDebu
11、gPriv thenbegin /打开远程线程 PROCESS_ALL_ACCESS 参数表示打开所有的权限 hRemoteProcess := OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwRemoteProcessId ); try / 为注入的dll文件路径分配内存大小,由于为WideChar,故要乘2 GetMem(pszLibAFilename, Length(DllFullPath) * 2 + 1); / 之所以要转换成 WideChar, 是因为当DLL位于有中文字符的路径下时不会出错 StringToWideChar(DllFullPath
12、, pszLibAFilename, Length(DllFullPath) * 2 + 1); / 计算 pszLibAFilename 的长度,注意,是以字节为单元的长度 memSize := (1 + lstrlenW(pszLibAFilename) * sizeof(WCHAR); /使用VirtualAllocEx函数在远程进程的内存地址空间分配DLL文件名空间 pszLibFileRemote := VirtualAllocEx( hRemoteProcess, nil, memSize, MEM_COMMIT, PAGE_READWRITE); if Assigned(pszL
13、ibFileRemote) then begin /使用WriteProcessMemory函数将DLL的路径名写入到远程进程的内存空间 if WriteProcessMemory(hRemoteProcess, pszLibFileRemote, pszLibAFilename, memSize, WriteSize) and (WriteSize = memSize) then begin lpThreadId := 0; / 计算LoadLibraryW的入口地址 pfnStartAddr := GetProcAddress(LoadLibrary(Kernel32.dll), Load
14、LibraryW); / 启动远程线程LoadLbraryW,通过远程线程调用创建新的线程 hRemoteThread := CreateRemoteThread(hRemoteProcess, nil, 0, pfnStartAddr, pszLibFileRemote, 0, lpThreadId); / 如果执行成功返回True; if (hRemoteThread 0) then result := TRUE; / 释放句柄 CloseHandle(hRemoteThread); end; end; finally / 释放句柄 CloseHandle(hRemoteProcess);
15、 end;end; end;接下来要说的是如何卸载注入目标进程中的DLL,其实原理和注入DLL是完全相同的,只是远程调用调用的函数不同而已,这里要调用的是FreeLibrary代码如下:function UnInjectDll(const DllFullPath : string;const dwRemoteProcessId : Cardinal) : Boolean;/ 进程注入和取消注入其实都差不多,只是运行的函数不同而已varhRemoteProcess, hRemoteThread : THANDLE;pszLibFileRemote : pchar;pszLibAFilename:
16、 PwideChar;pfnStartAddr : TFNThreadStartRoutine;memSize, WriteSize, lpThreadId, dwHandle : Cardinal;beginresult := FALSE;/ 调整权限,使程序可以访问其他进程的内存空间if EnableDebugPriv thenbegin /打开远程线程 PROCESS_ALL_ACCESS 参数表示打开所有的权限 hRemoteProcess := OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwRemoteProcessId ); try / 为注入的
17、dll文件路径分配内存大小,由于为WideChar,故要乘2 GetMem(pszLibAFilename, Length(DllFullPath) * 2 + 1); / 之所以要转换成 WideChar, 是因为当DLL位于有中文字符的路径下时不会出错 StringToWideChar(DllFullPath, pszLibAFilename, Length(DllFullPath) * 2 + 1); / 计算 pszLibAFilename 的长度,注意,是以字节为单元的长度 memSize := (1 + lstrlenW(pszLibAFilename) * sizeof(WCHA
18、R); /使用VirtualAllocEx函数在远程进程的内存地址空间分配DLL文件名空间 pszLibFileRemote := VirtualAllocEx( hRemoteProcess, nil, memSize,MEM_COMMIT, PAGE_READWRITE); if Assigned(pszLibFileRemote) then begin /使用WriteProcessMemory函数将DLL的路径名写入到远程进程的内存空间 if WriteProcessMemory(hRemoteProcess, pszLibFileRemote, pszLibAFilename, me
19、mSize, WriteSize) and (WriteSize = memSize) then begin / 计算GetModuleHandleW的入口地址 pfnStartAddr := GetProcAddress(LoadLibrary(Kernel32.dll), GetModuleHandleW); /使目标进程调用GetModuleHandleW,获得DLL在目标进程中的句柄 hRemoteThread := CreateRemoteThread(hRemoteProcess, nil, 0, pfnStartAddr, pszLibFileRemote, 0, lpThrea
20、dId); / 等待GetModuleHandle运行完毕 WaitForSingleObject(hRemoteThread,INFINITE); / 获得GetModuleHandle的返回值,存在dwHandle变量中 GetExitCodeThread(hRemoteThread, dwHandle); / 计算FreeLibrary的入口地址 pfnStartAddr := GetProcAddress(LoadLibrary(Kernel32.dll), FreeLibrary); / 使目标进程调用FreeLibrary,卸载DLL hRemoteThread := Create
21、RemoteThread(hRemoteProcess, nil, 0, pfnStartAddr, Pointer(dwHandle), 0, lpThreadId); / 等待FreeLibrary卸载完毕 WaitForSingleObject( hRemoteThread, INFINITE ); / 如果执行成功返回True; if hRemoteProcess0 then result := TRUE; / 释放目标进程中申请的空间 VirtualFreeEx(hRemoteProcess, pszLibFileRemote, Length(DllFullPath)+1, MEM_
22、DECOMMIT); / 释放句柄 CloseHandle(hRemoteThread); end; end; finally / 释放句柄 CloseHandle(hRemoteProcess); end;end;end;第二种方法:采用CreateProcess的方法,实现起来比较复杂,但没有上面几种方法的局限性。且可以用其他工具(VC等)调试注入的DLL。下面进行介绍。原理如下:1用CreateProcess(CREATE_SUSPENDED)启动目标进程。2找到目标进程的入口,用ImageHlp中的函数可以实现。3将目标进程入口的代码保存起来。4在目标进程的入口写入LoadLibrar
23、y(MyDll)实现Dll的注入。5用ResumeThread运行目标进程。6目标进程就运行了LoadLibrary(MyDll),实现DLL的注入。7目标进程运行完LoadLibrary(MyDll)后,将原来的代码写回目标进程的入口。8目标进程Jmp至原来的入口,继续运行程序。从原理上可以看出,DLL的注入在目标进程的开始就运行了,而且不是用Debug的方案,这样,就没有上面方案的局限性了。该方案的关键在6,7,8三步,实现方法需要监视进程和DLL合作。下面,结合代码进行分析。在监视进程中,创建FileMapping,用来保存目标进程的入口代码,同时保证DLL中可以访问。在第7步实现将原目
24、标代码写回目标进程的入口。 监视程序和DLL共用的结构体#pragma pack (push ,1) 保证下面的结构体采用BYTE对齐(必须)typedef struct BYTE int_PUSHAD; / pushad 0x60 BYTE int_PUSH; / push &szDLL 0x68 DWORD push_Value; / &szDLL = ApiSpy.dll的path BYTE int_MOVEAX; /move eax &LoadLibrary0xB8 DWORD eax_Value; / &LoadLibrary WORD call_eax; / call eax 0x
25、D0FF(FF D0) (LoadLibrary(ApiSpy.dll); BYTE jmp_MOVEAX; / move eax &ReplaceOldCode0xB8 DWORD jmp_Value; / JMP的参数 WORD jmp_eax; / jmp eax 0xE0FF(FF E0) jmp ReplaceOldCode; char szDLLMAX_PATH; /ApiSpy.dll的FullPathINJECT_LOADLIBRARY_CODE, *LPINJECT_CODE;#pragma pack (pop , 1)上面结构体的代码为汇编代码,对应的汇编为:pushadp
26、ush szDllmov eax, &LoadLibraryAcall eax/ 实现调用LoadLibrary(szDll)的代码mov eax, oldentryjmp eax / 实现在LoadLibrary运行完后, 跳至目标进程的入口继续运行/ FileMaping的结构体typedef struct LPBYTElpEntryPoint; / 目标进程的入口地址 BYTE oldcodesizeof(INJECT_CODE); / 目标进程的代码保存SPY_MEM_SHARE, * LPSPY_MEM_SHARE;准备工作:第一步:用CreateProcess(CREATE_SUSPENDED)启动目标进程。CreateProcessA(0, szRunFile, 0, 0, FALSE, CREATE_SUSPENDED 0, NULL, &stInfo, &m_proInfo) ;/ 用CreateProcess启动一个暂停的目标进程/ 找到目标进程的入口点,函数如下第二步:找到目标进程的入口,用I
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1