有了第一种方法,我们不难举一反三,由于原理是只需要我们的执行函数被写入到目标进程,所有相关的变量也在目标函数里,那么我们的函数就能被正确执行(前提是没有互相调用我们注入的函数,否则需要做地址处理。这也是为什么整个可执行文件注入时,要做很多重定位)。这里,我们进一步尝试,将LoadLibrary放在一个函数里,在同一个函数里,我们再调用一个MessageBox(这里只是一个测试,以后你可以调用任何你的函数,但必须要对地址进行处理)。
步骤一和步骤二:请参考Dll注入方法之一。
步骤三:书写需要被注入到目标进程的函数,这里我们先从最简单的naked函数开始,以汇编的形式注入。
#define PARAM_START 0x80000000 #define pfn_LoadLibraryA PARAM_START+1 #define pfn_MessageBoxA PARAM_START+2 #define szLibrary PARAM_START+3 #define szMsg PARAM_START+4 //定义上面这些唯一标识符,就像资源ID一样具有独特性,方便之后再缓冲中对地址进行修改 void __declspec(naked) YourFunction() { __asm { push szLibrary mov eax, pfn_LoadLibraryA call eax //LoadLibraryA(szLibrary) push MB_OK push NULL push szMsg push NULL mov eax, pfn_MessageBoxA call eax //MessageBoxA(NULL,szMsg,NULL,MB_OK) retn //由于远程线程是创建的函数,所以需要构造返回指令让堆栈平衡 } }
步骤四:写一个函数代码段计算函数,为了计算YourFunction()这个函数的大小
DWORD CodeSize(DWORD pfn_jmp) { byte *lpStartAddr; if (*(byte *)pfn_jmp==0xe9) lpStartAddr=(byte *)(*(DWORD *)(pfn_jmp+0x1)+pfn_jmp+0x5); else lpStartAddr=(byte *)pfn_jmp;//判断是否近跳还是远跳,至于为什么这样,各位要用ollydbg去看看,编译器在生成这些汇编代码时,是什么样的 //由于篇幅有限,这里就不说反汇编基础了,自己去补充 byte *StartCode=lpStartAddr;//处理jmp这条指令后得到的函数地址 while (*lpStartAddr!=0xc3) //计算以retn指令结尾的函数 { if (*lpStartAddr==0xc2 && *(lpStartAddr+1)==0x4)//由于默认只传入一个参数,并判断是否以ret4指令结尾的函数 { lpStartAddr+=2; break; } lpStartAddr++; } return (DWORD)(lpStartAddr+1-StartCode);//返回函数字节大小 }
步骤五:写一个YourFunction函数的执行代码复制到缓存区的函数
byte *BufferCode(DWORD pfn_jmp) { codeSize=CodeSize(pfn_jmp);//步骤四的函数 bufferCode=(byte *)malloc(codeSize);//为缓存区分配大小 assert(bufferCode!=NULL); byte *lpStartAddr; if (*(byte *)pfn_jmp==0xe9) lpStartAddr=(byte *)(*(DWORD *)(pfn_jmp+0x1)+pfn_jmp+0x5); else lpStartAddr=(byte *)pfn_jmp; RtlCopyMemory(bufferCode,lpStartAddr,codeSize); //由于是nacked函数,所以不用对堆栈平衡进行处理,但第三种dll注入方法必须处理! return bufferCode; }
步骤六:注入代码
BOOL ReplaceParamAddr(byte *buffer,DWORD dwOld,DWORD dwNew) {//这个函数是通过在buffer这个缓冲区搜索特征码,然后替换成新的数据(为了对地址进行调整) for (DWORD i=0;i<codeSize;i++) { if (*(DWORD *)&buffer[i]==dwOld) { *(DWORD *)&buffer[i]=dwNew; return TRUE; } } return FALSE; }
开始我们的注入代码
BOOL InjectDll() { BOOL iRet=-1; HANDLE hRemoteProcess=OpenProcess(PROCESS_CREATE_THREAD|PROCESS_VM_OPERATION|PROCESS_VM_WRITE, FALSE,GetProcessID(szInjectedExeFileName));//打开目标进程 assert(hRemoteProcess!=INVALID_HANDLE_VALUE); BufferCode((DWORD)YourFunction);//步骤五的函数 ReplaceParamAddr(bufferCode,pfn_LoadLibraryA,(DWORD)LoadLibraryA);//步骤六的函数,将唯一标识符替换为真实地址 ReplaceParamAddr(bufferCode,pfn_MessageBoxA,(DWORD)MessageBoxA); //写入字符串到目标进程 const char *szParameter="This is a Parameter!"; PVOID lpString=VirtualAllocEx(hRemoteProcess,NULL,strlen(szDll)+strlen(szParameter)+2, MEM_COMMIT, PAGE_READWRITE); assert(lpString!=NULL); WriteProcessMemory(hRemoteProcess,lpString,(LPVOID)szDll,strlen(szDll)+1,NULL); WriteProcessMemory(hRemoteProcess,(LPVOID)((DWORD)lpString+strlen(szDll)+1), (LPVOID)szParameter, strlen(szParameter)+1,NULL); ReplaceParamAddr(bufferCode,szLibrary,(DWORD)lpString);//同理,替换为字符串在目标进程中的地址 ReplaceParamAddr(bufferCode,szMsg,(DWORD)lpString+strlen(szDll)+1); //写入执行代码到目标进程 PVOID lpBaseAddr=VirtualAllocEx(hRemoteProcess,NULL,codeSize,MEM_COMMIT, PAGE_EXECUTE_READWRITE); assert(lpBaseAddr!=NULL); iRet=WriteProcessMemory(hRemoteProcess,lpBaseAddr,(LPVOID)bufferCode,codeSize,NULL); assert(iRet!=0); CreateRemoteThread(hRemoteProcess, NULL, NULL, (LPTHREAD_START_ROUTINE)lpBaseAddr, 0, NULL, NULL); return iRet; }
xp下测试效果图(win10同样测试通过,理由和”Dll注入方法之一“一样,注意字符串问题)
按下确定后,会继续再弹出一个窗口
时间: 2024-10-11 21:05:55