zoukankan      html  css  js  c++  java
  • PowerShell脚本之Invoke-Expression Hook

    PowerShell脚本之Invoke-Expression Hook

    开发环境

    vs2013,WDK8.1,dnspy

    Hook的起因

    最近在分析一个APT组织样本的时候,发现了一件有意思的事情,该样本进行了80多层混淆,可能还要多,没有查。

    原始文件第一层powershell脚本代码:

    放眼望去,一片混淆,有反转,有正则表达式。开始以为只有几层,就尝试手动解,没想到越解越多。

    这样谁能受得了?百度了一下没有找到这种去混淆工具。

    既然每次混淆后都会调用执行函数,那就把每次执行的参数记录下来,最后终将执行混淆后的代码

    Hook的原理

    原理就是Inlie Hook ,不管再怎么混淆,都将执行iex(invoke expression)函数。

    我们只需要对该函数进行Inlie Hook ,然后继续执行原始的代码就行了

    不过有意思的是,这个Powershell_ise.exe是基于C#开发的,他的dll也是

    那么我们要如何去hook呢?

    Hook的流程

    在Hook之前,我们要必定要找到 代码所在的模块

    我们可以使用Get-Command命令来查询一个Cmdlet所在模块

    (Get-Command Invoke-Expression).ModuleName
    

    可以使用以下命令查看实现iex所属.net类的完全限定名称:

    (Get-Command Invoke-Expression).ImplementingType.AssemblyQualifiedName
    

    我们用dnspy打开GAC程序集,并定位到该模块

    定位到Microsoft.Powershell.Commands包下的InvokeExpressionCommand类,发现该类只有一个函数

    验证是不是我们要Hook的函数,下个断点,调试程序powershell_ise.exe,并执行iex "get-process",发现确实是我们要Hook的代码

    我们在这里尝试修改代码是可以改的,但是却不能让程序执行我们的代码,这个模块所在的位置在用户层是没有权限访问的,或者说exploer.exe(资源管理器)是没有权限访问到这个目录的

    在修改完Hook代码后,这里有两种方式替换掉目标dll:

    第一种:用ARK工具,例如PcHunter等工具可以访问到该目录,并且可以删除掉。

    然后用cmd命令 ,复制到该目录(缺陷:powershell_ise程序在被调试加载和单独运行的时候,加载的模块并不是通一个,然而另外一个模块,用cmd复制过去也是没有用的)

    move C:Users15pb-win7DesktopMicrosoft.PowerShell.Commands.Utility.dll  C:WindowsassemblyGAC_MSILMicrosoft.PowerShell.Commands.Utility1.0.0.0__31bf3856ad364e35
    

    第二种,自己编写patch程序(驱动模块,因为需要system权限,我们可以替换cmd的token,也可以自己复制替换)

    patch程序的编写

    0x01--原始DLL的修改

    dll模块的Hook代码,有过C#开发经验的,编写代码还是比较简单的。

    我们在这里不改变程序的流程,只把参数记录下来,并写文件到C盘。改后如下:

    修改后将代码保存到桌面,原始路径你是没有权限保存的。

    0x02--3环程序的编写

    之前有说过,我们直接运行powershell_ise和在调试器里运行,其加载的模块是不一样,如下图:

    调试时要hook的模块:

    直接运行加载的模块:

    经过分析,加载的模块虽然大小不一样,但是包是一样的,起码我们要改的代码处是一样的。

    程序正常运行时,如果Microsoft.PowerShell.Commands.Utility.ni.dll不存在则加载Microsoft.PowerShell.Commands.Utility.dll

    我们的patch程序,要做的事有如下几件:

    • 删掉Microsoft.PowerShell.Commands.Utility.dll
    • 删掉Microsoft.PowerShell.Commands.Utility.ni.dll
    • 复制我们修改过dll,到Microsoft.PowerShell.Commands.Utility.dll所在目录下

    注:必须要先删除,直接覆盖是不行的

    正常运行时的路径如下:

    C:WindowsassemblyNativeImages_v2.0.50727_32Microsoft.PowerShel#4bdde288f147e3b3f2c090ecdf704e6dMicrosoft.PowerShell.ConsoleHost.ni.dll
    

    C:WindowsassemblyNativeImages_v2.0.50727_32Microsoft.PowerShel#不变,后边是在变化的,我们要去动态获取。

    获取代码(对该目录进行递归,找到文件,保存当前目录路径):

    void EnumFile(TCHAR *szPath, TCHAR* szFileName)
    {
    
    
    	TCHAR szTempPath[MAX_PATH] = {};
    	wsprintf(szTempPath, L"%s\*", szPath);
    	WIN32_FIND_DATA FindData = {};
    	TCHAR szNextPath[MAX_PATH] = {};
    	TCHAR szOutputPant[MAX_PATH] = {};
    	HANDLE hFind = FindFirstFile(szTempPath, &FindData);
    
    	do
    	{
    		if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)//输出目录
    		{
    
    			if (wcscmp(FindData.cFileName, L".") == 0 || wcscmp(FindData.cFileName, L"..") == 0)
    			{
    				//如果是这俩目录,就什么都不干
    
    			}
    			else
    			{
    
    				wsprintf(szNextPath, L"%s\%s", szPath, FindData.cFileName);
    				EnumFile(szNextPath, szFileName);
    
    			}
    		}
    		else
    		{
    			//如果不是目录,就查看这个玩意是不是我们的目标文件
    			if (wcscmp(szFileName, FindData.cFileName) == NULL)
    			{
    
    				wsprintf(szDestPath, L"\??\%s\%s", szPath, FindData.cFileName);
    			//是就输出
    				printf("%ws
    ", szDestPath);
    			}
    		}
    
    	} while (FindNextFile(hFind, &FindData));
    
    	FindClose(hFind);
    
    
    }
    

    将获取的路径写到本地(也可以不用写,把消息发送到0环也行)

    0x03--驱动程序的编写

    3环的程序,就是为了获取路径

    那么0环的程序,自然就是要读取路径,删除,复制(比较简单,并没有什么技术含量)

    在0环是没有删除文件和读取文件的,需要自己封装。

    驱动的所有代码如下:

    #include <Ntddk.h> //编写内核驱动需要包含NTddk头文件.
    #include<ntstrsafe.h>
    
    BOOLEAN MyCopyFile(PCWSTR desFile, PCWSTR srcFile)
    {
    
    
    	HANDLE readFileHandle;
    	HANDLE writeFileHandle;
    	OBJECT_ATTRIBUTES ObjectAttributes;
    	OBJECT_ATTRIBUTES ObjectAttributes1;
    	UNICODE_STRING readFilePath;
    	UNICODE_STRING writeFilePath;
    	IO_STATUS_BLOCK IoStatusBlock;
    	NTSTATUS status;
    
    	PVOID saveBuffer = NULL;
    	LARGE_INTEGER byteOffset;
    	ULONG length = 0;
    
    
    
    
    	byteOffset.QuadPart = 0;
    	RtlInitUnicodeString(&readFilePath, srcFile);
    	RtlInitUnicodeString(&writeFilePath, desFile);
    
    	saveBuffer = ExAllocatePoolWithTag(PagedPool, 1000, "tag1");
    	InitializeObjectAttributes(&ObjectAttributes, &readFilePath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
    	InitializeObjectAttributes(&ObjectAttributes1, &writeFilePath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
    	status = ZwCreateFile(&readFileHandle, GENERIC_ALL, &ObjectAttributes, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN_IF, FILE_NON_DIRECTORY_FILE | FILE_RANDOM_ACCESS | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
    
    	if (!NT_SUCCESS(status))
    	{
    		DbgPrint("Can not create");
    		if (readFileHandle != NULL)
    			ZwClose(readFileHandle);
    
    
    		if (saveBuffer != NULL)
    			ExFreePool(saveBuffer);
    
    
    		return FALSE;
    	}
    
    	status = ZwCreateFile(&writeFileHandle, GENERIC_ALL, &ObjectAttributes1, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN_IF, FILE_NON_DIRECTORY_FILE | FILE_RANDOM_ACCESS | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
    
    	if (!NT_SUCCESS(status))
    	{
    		if (readFileHandle != NULL)
    			ZwClose(readFileHandle);
    
    		if (writeFileHandle != NULL)
    			ZwClose(writeFileHandle);
    
    		if (saveBuffer != NULL)
    			ExFreePool(saveBuffer);
    		DbgPrint("Can not create");
    		return FALSE;
    	}
    
    
    	do
    	{
    
    		length = 1000;
    		status = ZwReadFile(readFileHandle, NULL, NULL, NULL, &IoStatusBlock, saveBuffer, length, &byteOffset, NULL);//读取数据
    
    
    
    		if (!NT_SUCCESS(status))
    		{
    			if (status == STATUS_END_OF_FILE)
    
    				DbgPrint("read File End");
    			if (readFileHandle != NULL)
    				ZwClose(readFileHandle);
    
    			if (writeFileHandle != NULL)
    				ZwClose(writeFileHandle);
    
    			if (saveBuffer != NULL)
    				ExFreePool(saveBuffer);
    			return FALSE;
    		}
    
    		length = IoStatusBlock.Information;//返回实际读取数据的大小
    
    		status = ZwWriteFile(writeFileHandle, NULL, NULL, NULL, &IoStatusBlock, saveBuffer, length, &byteOffset, NULL);
    
    		if (!NT_SUCCESS(status))
    		{
    			DbgPrint("Can not write File ");
    			if (readFileHandle != NULL)
    				ZwClose(readFileHandle);
    
    			if (writeFileHandle != NULL)
    				ZwClose(writeFileHandle);
    
    			if (saveBuffer != NULL)
    				ExFreePool(saveBuffer);
    			return FALSE;
    		}
    
    		byteOffset.QuadPart += length;//文件偏移移动
    
    	} while (1);
    
    	if (readFileHandle != NULL)
    		ZwClose(readFileHandle);
    
    	if (writeFileHandle != NULL)
    		ZwClose(writeFileHandle);
    
    	if (saveBuffer != NULL)
    		ExFreePool(saveBuffer);
    	return TRUE;
    }
    
    
    NTSTATUS ZwDeleteFile(IN POBJECT_ATTRIBUTES  ObjectAttributes); //函数声明
    
    
    NTSTATUS  IBinaryNtZwDeleteFile(UNICODE_STRING uDeletePathName)
    {
    
    
    	OBJECT_ATTRIBUTES obAttri = { 0 };
    
    
    	//初始化源文件路径并且打开
    
    	InitializeObjectAttributes(&obAttri,
    		&uDeletePathName,
    		OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
    		NULL,
    		NULL
    		);
    
    	return ZwDeleteFile(&obAttri);
    }
    
    //卸载回调函数
    VOID Unload(__in struct _DRIVER_OBJECT  *DriverObject)
    {
    	DbgPrint("Unload MyDrive
    ");
    }
    
    NTSTATUS  IBinaryNtReadFile(PVOID pszBuffer, UNICODE_STRING uPathName)
    {
    
    	OBJECT_ATTRIBUTES objAttri = { 0 };
    	NTSTATUS ntStaus;
    	HANDLE hFile;
    	IO_STATUS_BLOCK ioStatus = { 0 };
    
    
    	if (NULL == pszBuffer)
    		return STATUS_INTEGER_DIVIDE_BY_ZERO;
    
    
    	//打开文件读取文件.
    
    	InitializeObjectAttributes(&objAttri,
    		&uPathName,
    		OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
    		NULL,
    		0);
    
    	ntStaus = ZwCreateFile(&hFile,
    		GENERIC_READ | GENERIC_WRITE,
    		&objAttri,
    		&ioStatus,
    		NULL,
    		FILE_ATTRIBUTE_NORMAL,
    		FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
    		FILE_OPEN,
    		FILE_SYNCHRONOUS_IO_NONALERT,
    		NULL,
    		NULL);
    
    	if (!NT_SUCCESS(ntStaus))
    	{
    		ZwClose(hFile);
    		return ntStaus;
    
    	}
    
    
    
    	ntStaus = ZwReadFile(hFile, NULL, NULL, NULL, &ioStatus, pszBuffer, 500, NULL, NULL);
    	if (!NT_SUCCESS(ntStaus))
    	{
    		ZwClose(hFile);
    	}
    
    
    	return ntStaus;
    }
    
    NTSTATUS DriverEntry(__in struct _DRIVER_OBJECT  *DriverObject,
    	__in PUNICODE_STRING  RegistryPath)
    {
    	int i = 0;
    	//DbgBreakPoint();
    	DbgPrint("HelloWorld, %p
    ", &i);
    	
    	//UNICODE_STRING uDestPath = { 0 };
    
    	//RtlUnicodeStringInit(&uDestPath, L"\??\C:\Windows\assembly\NativeImages_v2.0.50727_32\Microsoft.PowerShel#\6337c36083b7d1f5cc12e6fb37d2d430\Microsoft.PowerShell.Commands.Utility.ni.dll1313131313");
    
    	PCWSTR pReadBuffer = NULL;
    
    	UNICODE_STRING uPathName = { 0 };
    
    	RtlUnicodeStringInit(&uPathName, L"\??\C:\patch\dest.txt");
    
    
    	pReadBuffer = ExAllocatePoolWithTag(PagedPool, 0x1000, "niBI");
    
    	RtlZeroMemory(pReadBuffer, 0x1000);
    
    	IBinaryNtReadFile(pReadBuffer, uPathName);
    
    	
    	//复制第一个
    	//初始化字符串路径
    	RtlUnicodeStringInit(&uPathName, L"\??\C:\Windows\assembly\GAC_MSIL\Microsoft.PowerShell.Commands.Utility\1.0.0.0__31bf3856ad364e35\Microsoft.PowerShell.Commands.Utility.dll");
    
    	IBinaryNtZwDeleteFile(uPathName);
    
    	//复制文件到目标目录
    	//C:\Windows\assembly\GAC_MSIL\Microsoft.PowerShell.Commands.Utility\1.0.0.0__31bf3856ad364e35\Microsoft.PowerShell.Commands.Utility.dll
    	MyCopyFile(L"\??\C:\Windows\assembly\GAC_MSIL\Microsoft.PowerShell.Commands.Utility\1.0.0.0__31bf3856ad364e35\Microsoft.PowerShell.Commands.Utility.dll",L"\??\C:\patch\patch.dll");
    
    
    	//复制第二个
    	RtlUnicodeStringInit(&uPathName,pReadBuffer);
    
    	NTSTATUS nt =IBinaryNtZwDeleteFile(uPathName);
    
    
    	//BOOLEAN bl = MyCopyFile(pReadBuffer, L"\??\C:\patch\patch-ni.dll");
    	
    
    //RtlUnicodeStringInit(&uPathName, L"\??\C:\Windows\assembly\NativeImages_v2.0.50727_32\Microsoft.PowerShel#\6337c36083b7d1f5cc12e6fb37d2d430\Microsoft.PowerShell.Commands.Utility.ni.dll");
    
    //NTSTATUS nt =IBinaryNtZwDeleteFile(uPathName);
    
    //BOOLEAN bl=	MyCopyFile(L"\??\C:\Windows\assembly\NativeImages_v2.0.50727_32\Microsoft.PowerShel#\6337c36083b7d1f5cc12e6fb37d2d430\Microsoft.PowerShell.Commands.Utility.ni.dll", L"\??\C:\patch\patch.dll");
    	//注册一下驱动卸载的函数
    	DriverObject->DriverUnload = Unload;
    
    	ExFreePoolWithTag(pReadBuffer, "niBI");
    	return STATUS_SUCCESS;
    }
    
    
    

    0x04--总结和梳理:

    1. iexpatch.exe运行(获取要pacth的dll路径,并写到本地,并运行patch.exe)
    2. patch.exe是驱动加载软件(加载我们的驱动,可以自己写,偷懒了就用了现成的)
    3. 驱动(删除两个dll,并复制我们修改过后的dll到目标目录)

    效果展示

    80层混淆:

    去混淆后:

    去混淆后是MuddyWater常用的powershell远控

  • 相关阅读:
    拓扑排序学习
    快速排序+归并排序
    邻接表的两种实现(链表和数组模拟)
    一起学Windows Phone7开发(十四.一 Phone Task)
    一起学Windows Phone7开发(十四.四 Web Task)
    一起学Windows Phone7开发(十四.三 Multimedia Task)
    一起学Windows Phone7开发(十五. Device)
    一起学Windows Phone7开发(十四.五 Market Task)
    深入学习Windows Phone7(三. Visual State Manager)
    深入学习Windows Phone7(一. Reactive Extension)
  • 原文地址:https://www.cnblogs.com/hongyeHK/p/11690826.html
Copyright © 2011-2022 走看看