您的位置:首页 > 其它

七. 让EXE导出函数及DLL木马

2010-04-24 13:36 169 查看
偶然发现OllyDBG.exe导出了一堆函数,这些函数都是供其插件调用的。对这种体系结构很感
兴趣,想弄清楚它的实现原理。后来又看到梁肇新的书《编程高手箴言》第278页提到的调用
门,觉得都应该差不多。

三种不同的解决办法(原理可能是一样的,:)):

1)在导出函数声明之前加上__declspec(dllexport)。例:
__declspec(dllexport) int Add(int a, int b);
__declspec(dllexport) int Sub(int a, int b);
__declspec(dllexport) int Mul(int a, int b);
__declspec(dllexport) int Div(int a, int b);

2)在链接器参数中设置。例:
#pragma comment(linker, "/EXPORT:_Add,@1,NONAME")
#pragma comment(linker, "/EXPORT:_Sub,@2,NONAME")
#pragma comment(linker, "/EXPORT:_Mul,@3,NONAME")
#pragma comment(linker, "/EXPORT:_Div,@4,NONAME")

3)添加一个def文件,例:
EXPORTS
Add @1 NONAME
Sub @2 NONAME
Mul @3 NONAME
Div @4 NONAME
另需要在链接器命令行参数中指定def文件名: /DEF:Callee.def
注意:在def文件中不要有 LIBRARY [library][BASE=address] 这样的语句。

相比较而言,后两种方法可以设置更多的参数。

函数举例:

extern "C"
{
/*这应在.h吧
__declspec(dllexport) int Add(int a, int b);
__declspec(dllexport) int Sub(int a, int b);
__declspec(dllexport) int Mul(int a, int b);
__declspec(dllexport) int Div(int a, int b);
*/

int Add(int a, int b)
{
return (a + b);
}

int Sub(int a, int b)
{
return (a - b);
}

int Mul(int a, int b)
{
return (a * b);
}

int Div(int a, int b)
{
if (b == 0)
return 0;
else
return (a / b);
}

}

编译时会自动生成相应的导出库(lib)文件,供调用者使用。

调用方法和普通的动态链接库调用一样。
调用者必须能够找到被调用者的位置,否则报错,被调用者是否运行不影响。

调用代码举例:

extern "C"
{
int Add(int a, int b);
int Sub(int a, int b);
int Mul(int a, int b);
int Div(int a, int b);
}

#pragma comment (lib, "Callee.lib")

void CCallerDlg::OnBnClickedCalculate()
{
// TODO: Add your control notification handler code here
UpdateData(TRUE);

switch (((CComboBox *)GetDlgItem(IDC_COMBO_OPERATOR))->GetCurSel())
{
case ADD:
{
m_iResult = Add(m_iNum1, m_iNum2);
break;
}
case SUB:
{
m_iResult = Sub(m_iNum1, m_iNum2);
break;
}
...
...

我在OD中跟了一下,发现这跟调用动态链接库也差不多。
不过那几个函数被映射到下面的地址处:

003810F0 > 8B4424 08 mov eax, dword ptr [esp+8]
003810F4 8B4C24 04 mov ecx, dword ptr [esp+4]
003810F8 03C1 add eax, ecx
003810FA C3 retn
003810FB CC int3
003810FC CC int3
003810FD CC int3
003810FE CC int3
003810FF CC int3

跟常规的动态链接库被映射到高地址处略有不同。
还不知道是什么原因。

结论:
EXE完全可以和DLL一样导出函数,一样被调用。

进一步的工作:
我发现这个例子跟OllyDbg.exe还是有些不同,跟“调用门”的说法也有不同。这里实际上还是
跟DLL差不多的原理。下一步争取实现一个跟OllyDbg.exe差不多的例子。

从前文可知,DLL在程序编制中可作出巨大贡献,它提供了具共性代码的复用能力。但是,正如一门高深的武学,若被掌握在正义之侠的手上,便可助其仗义江湖;但若被掌握在邪恶之徒的手上,则必然在江湖上掀起腥风血雨。DLL正是一种这样的武学。DLL一旦染上了魔性,就不再是正常的DLL程序,而是DLL木马,一种恶贯满盈的病毒,令特洛伊一夜之间国破家亡。

  DLL木马的原理

  DLL木马的实现原理是编程者在DLL中包含木马程序代码,随后在目标主机中选择特定目标进程,以某种方式强行指定该进程调用包含木马程序的DLL,最终达到侵袭目标系统的目的。

  正是DLL程序自身的特点决定了以这种形式加载木马不仅可行,而且具有良好的隐藏性:

  (1)DLL程序被映射到宿主进程的地址空间中,它能够共享宿主进程的资源,并根据宿主进程在目标主机的级别非法访问相应的系统资源;

  (2)DLL程序没有独立的进程地址空间,从而可以避免在目标主机中留下"蛛丝马迹",达到隐蔽自身的目的。

  DLL木马实现了"真隐藏",我们在任务管理器中看不到木马"进程",它完全溶进了系统的内核。与"真隐藏"对应的是"假隐藏","假隐藏"木马把自己注册成为一个服务。虽然在任务管理器中也看不到这个进程,但是"假隐藏"木马本质上还具备独立的进程空间。"假隐藏"只适用于Windows9x的系统,对于基于WINNT的操作系统,通过服务管理器,我们可以发现系统中注册过的服务。

  DLL木马注入其它进程的方法为远程线程插入。

  远程线程插入技术指的是通过在另一个进程中创建远程线程的方法进入那个进程的内存地址空间。将木马程序以DLL的形式实现后,需要使用插入到目标进程中的远程线程将该木马DLL插入到目标进程的地址空间,即利用该线程通过调用Windows API LoadLibrary函数来加载木马DLL,从而实现木马对系统的侵害。

  DLL木马注入程序

  这里涉及到一个非常重要的Windows API――CreateRemoteThread。与之相比,我们所习惯使用的CreateThread API函数只能在进程自身内部产生一个新的线程,而且被创建的新线程与主线程共享地址空间和其他资源。而CreateRemoteThread则不同,它可以在另外的进程中产生线程!CreateRemoteThread有如下特点:

  (1)CreateRemoteThread较CreateThread多一个参数hProcess,该参数用于指定要创建线程的远程进程,其函数原型为:

HANDLE CreateRemoteThread(
 HANDLE hProcess, //远程进程句柄
 LPSECURITY_ATTRIBUTES lpThreadAttributes,
 SIZE_T dwStackSize,
 LPTHREAD_START_ROUTINE lpStartAddress,
 LPVOID lpParameter,
 DWORD dwCreationFlags,
 LPDWORD lpThreadId
);
  (2)线程函数的代码不能位于我们用来注入DLL木马的进程所在的地址空间中。也就是说,我们不能想当然地自己写一个函数,并把这个函数作为远程线程的入口函数;

  (3)不能把本进程的指针作为CreateRemoteThread的参数,因为本进程的内存空间与远程进程的不一样。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: