恶意代码研究 -- 功能技术类 -- windows PE恶意代码(五)文件监控

具体过程

  • 打开目录,获取文件句柄,调用CreateFile获取文件句柄,文件句柄必须要有FILE_LIST_DIRECTORY权限。

  • 调用ReadDirectoryChangesW设置目录监控。

  • 判断文件操作类型,只要有满足过滤条件的文件操作,ReadDirectoryChangesW函数会立马返回信息,并将其返回到输出缓冲区中,而且返回数据是按结构体FILE_NOTIFY_INFORMATION返回的。

  • 调用一次ReadDirectoryChangesW函数只会监控一次,要想实现持续监控,则需要程序循环调用ReadDirectoryChangesW函数来设置监控并获取监控数据,由于持续的目录监控需要不停循环调用ReadDirectoryChangesW函数进行设置监控和获取监控数据,所以如果把这段代码放在主线程中则会导致程序阻塞,为了解决主线程阻塞的问题,可以创建一个文件监控子线程,把文件监控的实现代码放到子线程中。

相关API

CreateFile函数

可打开和创建文件、管道、邮槽、通信服务、设备以及控制台

HANDLE WINAPI CreateFile(
    _In_ LPCWSTR lpFileName,
    _In_ DWORD dwDesiredAccess,
    _In_ DWORD dwShareMode,
    _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
    _In_ DWORD dwCreationDisposition,
    _In_ DWORD dwFlagsAndAttributes,
    _In_opt_ HANDLE hTemplateFile
    );

