吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1598|回复: 2
上一主题 下一主题
收起左侧

[会员申请] iMonitorSDK

[复制链接]
跳转到指定楼层
楼主
吾爱游客  发表于 2023-7-11 13:37 回帖奖励 |自己
1、申 请 I D : iMonitorSDK2、个人邮箱: admin@iMonitorSDK.com

DLL挟持原理、分析、应用动态库基础很多程序,基本都不是仅仅程序本身就可以直接运行,经常会依赖到其他的动态库(Dynamic Link Library)。比如最基本的ntdll.dll、kernel32.dll。程序对动态库的依赖分为两种:
  • 静态依赖一般程序在编译的时候会依赖一个lib,编译后在程序的导入表就会生成相应的依赖信息
  • 动态加载指通过LoadLibrary、LdrLoadDll的方式进行手动加载动态库的方式
在一个DLL的加载过程,默认会通过以下的目录进行查找:
  • 程序所在目录
  • 程序设置的加载目录(SetCurrentDirectory)
  • 系统目录
    • System32目录
    • Windows目录
  • PATH环境变量的目录
更多的可以参考LoadLibrary的API说明。DLL挟持的原理为什么会造成DLL挟持,正是因为DLL的加载过程会搜索不同的路径导致。比如搜索的路径依次A、B、C,而需要的DLL在C位置,如果有恶意的软件把相同名字的DLL放在了A目录,就会导致优先加载A目录的DLL,从而导致DLL挟持的发生。几乎所有的程序都会存在DLL挟持的风险。比如古老的LPK病毒就是利用了这个原理。KnownDLLs系统为了解决这个问题,同时加快系统动态库的加载效率,会建立一份信任列表KnownDLLs,在这份列表的DLL,会直接从已经加载的镜像里面映射一份,不需要重新经过查找、加载的过程。KnownDLLs注册表项的路径为 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs。(针对KnownDlls的攻击本文不介绍,有兴趣的可以自行了解)但是仍然有很多其他的DLL没有在这个列表里面,从而还是会存在挟持的风险。DLL挟持可能带来的风险
  • 程序的完整性会被破坏
  • 程序的逻辑会被篡改
  • 程序信任关系被盗用比如利用白 + 黑的病毒、绕过某些安全软件的信任逻辑、强制结束带自保护的软件
而且DLL挟持是没法简单通过数字签名来得到解决的。DLL挟持分析使用iMonitor(冰镜)的最新版本,可以直接分析出哪些程序存在DLL挟持(包括静态依赖、动态加载)的风险。DLL挟持应用如果已经知道一个程序存在DLL挟持,一般是怎么应用的呢?这个具体得挟持的目的是什么,常见的有下面的几种方式。拷贝DLL到程序目录的方式比如,基本的程序都会依赖version.dll(其他的DLL都可以),而version.dll是系统的DLL,但是加载搜索的过程,优先级最高是程序的当前目录,那么就可以自己制作一个假的version.dll(为了功能可以,需要同时把调用映射回真实的version.dll,如果不会操作,后续可以提供对应的工具),然后放到程序的目录下面,这样程序启动就会自动加载这个假的DLL。在这个DLL里面可以做一些Hook操作,这样就可以篡改程序的逻辑,比如修改日期绕过试用期达到破解的目的。
之前还有一些奇葩的安全厂商,提交了针对某会议、某聊天工具的所谓漏洞,竟然是利用DLL挟持来实现。DLL挟持是几乎无法避免的,这样说的话所有软件都会存在漏洞了。
这种方式的DLL挟持,其实比较容易添加一些保护。比如在程序的目录添加自保护(如果需要自保护功能,可以接入iMonitorSDK快速实现),防止其他人拷贝恶意DLL进来,同时可以添加一个对当前目录DLL的扫描,删除不是自己的其他DLL。白 + 黑的方式拷贝一个存在DLL挟持的存在信任签名的程序,然后制作一个可以被他加载的DLL放到一起,这样启动这个程序的时候,就会自动加载这个DLL了。因为这个程序存在信任签名,这样就可以绕过一些安全软件的检测,从而提高权限、或者执行恶意逻辑。比如:某知名安全软件的自保护控制,只允许自己的固定一个进程来操作,驱动会校验通信的进程名,进程的签名是否有效,这样只要把这个信任的进程拷贝出来,然后跟关闭自保护的恶意DLL放一起,就可以轻松关闭自保护。拷贝DLL到PATH目录的方式上面的方式都是主动发起挟持注入DLL的,达到利用条件的前提是具备程序目录的文件拷贝权限、或者执行的时候已经不是完整的程序了。另外一种挟持方式是程序依赖的DLL在程序的目录根本没有,而且不是系统DLL。这种在搜索DLL的过程,就会找到PATH目录,但是因为很多软件可能会修改PATH目录,导致里面的条目非常多,这样就会从很多位置尝试去加载DLL。如果存在一个低权限的PATH目录,那么拷贝一个DLL过去,会到导致程序启动的时候自动加载这个DLL,恶意DLL就有可能达到提权的目的,从而做出更多恶意行为。建议在软件发布前,都分析程序中是否存在DLL丢失的情况,只要存在DLL丢失,就会潜在一个很大的挟持风险。红蓝对抗中,很多是通过这样的方式来达到提权的目的的。如果因为某一款软件的漏洞引发系统被入侵,这款软件可能会被抛弃的,特别是对等保要求比较高的企业。
DLL挟持模块的生成原理程序对一个模块有静态依赖和动态依赖两种。静态依赖 编译的时候,会把依赖的模块信息写入到PE结构的导入表中。加载的时候,会根据导入表,加载对应的模块,然后从该模块的导出表检索函数,修复PE映像的导入函数调用地址,这样调用函数的时候就会跳转模块里面执行。动态依赖程序对模块不会有直接依赖的关系,而是执行过程中,通过LoadLibrary + GetProcAddress的方式加载模块,然后获取需要调用的函数去执行。挟持模块无论是哪种依赖关系,制作一个挟持模块去替换原始的模块,如果要保证程序运行正常,挟持模块需要提供程序所依赖的全部函数,通常挟持模块会导出被挟持模块的全部导出函数。怎么查看一个模块的导出函数呢?可以通过dumpbin查看,也可以通过其他的PE工具。下面的通过dumpbin查看version.dll的导入函数的命令。dumpbin /exports C:\windows\system32\version.dll
​
Dump of file C:\windows\system32\version.dll
​
File Type: DLL
​
  Section contains the following exports for VERSION.dll
