多数ARK反内核工具中都存在驱动级别的内存转存功能,该功能可以将应用层中运行进程的内存镜像转存到特定目录下,内存转存功能在应对加壳程序的分析尤为重要,当进程在内存中解码后,我们可以很容易的将内存镜像导出,从而更好的对样本进行分析,当然某些加密壳可能无效但绝大多数情况下是可以被转存的。

在上一篇文章《驱动开发:内核R3与R0内存映射拷贝》介绍了一种方式SafeCopyMemory_R3_to_R0可以将应用层进程的内存空间映射到内核中,要实现内存转储功能我们还是需要使用这个映射函数,只是需要在此函数上增加一些功能而已。

在实现转存之前,需要得到两个东西,进程内模块基地址以及模块长度这两个参数是必不可少的,至于内核中如何得到指定进程的模块数据,在很早之前的文章《驱动开发:内核中枚举进线程与模块》中有详细的参考方法,这里就在此基础之上实现一个简单的进程模块遍历功能。

如下代码中使用的就是枚举进程PEB结构得到更多参数的具体实现,如果不懂得可以研读《驱动开发:内核通过PEB得到进程参数》这篇文章此处不再赘述。

#include <ntddk.h>
#include <windef.h> // 声明结构体
typedef struct _KAPC_STATE
{
LIST_ENTRY ApcListHead[2];
PKPROCESS Process;
UCHAR KernelApcInProgress;
UCHAR KernelApcPending;
UCHAR UserApcPending;
} KAPC_STATE, *PKAPC_STATE; typedef struct _LDR_DATA_TABLE_ENTRY
{
LIST_ENTRY64 InLoadOrderLinks;
LIST_ENTRY64 InMemoryOrderLinks;
LIST_ENTRY64 InInitializationOrderLinks;
PVOID DllBase;
PVOID EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
ULONG Flags;
USHORT LoadCount;
USHORT TlsIndex;
PVOID SectionPointer;
ULONG CheckSum;
PVOID LoadedImports;
PVOID EntryPointActivationContext;
PVOID PatchInformation;
LIST_ENTRY64 ForwarderLinks;
LIST_ENTRY64 ServiceTagLinks;
LIST_ENTRY64 StaticLinks;
PVOID ContextInformation;
ULONG64 OriginalBase;
LARGE_INTEGER LoadTime;
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY; // 偏移地址
ULONG64 LdrInPebOffset = 0x018; //peb.ldr
ULONG64 ModListInPebOffset = 0x010; //peb.ldr.InLoadOrderModuleList // 声明API
NTKERNELAPI UCHAR* PsGetProcessImageFileName(IN PEPROCESS Process);
NTKERNELAPI PPEB PsGetProcessPeb(PEPROCESS Process);
NTKERNELAPI HANDLE PsGetProcessInheritedFromUniqueProcessId(IN PEPROCESS Process); // 根据进程ID返回进程EPROCESS,失败返回NULL
PEPROCESS LookupProcess(HANDLE Pid)
{
PEPROCESS eprocess = NULL;
if (NT_SUCCESS(PsLookupProcessByProcessId(Pid, &eprocess)))
return eprocess;
else
return NULL;
} // 枚举指定进程的模块
// By: LyShark.com
VOID EnumModule(PEPROCESS Process)
{
SIZE_T Peb = 0;
SIZE_T Ldr = 0;
PLIST_ENTRY ModListHead = 0;
PLIST_ENTRY Module = 0;
ANSI_STRING AnsiString;
KAPC_STATE ks; // EPROCESS地址无效则退出
if (!MmIsAddressValid(Process))
return; // 获取PEB地址
Peb = (SIZE_T)PsGetProcessPeb(Process); // PEB地址无效则退出
if (!Peb)
return; // 依附进程
KeStackAttachProcess(Process, &ks);
__try
{
// 获得LDR地址
Ldr = Peb + (SIZE_T)LdrInPebOffset;
// 测试是否可读,不可读则抛出异常退出
ProbeForRead((CONST PVOID)Ldr, 8, 8);
// 获得链表头
ModListHead = (PLIST_ENTRY)(*(PULONG64)Ldr + ModListInPebOffset);
// 再次测试可读性
ProbeForRead((CONST PVOID)ModListHead, 8, 8);
// 获得第一个模块的信息
Module = ModListHead->Flink; while (ModListHead != Module)
{
//打印信息:基址、大小、DLL路径
DbgPrint("模块基址 = %p | 大小 = %ld | 模块名 = %wZ | 完整路径= %wZ \n",
(PVOID)(((PLDR_DATA_TABLE_ENTRY)Module)->DllBase),
(ULONG)(((PLDR_DATA_TABLE_ENTRY)Module)->SizeOfImage),
&(((PLDR_DATA_TABLE_ENTRY)Module)->BaseDllName),
&(((PLDR_DATA_TABLE_ENTRY)Module)->FullDllName)
);
Module = Module->Flink; // 测试下一个模块信息的可读性
ProbeForRead((CONST PVOID)Module, 80, 8);
}
}
__except (EXCEPTION_EXECUTE_HANDLER){ ; } // 取消依附进程
KeUnstackDetachProcess(&ks);
} VOID DriverUnload(IN PDRIVER_OBJECT DriverObject)
{ } NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
DbgPrint("hello lyshark.com \n"); ULONG i = 0;
PEPROCESS eproc = NULL;
for (i = 4; i<100000000; i = i + 4)
{
eproc = LookupProcess((HANDLE)i);
if (eproc != NULL)
{
ObDereferenceObject(eproc);
if (strstr(PsGetProcessImageFileName(eproc), "lyshark.exe") != NULL)
{
EnumModule(eproc);
}
}
} DriverObject->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}

