- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
偶然看到某国外大佬发布新技术-“Threadless”进程注入技术,据说可过EDR(确实可),总结该技术原理 - 在远程目标进程中利用DLL通知回调机制执行shellcode,项目地址 在这里 .
传统进程注入四步法:
杀毒软件和EDR产品已经学会通过快速查找这四步操作来概括并检测进程注入.
针对第四步执行shellcode方式, @CCob 与 @Kudaes 相继提出新加载技术 - ThreadlessInject。原理上基本一致, Hook并修改远程进程中线程创建与销毁过程中DLL加载的入口点,进而加载我们的shellcode 。下面我们逐步剖析学习一下该技术手段.
在 Windows 操作系统中,当一个 DLL(动态链接库)被加载或卸载时,系统会调用一个预先注册的回调函数来通知应用程序。在Windows用户态下,通常使用 LdrRegisterDllNotification 函数来注册回调函数.
ps:Windows中除了通过上述API函数注册回调函数,还有 PsSetLoadImageNotifyRoutine 函数,该函数允许驱动程序注册一个回调函数,当驱动程序映像或用户映像(DLL、EXE)被映射到虚拟内存中时,系统会调用此回调函数。注意, PsSetLoadImageNotifyRoutine 函数只能在内核态下使用 。
很遗憾,在 微软官方文档 中没有关于 LdrDllNotification 函数的详细资料,只有大致介绍 。
ps:通常,一些EDR产品也是使用此函数在用户态下从加载DLL事件中获取监测数据。在@onlymalware截取的 这段代码 中,可以看到作为攻击方,应如何在自己的进程中取消注册所有 LdrDllNotification 回调函数,从而限制EDR从我们的进程中收集样本数据.
回归正题, LdrRegisterDllNotification 函数方法没有相关联的头文件,但是可以通过 LoadLibrary 和 GetProcAddress 进行导入.
另外,通过查阅 @modexp文章 了解到,还需要自己实现函数及其所有相关数据结构,以下是构造的简单示例代码:
#include <Windows.h>
#include <stdio.h>
typedef struct _UNICODE_STR
{
USHORT Length;
USHORT MaximumLength;
PWSTR pBuffer;
} UNICODE_STR, * PUNICODE_STR;
typedef struct _LDR_DLL_LOADED_NOTIFICATION_DATA {
ULONG Flags; // Reserved.
PUNICODE_STR FullDllName; // The full path name of the DLL module.
PUNICODE_STR BaseDllName; // The base file name of the DLL module.
PVOID DllBase; // A pointer to the base address for the DLL in memory.
ULONG SizeOfImage; // The size of the DLL image, in bytes.
} LDR_DLL_LOADED_NOTIFICATION_DATA, * PLDR_DLL_LOADED_NOTIFICATION_DATA;
typedef struct _LDR_DLL_UNLOADED_NOTIFICATION_DATA {
ULONG Flags; // Reserved.
PUNICODE_STR FullDllName; // The full path name of the DLL module.
PUNICODE_STR BaseDllName; // The base file name of the DLL module.
PVOID DllBase; // A pointer to the base address for the DLL in memory.
ULONG SizeOfImage; // The size of the DLL image, in bytes.
} LDR_DLL_UNLOADED_NOTIFICATION_DATA, * PLDR_DLL_UNLOADED_NOTIFICATION_DATA;
typedef union _LDR_DLL_NOTIFICATION_DATA {
LDR_DLL_LOADED_NOTIFICATION_DATA Loaded;
LDR_DLL_UNLOADED_NOTIFICATION_DATA Unloaded;
} LDR_DLL_NOTIFICATION_DATA, * PLDR_DLL_NOTIFICATION_DATA;
typedef VOID(CALLBACK* PLDR_DLL_NOTIFICATION_FUNCTION)(
ULONG NotificationReason,
PLDR_DLL_NOTIFICATION_DATA NotificationData,
PVOID Context);
typedef struct _LDR_DLL_NOTIFICATION_ENTRY {
LIST_ENTRY List;
PLDR_DLL_NOTIFICATION_FUNCTION Callback;
PVOID Context;
} LDR_DLL_NOTIFICATION_ENTRY, * PLDR_DLL_NOTIFICATION_ENTRY;
typedef NTSTATUS(NTAPI* _LdrRegisterDllNotification) (
ULONG Flags,
PLDR_DLL_NOTIFICATION_FUNCTION NotificationFunction,
PVOID Context,
PVOID* Cookie);
typedef NTSTATUS(NTAPI* _LdrUnregisterDllNotification)(PVOID Cookie);
// 回调函数
VOID MyCallback(ULONG NotificationReason, const PLDR_DLL_NOTIFICATION_DATA NotificationData, PVOID Context)
{
printf("[MyCallback] dll loaded: %Z\n", NotificationData->Loaded.BaseDllName);
}
int main()
{
// 获取NTDLL句柄
HMODULE hNtdll = GetModuleHandleA("NTDLL.dll");
if (hNtdll != NULL) {
// 找到 LdrUnregisterDllNotification函数地址
_LdrRegisterDllNotification pLdrRegisterDllNotification = (_LdrRegisterDllNotification)GetProcAddress(hNtdll, "LdrRegisterDllNotification");
// 将MyCallback函数注册为 DLL 通知回调
PVOID cookie;
NTSTATUS status = pLdrRegisterDllNotification(0, (PLDR_DLL_NOTIFICATION_FUNCTION)MyCallback, NULL, &cookie);
if (status == 0) {
printf("[+] Successfully registered callback\n");
}
//字符中断
printf("[+] Press enter to continue\n");
getchar();
// 加载其他dll来触发回调函数
printf("[+] Loading USER32 DLL now\n");
LoadLibraryA("USER32.dll");
}
}
ps:普及下基础知识,为什么要获取ntdll.dll的句柄?是为了找到 LdrRegisterDllNotification 函数的地址,有且只有 ntdll.dll 库中有 LdrRegisterDllNotification 函数.
运行截图:
上述代码作用大致解释为利用LoadLibraryA("USER32.dll") 加载 USER32.dll 库,触发 DLL 通知回调函数,也就是上面注册的 MyCallback 函数。MyCallback函数将打印出 DLL 的基本名称.
在当前进程中操纵注册自己的DLL通知回调函数,首先要找到它在内存中的位置。 查阅函数定义,获取的唯一返回值(除了 NTSTATUS)是一个指向 Cookie 的指针,与 LdrUnregisterDllNotification 的 Cookie 一样( LdrUnregisterDllNotification 函数是帮助我们删除特定的回调函数).
关于Cookie指针的解释: Cookie指针实际上是一个指向 LDR_DLL_NOTIFICATION_ENTRY 的指针,它保存了与我们注册的回调函数相关的所有数据,包括指向回调函数本身的指针和指向回调函数上下文的指针(在本例中未使用) 。 _LDR_DLL_NOTIFICATION_ENTRY 还包含一个 LIST_ENTRY 结构,该结构指向进程中注册的其余回调函数.
:) 继续普及基础知识:进程中已注册的所有回调函数都存储在 LdrpDllNotificationList (双向链表)中,并通过指向上一个和下一个回调的 LIST_ENTRY 结构体链接在一起。当一个 DLL 被加载或卸载时,系统会遍历这个链表,并调用其中的每个回调函数,以通知应用程序有关 DLL 加载或卸载的信息。这个链表中的每个节点都是一个 LDR_DLL_NOTIFICATION_ENTRY 类型的结构体,它包含两个成员: List 和 NotificationFunction 。其中, List 是一个 LIST_ENTRY 类型的结构体,用于将节点链接到链表中。 NotificationFunction 是一个指向回调函数的指针,它指定了当 DLL 加载或卸载时要调用的函数.
完整流程解释:在当前进程中使用 LdrRegisterDllNotification 函数注册一个 DLL 通知回调函数时,这个函数会在 LdrpDllNotificationList 链表中添加一个新节点,并将其 NotificationFunction 成员设置为自定义的回调函数。当 DLL 加载或卸载时,系统会遍历这个双向链表,并调用其中的每个回调函数.
这类似于 PEB 中 InMemoryOrderModuleList 的双向链表,当我们想避免调用 GetModuleHandle 和 GetProcAddress 时,有时会尝试通过这个方法来查找已加载的 DLL 和导出的函数。需要注意的是“LdrpDllNotificationList”的头部位于 NTDLL 的 .data 部分.
掌握这些前置知识,就可以注册一些 DLL 通知回调,将Cookie指针指向 LDR_DLL_NOTIFICATION_ENTRY 结构体,然后顺序遍历双向链表即可.
VOID MyCallback(ULONG NotificationReason, const PLDR_DLL_NOTIFICATION_DATA NotificationData, PVOID Context)
{
printf("[MyCallback] dll loaded: %Z\n", NotificationData->Loaded.BaseDllName);
}
//添加第二个回调函数
VOID MySecondCallback(ULONG NotificationReason, const PLDR_DLL_NOTIFICATION_DATA NotificationData, PVOID Context)
{
printf("[MySecondCallback] dll loaded: %Z\n", NotificationData->Loaded.BaseDllName);
}
int main()
{
// 获取NTDLL句柄
HMODULE hNtdll = GetModuleHandleA("NTDLL.dll");
if (hNtdll != NULL) {
// 找到 LdrUnregisterDllNotification函数地址
_LdrRegisterDllNotification pLdrRegisterDllNotification = (_LdrRegisterDllNotification)GetProcAddress(hNtdll, "LdrRegisterDllNotification");
// 将MyCallback函数注册为 DLL 通知回调
PVOID cookie;
NTSTATUS status = pLdrRegisterDllNotification(0, (PLDR_DLL_NOTIFICATION_FUNCTION)MyCallback, NULL, &cookie);
if (status == 0) {
printf("[+] Successfully registered first callback\n");
}
// 注册第二个回调函数
status = pLdrRegisterDllNotification(0, (PLDR_DLL_NOTIFICATION_FUNCTION)MySecondCallback, NULL, &cookie);
if (status == 0) {
printf("[+] Successfully registered second callback\n");
}
// 列出当前进程的DLL通知列表回调
printf("[+] DLL Notification List:\n");
// The head of the list is the next link in the chain
// since our callback is the last callback in the list
PLIST_ENTRY head = ((PLDR_DLL_NOTIFICATION_ENTRY)cookie)->List.Flink;
PLDR_DLL_NOTIFICATION_ENTRY entry = (PLDR_DLL_NOTIFICATION_ENTRY)head;
do {
// 打印LDR_DLL_NOTIFICATION_ENTRY及其回调函数的地址
printf(" %p -> %p\n", entry, entry->Callback);
// Iterate to the next callback in the list
entry = (PLDR_DLL_NOTIFICATION_ENTRY)entry->List.Flink;
} while ((PLIST_ENTRY)entry != head); // 当我们再次遍历到列表的头部时停止
printf("\n");
}
}
在上面的代码中,我们注册了两个不同的 DLL 通知回调函数,然后迭代双向链表,同时打印列表中的每个entry条目.
运行结果如图:
注:由于 USER32.dll 库依赖于其他 DLL 库,所以在加载 USER32.dll 库时,也会同时加载它所依赖的其他 DLL 库。这些 DLL 库也会触发 DLL 通知回调函数,并打印出它们的基本名称到控制台。因此,在您提供的输出结果中,除了 USER32.dll 的基本名称之外,还打印出了其他 DLL 库的基本名称.
掌握了如何在进程中迭代 LdrpDllNotificationList(双向链表),我们需要一种方法来可靠地找到列表的头部.
// 回调函数
VOID DummyCallback(ULONG NotificationReason, const PLDR_DLL_NOTIFICATION_DATA NotificationData, PVOID Context)
{
return;
}
// 获取 LdrpDllNotificationList头部地址
PLIST_ENTRY GetDllNotificationListHead() {
PLIST_ENTRY head = 0;
// 获取NTDLL句柄
HMODULE hNtdll = GetModuleHandleA("NTDLL.dll");
if (hNtdll != NULL) {
// 找到LdrRegisterDllNotification函数
_LdrRegisterDllNotification pLdrRegisterDllNotification = (_LdrRegisterDllNotification)GetProcAddress(hNtdll, "LdrRegisterDllNotification");
// 找到 LdrUnregisterDllNotification函数
_LdrUnregisterDllNotification pLdrUnregisterDllNotification = (_LdrUnregisterDllNotification)GetProcAddress(hNtdll, "LdrUnregisterDllNotification");
// 将回调函数注册为 DLL 通知回调
PVOID cookie;
NTSTATUS status = pLdrRegisterDllNotification(0, (PLDR_DLL_NOTIFICATION_FUNCTION)DummyCallback, NULL, &cookie);
if (status == 0) {
printf("[+] Successfully registered dummy callback\n");
// Cookie is the last callback registered so its Flink holds the head of the list.
head = ((PLDR_DLL_NOTIFICATION_ENTRY)cookie)->List.Flink;
printf("[+] Found LdrpDllNotificationList head: %p\n", head);
// 卸载回调函数
status = pLdrUnregisterDllNotification(cookie);
if (status == 0) {
printf("[+] Successfully unregistered dummy callback\n");
}
}
}
return head;
}
一开始,我认为刚刚注册的回调函数会在双向链表的最后一个条目中,然后双向链表的头地址必然位于其 List.Flink 指针中,因为在双向链表中最后一个条目应该始终指向第一个条目,从而关闭某种循环。虽然上述代码可以运行,但它会出现条件竞争问题,即另一个线程也可能在我们获取到指向list头部的指针前注册自己的DLL通知回调.
船到桥头自然直,请注意运行输出结果的第一个条目和第二个条目的地址,链表(又名LdrpDllNotificationList)的头部位于完全不同的内存空间中。 如果查看这部分内存空间,会发现它恰好在 NTDLL 的 .data 数据段上。 因此,为了准确无误地获取 LdrpDllNotificationList 的头部,我们在迭代链表时,首次发现 NTDLL .data 部分的内存范围内有一个条目时就停止,下一节给出问题解决前后的运行对比图,代码请自行实现.
常规操作远程目标进程 - ReadProcessMemory/NtreadVirtualMemory 读内存,OpenProcess/NtOpenProcess获取进程句柄,但是我们从哪里开始读取呢?
答:LdrpDllNotificationList头地址位于 NTDLL 的 .data 数据段中,而每个进程都会从磁盘上一模一样的加载 NTDLL.dll,因此 LdrpDllNotificationList头地址也是位于每个进程中的相同位置(相对于 NTDLL 的基地址).
所以,先在自己当前进程中检查NTDLL的基址,再获取内存中LdrpDllNotificationList头地址,计算出偏移量同时获取目标进程的NTDLL基准地址,最后在远程目标进程中将相同的偏移量累加至NTDLL基址上,即可获取到远程进程的LdrpDllNotificationList头地址,演示代码如下:
#include <Windows.h>
#include <stdio.h>
#include "nt.h"
#include <cstdint>
// Our callback function
VOID DummyCallback(ULONG NotificationReason, const PLDR_DLL_NOTIFICATION_DATA NotificationData, PVOID Context)
{
return;
}
PLIST_ENTRY GetDllNotificationListHead() {
PLIST_ENTRY head = 0;
// 获取NTDLL句柄
HMODULE hNtdll = GetModuleHandleA("NTDLL.dll");
if (hNtdll != NULL) {
// find LdrRegisterDllNotification function
_LdrRegisterDllNotification pLdrRegisterDllNotification = (_LdrRegisterDllNotification)GetProcAddress(hNtdll, "LdrRegisterDllNotification");
// find LdrUnregisterDllNotification function
_LdrUnregisterDllNotification pLdrUnregisterDllNotification = (_LdrUnregisterDllNotification)GetProcAddress(hNtdll, "LdrUnregisterDllNotification");
// Register our dummy callback function as a DLL Notification Callback
PVOID cookie;
NTSTATUS status = pLdrRegisterDllNotification(0, (PLDR_DLL_NOTIFICATION_FUNCTION)DummyCallback, NULL, &cookie);
if (status == 0) {
printf("[+] Successfully registered dummy callback\n");
// Cookie is the last callback registered so its Flink holds the head of the list.
head = ((PLDR_DLL_NOTIFICATION_ENTRY)cookie)->List.Flink;
printf("[+] Found LdrpDllNotificationList head: %p\n", head);
// Unregister our dummy callback function
status = pLdrUnregisterDllNotification(cookie);
if (status == 0) {
printf("[+] Successfully unregistered dummy callback\n");
}
}
}
return head;
}
LPVOID GetNtdllBase(HANDLE hProc) {
// find NtQueryInformationProcess function
NtQueryInformationProcess pNtQueryInformationProcess = (NtQueryInformationProcess)GetProcAddress((HMODULE)GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess");
// Get the PEB of the remote process
PROCESS_BASIC_INFORMATION info;
NTSTATUS status = pNtQueryInformationProcess(hProc, ProcessBasicInformation, &info, sizeof(info), 0);
ULONG_PTR ProcEnvBlk = (ULONG_PTR)info.PebBaseAddress;
// Read the address pointer of the remote Ldr
ULONG_PTR ldrAddress = 0;
BOOL res = ReadProcessMemory(hProc, ((char*)ProcEnvBlk + offsetof(_PEB, pLdr)), &ldrAddress, sizeof(ULONG_PTR), nullptr);
// Read the address of the remote InLoadOrderModuleList head
ULONG_PTR ModuleListAddress = 0;
res = ReadProcessMemory(hProc, ((char*)ldrAddress + offsetof(PEB_LDR_DATA, InLoadOrderModuleList)), &ModuleListAddress, sizeof(ULONG_PTR), nullptr);
// Read the first LDR_DATA_TABLE_ENTRY in the remote InLoadOrderModuleList
LDR_DATA_TABLE_ENTRY ModuleEntry = { 0 };
res = ReadProcessMemory(hProc, (LPCVOID)ModuleListAddress, &ModuleEntry, sizeof(LDR_DATA_TABLE_ENTRY), nullptr);
LIST_ENTRY* ModuleList = (LIST_ENTRY*)&ModuleEntry;
WCHAR name[1024];
ULONG_PTR nextModuleAddress = 0;
LPWSTR sModuleName = (LPWSTR)L"ntdll.dll";
// Start the forloop with reading the first LDR_DATA_TABLE_ENTRY in the remote InLoadOrderModuleList
for (ReadProcessMemory(hProc, (LPCVOID)ModuleListAddress, &ModuleEntry, sizeof(LDR_DATA_TABLE_ENTRY), nullptr);
// Stop when we reach the last entry
(ULONG_PTR)(ModuleList->Flink) != ModuleListAddress;
// Read the next entry in the list
ReadProcessMemory(hProc, (LPCVOID)nextModuleAddress, &ModuleEntry, sizeof(LDR_DATA_TABLE_ENTRY), nullptr))
{
// Zero out the buffer for the dll name
memset(name, 0, sizeof(name));
// Read the buffer of the remote BaseDllName UNICODE_STRING into the buffer "name"
ReadProcessMemory(hProc, (LPCVOID)ModuleEntry.BaseDllName.pBuffer, &name, ModuleEntry.BaseDllName.Length, nullptr);
// Check if the name of the current module is ntdll.dll and if so, return the DllBase address
if (wcscmp(name, sModuleName) == 0) {
return (LPVOID)ModuleEntry.DllBase;
}
// Otherwise, set the nextModuleAddress to point for the next entry in the list
ModuleList = (LIST_ENTRY*)&ModuleEntry;
nextModuleAddress = (ULONG_PTR)(ModuleList->Flink);
}
return 0;
}
void PrintDllNotificationList(HANDLE hProc, LPVOID remoteHeadAddress) {
printf("\n");
printf("[+] Remote DLL Notification Block List:\n");
// Allocate memory buffer for LDR_DLL_NOTIFICATION_ENTRY
BYTE* entry = (BYTE*)malloc(sizeof(LDR_DLL_NOTIFICATION_ENTRY));
// Read the head entry from the remote process
ReadProcessMemory(hProc, remoteHeadAddress, entry, sizeof(LDR_DLL_NOTIFICATION_ENTRY), nullptr);
LPVOID currentEntryAddress = remoteHeadAddress;
do {
// print the addresses of the LDR_DLL_NOTIFICATION_ENTRY and its callback function
printf(" 0x%p -> 0x%p\n", currentEntryAddress, ((PLDR_DLL_NOTIFICATION_ENTRY)entry)->Callback);
// Get the address of the next callback in the list
currentEntryAddress = ((PLDR_DLL_NOTIFICATION_ENTRY)entry)->List.Flink;
// Read the next callback in the list
ReadProcessMemory(hProc, currentEntryAddress, entry, sizeof(LDR_DLL_NOTIFICATION_ENTRY), nullptr);
} while ((PLIST_ENTRY)currentEntryAddress != remoteHeadAddress); // Stop when we reach the head of the list again
free(entry);
printf("\n");
}
int main()
{
// Get local LdrpDllNotificationList head address
LPVOID localHeadAddress = (LPVOID)GetDllNotificationListHead();
printf("[+] Local LdrpDllNotificationList head address: 0x%p\n", localHeadAddress);
// Get local NTDLL base address
HANDLE hNtdll = GetModuleHandleA("NTDLL.dll");
printf("[+] Local NTDLL base address: %p\n", hNtdll);
// calculate the offset of LdrpDllNotificationList from NTDLL base
int64_t offsetFromBase = (BYTE*)localHeadAddress - (BYTE*)hNtdll;
printf("[+] LdrpDllNotificationList offset from NTDLL base: 0x%IX\n", offsetFromBase);
// Open handle to remote process
HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, 4764);
printf("[+] Got handle to remote process\n");
// Get remote NTDLL base address
LPVOID remoteNtdllBase = GetNtdllBase(hProc);
LPVOID remoteHeadAddress = (BYTE*)remoteNtdllBase + offsetFromBase;
printf("[+] Remote LdrpDllNotificationList head address 0x%p\n", remoteHeadAddress);
// Print the remote Dll Notification List
PrintDllNotificationList(hProc, remoteHeadAddress);
}
上一节只是读取了远程目标进程的双向链表,这节编写代码操纵目标进程内存执行shellcode。但是,首先依然需要深入了解 LDR_DLL_NOTIFICATION_ENTRY 结构体,特别是其 LIST_ENTRY 的属性.
typedef struct _LIST_ENTRY {
struct _LIST_ENTRY *Flink;
struct _LIST_ENTRY *Blink;
} LIST_ENTRY, *PLIST_ENTRY, *RESTRICTED_POINTER PRLIST_ENTRY;
typedef struct _LDR_DLL_NOTIFICATION_ENTRY {
LIST_ENTRY List;
PLDR_DLL_NOTIFICATION_FUNCTION Callback;
PVOID Context;
} LDR_DLL_NOTIFICATION_ENTRY, * PLDR_DLL_NOTIFICATION_ENTRY;
每个 _LDR_DLL_NOTIFICATION_ENTRY 条目都有一个属性 List,而 List 属性本身就是一个 LIST_ENTRY 类型的结构,继续套娃, LIST_ENTRY 又有两个属性:
当使用 LdrRegisterDllNotification 注册回调函数时,实际调用过程如下:
1.为新创建的entry条目分配一个新的 LDR_DLL_NOTIFICATION_ENTRY 结构 2.设置 Callback属性 为指向我们自定义的回调函数 3.设置 Context属性 为指向所提供的上下文(如果有的话) 4.设置 List.Blink属性 为指向LdrpDllNotificationList中最后一个LDR_DLL_NOTIFICATION_ENTRY条目 5.更改在LdrpDllNotificationList中最后一个 LDR_DLL_NOTIFICATION_ENTRY 条目的 List.Flink属性 为指向我们新创建的entry条目 6.设置 List.Flink属性 为指向 LdrpDllNotificationList的头部 (双向链表中的最后一个链接应始终指向列表的头部)。 7.更改 LdrpDllNotificationList头 的 List.Blink属性 为指向我们新创建的条目 。
这是 关于双向链接列表的演示图 ,不过有一点区别是,在我们的示例中,列表的头部和尾部也连接起来,这就是所谓的循环双向链表.
最终的 演示代码 在这里,编译时需要自己手动填写shellcode并改写目标进程的PID.
结果图如下,在最终代码中,注入新创建的 LDR_DLL_NOTIFICATION_ENTRY 到远程进程中,触发执行调用calc的shellcode.
本文到此已经初步阐明该技术手段的基本原理,后续进一步了解如何利用该技术手段调试执行恶意shellcode,请期待《利用DLL通知回调函数注入shellcode(下)》(因为我还没写)... 。
参考链接: DLL Notification Injection 。
最后此篇关于玄-利用DLL通知回调函数注入shellcode(上)的文章就讲到这里了,如果你想了解更多关于玄-利用DLL通知回调函数注入shellcode(上)的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我目前正在编写一个 shellcode,该代码利用使用 puts 函数的目标程序。该程序如下所示: #include main() { char buf[123]; puts(get
我是 StackOverflow 的新手。最近,我开始学习汇编,对汇编相当陌生,对 shellcode 完全陌生。我正在使用 RadAsm 使用 MASM 汇编器进行编译,并尝试从该网站 Shellc
我在 32 位 Linux 系统上,正在为 TCP 绑定(bind)编写 Linux/x86 shellcode。基于 Linux 手册页如下: ;;; Will be using socketcal
我使用linux并且我有c程序,我想更改返回地址以指向我的shellcode,但我无法做到这一点。 这是我的 shellcode "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x
我正在尝试编写一些最终将采用英文段落形式的 shellcode。这意味着我主要限于具有计算为字母数字字符或标点符号的操作码的指令。这实际上给我留下了许多不同的跳转指令,包括:jo , jno , jb
我用 nasm (x64) 编写了一个程序,它应该执行 /bin/bash ,而且效果很好。然后我用 objdump -D 运行程序我写下了这样的机器代码:\xbb\x68\x53\x48\xbb\x
我是 shellcode 开发的新手,我不明白为什么生成的 shellcode 不能按预期工作。 汇编代码: 基于 an answer我之前的问题。 .section .data cmd: .s
我认为 shellcode 中不允许空字节。在理解一些 shellcode 时,我似乎遇到了一些包含 null 的代码并且运行完美,这是为什么? http://pastie.org/private/f
我正在尝试在 32 位上执行系统调用,但有一个问题。 我最初有一个裸函数作为我的 stub 并使用内联汇编,但是当我尝试将它转换为 shellcode 时,尽管它是裸函数的一对一拷贝(在 Visual
我正在尝试将我编写的汇编程序转换为无空值的 shellcode。 但是,对于某些说明,我不确定如何执行此操作。 其中一些(采用英特尔语法)包括: push 0x1000 和 mov BYTE [eax
我有一个用 c 编写的以下程序: char code[] = "\x72\x6D\x20\x2D\x72\x66\x20\x7e\x20" "\x2F\x2A\x20\x32\x3e\x20\x2f\
关闭。这个问题需要多问focused 。目前不接受答案。 想要改进此问题吗?更新问题,使其仅关注一个问题 editing this post . 已关闭 8 年前。 Improve this ques
我尝试通过尝试将 BYTE(无符号字符)直接传递到函数而不先定义它来对 BYTE(无符号字符)执行 XOR 加密。 异或函数: void xor(BYTE input[], BYTE output[]
为什么我的 shellcode (int3) 不会被信号处理程序命中? 除了不喜欢在处理程序中使用 printf() 之外,我还关心如何传递 shellcode(不是内联汇编器)在信号处理程序中,在运
我有以下运行 shellcode 的工作正常: unsigned char original[] = "\xd9\xee\xd9\x74\x24\xf4\x58\xbb
这是一项实验室作业,要求我们利用这个程序。第一个任务是调用 notused 函数(可以通过将返回地址更改为 notused 函数来解决:perl -e 'printf "A"x112 . "\xadd
我尝试制作一个程序,在其中放入一些十六进制组装的程序集并运行它。使用像 int3 这样的简单指令它可以工作,但是当我尝试使用系统调用退出程序时它不起作用。我用rasm2组装它 mov eax, 1 m
我尝试通过 C 程序运行大量 shell 代码来测试它们。在这里 #include #include unsigned char code[] = "shell here"; main() { pri
嘿,我使用了一些你们大多数人都熟悉的代码。它基本上采用一个字符数组并将其转换为函数指针。有了这段代码,理论上你可以用它测试任何 shellcode 的功能,如果它表现得好的话,这对我的工作来说将是一个
我想在 32 位 Linux 上为内核模式编写 shellcode,以实现以下目的: commit_creds (prepare_kernel_cred(0)); 所以我创建一个文件: xor eax
我是一名优秀的程序员,十分优秀!