// 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 // 判断处理器相关信息
Windows 内核-句柄
用 Python 去除图片背景:Rembg 库
Rembg is a tool to remove images background.
https://github.com/danielgatis/rembg
智能电表安全之通讯分析
DebugPort 是用于调试的一个端口,如果把这个端口清零,常规手段则无法调试我们的进程。
模拟 windwos 系统调用例子,重写 writeprocessmemory 和 readprocessmemory
分别以 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)
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 注入实现
反射式 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 实现思路如下:
获得被注入进程未解析的 dll 的基地址。
获得必要的 dll 句柄和函数为修复导入表做准备。
分配一块新内存去取解析 dll,并把 pe 头复制到新内存中和将各节复制到新内存中。
修复导入表和重定向表。
执行 DllMain() 函数。
https://bbs.pediy.com/thread-272569.htm
https://github.com/MrXiao7/DllInjector
https://github.com/Kerrbty/RemoteLoadDll
API Hook 的几种实现
API Hook 的几种实现 
改写函数的首地址。
改写导入表 IAT(Import Address Table)
改写虚函数表。简单的虚表 Hook
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
Typora 解密之跳动的二进制
Typora 是一款由 Abner Lee 开发的轻量级 Markdown 编辑器,与其他 Markdown 编辑器不同的是,Typora 没有采用源代码和预览双栏显示的方式,而是采用所见即所得的编辑方式,实现了即时预览的功能,但也可切换至源代码编辑模式。
使用 FindCrypt3 插件 ,搜索一下算法常量吧。
简单说几种思路,由于 main.node 是后加载的模块,所以内存破解有些难度。
调试器加载 :参照上述手段,在模块加载通知中断下,定位到解密函数下断,修改内存中的 JS 代码
导出表 HOOK:参考病毒木马使用的进程替换(傀儡进程)技术,创建进程后挂起,由于 main.node 中的 node api 是使用框架中的导出 api,所以可以替换导出函数为自己的函数,在调用时进行参数判断,如果为 JS 代码,则修改
DLL 劫持:替换 main.node,由自己加载真正的 main.node 并调用,调用时,定位到解密函数并 hook,等待 JS 代码并修改
PE 代码注入 :修改框架的 PE 文件,并加载自己的 DLL,加载后进行导出表 hook
可能遇到的问题:对 main.node 或者框架进行完整性校验,更加强大的反调试手段。
Fuzz 学习记录
进行软件漏洞挖掘时,通常有静态分析(staticanalysis)、动态分析(dynamicanalysis)、符号执行(symbolicexecution)、模糊测试(fuzzing)这几种技术手段。
模糊测试不需要人过多的参与,也不像动态分析那样要求分析人员有丰富的知识。简单解释,它就是用大量的输入数据自动去执行程序,从而发现哪些输入能够使程序发生异常,进而分析可能存在的漏洞。当前比较成功的 fuzzer(执行模糊测试的程序)有 AFL、libFuzzer、OSS-Fuzz 等。
用 AFL 来示意一个典型的 Fuzz 过程。
kmeans++
从上面的分析可以看出,k-means 是随机的分配 k 个初始聚类中心。而聚类的结果高度依赖质心的初始化。如果初始聚类中心选的不好,k-means 算法最终会收敛到一个局部最优值,而不是全局最优值。为了解决这个问题,引入了 k-means++ 算法,它的基本思想就是:初始的聚类中心之间的相互距离要尽可能的远。而且在计算过程中,我们通常采取的措施是进行不止一次的聚类,每次都初始化不同的中心,以 inertial 最小的聚类结果作为最终聚类结果。
pyclusring 库下的 kmeans 聚类
某达路由器测试
危险函数表
其实上一节中的 https 协议依旧是简化版本,真正完整的 https 协议是支持双向认证的,即客户端不仅要认证服务器、服务器也要认证客户端。
只不过是平时大家访问的网站都是开门做生意的状态 —— 来的都是客。因此服务器并不会验证客户端的身份。
一文读懂 PE 文件签名并手工验证签名有效性
AuthentiCode 哈希
根据微软的文档,AuthenticCode 的计算需要跳过 CheckSum 字段、SecurityDirectory 字段以及最后的 PKCS#7 格式的证书数据部分。
2022 腾讯游戏安全决赛 wp
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 的实际地址进而实现各种各样的功能。