动态调用无导入表编译

重新编译开源代码绕过杀毒软件

这个是以前发在Tools的文章,不是我偷的!

由于大多数加壳软件并不会修改被修改文件的导入表,所以杀毒软件除了计算整个可执行文件的hash值外还会计算pe文件的导入表(import address tables)的hash值,通常采用的hash算法为MD5,本文旨在使木马文件不再依赖导入表,从而绕过部分静态查杀。

阅读本文前置知识

  1. pe文件结构

  2. windows api

  3. c/c++语言编程基础

敏感api调用

杀软会对iat表中的一些敏感函数进行检查,如CreateRemoteThread,VirtualAlloc等,CreateRemoteThread的功能是在其他进程中创建一个线程,众所周知线程就是实际的执行体,那么我们的这个行为就会被杀软关注并检查,那么我们如何解决这个问题呢?

确定APi函数

在我们尝试规避检测之前我们需要先确定哪一些是API函数,比如下面这个代码:

#include<stdio.h>
#include<Windows.h>

int main()
{
    printf("hello world\n");
    MessageBoxW(0, TEXT("hello world"), 0, 0);
    return 0;
}

如果你不确定哪一个函数是windows的api的话,我们可疑先把他编译出来,然后通过pe查看工具来定位api函数,这里我使用了大家都很熟悉的printf和messageboxW函数。

->Import Table

现在我们确定了messageboxw是windows函数外,还看到很多并非我们指定的函数加载,这是由于在进程初始化前并不单单执行我们定义的代码,还会调用其他的系统函数,至于printf函数至少最外层并非windows api函数,这里不过多讲解。

GetProcAddress函数

在确定api函数后,我们可以通过windows提供的另一个函数来确定其他函数的函数地址,获得地址后,我们可以通过定义函数指针的方式来执行函数。

到这里我们就已经避免了iat表中出现敏感函数,但是如果再极端一点,杀毒软件做了非常严格的限制,以至于LoadLibraryW函数也被监视并限制,在前面我们看到了,哪怕不使用getprocaddress函数,它也会出现在iat表中,我们知道了getprocaddress是存放在kernel32.dll中的,那就存在一个问题,我们如何在不实用LoadLibraryW函数的情况下获取Kernel32的地址。 到这里我们的大致思路已经清晰了。

  1. 获取kernel32.dll 基地址;

  2. 定位 GetProcAddress函数的地址;

  3. 使用GetProcAddress确定 LoadLibrary函数的地址;

  4. 然后使用 LoadLibrary加载DLL文件(例如user32.dll);

  5. 使用 GetProcAddress查找某个函数的地址(例如MessageBox);

  6. 指定函数参数;

  7. 调用函数。

获取kernel32.dll的地址

在windows操作系统中每一个进程系统都维护着一个描述该进程的结构体,我们称之为peb(进程环境块),如可执行文件加载到内存的位置,模块列表(DLL),指示进程是否被调试的标志,不同发行版的windows系统该结构体可能存在着差异,在这个结构体里就维护者一个描述所有载入模块的链表(InMemoryOrderModuleList),无论我们是否使用,系统都会载入kernel32.dll到进程的虚拟地址空间。

InMemoryOrderModuleList链表按照如下次序显示所有已加载模块:

  1. calc.exe (可执行文件)

  2. ntdll.dll

  3. kernel32.dll

查看微软的官方文档有着如下定义:

由于描述过于繁琐,我们采用图例来表示。

因为使用结构体本身使用并不方便,这里使用内嵌汇编的方式来获取kernel32.dll的dllbase。

无导入表,编译危险可执行文件

现在,我们就已经获取了kernel32.dll的模块地址。

接下来我们就可以不在导入表中出现敏感函数并使用它了。

比如这样,或者我们可以下载mimikatz的源码并在开头使用这个方法替换掉所有mimikatz调用的api,这样我们就得到了一个没有导入表的mimikatz。

最后更新于

这有帮助吗?