如上我们指定获取应用层lyshark.exe进程的模块信息,并可得到以下输出效果:

上篇文章中的代码就不再啰嗦了,这里只给出内存转存的核心代码,如下代码:

  • RtlInitUnicodeString 用于初始化转存后的名字字符串
  • ZwCreateFile 内核中创建文件到应用层
  • ZwWriteFile 将文件写出到文件
  • ZwClose 最后是关闭文件并释放堆空间

很简单只是利用了SafeCopyMemory_R3_to_R0将进程内存读取到缓冲区内,并将缓冲区写出到C盘目录下。

// 进程内存拷贝函数
// By: LyShark.com
NTSTATUS ProcessDumps(PEPROCESS pEprocess, ULONG_PTR nBase, ULONG nSize)
{
BOOLEAN bAttach = FALSE;
KAPC_STATE ks = { 0 };
PVOID pBuffer = NULL;
NTSTATUS status = STATUS_UNSUCCESSFUL; if (nSize == 0 || pEprocess == NULL)
{
return status;
} pBuffer = ExAllocatePoolWithTag(PagedPool, nSize, 'lysh');
if (!pBuffer)
{
return status;
} memset(pBuffer, 0, nSize); if (pEprocess != IoGetCurrentProcess())
{
KeStackAttachProcess(pEprocess, &ks);
bAttach = TRUE;
} status = SafeCopyMemory_R3_to_R0(nBase, (ULONG_PTR)pBuffer, nSize); if (bAttach)
{
KeUnstackDetachProcess(&ks);
bAttach = FALSE;
} OBJECT_ATTRIBUTES object;
IO_STATUS_BLOCK io;
HANDLE hFile;
UNICODE_STRING log; // 导出文件名称
RtlInitUnicodeString(&log, L"\\??\\C:\\lyshark_dumps.exe");
InitializeObjectAttributes(&object, &log, OBJ_CASE_INSENSITIVE, NULL, NULL); status = ZwCreateFile(&hFile,
GENERIC_WRITE,
&object,
&io,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_WRITE,
FILE_OPEN_IF,
FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0); if (!NT_SUCCESS(status))
{
DbgPrint("打开文件错误 \n");
return STATUS_SUCCESS;
} ZwWriteFile(hFile, NULL, NULL, NULL, &io, pBuffer, nSize, NULL, NULL);
DbgPrint("写出字节数: %d \n", io.Information);
DbgPrint("[*] LyShark.exe 已转存");
ZwClose(hFile); if (pBuffer)
{
ExFreePoolWithTag(pBuffer, 'lysh');
pBuffer = NULL;
} return status;
} VOID UnDriver(PDRIVER_OBJECT driver)
{
DbgPrint(("Uninstall Driver Is OK \n"));
} // lyshark.com
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
DbgPrint("hello lyshark.com \n"); NTSTATUS ntStatus;
PEPROCESS pCurProcess = NULL; __try
{
ntStatus = PsLookupProcessByProcessId((HANDLE)272, &pCurProcess);
if (NT_SUCCESS(ntStatus))
{
// 设置基地址以及长度
ntStatus = ProcessDumps(pCurProcess, 0x140000000, 1024);
ObDereferenceObject(pCurProcess);
}
}
__except (1)
{
ntStatus = GetExceptionCode();
} Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}

转存后效果如下所示:

至于导出的进程无法运行只是没有修复而已(后期会讲),可以打开看看是没错的。

