mmdthash ssdeep vhash tlsh
Quic 全称 quick udp internet connection,“快速 UDP 互联网连接”,(和英文 quick 谐音,简称“快”)是由 google 提出的使用 udp 进行多路并发传输的协议。 一种基于 UDP 的传输协议,用于提升网络加载速度,在弱网环境下表现较好。 Quic 相比现在广泛应用的 http2+tcp+tls 协议有如下优势:
进程注入的方法非常之多,很多与 DLL 注入有关,比如注册表(Image File Execution Options)、DLL 劫持、输入法、COM、LSP 劫持(LayerService Provider,与 winsock 有关)
除了 DLL 注入,还有 shellcode 注入,因为 shellcode 更小,所以 shellcode 的使用也更加多样。
2017 年,在黑客大会上 Eugene Kogan 和 Tal Liberman 又分享了更加隐蔽和特别的方法,比如 Process Doppelganging。
#include <windows.h>
#include <stdio.h>
// 内嵌汇编获取 Kernel32 的地址
__declspec(naked) DWORD getKernel32()
{
__asm
{
mov eax,fs:[30h]
mov eax,[eax+0ch]
mov eax,[eax+14h]
mov eax,[eax]
mov eax,[eax]
mov eax,[eax+10h]
ret
}
}
// 通过 kernel32 基址获取 GetProcAddress 的地址
FARPROC _GetProcAddress(HMODULE hModuleBase)
{
PIMAGE_DOS_HEADER lpDosHeader = (PIMAGE_DOS_HEADER)hModuleBase;
PIMAGE_NT_HEADERS32 lpNtHeader = (PIMAGE_NT_HEADERS)((DWORD)hModuleBase + lpDosHeader->e_lfanew);
if (!lpNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size){
return NULL;
}
if (!lpNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress) {
return NULL;
}
PIMAGE_EXPORT_DIRECTORY lpExports = (PIMAGE_EXPORT_DIRECTORY)((DWORD)hModuleBase +
(DWORD)lpNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
PDWORD lpdwFunName = (PDWORD)((DWORD)hModuleBase + (DWORD)lpExports->AddressOfNames);
PWORD lpword = (PWORD)((DWORD)hModuleBase + (DWORD)lpExports->AddressOfNameOrdinals);
PDWORD lpdwFunAddr = (PDWORD)((DWORD)hModuleBase + (DWORD)lpExports->AddressOfFunctions);
DWORD dwLoop = 0;
FARPROC pRet = NULL;
for (; dwLoop <= lpExports->NumberOfNames - 1; dwLoop++) {
char* pFunName = (char*)(lpdwFunName[dwLoop] + (DWORD)hModuleBase);
if (pFunName[0] == 'G' &&
pFunName[1] == 'e' &&
pFunName[2] == 't' &&
pFunName[3] == 'P' &&
pFunName[4] == 'r' &&
pFunName[5] == 'o' &&
pFunName[6] == 'c' &&
pFunName[7] == 'A' &&
pFunName[8] == 'd' &&
pFunName[9] == 'd' &&
pFunName[10] == 'r' &&
pFunName[11] == 'e' &&
pFunName[12] == 's' &&
pFunName[13] == 's') {
pRet = (FARPROC)(lpdwFunAddr[lpword[dwLoop]] + (DWORD)hModuleBase);
break;
}
}
return pRet;
}
int main()
{
// kernel32.dll 基址的动态获取
HMODULE hLoadLibrary = LoadLibraryA("kernel32.dll");
// 使用内嵌汇编来获取基址
HMODULE _hLoadLibrary = (HMODULE)getKernel32();
// 效果是一样的
printf("LoadLibraryA 动态获取的地址: 0x%x\n", hLoadLibrary);
printf("内嵌汇编获取的地址: 0x%x\n", _hLoadLibrary);
// 声明定义,先转到到原函数定义,然后重新定义
typedef FARPROC(WINAPI *FN_GetProcAddress)(
_In_ HMODULE hModule,
_In_ LPCSTR lpProcName
);
FN_GetProcAddress fn_GetProcAddress;
fn_GetProcAddress = (FN_GetProcAddress)_GetProcAddress(_hLoadLibrary);
printf("动态获取 GetProcAddress 地址: 0x%x\n", fn_GetProcAddress);
printf("内置函数获取: 0x%x\n", GetProcAddress);
return 0;
}
note 扔到 ida 看导出函数发现是 jni 动态注册没找到 getServerApi() 这个方法,接下来使用 unidbg 进行分析 call_jni_onload 后找到偏移地址 0x12795。
在 ida 中 G 跳转到偏移地址。
查看 sub_4DF0 函数 F5 生成伪代码。
note sysenter 中断门和调用门比较类似,也是一种用来提权的东西。其核心理念在于 Windows 对 intel CPU 的一种利用。
整个调用方式最关键的就是通过 sysenter 从 User 层到达 Kernel 层,可以说前面的都是在给这一步做铺垫。
sysenter 叫做快速系统调用,叫快速是因为之前的系统调用不快,在 Pentium II(奔腾 2 代 CPU)之后才有的 sysenter,在其之前是采用的 KiIntSystemCall 函数来处理的。
// 1:设置寄存器
CS = IA32_SYSENTER_CS
SS = IA32_SYSENTER_CS+8
eip = IA32_SYSENTER_EIP
esp = IA32_SYSENTER_ESP
// 2:切换特权级
切换到 0 环特权级,(其实设置了寄存器就是切换了)
// 3:切换 CPU 模式
清楚 eflags 寄存器中的虚拟 8086 模式(VM 标志)
// 4:执行
执行系统例程调用
查壳工具 ExeinfoPE
反调试相关: IsDebuggerPresent IsProcessorFeaturePresent // 判断处理器相关信息
Rembg is a tool to remove images background. https://github.com/danielgatis/rembg
https://bbs.pediy.com/thread-271460.htm DebugPort 是用于调试的一个端口,如果把这个端口清零,常规手段则无法调试我们的进程。
分别以 int21 和 syscall 两种调用方式实现 代码如下 https://bbs.pediy.com/thread-271471.htm
#include "stdafx.h"
#include <Windows.h>
// 读进程内存(中断门调用)
BOOL WINAPI HbgReadProcessMemory_INT(
HANDLE hProcess,
LPCVOID lpBaseAddress,
LPVOID lpBuffer,
DWORD nSize,
LPDWORD lpNumberOfBytesRead)
{
LONG NtStatus;
__asm
{
// 直接模拟 KiIntSystemCall
lea edx,hProcess; // 要求 edx 存储最后入栈的参数
mov eax, 0xBA;
int 0x2E;
mov NtStatus, eax;
}
if (lpNumberOfBytesRead != NULL)
{
*lpNumberOfBytesRead = nSize;
}
// 错误检查
if (NtStatus < 0)
{
return FALSE;
}
return TRUE;
}
// 读进程内存(快速调用)
BOOL WINAPI HbgReadProcessMemory_FAST(
HANDLE hProcess,
LPCVOID lpBaseAddress,
LPVOID lpBuffer,
DWORD nSize,
LPDWORD lpNumberOfBytesRead)
{
LONG NtStatus;
/*
__asm
{
// 模拟 ReadProcessMemory
lea eax,nSize;
push eax;
push nSize;
push lpBuffer;
push lpBaseAddress;
push hProcess;
sub esp, 0x04; // 模拟 ReadProcessMemory 里的 CALL NtReadVirtualMemory
// 模拟 NtReadVirtualMemory
mov eax, 0xBA;
push NtReadVirtualMemoryReturn; // 模拟 NtReadVirtualMemory 函数里的 CALL [0x7FFE0300]
// 模拟 KiFastSystemCall
mov edx, esp;
_emit 0x0F; // sysenter
_emit 0x34;
NtReadVirtualMemoryReturn:
add esp, 0x18; // 模拟 NtReadVirtualMemory 返回到 ReadProcessMemory 时的 RETN 0x14
mov NtStatus, eax;
}
if (lpNumberOfBytesRead != NULL)
{
*lpNumberOfBytesRead = nSize;
}
*/
__asm
{
lea eax,nSize;
push eax;
push nSize;
push lpBuffer;
push lpBaseAddress;
push hProcess;
sub esp, 0x04; // 模拟 ReadProcessMemory 里的 CALL NtReadVirtualMemory
// 模拟 NtReadVirtualMemory
mov eax, 0xBA;
mov edx, 0X7FFE0300 // 不能直接调用内核,间接 call 函数地址来实现
CALL DWORD PTR[EDX]
add esp, 0x18;
mov NtStatus, eax;
}
// 错误检查
if (NtStatus < 0)
{
return FALSE;
}
return TRUE;
}
// 写进程内存(中断门调用)
BOOL WINAPI HbgWriteProcessMemory_INT(
HANDLE hProcess,
LPCVOID lpBaseAddress,
LPVOID lpBuffer,
DWORD nSize,
LPDWORD lpNumberOfBytesWritten)
{
LONG NtStatus;
__asm
{
lea edx, hProcess;
mov eax, 0x115;
int 0x2E;
mov NtStatus, eax;
}
if (lpNumberOfBytesWritten != NULL)
{
*lpNumberOfBytesWritten = nSize;
}
// 错误检查
if (NtStatus < 0)
{
return FALSE;
}
return TRUE;
}
// 写进程内存(快速调用)
BOOL WINAPI HbgWriteProcessMemory_FAST(
HANDLE hProcess,
LPCVOID lpBaseAddress,
LPVOID lpBuffer,
DWORD nSize,
LPDWORD lpNumberOfBytesWritten)
{
LONG NtStatus;
__asm
{
// 模拟 WriteProcessMemory
lea eax,nSize;
push eax;
push nSize;
push lpBuffer;
push lpBaseAddress;
push hProcess;
sub esp, 0x04; // 模拟 WriteProcessMemory 里的 CALL NtWriteVirtualMemory
// 模拟 NtWriteVirtualMemory
mov eax, 0x115;
/*
push NtWriteVirtualMemoryReturn; // 模拟 NtWriteVirtualMemory 函数里的 CALL [0x7FFE0300]
// 模拟 KiFastSystemCall
mov edx, esp;
_emit 0x0F; // sysenter
_emit 0x34;
NtWriteVirtualMemoryReturn:
*/
mov edx, 0X7FFE0300 // 不能直接调用内核,间接 call 函数地址来实现
CALL DWORD PTR[EDX]
add esp, 0x18; // 模拟 NtWriteVirtualMemory 返回到 WriteProcessMemory 时的 RETN 0x14
mov NtStatus, eax;
}
if (lpNumberOfBytesWritten != NULL)
{
*lpNumberOfBytesWritten = nSize;
}
// 错误检查
if (NtStatus < 0)
{
return FALSE;
}
return TRUE;
}
void __declspec(naked) MyReadMem(HANDLE hProcess,
LPVOID addr,
LPVOID buffer,
DWORD len,
LPDWORD lpNumberOfBytesWritten)
{
_asm
{
mov eax, 0BAh
mov edx, 7FFE0300h
call dword ptr[edx]
ret 0x14
}
}
BOOL WINAPI HbgReadProcessMemory_FAST2(
HANDLE hProcess,
LPCVOID lpBaseAddress,
LPVOID lpBuffer,
DWORD nSize,
LPDWORD lpNumberOfBytesRead)
{
LONG NtStatus;
__asm
{
lea eax, nSize;
push eax;
push nSize;
push lpBuffer;
push lpBaseAddress;
push hProcess;
call MyReadMem
}
// 错误检查
if (NtStatus < 0)
{
return FALSE;
}
return TRUE;
}
反射式 DLL 注入实现(ManualMap,手动解析 PE 并映射到目标进程再运行)
一般而言要注入 DLL 到一个目标进程最简单的方法 就是先获取 DLL 文件路径,然后在目标进程分配内存空间将路径写入到目标进程,写入到目标进程后再调用 CreateRemoteThread()/NtCreateThread()/RtlCreateUserThread() 函数来运行 LoadLibraryA/W 函数调用自己的 DLL,这种方法的缺陷也很明显那就是容易被游戏检测到,很容易被游戏拦截,比如 CSGO 最新版就已经有这个限制了。
想要突破 CSGO 的限制注入 DLL 进去,我们可以采用反射式注入的方法(也可以先恢复 CSGOhook 的 api 进行远程线程注入),那么什么是反射式注入呢?又有什么有点呢? 反射式 dll 注入与常规 dll 注入类似,而不同的地方在于反射式 dll 注入技术自己实现了一个 reflective loader() 函数来代替 LoadLibaryA() 函数去加载 dll,示意图如下图所示。蓝色的线表示与用常规 dll 注入相同的步骤,红框中的是 reflective loader() 函数行为,也是下面重点描述的地方。
Reflective loader 实现思路如下:
https://bbs.pediy.com/thread-272569.htm https://github.com/MrXiao7/DllInjector https://github.com/Kerrbty/RemoteLoadDll
VEH + 硬件断点实现无痕 HOOK AddVectoredExceptionHandler 设置 VEH 异常捕获
PVOID WINAPI AddVectoredExceptionHandler(
_In_ ULONG FirstHandler,
_In_ PVECTORED_EXCEPTION_HANDLER VectoredHandler
);
ULONG WINAPI RemoveVectoredExceptionHandler(
_In_ PVOID Handler
);
PVOID WINAPI AddVectoredContinueHandler(
_In_ ULONG FirstHandler,
_In_ PVECTORED_EXCEPTION_HANDLER VectoredHandler
);
ULONG WINAPI RemoveVectoredContinueHandler(
_In_ PVOID Handler
);
注意:由于 CPU 提供的 Dr 寄存器有限,DrxHook 只能下 4 个断点! 注意:由于程序异常会优先被调试器捕获,所以程序必须编译出来后才能生效!
Typora 是一款由 Abner Lee 开发的轻量级 Markdown 编辑器,与其他 Markdown 编辑器不同的是,Typora 没有采用源代码和预览双栏显示的方式,而是采用所见即所得的编辑方式,实现了即时预览的功能,但也可切换至源代码编辑模式。
内存破解 简单说几种思路,由于 main.node 是后加载的模块,所以内存破解有些难度。
可能遇到的问题:对 main.node 或者框架进行完整性校验,更加强大的反调试手段。
note 进行软件漏洞挖掘时,通常有静态分析(staticanalysis)、动态分析(dynamicanalysis)、符号执行(symbolicexecution)、模糊测试(fuzzing)这几种技术手段。
模糊测试不需要人过多的参与,也不像动态分析那样要求分析人员有丰富的知识。简单解释,它就是用大量的输入数据自动去执行程序,从而发现哪些输入能够使程序发生异常,进而分析可能存在的漏洞。当前比较成功的 fuzzer(执行模糊测试的程序)有 AFL、libFuzzer、OSS-Fuzz 等。
用 AFL 来示意一个典型的 Fuzz 过程。
从上面的分析可以看出,k-means 是随机的分配 k 个初始聚类中心。而聚类的结果高度依赖质心的初始化。如果初始聚类中心选的不好,k-means 算法最终会收敛到一个局部最优值,而不是全局最优值。为了解决这个问题,引入了 k-means++ 算法,它的基本思想就是:初始的聚类中心之间的相互距离要尽可能的远。而且在计算过程中,我们通常采取的措施是进行不止一次的聚类,每次都初始化不同的中心,以 inertial 最小的聚类结果作为最终聚类结果。
pyclusring 库下的 kmeans 聚类
到这里,可能你会有两个疑问:
http 协议是一个 4 层协议,wireshark 抓到的对百度这个 ip 地址一个个数据包。
note 其实上一节中的 https 协议依旧是简化版本,真正完整的 https 协议是支持双向认证的,即客户端不仅要认证服务器、服务器也要认证客户端。
只不过是平时大家访问的网站都是开门做生意的状态 —— 来的都是客。因此服务器并不会验证客户端的身份。
根据微软的文档,AuthenticCode 的计算需要跳过 CheckSum 字段、SecurityDirectory 字段以及最后的 PKCS#7 格式的证书数据部分。
RawPDB RawPDB is a C++11 library that directly reads Microsoft Program DataBase PDB files. The code is extracted almost directly from the upcoming 2.0 release of Live++.
通过 Ldr 来遍历自身的 dll 模块,然后通过都 PE 文件的解析寻找函数地址,并添加到全局函数表中。
LDR 链调试的方法,这实际上就是一种利用 PEB 关系链获得各个模块基址进而实现遍历其导出表的技术,通过这种技术我们可以轻易的在程序运行中获取到动态加载的 api 的实际地址进而实现各种各样的功能。
fs 寄存器 -> TEB -> PEB -> PEB_LDR_DATA -> LIST_ENTRY -> LDR_DATA_TABLE_ENTRY -> dll_base
《Android APP 漏洞之战系列》