进行DLL注入的三种方法

DLL注入是将DLL动态链接库加载到目标进程的技术,常用于跨进程操作。本文详细介绍了三种DLL注入方法:应用HOOK技术、创建远程线程和特洛伊DLL。通过示例代码展示了如何实现DLL注入,包括安装和卸载钩子函数、在远程进程中分配内存、调用远程线程等步骤。最后,提到了DLL注入在Windows登录管理中的潜在应用,如GINA替换以获取登录密码。
摘要由CSDN通过智能技术生成
进行DLL注入的三种方法


作者:陶冶(无邪)

MAIL: taoy5178@hotmail.com

OICQ:24149877


  在WINDOWS中,每个进程都有自己独立的地址空间,这样一个应用程序就无法进入另一个进程的地址空间而不会破坏另一个进程的运行,这样使得系统更加的稳定。但这样一来,相反的,如果我们要对我们感兴趣的进程进行操作也就变得复杂起来。比如,我们要为另一个进程创建的窗口建立子类或是要想从其中一个感兴趣的进程中取得一些有趣的信息(比如你想得到WIN2000用户登录的密码)。而DLL注入技术就是正好可以解决这些问题。DLL注入就是将DLL插入到其它你指定的进程的地址空间中,使得我们可以对感兴趣的进程进行操作。



  在我们的DLL注入到指定的进程空间时,为了可以使我们更清楚地看到它已经成功对注入到了指定的进程空间,所以我们有需要使用一个简单的工具来查看指定的进程空间中所载入的所有模块,以便确定我们的DLL是否已经成功注入。如果你系统中装有WINDOWS优化大师,那么你可以利用它提供的进程工具来查看,没有也没关系,我用BCB写了一个小工具,虽然不怎么方便,但也可以清楚地看到指定进程空间中的所有载入模块。

  该工具的主要代码如下:



//---------------------------------------------------------------------------

void __fastcall TfrmMain::btLookClick(TObject *Sender)

{

DWORD dwProcessId;

BOOL bRet;

MODULEENTRY32 hMod = {sizeof(hMod)};

HANDLE hthSnapshot = NULL;

BOOL bMoreMods = FALSE;





ListView->Clear();

if (Edit->Text == "")

return;

else

dwProcessId = StrToInt(Edit->Text);



// 为进程建立一个快照

hthSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,

dwProcessId);

if (hthSnapshot == NULL)

{

MessageBox(Handle,("CreateToolhelp32Snapshot failed with error "

+ IntToStr(GetLastError())).c_str(),"Error!",

MB_ICONINFORMATION + MB_OK);

return;

}

// 获取模块列表中的模块

bMoreMods = Module32First(hthSnapshot, &hMod);

if (bMoreMods == FALSE)

{

MessageBox(Handle,("Module32First failed with error "

+ IntToStr(GetLastError())).c_str(),"Error!",

MB_ICONINFORMATION + MB_OK);

return;

}

for (; bMoreMods; bMoreMods = Module32Next(hthSnapshot, &hMod))

{

TListItem *Item;

Item = ListView->Items->Add();

Item->Caption = String(hMod.szExePath);

Item->ImageIndex = 0;

}

// 关闭句柄

CloseHandle(hthSnapshot);

}



  接下来就开始我们的正题吧。

  DLL注入主要有三种方法,即应用HOOK技术、创建远程线程和特洛伊DLL三种。



  一、应用HOOK技术进行DLL注入

  我原来写过有关HOOK的介绍,如果你看过了或者是以前写过HOOK程序,那么你已经会这种DLL注入了。它其它就是为系统或某个线程安装一个钩子。这里要说的是,如果是全局钩子,那么你的DLL将会在进程调用时载入到任意一个调用的进程的地址空间中,这样是相当浪费资源的。因此我在下载的演示中就只对某一个指定的线程安装线程钩子。

  1、用BCB建立一个DLL工程(如果你用的是VC或其它,请自己对照),输入以下代码:

//===========================================================================

// 文件: UnitLib.cpp

// 说明: 演示利用钩子技术进行DLL注入.

// 将本DLL中的代码注入到指定的进程空间.

// 作者: 陶冶(无邪)

//===========================================================================





// 函数声明

extern "C" __declspec(dllexport) __stdcall

bool SetHook(DWORD dwThreadId);

extern "C" __declspec(dllexport) __stdcall

LRESULT CALLBACK MyProc(int nCode, WPARAM wParam, LPARAM lParam);



static HHOOK hHook = NULL; // 钩子句柄

static HINSTANCE hInst; // 当前DLL句柄





int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)

{

hInst = hinst;

return 1;

}

//---------------------------------------------------------------------------

// 安装钩子函数

bool __declspec(dllexport) __stdcall SetHook(DWORD dwThreadId)

{

if (dwThreadId != 0)

{

MessageBox(NULL, ("DLL已经注入!/nThreadId = " +

IntToStr(dwThreadId)).c_str(),"DLL",

MB_ICONINFORMATION + MB_OK);

    // 安装指定线程的钩子

hHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)MyProc,

hInst,dwThreadId);

if (hHook != NULL)