驱动开发:内核中实现Dump进程转储的更多相关文章

  1. Windows驱动开发-内核常用内存函数

    搞内存常用函数 C语言 内核 malloc ExAllocatePool memset RtlFillMemory memcpy RtlMoveMemory free ExFreePool

  2. 驱动开发:内核遍历进程VAD结构体

    在上一篇文章<驱动开发:内核中实现Dump进程转储>中我们实现了ARK工具的转存功能,本篇文章继续以内存为出发点介绍VAD结构,该结构的全程是Virtual Address Descrip ...

  3. Linux 内核开发—内核简单介绍

    内核简单介绍 Linux 构成 Linux 为什么被划分为系统空间和内核空间 隔离核心程序和应用程序,实现对核心程序和数据的保护. 什么内核空间,用户空间 内核空间和用户空间是程序执行的两种不同的状态 ...

  4. Android系统移植与驱动开发----第一章

    第一章 Android系统移植与驱动开发 Android源代码定制完全属于自己的嵌入式系统,但是支持的设备不多,所以要移植,而在移植的过程中使用的不得不提的是驱动开发. Android系统构架主要包括 ...

  5. Linux 驱动开发

    linux驱动开发总结(一) 基础性总结 1, linux驱动一般分为3大类: * 字符设备 * 块设备 * 网络设备 2, 开发环境构建: * 交叉工具链构建 * NFS和tftp服务器安装 3, ...

  6. Window驱动开发

    驱动开发 参考文章: Windbg+Vmware驱动调试 http://blog.csdn.net/xuepiaosong/article/details/8236702 驱动调试攻略(WinDbg) ...

  7. KSM剖析——Linux 内核中的内存去耦合

    简介: 作为一个系统管理程序(hypervisor),Linux® 有几个创新,2.6.32 内核中一个有趣的变化是 KSM(Kernel Samepage Merging)  允许这个系统管理程序通 ...

  8. (转)FS_S5PC100平台上Linux Camera驱动开发详解(一) .

     平台linuxstructlinux内核videocam 说明:        理解摄像头驱动需要四个前提:        1)摄像头基本的工作原理和S5PC100集成的Camera控制器的工作原理 ...

  9. 第六周分析Linux内核创建一个新进程的过程

    潘恒 原创作品转载请注明出处<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 task_struct结构: ...

随机推荐

  1. MySQL8.0错误日志Error log

    GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源. 理论知识 错误日志内容 错误日志包含mysqld启动和关闭的时间信息,还包含诊断消息,如服务器启动和关闭期间以及服务器运行 ...

  2. DolphinScheduler 源码剖析之 Master 容错处理流程

    点击上方蓝字关注 Apache DolphinScheduler Apache DolphinScheduler(incubating),简称"DS", 中文名 "海豚调 ...

  3. 化整为零优化重用,Go lang1.18入门精炼教程,由白丁入鸿儒,go lang函数的定义和使用EP07

    函数是基于功能或者逻辑进行聚合的可复用的代码块.将一些复杂的.冗长的代码抽离封装成多个代码片段,即函数,有助于提高代码逻辑的可读性和可维护性.不同于Python,由于 Go lang是编译型语言,编译 ...

  4. 牛客网 十二桥问题(状压DP)

    https://ac.nowcoder.com/acm/contest/1104/B 注意到\(\text{K}\)只有\(12\),因此对起点与每个毕经边对应的点单源最短路,\(\text{DP}\ ...

  5. Luogu2543[AHOI2004]奇怪的字符串 (动态规划 LCS)

    04年的省选这么water吗,开个滚动数组算了 #include <iostream> #include <cstdio> #include <cstring> # ...

  6. JavaScript基础回顾知识点记录4-正则表达式篇(介绍基本使用)

    js 中 正则表达式使用 创建正则对象和test方法使用 /* 创建正则表达式的对象 语法: var 变量 = new RegExp("正则表达式","匹配模式" ...

  7. django_day04

    django_day04 路由 from django.conf.urls import url urlpatterns = [ url(正则表达式, views视图,参数,别名), ] 正则表达式 ...

  8. IDEA整合Docker

    创建项目 1.使用 IDEA 构建一个 SpringBoot 项目 2.编写一个helloController @RestController public class HelloController ...

  9. grub2配置文件丢失如何修复

    实验操作准备 此步骤因实验需要所做,系统开机属grub界面无需此步! lsblk命令为了查看根分区挂载在什么位置 rm -rf /boot/grub2/grub.cfg命令为删除grub2配置文件到达 ...

  10. Docker 入门指南

    Docker 入门指南 目录 基础概念 安装教程 基本操作 常用安装 构建操作 容器编排 壹.基础概念 什么是Docker? Docker是基于Go开发的应用容器引擎,属于 Linux 容器的一种封装 ...