注册半年有余也潜水半年有余。今天发一个原创处女贴,希望大家顶一下。
前段时间处理EXE文件进程隐藏的问题,在网上看到过很多方法,都不太满意。最终决定自己写一个,主体思路是代码重定位和线程的注入。首先,介绍一下我参考过的一些方法。
如果单纯从隐藏的角度来说,罗云彬的《Windows环境下32位汇编语言程序设计》中的方法基本可以说完美。然而从开发角度来看却有以下几个问题:
1.  他没有进行数据的重定位,他的代码中也不包含全局变量。
2.  所有需要的API全部手动写进去。比较繁琐。
3.  其代码重定位采用注入后的代码实现:
call  @F
@@:
pop  ebx
这种方式的重定位只能采用汇编语言实现。
而其他的进程隐藏方式,总结起来有以下几种;
1.  Windows 钩子把你的DLL映射到目标进程。
2.  把你的代码放到一个DLL中,然后放入其他进程空间,用 CreateRemoteThread 和 LoadLibrary 让它跑起来
3.  直接把你的代码注入目标进程。
另外,Shoooo的文章“野猪的力量注入”的方法非常的独到,把Ollydbg.exe文件改成DLL来跑(OD中有重定位数据)。
不过通过DLL实现进程隐藏可以通过查看进程加载的模块来发现,并没有实现完全的隐藏。而注入代码段则无法实现复杂功能(有耐性慢慢磨的除外)。
我在这里介绍一种思路,可以实现一种对自身或其他整个EXE的完全注入。(本文以自身注入为例)
如果一个EXE想要在别的进程运行自己,所面临的问题主要有下面几个。
1.  数据重定位。因为EXE文件的运行基地址默认都是一样的,在你的程序中CALL 全局变量在其他进程根本就是错误的。
2.  输入函数地址。在别的进程,不能保证你所需要的DLL文件已经被载入,即使被载入了。其地址也未必正确。所以必须对每个输入函数地址进行获取。
3.  资源的使用。因为Windows在资源的时候会使用:
HRSRC FindResource
(
HMODULE hModule,
LPCTSTR lpName,
LPCTSTR lpType
);
注意第一个参数,你的程序采用GetModuleHandle(0)传递参数时必然会发生错误,因为你的起始地址不是0x00400000。当然,你可以不使用这个函数。但你不能确保别的程序不使用,当你想将某个含有GetModuleHandle(0)代码的EXE程序注入其他进程的时候就必须进行处理。
4.  ExitProcess > ExitThread。
具体方法如下:
首先,你的EXE程序需要有2个入口(本人比较懒,不喜欢用复杂的判断,还是用2个入口方便些)。第一个入口是程序OEP,这个不必多说。第二个入口是你自定义的函数。用来获取你程序输入表中函数的地址。而第二个入口建议使用导出表来实现。因为使用偏移等方式每次更改代码编译都会变,不太方便。(注:VC中EXE需要导出的函数只要在前面加上__declspec(dllexport)即可,和DLL文件没什么区别,只是不能重定位和调用的时候不能使用LoadLibrary而已。)
其次,你的程序需要有重定位表,而有些编译器生成的EXE文件中是没有重定位表的。你需要强制编译器生成,否则无法实现数据的重定位。以VC为例子,加入#pragma  comment(linker,"/fixed:no")即可。对于没有重定位表的EXE,我想通过判断E8,E9后的地址并修正应该也可以实现。不过我没有动手测试。
函数的流程主要如下:
1.  读取你自己,按照PE文件在内存中的方式展开。
2.  提升程序权限以确保具有注入的权限。主要用到以下几个函数:OpenProcess,LookupPrivilegeValue,OpenProcessToken,AdjustTokenPrivileges。
3.  打开你需要注入的进程,分配大小和你展开PE文件大小的内存。获取基地址指针A
4.  通过指针A对你读取的代码实施重定位(处理重定位表)。
5.  获取本程序kernel32.dll中GetProcAddress和LoadLibrary的地址(所有进程中位置一样),《Windows环境下32位汇编语言程序设计》中有动态获取API地址的方法,怕不兼容可以采用。我比较懒,没有采用。
6.  获取你的第二个入口的地址偏移(通过输出函数偏移加上指针A)。
7.  将处理过的内存数据写入目标进程。
8.  将GetProcAddress和LoadLibrary地址和基址(指针A)作为参数来CreateRemoteThread,函数是你的输出函数。
9.  收尾工作,退出程序。
现在放入别的进程的目标代码已经重定位好了。但是输入表函数地址确还是原来的。这个就需要你的入口函数进行处理了。因为LoadLibrary和GetProcAddress地址以及PE文件的基地址已经被作为参数传了进来,现在你要做的就是通过基地址定位输入表,再遍历你的输入表载入所有的DLL并将函数地址填入。
现在你的程序的运行环境已经修正完毕。接着你就可以执行你所要执行的代码了,与正常EXE文件没有什么区别。而这个程序完全是内存拷贝,不会再读取原EXE,如果写病毒的话可以删除原EXE并在你设定的情况下重组以躲避查杀。呵呵。
再来说一下资源的问题。
由于资源都是采用相对地址偏移来存储数据,所以没必要重定位。使用资源主要在于传入参数的地址。创建对话框的时候你可以直接吧你传入的基地址作为首参数来创建,或者使用GetModuleHandle来获取地址,只要你将GetModuleHandle的地址改为自己的函数,然后进行判断参数是否是O,如果是O则返回基地址,不然再调用GetModuleHandle。
不过有个问题我一直还没解决。普通的对话框或窗口资源都没有问题,然而Child类型的无边框对话框却无法创建成功。(我用TAB控件时发现的)
这种思路我在WIN XP下测试通过。
注意:你的程序结束的时候会调用ExitProcess,会把宿主进程给Kill掉。因此你需要把ExitProcess的地址改为ExitThread。
同样的方法用在DLL文件中也一样,但是缺陷是DLL文件不能单独运行,必须编写一个程序负责载入dll。
这种方式的进程隐藏,避免了以上几种问题,采用C、C++语言编写即可。封装起来也实现了代码的可重复利用。
附:
由于水平有限,本文不足和遗漏之处请大家批评指正。