return true;

}else

{

MessageBox(NULL, "DLL即将从记事本进程空间中撤出!","DLL",

MB_ICONINFORMATION + MB_OK);

return (UnhookWindowsHookEx(hHook));

}

return true;

}



// 钩子函数

LRESULT CALLBACK __declspec(dllexport) __stdcall

MyProc(int nCode, WPARAM wParam, LPARAM lParam)

{

  // 因为只是演示DLL注入,所以这里什么也不做,交给系统处理

return (CallNextHookEx(hHook, nCode, wParam, lParam));

}

//---------------------------------------------------------------------------
远程注入DLL方法有很多种,也是很多木马病毒所使用的隐藏进程的方法,因为通过程序加载的DLL在进程管理器是没有显示的.这里介绍一种用 CreateRemoteThread 远程建立线程的方式注入DLL. 首先,我们要提升自己的权限,因为远程注入必不可免的要访问到目标进程的内存空间,如果没有足够的系统权限,将无法作任何事.下面是这个函数是用来提升我们想要的权限用的. function EnableDebugPriv: Boolean; var hToken: THandle; tp: TTokenPrivileges; rl: Cardinal; begin Result := false; //打开进程令牌环 OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, hToken); //获得进程本地唯一ID if LookupPrivilegeValue(nil, 'SeDebugPrivilege', tp.Privileges[0].Luid) then begin tp.PrivilegeCount := 1; tp.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED; //调整权限 Result := AdjustTokenPrivileges(hToken, false, tp, SizeOf(tp), nil, rl); end; end; 关于 OpenProcessToken() 和 AdjustTokenPrivileges() 两个 API 的简单介绍: OpenProcessToken():获得进程访问令牌的句柄. function OpenProcessToken( ProcessHandle: THandle; //要修改访问权限的进程句柄 DesiredAccess: DWORD; //指定你要进行的操作类型 var TokenHandle: THandle//返回的访问令牌指针 ): BOOL; AdjustTokenPrivileges() :调整进程的权限. function AdjustTokenPrivileges( TokenHandle: THandle; // 访问令牌的句柄 DisableAllPrivileges: BOOL; // 决定是进行权限修改还是除能(Disable)所有权限 const NewState: TTokenPrivileges; { 指明要修改的权限,是一个指向TOKEN_PRIVILEGES结构的指针,该结构包含一个数组, 数据组的每个项指明了权限的类型和要进行的操作; } BufferLength: DWORD; //结构PreviousState的长度,如果PreviousState为空,该参数应为 0 var PreviousState: TTokenPrivileges; // 指向TOKEN_PRIVILEGES结构的指针,存放修改前的访问权限的信息 var ReturnLength: DWORD //实际PreviousState结构返回的大小 ) : BOOL; 远程注入DLL其实是通过 CreateRemoteThread 建立一个远程线程调用 LoadLibrary 函数来加载我们指定的DLL,可是如何能让远程线程知道我要加载DLL呢,要知道在Win32系统下,每个进程都拥有自己的4G虚拟地址空间,各个进程之间都是相互独立的。所我们需要在远程进程的内存空间里申请一块内存空间,写入我们的需要注入DLL 的路径. 需要用到的 API 函数有: OpenProcess():打开目标进程,得到目标进程的操作权限,详细参看MSDN function OpenProcess( dwDesiredAccess: DWORD; // 希望获得的访问权限 bInheritHandle: BOOL; // 指明是否希望所获得的句柄可以继承 dwProcessId: DWORD // 要访问的进程ID ): THandle; VirtualAllocEx():用于在目标进程内存空间中申请内存空间以写入DLL的文件名 function VirtualAllocEx( hProcess: THandle; // 申请内存所在的进程句柄 lpAddress: Pointer; // 保留页面的内存地址;一般用nil自动分配 dwSize, // 欲分配的内存大小,字节单位;注意实际分 配的内存大小是页内存大小的整数倍 flAllocationType: DWORD; flProtect: DWORD ): Pointer; WriteProcessMemory():往申请到的空间中写入DLL的文件名 function WriteProcessMemory( hProcess: THandle; //要写入内存数据的目标进程句柄 const lpBaseAddress: Pointer; //要写入的目标进程的内存指针, 需以 VirtualAllocEx() 来申请 lpBuffer: Pointer; //要写入的数据 nSize: DWORD; //写入数据的大小 var lpNumberOfBytesWritten: DWORD //实际写入的大小 ): BOOL; 然后就可以调用 CreateRemoteThread 建立远程线程调用 LoadLibrary 函数来加载我们指定的DLL. CreateRemoteThread() //在一个远程进程中建立线程 function CreateRemoteThread( hProcess: THandle; //远程进程的句柄 lpThreadAttributes: Pointer; //线程安全描述字,指向SECURITY_ATTRIBUTES结构的指针 dwStackSize: DWORD; //线程栈大小,以字节表示 lpStartAddress: TFNThreadStartRoutine; // 一个TFNThreadStartRoutine类型的指针,指向在远程进程中执行的函数地址 lpParameter: Pointer; //传入参数的指针 dwCreationFlags: DWORD; //创建线程的其它标志 var lpThreadId: DWORD //线程身份标志,如果为0, 则不返回 ): THandle; 整个远程注入DLL的具体实现代码如下: function InjectDll(const DllFullPath: string; const dwRemoteProcessId: Cardinal): Boolean; var hRemoteProcess, hRemoteThread: THandle; pszLibFileRemote: Pointer; pszLibAFilename: PwideChar; pfnStartAddr: TFNThreadStartRoutine; memSize, WriteSize, lpThreadId: Cardinal; begin Result := false; // 调整权限,使程序可以访问其他进程的内存空间 if EnableDebugPriv then begin //打开远程线程 PROCESS_ALL_ACCESS 参数表示打开所有的权限 hRemoteProcess := OpenProcess(PROCESS_ALL_ACCESS, false, dwRemoteProcessId); try // 为注入dll文件路径分配内存大小,由于为WideChar,故要乘2 GetMem(pszLibAFilename, Length(DllFullPath) * 2 + 1); // 之所以要转换成 WideChar, 是因为当DLL位于有中文字符的路径下时不会出错 StringToWideChar(DllFullPath, 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(pszLibFileRemote) then begin //使用WriteProcessMemory函数将DLL的路径名写入到远程进程的内存空间 if WriteProcessMemory(hRemoteProcess, pszLibFileRemote, pszLibAFilename, memSize, WriteSize) and (WriteSize = memSize) then begin lpThreadId := 0; // 计算LoadLibraryW的入口地址 pfnStartAddr := GetProcAddress(LoadLibrary('Kernel32.dll'), 'LoadLibraryW'); // 启动远程线程LoadLbraryW,通过远程线程调用创建新的线程 hRemoteThread := CreateRemoteThread(hRemoteProcess, nil, 0, pfnStartAddr, pszLibFileRemote, 0, lpThreadId); // 如果执行成功返回 True; if (hRemoteThread 0) then Result := true; // 释放句柄 CloseHandle(hRemoteThread); end; end; finally // 释放句柄 CloseHandle(hRemoteProcess); end; end; end; 接下来要说的是如何卸载注入目标进程中的DLL,其实原理和注入DLL是完全相同的,只是远程调用调用的函数不同而已,这里要调用的是FreeLibrary,代码如下: function UnInjectDll(const DllFullPath: string; const dwRemoteProcessId: Cardinal): Boolean; // 进程注入和取消注入其实都差不多,只是运行的函数不同而已 var hRemoteProcess, hRemoteThread: THandle; pszLibFileRemote: PChar; pszLibAFilename: PwideChar; pfnStartAddr: TFNThreadStartRoutine; memSize, WriteSize, lpThreadId, dwHandle: Cardinal; begin Result := false; // 调整权限,使程序可以访问其他进程的内存空间 if EnableDebugPriv then begin //打开远程线程 PROCESS_ALL_ACCESS 参数表示打开所有的权限 hRemoteProcess := OpenProcess(PROCESS_ALL_ACCESS, false, dwRemoteProcessId); try // 为注入dll文件路径分配内存大小,由于为WideChar,故要乘2 GetMem(pszLibAFilename, Length(DllFullPath) * 2 + 1); // 之所以要转换成 WideChar, 是因为当DLL位于有中文字符的路径下时不会出错 StringToWideChar(DllFullPath, 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(pszLibFileRemote) then begin //使用WriteProcessMemory函数将DLL的路径名写入到远程进程的内存空间 if WriteProcessMemory(hRemoteProcess, pszLibFileRemote, pszLibAFilename, memSize, WriteSize) and (WriteSize = memSize) then begin // 计算GetModuleHandleW的入口地址 pfnStartAddr := GetProcAddress(LoadLibrary('Kernel32.dll'), 'GetModuleHandleW'); //使目标进程调用GetModuleHandleW,获得DLL在目标进程中的句柄 hRemoteThread := CreateRemoteThread(hRemoteProcess, nil, 0, pfnStartAddr, pszLibFileRemote, 0, lpThreadId); // 等待GetModuleHandle运行完毕 WaitForSingleObject(hRemoteThread, INFINITE); // 获得GetModuleHandle的返回值,存在dwHandle变量中 GetExitCodeThread(hRemoteThread, dwHandle); // 计算FreeLibrary的入口地址 pfnStartAddr := GetProcAddress(LoadLibrary('Kernel32.dll'), 'FreeLibrary'); // 使目标进程调用FreeLibrary,卸载DLL hRemoteThread := CreateRemoteThread(hRemoteProcess, nil, 0, pfnStartAddr, Pointer(dwHandle), 0, lpThreadId); // 等待FreeLibrary卸载完毕 WaitForSingleObject(hRemoteThread, INFINITE); // 如果执行成功返回 True; if hRemoteProcess 0 then Result := true; // 释放目标进程中申请的空间 VirtualFreeEx(hRemoteProcess, pszLibFileRemote, Length(DllFullPath) + 1, MEM_DECOMMIT); // 释放句柄 CloseHandle(hRemoteThread); end; end; finally // 释放句柄 CloseHandle(hRemoteProcess); end; end; end;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值