参数(没用到的暂时略过):

  • lpFileName:要打开的文件名
  • dwDesiredAccess:文件的操作属性(这里需要设置为FILE_NOTIFY_INFORMATION
  • dwShareMode:文件共享属性
  • lpSecurityAttributes:文件安全特性
  • dwCreationDisposition:文件操作
  • dwFlagsAndAttributes:文件属性
  • hTemplateFile:如果不为零,则指定一个文件句柄。新文件将从这个文件中复制扩展属性

CreateThread函数

创建一个新线程

HANDLE WINAPI CreateThread(
    _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
    _In_ SIZE_T dwStackSize,
    _In_ LPTHREAD_START_ROUTINE lpStartAddress,
    _In_opt_ __drv_aliasesMem LPVOID lpParameter,
    _In_ DWORD dwCreationFlags,
    _Out_opt_ LPDWORD lpThreadId
    );

参数:

  • lpThreadAttributes:指向SECURITY_ATTRIBUTES结构的指针,该结构确定子进程是否可以继承返回的句柄。如果lpThreadAttributesNULL,则无法继承句柄,而线程获取默认安全描述符
  • dwStackSize:线程堆栈的初始大小,以字节为单位。如果此参数为零,则新线程使用可执行文件的默认大小
  • lpStartAddress:指向由线程执行的应用程序定义函数的指针。该指针表示线程的起始地址。
  • lpParameter:要传递给新建线程的命令行参数。
  • dwCreationFlags:控制线程创建的标志,定义如下:
含义
0该线程在创建后立即运行
CREATE_SUSPENDED 0x00000004线程是在挂起状态下创建的,并且在调用ResumeThread函数之会运行
STACK_SIZE_PARAM_IS_A_RESERVATION 0x00010000所述dwStackSize参数指定堆栈的初始保留大小。如果未指定此标志,则dwStackSize指定提交大小
  • lpThreadId:指向新建线程的ID号。如果此参数为NULL,则不返回线程标识符。

FILE_NOTIFY_INFORMATION结构体

typedef struct _FILE_NOTIFY_INFORMATION {
    DWORD NextEntryOffset;
    DWORD Action;
    DWORD FileNameLength;
    WCHAR FileName[1];
} FILE_NOTIFY_INFORMATION, *PFILE_NOTIFY_INFORMATION;

ReadDirectoryChangesW函数

监控文件目录函数

BOOL WINAPI ReadDirectoryChangesW(
    _In_        HANDLE hDirectory,
    _Out_writes_bytes_to_(nBufferLength, *lpBytesReturned) LPVOID lpBuffer,
    _In_        DWORD nBufferLength,
    _In_        BOOL bWatchSubtree,
    _In_        DWORD dwNotifyFilter,
    _Out_opt_   LPDWORD lpBytesReturned,
    _Inout_opt_ LPOVERLAPPED lpOverlapped,
    _In_opt_    LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
    );

参数(没用到的暂时略过):

  • hDirectory:指向要监视的目录句柄。
  • lpBuffer:指向要读取的格式化缓存区的指针,缓冲区是FILE_NOTIFY_INFORMATION结构体类型。
  • nBufferLength:lpBuffer指向的缓冲区大小。
  • bWatchSubtree:
    • 如果该参数设置为True,则监视以指定目录为根的目录树
    • 如果该参数设置为False,仅监视由hDirectory指定的目录
  • dwNotifyFilter:检查函数是否满足等待条件(即监视文件目录的哪些改动)
含义
FILE_NOTIFY_CHANGE_FILE_NAME 0x00000001监视目录或子树中任何文件名的改动,包括重命名,创建或删除文件
FILE_NOTIFY_CHANGE_DIR_NAME 0x00000002监视目录或子树中任何文件夹的改动,包括创建或删除目录
FILE_NOTIFY_CHANGE_ATTRIBUTES 0x00000004监视目录或子树中的任何属性改动
FILE_NOTIFY_CHANGE_SIZE 0x00000008监视目录或子树中任何文件大小的改动
FILE_NOTIFY_CHANGE_LAST_WRITE 0x00000010监视目录或子树中文件上次写入时间的改动
FILE_NOTIFY_CHANGE_LAST_ACCESS 0x00000020监视目录或子树中文件上次访问时间的改动
FILE_NOTIFY_CHANGE_CREATION 0x00000040监视目录或子树中文件创建时间的改动
FILE_NOTIFY_CHANGE_SECURITY 0x00000100监视目录或子树中文件文件描述符的改动
  • lpBytesReturned:对于同步调用,接受传输到lpBuffer中的字节数。

示例代码

#include<stdio.h>
#include<Windows.h>
#include <tchar.h>

void ShowError(char *pszText)
{
	char szErr[MAX_PATH] = { 0 };
	::wsprintf(szErr, "%s Error[%d]\n", pszText, ::GetLastError());
	::MessageBox(NULL, szErr, "ERROR", MB_OK | MB_ICONERROR);
}


// 宽字节字符串转多字节字符串
void W2C(wchar_t *pwszSrc, int iSrcLen, char *pszDest, int iDestLen)
{
	::RtlZeroMemory(pszDest, iDestLen);
	// 宽字节字符串转多字节字符串
	::WideCharToMultiByte(CP_ACP,
		0,
		pwszSrc,
		(iSrcLen / 2),
		pszDest,
		iDestLen,
		NULL,
		NULL);
}

// 目录监控多线程
UINT MonitorFileThreadProc(LPVOID lpVoid)
{
	char *pszDirectory = (char *)lpVoid;

	// 打开目录, 获取文件句柄

	HANDLE hDirectory = ::CreateFile(pszDirectory, FILE_LIST_DIRECTORY,
		FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
		FILE_FLAG_BACKUP_SEMANTICS, NULL);
	if (INVALID_HANDLE_VALUE == hDirectory)
	{
		ShowError("CreateFile");
		return 1;
	}

	char szTemp[MAX_PATH] = { 0 };
	BOOL bRet = FALSE;
	DWORD dwRet = 0;
	DWORD dwBufferSize = 2048;

	// 申请一个足够大的缓冲区
	BYTE *pBuf = new BYTE[dwBufferSize];
	if (NULL == pBuf)
	{
		ShowError("new");
		return 2;
	}
	FILE_NOTIFY_INFORMATION *pFileNotifyInfo = (FILE_NOTIFY_INFORMATION *)pBuf;

	// 开始循环设置监控
	do
	{
		::RtlZeroMemory(pFileNotifyInfo, dwBufferSize);
		// 设置监控目录
		bRet = ::ReadDirectoryChangesW(hDirectory,
			pFileNotifyInfo,
			dwBufferSize,
			TRUE,
			FILE_NOTIFY_CHANGE_FILE_NAME |            // 修改文件名
			FILE_NOTIFY_CHANGE_ATTRIBUTES |            // 修改文件属性
			FILE_NOTIFY_CHANGE_LAST_WRITE,            // 最后一次写入
			&dwRet,
			NULL,
			NULL);
		if (FALSE == bRet)
		{
			ShowError("ReadDirectoryChangesW");
			break;
		}
		// 将宽字符转换成窄字符
		W2C((wchar_t *)(&pFileNotifyInfo->FileName), pFileNotifyInfo->FileNameLength, szTemp, MAX_PATH);
		// 判断操作类型并显示
		switch (pFileNotifyInfo->Action)
		{
		case FILE_ACTION_ADDED:
		{
			// 新增文件
			printf("[File Added Action]%s\n", szTemp);
			break;
		}
		case FILE_ACTION_MODIFIED:
		{	// 修改文件
			printf("[File Modified Action]%s\n", szTemp);
			break;
		}
		case FILE_ACTION_REMOVED:
		{   // 删除文件
			printf("[File Remove Action]%s\n", szTemp);
			break;
		}
		case FILE_ACTION_RENAMED_OLD_NAME:
		{	
			printf("[File Rename Old Name Action]%s\n", szTemp);
			break;
		}
		case FILE_ACTION_RENAMED_NEW_NAME:
		{	
			printf("[File Rename New Name Action]%s\n", szTemp);
			break;
		}
		default:
		{
			break;
		}
		}


	} while (bRet);
	// 关闭句柄, 释放内存
	::CloseHandle(hDirectory);
	delete[] pBuf;
	pBuf = NULL;

	return 0;
}


// 创建目录监控多线程
void MonitorFile(char *pszDirectory)
{
	// 创建文件监控多线程
	::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)MonitorFileThreadProc, pszDirectory, 0, NULL);
}


int _tmain(int argc, _TCHAR* argv[])
{
	// 注意目录路径的末尾要加上反斜杠'\'
	MonitorFile("E:\\test\\");
	printf("monitor...\n");
	getchar();
	return 0;
}

运行结果

在E盘下新建test文件夹,在里面进行如下操作:

  • 新建文本文档
  • 将文档改名为1.txt
  • 将文档改名为2.txt
  • 将文档改名为1.txt
  • 将字符串aaaaa写入1.txt
  • 将1.txt改名为1.php
  • 删除1.php

运行结果如下:
在这里插入图片描述

流程图

  • 主线程: 调用CreateProcess创建子线程
  • 子线程:
Created with Raphaël 2.2.0 调用CreateFile获取目录句柄 调用ReadDirectoryChangesW循环监控目录
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值