标签:
有了第一种方法,我们不难举一反三,由于原理是只需要我们的执行函数被写入到目标进程,所有相关的变量也在目标函数里,那么我们的函数就能被正确执行(前提是没有互相调用我们注入的函数,否则需要做地址处理。这也是为什么整个可执行文件注入时,要做很多重定位)。这里,我们进一步尝试,将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注入方法之一“一样,注意字符串问题)

按下确定后,会继续再弹出一个窗口

标签:
原文地址:http://www.cnblogs.com/dalgleish/p/5438982.html