​
    00000000 characteristics
    90A1B58A time date stamp
        0.00 version
           1 ordinal base
          17 number of functions
          17 number of names
​
    ordinal hint RVA      name
​
          1    0 00001EE0 GetFileVersionInfoA
          2    1 000023E0 GetFileVersionInfoByHandle
          3    2 00001F00 GetFileVersionInfoExA
          4    3 00001070 GetFileVersionInfoExW
          5    4 00001010 GetFileVersionInfoSizeA
          6    5 00001F20 GetFileVersionInfoSizeExA
          7    6 00001090 GetFileVersionInfoSizeExW
          8    7 000010B0 GetFileVersionInfoSizeW
          9    8 000010D0 GetFileVersionInfoW
         10    9 00001F40 VerFindFileA
         11    A 000025A0 VerFindFileW
         12    B 00001F60 VerInstallFileA
         13    C 00003390 VerInstallFileW
         14    D          VerLanguageNameA (forwarded to KERNEL32.VerLanguageNameA)
         15    E          VerLanguageNameW (forwarded to KERNEL32.VerLanguageNameW)
         16    F 00001030 VerQueryValueA
         17   10 00001050 VerQueryValueW知道所有的导出函数了,那么下一步就是实现这些函数。大部分场景,我们并不关心这些函数具体的功能,也没有必要完整实现这些函数,只需要把这些函数映射到原来模块的函数就好了。方式一:使用导出表重定向的方式(比如上面的:VerLanguageNameA (forwarded to KERNEL32.VerLanguageNameA) )VC提供#pragma的编译指令,可以手动导出一个指向其他模块的函数。比如:导出GetFileVersionInfoA实际是指定到original_version.dll里面的GetFileVersionInfoA#pragma comment(linker,"/export:GetFileVersionInfoA=original_version.GetFileVersionInfoA")这种方式在替换模块的时候,存在一个问题,就是引入了一个新的模块依赖(原始模块),需要从原始模块拷贝一份重命名后放在一起才能正常加载。这种方式操作会比较麻烦,因为没法提前把原始模块打包在一起(每个系统可能不一样),而是需要在目标机器上手动拷贝一份。方式二:通过动态依赖原始模块,然后GetProcAddress + JMP的方式。// 初始化的时候,获取到全部需要的原始函数
// original_GetFileVersionInfoA = GetProcAddress(original_version, "GetFileVersionInfoA");
​
__declspec(naked) GetFileVersionInfoA
{
    __asm jmp original_GetFileVersionInfoA;
}但是在x64编译器上,不支持直接内联汇编的方式。为了通用,直接去掉了手动编写函数,而是使用编辑字节码的方式。#ifdef _M_IX86
​
struct JMPStub
{
    //
    // jmp address
    //
    BYTE JMP;
    ULONG Address;
};
​
#else
​
struct JMPStub
{
    //
    // mov rax, address
    // jmp rax
    //
    USHORT MOVRax;
    ULONGLONG Address;
    USHORT JMPRax;
};
​
#endif完整代码#pragma pack(push, 1)
//******************************************************************************
#ifdef _M_IX86
//******************************************************************************
struct JMPStub
{
    BYTE JMP;
    ULONG Address;
​
    bool Hook(HMODULE module, const char* Name)
    {
        PVOID addr = GetProcAddress(module, Name);
​
        if (addr == NULL)
            return false;
​
        JMP = 0xE9;
        Address = (ULONG)addr - (ULONG)this - 5;
​
        return true;
    }
};
//******************************************************************************
#else
//******************************************************************************
struct JMPStub
{
    USHORT MOVRax;
    ULONGLONG Address;
    USHORT JMPRax;
​
    bool Hook(HMODULE module, const char* Name)
    {
        PVOID addr = GetProcAddress(module, Name);
​
        if (addr == NULL)
            return false;
​
        MOVRax = 0xB848;
        Address = (ULONGLONG)addr;
        JMPRax = 0xE0FF;
​
        return true;
    }
};
//******************************************************************************
#endif
//******************************************************************************
#pragma pack(pop)
//******************************************************************************
#pragma data_seg(".jmp")
__declspec(selectany) HMODULE g_jmp_module = NULL;
#pragma data_seg()
#pragma comment(linker, "/SECTION:.jmp,RWE")
//******************************************************************************
class JMPSetModule
{
public:
    JMPSetModule(LPCTSTR Name)
    {
        //
        // 默认替换系统的镜像,如果需要其他的可以自行修改
        //
        TCHAR path[MAX_PATH] = {};
        GetWindowsDirectory(path, MAX_PATH);
        PathAppend(path, Name);
        g_jmp_module = LoadLibrary(path);
    }
};
//******************************************************************************
class JMPSetHook
{
public:
    JMPSetHook(JMPStub& stub, const char* Name)
    {
        stub.Hook(g_jmp_module, Name);
    }
};
//******************************************************************************
#define CONCAT_RAW_(a, b) a##b
#define CONCAT_(a, b)     CONCAT_RAW_(a, b)
//******************************************************************************
#define BEGIN_EXPORT_MAP(module) JMPSetModule CONCAT_(module_, __COUNTER__)(_T(module));
#define EXPORT_MAP(name)                                                                 \
    namespace JMPStubPrivate                                                             \
    {                                                                                    \
        extern "C" __declspec(dllexport) __declspec(allocate(".jmp")) JMPStub name = {}; \
        JMPSetHook sethook_##name(name, #name);                                          \
    };
#define END_EXPORT_MAP()
//******************************************************************************最终使用效果添加下面的代码,编译后就可以自动生成一个替换version.dll的挟持模块。BEGIN_EXPORT_MAP("system32\\version.dll")
    EXPORT_MAP(GetFileVersionInfoA)
    EXPORT_MAP(GetFileVersionInfoW)
    EXPORT_MAP(GetFileVersionInfoByHandle)
    EXPORT_MAP(GetFileVersionInfoExA)
    EXPORT_MAP(GetFileVersionInfoExW)
    EXPORT_MAP(GetFileVersionInfoSizeA)
    EXPORT_MAP(GetFileVersionInfoSizeW)
    EXPORT_MAP(GetFileVersionInfoSizeExA)
    EXPORT_MAP(GetFileVersionInfoSizeExW)
    EXPORT_MAP(VerFindFileA)
    EXPORT_MAP(VerFindFileW)
    EXPORT_MAP(VerInstallFileA)
    EXPORT_MAP(VerInstallFileW)
    EXPORT_MAP(VerLanguageNameA)
    EXPORT_MAP(VerLanguageNameW)
    EXPORT_MAP(VerQueryValueA)
    EXPORT_MAP(VerQueryValueW)
END_EXPORT_MAP()

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

沙发
Hmily 发表于 2023-7-11 17:21
抱歉,未能达到申请要求,申请不通过,可以关注论坛官方微信(吾爱破解论坛),等待开放注册通知。

ps:DLL劫持是可以防护的,参见:
如何禁用软件DLL劫持(Disable DLL Hijacking)
https://www.52pojie.cn/thread-1499316-1-1.html
3#
mokson 发表于 2023-7-11 17:32

本版积分规则

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-5-3 12:54

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表