watermelon 发表于 2019-1-30 16:47:59

【C语言】文件擦写删除操作

小弟自从看到论坛中站长转发的Tesla.Angela大佬的学习win64驱动程序的帖子后,跟着学了学,感觉还是很不错哈哈。
目前看到了课2-7,学完了内核中进程的基本操作和文件的基本操作,回想到了原先一直想写的文件粉碎,所以就想着复习一下之前的驱动程序的编写并且加深与它的亲密感,实践出真知yes!

事实上文件粉碎的机制我还是没有很搞清楚,直到目前我还不了解怎么用驱动程序来编写在物理磁盘上来操作文件的程序。站长之前转发过一篇关于ring3上直接操作物理磁盘的帖子:https://www.0xaa55.com/forum.php?mod=viewthread&tid=289&extra=&page=1

所以我日后该想一想如何编写驱动程序来直接操作电脑上文件在对应的位置的物理磁盘。

下面开始正文:
1.程序的本意是将指定删除的文件进行覆写,之后删除掉。我的思路是如果是exe文件,就先查看它有没有开启,如果有开启就先关闭它,然后再进行覆写删除。
2.遍历进程用的教程上的枚举PspCidTable然后得到EPROCESS进行的,这个和我之前在ring3上学习的快照遍历进程管理器上的进程不太一样,而教程上获取变量名用的是PsGetProcessImageFileName,这个api在我的win7 x64虚拟机上运行时候发现只能获得进程名称的前14个字符,后来在棉花糖boss群里问的时候“有人么”大佬说不要用这个函数,当时他没有说用那个以及为什么不要用PsGetProcessImageFileName,后来我查资料发现PsGetProcessImageFileName会发生15字节截断,最后一个字符是'\0',所以我最多只能得到前14个字符。后来我自己在枚举进程后用ZwQueryInformationProcess获得进程路径后分离出进程名称,这样就好多了(在这里要提醒一下,进程pid和进程句柄一定要分清楚啊,ZwQueryInformationProcess用的参数为进程句柄,而不是进程pid,虽然二者都是HANDLE,但是不一样,我传进的参数是进程pid,要将其转化为进程句柄再给ZwQueryInformationProcess用)。
3.结束进程我很喜欢教程中的内存清零法结束进程,很暴力很有效很纯,小弟我很喜欢:D
4.驱动程序中的文件操作基本思路是判断文件是否有隐藏或者只读属性,如果有,则将取消他们的隐藏属性或只读属性,然后覆写3遍0xCC,最后删除。
5.我把教程中Tesla.Angela大佬的驱动加载模板(C++)改成了C语言的,因为小弟我感觉纯C语言的比较好看(驱动加载程序+驱动)
6.小弟的代码在较大程度上借鉴了许多网上博客和Tesla.Angela大佬教程和其他书籍,但是所有程序均为手打,拒绝CTRL C+V。
7.测试驱动时候发现有一些玄学的事情,就是驱动加载程序给驱动程序传递字符串的时候会有一些奇怪的事情发生,有时候会多上一些乱码。小弟在win7 x64虚拟机上测试还好,文件隐藏属性,只读属性,内存清零法关闭进程,文件覆写与删除等都还可以。小弟给虚拟机装上250,用内存清零法结束掉了250tray.exe和250Safe.exe,程序最后执行完毕后,桌面上的250图标变成了无用的快捷方式(应该是250Safe.exe被删除掉了)。
8.忘了说了,之前在遍历进程时候发现会有较多同名的进程,而有一些同名的进程好像是没有用并且没有开启的,我想了个办法就是把那些同名进程的pid全部存到一个数组中,挨个结束数组中的进程,内存清零法结束不掉的就不管他了,去干下一个,知道结束掉某一个进程时候停止,效果还不错,当初第一次在棉花糖boss群里讨论时候“有人么”大佬说我语文没学好,的确当时表述不是很清楚。
9.开发环境:Visual Studio 2013 Ultimate + WDK8.1编译,windbg+vmware(win7 x64)双机调试,编译出64位驱动程序:watermelon.sys
10.希望各位大佬多多指正,不必留情,小弟会很高清的!


watermelon.sys
/*
本程序参考:
1.>      Tesla.Angela大佬的教程,orz
2.>   《windows驱动开发详解》
3.>               MSDN的参考文档
*/


#include <ntifs.h>
#include <ntddk.h>
#include <wdm.h>
#include <windef.h>
#include <string.h>


//声明结构体
typedef struct _SYSTEM_PROCESSES
{
        ULONG NextEntryDelta;
        ULONG ThreadCount;
        ULONG Reserved;
        LARGE_INTEGER CreateTime;
        LARGE_INTEGER UserTime;
        LARGE_INTEGER KernelTime;
        UNICODE_STRING ProcessName;
        KPRIORITY BasePriority;
        ULONG ProcessId;
        ULONG InheritedFromProcessId;
        ULONG HandleCount;
        ULONG Reserved2;
        VM_COUNTERS VmCounters;
        IO_COUNTERS IoCounters;
} _SYSTEM_PROCESSES, *PSYSTEM_PROCESSES;


//声明API
NTSTATUS WINAPI ZwQueryInformationProcess(
        _In_      HANDLE         ProcessHandle,
        _In_      PROCESSINFOCLASS ProcessInformationClass,
        _Out_   PVOID            ProcessInformation,
        _In_      ULONG            ProcessInformationLength,
        _Out_opt_ PULONG         ReturnLength
        );
NTKERNELAPI UCHAR* PsGetProcessImageFileName(IN PEPROCESS Process);
NTKERNELAPI HANDLE PsGetProcessInheritedFromUniqueProcessId(IN PEPROCESS Process);
NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(HANDLE Id, PEPROCESS *Process);
NTKERNELAPI VOID NTAPI KeAttachProcess(PEPROCESS Process);
NTKERNELAPI VOID NTAPI KeDetachProcess();


//声明自定义函数
VOID DriverUnload(IN PDRIVER_OBJECT pDriverObj);
NTSTATUS DispatchCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp);
NTSTATUS DispatchClose(PDEVICE_OBJECT pDevObj, PIRP pIrp);
NTSTATUS DispatchIoctl(PDEVICE_OBJECT pDevObj, PIRP pIrp);
VOID MySleep(IN LONG msec);
PEPROCESS LookupProcess(IN HANDLE Pid);
VOID GetProcessName(IN HANDLE hPid, IN OUT CHAR *szProcessName);
DWORD FindProcess(IN PUNICODE_STRING target, OUT HANDLE *targetPidList);
BOOLEAN ZwKillProcess(IN HANDLE Pid);
VOID PVASE(IN PEPROCESS targetEprocess);
VOID KillProcess(IN UNICODE_STRING target);
BOOLEAN WriteFile(IN CHAR *filename);
BOOLEAN DeleteFileFolder(IN CHAR *sFileName);



//定义符号连接,一般来说修改为驱动名字即可
#define DEVICE_NAME L"\\Device\\watermelon"
#define LINK_NAME L"\\DosDevices\\watermelon"
#define LINK_GLOBAL_NAME L"\\DosDevices\\Global\\watermelon"

//定义驱动功能号和名字,提供接口给应用程序调用
#define IOCTL_EXE CTL_CODE(FILE_DEVICE_UNKNOWN,0x801,METHOD_BUFFERED,FILE_ANY_ACCESS)                                //EXE文件
#define IOCTL_TEXT CTL_CODE(FILE_DEVICE_UNKNOWN,0x802,METHOD_BUFFERED,FILE_ANY_ACCESS)                                //文本文件
#define IOCTL_OFFICEWORD CTL_CODE(FILE_DEVICE_UNKNOWN,0x803,METHOD_BUFFERED,FILE_ANY_ACCESS)                //OFFICE word文件
#define IOCTL_OFFICEEXCEL CTL_CODE(FILE_DEVICE_UNKNOWN,0x804,METHOD_BUFFERED,FILE_ANY_ACCESS)                //office excel文件
#define IOCTL_OFFICEPPT CTL_CODE(FILE_DEVICE_UNKNOWN,0x805,METHOD_BUFFERED,FILE_ANY_ACCESS)                        //office ppt文件
#define IOCTL_DELETE_FILE CTL_CODE(FILE_DEVICE_UNKNOWN,0x806,METHOD_BUFFERED,FILE_ANY_ACCESS)                //删除文件
#define IOCTL_OTHERS CTL_CODE(FILE_DEVICE_UNKNOWN,0x807,METHOD_BUFFERED,FILE_ANY_ACCESS)                        //其他文件



//驱动卸载处理例程
VOID DriverUnload(IN PDRIVER_OBJECT pDriverObj)
{
        UNICODE_STRING strLink;
        DbgPrint("DriverUnload!!!\n");
        RtlInitUnicodeString(&strLink, LINK_NAME);
        IoDeleteSymbolicLink(&strLink);
        IoDeleteDevice(pDriverObj->DeviceObject);
}


//IRP_MJ_CREATE对应的处理程序
NTSTATUS DispatchCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
        DbgPrint("DispatchCreate\n");
        pIrp->IoStatus.Status = STATUS_SUCCESS;
        pIrp->IoStatus.Information = 0;
        IoCompleteRequest(pIrp, IO_NO_INCREMENT);
        return STATUS_SUCCESS;
}

//IRP_MJ_CLOSE
NTSTATUS DispatchClose(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
        DbgPrint("DispatchClose\n");
        pIrp->IoStatus.Status = STATUS_SUCCESS;
        pIrp->IoStatus.Information = 0;
        IoCompleteRequest(pIrp, IO_NO_INCREMENT);
        return STATUS_SUCCESS;
}

//IRP_MY_DEVICE_CONTROL派遣函数,非常重要的一个函数。
NTSTATUS DispatchIoctl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
        NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
        PIO_STACK_LOCATION pIrpStack;
        ULONG uIoControlCode;
        PVOID pIoBuffer;
        ULONG uInSize;
        ULONG uOutSize;
        DbgPrint("DispatchIoCtl\n");
        //获得IRP里的关键数据
        pIrpStack = IoGetCurrentIrpStackLocation(pIrp);
        //控制码
        uIoControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;
        //输入输出缓冲区
        pIoBuffer = pIrp->AssociatedIrp.SystemBuffer;
        //输入区域大小
        uInSize = pIrpStack->Parameters.DeviceIoControl.InputBufferLength;
        //输出区域大小
        uOutSize = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
        switch (uIoControlCode)
        {
        case IOCTL_EXE:
        {

                                          CHAR target = { 0 };
                                          strcpy(target, (CHAR*)pIoBuffer);

                                          if (target != 'e' || target != 'E')
                                          {
                                                  target = '\0';
                                          }


                                          DbgPrint("EXE文件:%s\n", target);

                                          //将pIoBuffer转化为UNICODE_STRING
                                          ANSI_STRING astr1;
                                          RtlInitAnsiString(&astr1, target);
                                          UNICODE_STRING ustr1;
                                          RtlAnsiStringToUnicodeString(&ustr1, &astr1, TRUE);
                                          KillProcess(ustr1);                                //结束指定进程
                                          RtlFreeUnicodeString(&ustr1);
                                          status = STATUS_SUCCESS;
                                          break;
        }
        case IOCTL_TEXT:
        {
                                           CHAR target = { 0 };
                                           strcpy(target, (CHAR*)pIoBuffer);

                                           if (target != 't' || target != 'T')
                                           {
                                                   target = '\0';
                                           }

                                           DbgPrint("文本文件:%s\n", target);
                                           UNICODE_STRING ustr;
                                           RtlInitUnicodeString(&ustr, L"notepad.exe");
                                           KillProcess(ustr);
                                           status = STATUS_SUCCESS;
                                           break;
        }
        case IOCTL_OFFICEWORD:
        {


                                                       CHAR target = { 0 };
                                                       strcpy(target, (CHAR*)pIoBuffer);
                                                       if (target != 'c' || target != 'C'
                                                               ||target!='x' || target!='X')
                                                       {
                                                               target = '\0';
                                                       }

                                                       DbgPrint("word类型文件:%s\n", target);

                                                       UNICODE_STRING ustr;
                                                       RtlInitUnicodeString(&ustr, L"WINWORD.EXE");
                                                       KillProcess(ustr);
                                                       status = STATUS_SUCCESS;
                                                       break;
        }
        case IOCTL_OFFICEEXCEL:
        {
                                                          CHAR target = { 0 };
                                                          strcpy(target, (CHAR*)pIoBuffer);

                                                          if (target != 's' || target != 'S'
                                                                  || target != 'x' || target != 'X')
                                                          {
                                                                  target = '\0';
                                                          }

                                                          DbgPrint("excel类型文件:%s\n", target);
                                                          UNICODE_STRING ustr;
                                                          RtlInitUnicodeString(&ustr, L"EXCEL.EXE");
                                                          KillProcess(ustr);
                                                          status = STATUS_SUCCESS;
                                                          break;
        }
        case IOCTL_OFFICEPPT:
        {

                                                        CHAR target = { 0 };
                                                        strcpy(target, (CHAR*)pIoBuffer);

                                                        if (target != 't' || target != 'T'
                                                                || target != 'x' || target != 'X')
                                                        {
                                                                target = '\0';
                                                        }

                                                        DbgPrint("ppt类型文件:%s\n", target);

                                                        UNICODE_STRING ustr;
                                                        RtlInitUnicodeString(&ustr, L"POWERPNT.EXE");
                                                        KillProcess(ustr);
                                                        status = STATUS_SUCCESS;
                                                        break;
        }

        case IOCTL_DELETE_FILE:
        {

                                                          CHAR target = { 0 };
                                                          strcpy(target, (CHAR*)pIoBuffer);

                                                          DbgPrint("开始删除文件:%s\n", target);

                                                          CHAR temp = "\\??\\";
                                                          strcat(temp, target);
                                                          DbgPrint("%s\n", temp);

                                                          WriteFile(temp);                                //覆写文件操作
                                                          DeleteFileFolder(temp);                //删除文件操作.
                                                          status = STATUS_SUCCESS;
                                                          break;
        }
        case IOCTL_OTHERS:
        {

                                               DbgPrint("文件类型为其他文件:%s\n", (CHAR*)pIoBuffer);
                                               status = STATUS_SUCCESS;
                                               break;
        }
        }


        if (status == STATUS_SUCCESS)
        {
                pIrp->IoStatus.Information = uOutSize;
        }
        else
        {
                pIrp->IoStatus.Information = 0;

        }
        pIrp->IoStatus.Status = status;
        IoCompleteRequest(pIrp, IO_NO_INCREMENT);
        return status;
}


//睡眠函数
VOID MySleep(IN LONG msec)
{
        LARGE_INTEGER my_interval;
        my_interval.QuadPart = -10 * 1000 * 1000;
        my_interval.QuadPart *= msec;
        KeDelayExecutionThread(KernelMode, FALSE, &my_interval);
}


//根据进程ID返回进程EPROCESS,失败返回NULL
PEPROCESS LookupProcess(IN HANDLE Pid)
{
        PEPROCESS eprocess = NULL;
        if (NT_SUCCESS(PsLookupProcessByProcessId(Pid, &eprocess)))
        {
                return eprocess;
        }
        else
        {
                return NULL;
        }
}

//在内核态通过PID获取进程路径从而分离出进程名称
//输入参数为进程pid,在函数内要将进程pid转化为进程句柄给ZwQueryInformationProcess使用。
VOID GetProcessName(IN HANDLE hPid, IN OUT CHAR *szProcessName)
{
        HANDLE hProcess = NULL;
        PVOID ProcInfo = NULL;
        ULONG ulLen = 0;
        CHAR pszProcessPath;

        //将进程pid转化为进程句柄
        OBJECT_ATTRIBUTES ObjectAttributes;
        CLIENT_ID clientId;
        InitializeObjectAttributes(&ObjectAttributes,
                0,
                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
                0,
                0);
        clientId.UniqueProcess = (HANDLE)hPid;
        clientId.UniqueThread = 0;
        ZwOpenProcess(&hProcess,
                PROCESS_ALL_ACCESS,
                &ObjectAttributes,
                &clientId);

        //开始查询
        NTSTATUS status = ZwQueryInformationProcess(hProcess,
                ProcessImageFileName,
                NULL,
                0,
                &ulLen);

        if (status != STATUS_INFO_LENGTH_MISMATCH)
        {
                DbgPrint("查询进程名长度失败ulLen:%d, status = 0x%08X\n", ulLen, status);
                ZwClose(hProcess);                //关闭进程句柄
                return;
        }

        ProcInfo = ExAllocatePool(PagedPool, ulLen);
        if (ProcInfo == NULL)
        {
                DbgPrint("ExAllocatePool Failed\n");
                ZwClose(hProcess);
                return;
        }

        status = ZwQueryInformationProcess(hProcess,
                ProcessImageFileName,
                ProcInfo,
                ulLen,
                &ulLen);

        if (!NT_SUCCESS(status))
        {
                DbgPrint("ZwQueryInformationProcess error! :0x%XL\n", status);
                ExFreePool(ProcInfo);
                ZwClose(hProcess);
                return;
        }

        ANSI_STRING astring;
        RtlUnicodeStringToAnsiString(&astring, (PUNICODE_STRING)ProcInfo, TRUE);
        strncpy(pszProcessPath, astring.Buffer, astring.Length);

        if (astring.Length >= MAX_PATH)
        {
                pszProcessPath = '\0';
        }
        else
        {
                *(pszProcessPath + astring.Length) = '\0';
        }

        //开始根据进程路径截取出进程名称
        //例如:\Device\HarddiskVolume1\Users\pc\Desktop\KmdManager.exe --> KmdManager.exe
        CHAR *result_ptr = szProcessName;

        INT cnt = 0;
        for (INT i = strlen(pszProcessPath) - 1; i >= 0; i--)
        {
                if (pszProcessPath != '\\')
                {
                        cnt++;
                }
                else
                {
                        break;
                }
        }

        for (INT i = strlen(pszProcessPath) - cnt; i <strlen(pszProcessPath); i++)
        {
                *result_ptr++ = pszProcessPath;
        }

        *result_ptr = '\0';

        RtlFreeAnsiString(&astring);
        ExFreePool(ProcInfo);
        ZwClose(hProcess);
}


//查找指定进程所有同名字的PID,并且返回他们的个数。
DWORD FindProcess(IN PUNICODE_STRING target, OUT HANDLE *targetPidList)
{
        DbgPrint("%wZ\n", target);
        ULONG i = 0;
        DWORD k = 0;
        PEPROCESS eproc = NULL;
        for (i = 4; i < 262144; i = i + 4)
        {
                eproc = LookupProcess((HANDLE)i);                //调用PsLookupProcessByProcessId函数,返回有效的EPROCESS
                if (eproc)
                {

                        ObDereferenceObject(eproc);

                        //PsGetProcessImageFileName并不好,因为会发生15字节截断。
                        //UCHAR *tempName = PsGetProcessImageFileName(eproc);//获取当前进程的名称
                        CHAR tempName;
                        HANDLE TargetPid = PsGetProcessId(eproc);
                        GetProcessName(TargetPid, tempName);

                        //一些字符串的转化
                        ANSI_STRING temp;
                        RtlInitAnsiString(&temp, (CHAR*)tempName);
                        UNICODE_STRING name;
                        RtlAnsiStringToUnicodeString(&name, &temp, TRUE);

                        //将目标进程名称和当前进程名称相比较,如果相等,就将那个PID存入targetPidList中。
                        if (RtlEqualUnicodeString(target, &name, TRUE))
                        {
                                //HANDLE TargetPid = PsGetProcessId(eproc);
                                DbgPrint("指定进程%wZ, PID:(DEC)%ld--->(HEX)0x%X\n", name, (DWORD)TargetPid, (DWORD)TargetPid);
                                targetPidList = TargetPid;
                        }
                        RtlFreeUnicodeString(&name);
                }
        }
        return k;
}


//正规方法结束进程
BOOLEAN ZwKillProcess(IN HANDLE Pid)
{
        HANDLE hProcess = NULL;
        CLIENT_ID ClientId;
        OBJECT_ATTRIBUTES oa;
        //填充CID
        ClientId.UniqueProcess = Pid;
        ClientId.UniqueThread = 0;
        //填充OA
        oa.Length = sizeof(oa);
        oa.RootDirectory = 0;
        oa.ObjectName = 0;
        oa.Attributes = 0;
        oa.SecurityDescriptor = 0;
        oa.SecurityQualityOfService = 0;

        //打开进程,如果句柄有效,则结束进程
        ZwOpenProcess(&hProcess, 1, &oa, &ClientId);
        if (hProcess)
        {
                NTSTATUS KillResult = ZwTerminateProcess(hProcess, 0);
                if (NT_SUCCESS(KillResult))
                {
                        ZwClose(hProcess);
                        return TRUE;
                }
                else
                {
                        DbgPrint("ZwKillProcess函数中ZwTerminateProcess无法结束进程\n");
                        ZwClose(hProcess);
                        return FALSE;
                }
        }
        else
        {
                DbgPrint("ZwKillProcess函数中ZwOpenProcess函数无法获取进程句柄\n");
                return FALSE;
        }
}

//内存清零法结束进程
VOID PVASE(IN PEPROCESS targetEprocess)
{
        if (targetEprocess == NULL)
        {
                DbgPrint("内存清零法发生错误,参数为空指针....\n");
                return;
        }

        SIZE_T i = 0;
        //依附进程
        KeAttachProcess(targetEprocess);
        for (i = 0x10000; i < 0x20000000; i += PAGE_SIZE)
        {
                __try
                {
                        memset((PVOID)i, 0, PAGE_SIZE);                //把进程内存全部清零

                }
                __except (1)
                {
                        ;
                }
        }
        //退出依附进程
        KeDetachProcess();
}

//使用正规方法和内存清零法结束进程。
VOID KillProcess(IN UNICODE_STRING target)
{
        HANDLE targetPidList;                //目标进程列表
        DbgPrint("正在查找%wZ进程...\n", target);
        DWORD targetPidNumber = FindProcess(&target, targetPidList);
        if (targetPidNumber)
        {
                DbgPrint("发现进程:%wZ,\t共有%d个\n", target, targetPidNumber);
        }
        else
        {
                DbgPrint("没有发现目标进程...\n");
                return;
        }

        for (DWORD i = 0; i < targetPidNumber; i++)
        {
                if (ZwKillProcess(targetPidList))
                {
                        DbgPrint("进程已经结束....\n");
                }
                else
                {
                        DbgPrint("开始暴力结束进程...\n");
                        PVASE(LookupProcess(targetPidList));
                }
                MySleep(1);                //暂停1秒,使进程列表刷新一下。
                //再用LoopupProcess查看一下那个进程id是否还存在,如果存在就进行targetPidList中的下一个pid
                //如果不存在,说明进程结束完毕...
                if (LookupProcess(targetPidList) == NULL)
                {
                        DbgPrint("结束的是第%d个同名目标进程\n", i + 1);
                        return;
                }
        }
}


//删除文件/文件夹
BOOLEAN DeleteFileFolder(IN CHAR *sFileName)
{
        NTSTATUS st;
        OBJECT_ATTRIBUTES ObjectAttributes;
        UNICODE_STRING UniFileName;
        ANSI_STRING AnsiFileName;
        RtlInitAnsiString(&AnsiFileName, sFileName);

        //把WCHAR* 转化为 UNICODE_STRING
        RtlAnsiStringToUnicodeString(&UniFileName, &AnsiFileName, TRUE);
        DbgPrint("%wZ\n", UniFileName);

        //设置包OBJECT对象并使用ZwDeleteFile删除
        InitializeObjectAttributes(&ObjectAttributes,
                &UniFileName,
                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
                NULL,
                NULL);

        //删除文件
        st = ZwDeleteFile(&ObjectAttributes);
        if (!NT_SUCCESS(st))
        {
                DbgPrint("删除文件出错:0x%XL\n", st);
                RtlFreeUnicodeString(&UniFileName);
                return FALSE;
        }

        //回收UniFileName字符串空间
        RtlFreeUnicodeString(&UniFileName);
        return TRUE;

}



//重新覆写3次目标文件,以至于无法恢复。
BOOLEAN WriteFile(IN CHAR *filename)
{
        ANSI_STRING astr;
        RtlInitAnsiString(&astr, filename);
        UNICODE_STRING u_filename;
        RtlAnsiStringToUnicodeString(&u_filename, &astr, TRUE);

        OBJECT_ATTRIBUTES objectAttributes;
        IO_STATUS_BLOCK iostatus;
        FILE_BASIC_INFORMATION fbi;
        HANDLE hfile;

        //初始化objectAttributes
        InitializeObjectAttributes(&objectAttributes, &u_filename,
                OBJ_CASE_INSENSITIVE,                //对大小写敏感
                NULL,
                NULL);

        //创建文件
        NTSTATUS status = ZwCreateFile(&hfile,
                STANDARD_RIGHTS_ALL | GENERIC_WRITE,                //使用最大权限创建文件,并且赋予write权限
                &objectAttributes,
                &iostatus,
                NULL,
                FILE_ATTRIBUTE_NORMAL,
                FILE_SHARE_WRITE,
                FILE_OPEN_IF,                //即使该文件存在,也要创建
                FILE_SYNCHRONOUS_IO_NONALERT,
                NULL,
                0);

        if (!NT_SUCCESS(status))
        {
                DbgPrint("ZwCreateFile创建文件出错:0x%XL\n", status);
                ZwClose(hfile);
                RtlFreeUnicodeString(&u_filename);
                return FALSE;
        }

        /*首先对文件属性进行判断和更改,如果是只读或者隐藏属性,把他们的属性改掉*/
        status = ZwQueryInformationFile(hfile,
                &iostatus,
                &fbi,
                sizeof(FILE_BASIC_INFORMATION),
                FileBasicInformation);

        if (!NT_SUCCESS(status))
        {
                DbgPrint("ZwQueryInformationFile error:0x%XL\n", status);
                ZwClose(hfile);
                RtlFreeUnicodeString(&u_filename);
                return FALSE;
        }

        DbgPrint("CreationTime:0x%X\t Attributes:0x%X\n", fbi.CreationTime, fbi.FileAttributes);

        if (fbi.FileAttributes & FILE_ATTRIBUTE_HIDDEN)
        {
                DbgPrint("文件具有隐藏属性,即将取消文件的隐藏属性\n");
                fbi.FileAttributes &= ~FILE_ATTRIBUTE_HIDDEN;
        }
        if (fbi.FileAttributes & FILE_ATTRIBUTE_READONLY)
        {
                DbgPrint("文件具有只读属性,即将取消文件的只读属性\n");
                fbi.FileAttributes &= ~FILE_ATTRIBUTE_READONLY;
        }

        DbgPrint("下面开始进行文件的属性设置\n");
        status = ZwSetInformationFile(hfile,
                &iostatus,
                &fbi,
                sizeof(FILE_BASIC_INFORMATION),
                FileBasicInformation);

        if (!NT_SUCCESS(status))
        {
                DbgPrint("Set information error:0x%XL\n", status);
                RtlFreeUnicodeString(&u_filename);
                ZwClose(hfile);
                return FALSE;
        }


        //下面开始写文件操作
        ULONG buffer_size = 1024;
        PUCHAR pBuffer = (PUCHAR)ExAllocatePool(PagedPool, buffer_size);

        /*构造要填充的数据:
        手持两把锟斤拷
        口中疾呼烫烫烫
        脚踏千朵屯屯屯
        笑看万物锘锘锘*/
        RtlFillMemory(pBuffer, buffer_size, 0xCC);

        //写3次文件
        for (int i = 0; i < 3; i++)
        {
                status = ZwWriteFile(hfile,
                        NULL,
                        NULL,
                        NULL,
                        &iostatus,
                        pBuffer,
                        buffer_size,
                        NULL,
                        NULL);

                if (!NT_SUCCESS(status))
                {
                        DbgPrint("写文件操作出错:0x%XL\n", status);
                        ExFreePool(pBuffer);
                        RtlFreeUnicodeString(&u_filename);
                        ZwClose(hfile);
                        return FALSE;
                }
        }
        ExFreePool(pBuffer);
        RtlFreeUnicodeString(&u_filename);
        ZwClose(hfile);
        return TRUE;
}


//驱动加载函数,里面进行了驱动的初始化工作
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObj, PUNICODE_STRING pRegistryString)
{
        NTSTATUS status = STATUS_SUCCESS;
        UNICODE_STRING ustrLinkName;
        UNICODE_STRING ustrDevName;
        PDEVICE_OBJECT pDevObj;

        //初始化驱动例程
        pDriverObj->MajorFunction = DispatchCreate;
        pDriverObj->MajorFunction = DispatchClose;
        pDriverObj->MajorFunction = DispatchIoctl;
        pDriverObj->DriverUnload = DriverUnload;


        //创建驱动设备
        RtlInitUnicodeString(&ustrDevName, DEVICE_NAME);
        status = IoCreateDevice(pDriverObj, 0, &ustrDevName,
                FILE_DEVICE_UNKNOWN, 0, FALSE, &pDevObj);
        //判断是否创建成功
        if (!NT_SUCCESS(status)) return status;
        //设置读写方式为缓冲区方式读写
        pDriverObj->Flags |= DO_BUFFERED_IO;


        if (IoIsWdmVersionAvailable(1, 0x10))
                RtlInitUnicodeString(&ustrLinkName, LINK_GLOBAL_NAME);
        else
                RtlInitUnicodeString(&ustrLinkName, LINK_NAME);

        //创建符号连接
        status = IoCreateSymbolicLink(&ustrLinkName, &ustrDevName);
        if (!NT_SUCCESS(status))
        {
                IoDeleteDevice(pDevObj);
                return status;
        }

        //添加功能初始化的代码
        DbgPrint("DriverEntry!\n");

        return STATUS_SUCCESS;
}




驱动加载程序:SCMload.exe
//参考自Tesla.Angela大佬的教程SCM加载驱动,orz
//参考《windows驱动开发详解》

#include <stdio.h>
#include <windows.h>
#include <string.h>
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "advapi32.lib")

//函数声明
void cDrvCtrl();
void Release_cDrvCtrl();
BOOL GetSvcHandle(PCHAR pServiceName);
BOOL Install(PCHAR pSysPath, PCHAR pServiceName, PCHAR pDisplayName);
BOOL Start();
BOOL Stop();
BOOL Remove();
BOOL Open(PCHAR pLinkName);
BOOL IoControl(DWORD dwIoCode, PVOID InBuff, DWORD InBuffLen, PVOID OutBuffer, DWORD OutBuffLen, DWORD *RealRetBytes);
DWORD CTL_CODE_GEN(DWORD lngFunction);

//常量定义
DWORD m_dwLastError;
PCHAR m_pSysPath;
PCHAR m_pServiceName;
PCHAR m_pDisplayName;
HANDLE m_hDriver;
SC_HANDLE m_hSCManager;
SC_HANDLE m_hService;

//初始化函数
void cDrvCtrl()
{
        m_pSysPath = NULL;
        m_pServiceName = NULL;
        m_pDisplayName = NULL;
        m_hService = NULL;
        m_hDriver = INVALID_HANDLE_VALUE;
}

//结尾函数,一些释放资源的操作
void Release_cDrvCtrl()
{
        CloseServiceHandle(m_hService);
        CloseServiceHandle(m_hSCManager);
        CloseHandle(m_hDriver);
}

//获取服务句柄
BOOL GetSvcHandle(PCHAR pServiceName)
{
        m_pServiceName = pServiceName;
        m_hSCManager = OpenSCManagerA(NULL, NULL, SC_MANAGER_ALL_ACCESS);
        if (m_hSCManager == NULL)
        {
                m_dwLastError = GetLastError();
                return FALSE;
        }

        m_hService = OpenServiceA(m_hSCManager, m_pServiceName, SERVICE_ALL_ACCESS);
        if (m_hService == NULL)
        {
                CloseServiceHandle(m_hSCManager);
                return FALSE;
        }
        else
        {
                return TRUE;
        }
}

//安装服务
BOOL Install(PCHAR pSysPath, PCHAR pServiceName, PCHAR pDisplayName)
{
        m_pSysPath = pSysPath;
        m_pServiceName = pServiceName;
        m_pDisplayName = pDisplayName;
        m_hSCManager = OpenSCManagerA(NULL, NULL, SC_MANAGER_ALL_ACCESS);
        if (m_hSCManager == NULL)
        {
                m_dwLastError = GetLastError();
                return FALSE;
        }

        m_hService = CreateServiceA(m_hSCManager, m_pServiceName, m_pDisplayName,
                SERVICE_ALL_ACCESS,
                SERVICE_KERNEL_DRIVER,
                SERVICE_DEMAND_START,
                SERVICE_ERROR_NORMAL,
                m_pSysPath, NULL, NULL, NULL, NULL, NULL);

        if (m_hService == NULL)
        {
                m_dwLastError = GetLastError();
                if (ERROR_SERVICE_EXISTS == m_dwLastError)
                {
                        m_hService = OpenServiceA(m_hSCManager, m_pServiceName, SERVICE_ALL_ACCESS);
                        if (m_hService == NULL)
                        {
                                CloseServiceHandle(m_hSCManager);
                                return FALSE;
                        }
                }
                else
                {
                        CloseServiceHandle(m_hSCManager);
                        return FALSE;
                }
        }
        return TRUE;
}

//开始启动服务
BOOL Start()
{
        if (!StartServiceA(m_hService, NULL, NULL))
        {
                m_dwLastError = GetLastError();
                return FALSE;
        }
        return TRUE;
}


//停止服务
BOOL Stop()
{
        SERVICE_STATUS ss;
        GetSvcHandle(m_pServiceName);
        if (!ControlService(m_hService, SERVICE_CONTROL_STOP, &ss))
        {
                m_dwLastError = GetLastError();
                return FALSE;
        }
        return TRUE;
}


//移除服务
BOOL Remove()
{
        GetSvcHandle(m_pServiceName);
        if (!DeleteService(m_hService))
        {
                m_dwLastError = GetLastError();
                return FALSE;
        }
        return TRUE;
}


//打开链接,例如:\\\\.\\xxoo
BOOL Open(IN PCHAR pLinkName)
{
        if (m_hDriver != INVALID_HANDLE_VALUE)
        {
                return TRUE;
        }

        m_hDriver = CreateFileA(pLinkName,
                GENERIC_READ | GENERIC_WRITE,
                0,
                NULL,
                OPEN_EXISTING,
                FILE_ATTRIBUTE_NORMAL,
                0);

        if (m_hDriver != INVALID_HANDLE_VALUE)
        {
                return TRUE;
        }
        else
        {
                return FALSE;
        }
}


//最重要的DeviceIoControl
BOOL IoControl(DWORD dwIoCode, PVOID InBuff, DWORD InBuffLen, PVOID OutBuffer, DWORD OutBuffLen, DWORD *RealRetBytes)
{
        DWORD dw;
        BOOL b = DeviceIoControl(m_hDriver, CTL_CODE_GEN(dwIoCode), InBuff, InBuffLen, OutBuffer, OutBuffLen, &dw, NULL);
        if (RealRetBytes)
        {
                *RealRetBytes = dw;
        }
        return b;
}


DWORD CTL_CODE_GEN(DWORD lngFunction)
{
        return (FILE_DEVICE_UNKNOWN * 65536) | (FILE_ANY_ACCESS * 16384) | (lngFunction * 4) | METHOD_BUFFERED;
}


//获取路径
void GetAppPath(char *szCurFile)
{
        GetModuleFileNameA(0, szCurFile, MAX_PATH);
        for (SIZE_T i = strlen(szCurFile) - 1; i >= 0; i--)
        {
                if (szCurFile == '\\')
                {
                        szCurFile = '\0';
                        break;
                }
        }
}

//从文件路径中分离文件名称和后缀
void GetNameAndExtension(char *filepath, char *name, char *extension)
{
        //获取文件名称
        char *nameptr = name;
        int cnt = 0;
        for (int i = strlen(filepath) - 1; i >= 0; i--)
        {
                if (filepath != '\\')
                {
                        cnt++;
                }
                else
                {
                        break;
                }
        }

        for (int i = strlen(filepath) - cnt; i < strlen(filepath); i++)
        {
                *nameptr++ = filepath;
        }

        *nameptr = '\0';

        //获取文件扩展名
        char *extenptr = extension;
        cnt = 0;
        for (int i = strlen(name) - 1; i >= 0; i--)
        {
                if (name != '.')
                {
                        cnt++;
                }
                else
                {
                        break;
                }
        }

        for (int i = strlen(name) - cnt; i < strlen(name); i++)
        {
                *extenptr++ = name;
        }

        *extenptr = '\0';
}


//主函数
int main(void)
{
        BOOL b;
        //初始化cDrvCtrl
        cDrvCtrl();

        //设置驱动名称
        CHAR szSysFile = { 0 };
        CHAR szSvcLnkName[] = "watermelon";
        GetAppPath(szSysFile);
        strcat(szSysFile, "watermelon.sys");

        //安装并且启动驱动
        b = Install(szSysFile, szSvcLnkName, szSvcLnkName);
        b = Start();
        if (b == 1)
        {
                printf("驱动加载完毕...\n");
        }
        else
        {
                printf("驱动加载失败,请联系starlight_chou@163.com,谢谢。\n");
                return 0;
        }

        //打开驱动的符号链接
        Open("\\\\.\\watermelon");

        //获取文件名称和后缀名
        CHAR filename = { 0 };
        CHAR name = { 0 };
        CHAR extension = { 0 };

        printf("请将目标文件拖拽至此:");
        gets(filename);                                       
        GetNameAndExtension(filename, name, extension);
        printf("文件名称:%s\n文件后缀名:%s\n", name, extension);


        //根据文件后缀名不同来对不同的文件进行不同的控制码控制驱动
        if (strcmp(extension, "exe") == 0)
        {
                IoControl(0x801, name, strlen(name), 0, 0, 0);
        }
        else if (strcmp(extension, "txt") == 0)
        {
                printf("文件类型为TXT文本文件,如果默认打开程序非notepad.exe,请先确保关闭它\n");
                IoControl(0x802, name, strlen(name), 0, 0, 0);
        }
        else if (strcmp(extension, "doc") == 0 || strcmp(extension,"docx")==0)
        {
                IoControl(0x803, name, strlen(name), 0, 0, 0);
        }
        else if(strcmp(extension,"xls")==0 || strcmp(extension,"xlsx")==0)
        {
                IoControl(0x804, name, strlen(name), 0, 0, 0);
        }
        else if (strcmp(extension, "ppt") == 0 || strcmp(extension, "pptx") == 0)
        {
                IoControl(0x805, name, strlen(name), 0, 0, 0);
        }
        else
        {
                printf("文件类型为其他文件,请先确认已经关闭它,确保能够正常删除\n");
                IoControl(0x807, name, strlen(name), 0, 0, 0);
        }               

        printf("文件路径为:%s\n", filename);
        IoControl(0x806, filename, strlen(filename), 0, 0, 0);                //删除文件通信控制


        //关闭符号连接句柄
        CloseHandle(m_hDriver);
        //停止并卸载驱动
        b = Stop();
        b = Remove();
        if (b == 1)
        {
                printf("驱动卸载完毕...\n");
        }
        else
        {
                printf("驱动卸载失败,请联系starlight_chou@163.com,谢谢。\n");
        }


        //释放资源
        Release_cDrvCtrl();

        getchar();
        return 0;
}



最后再次感谢站长和Tesla.Angela大佬分享的丰富的教学资源,同时感谢众多老哥给的指导。

watermelon 发表于 2019-1-30 16:49:33

忘了说了,最好在win7 x64双机调试环境下测试程序。

唐凌 发表于 2019-1-30 21:51:22

你枚举并结束进程是为了解锁文件被进程的占用?搞得太繁琐了,而且不太优雅。直接用ZwQuerySystemInformation枚举系统内所有句柄,然后用ZwDuplicateObject关闭句柄就可以解锁了(如果文件被占用,还可以省掉一步ZwCreateFile),这样一来就不必结束进程了。
另外要说直接读写磁盘,这个是个大坑,你后面可能会学到磁盘类驱动级别的读写,磁盘小端口类驱动的读写(这两种你要学会构建IRP并发送,弄后者还要会构建SCSI指令,到这个级别时普通的还原软件已经可以被穿透了),磁盘DMA级别的读写(基本上就是在写磁盘驱动了,你需要适配IDE,AHCI,NVMe,SCSI四种协议。大多数还原软件都会被穿透),此外你还要学会解析文件系统(NTFS,FAT等上百种文件系统,虽然会搞前两种大概就够了)

watermelon 发表于 2019-1-30 22:08:40

tangptr@126.com 发表于 2019-1-30 21:51
你枚举并结束进程是为了解锁文件被进程的占用?搞得太繁琐了,而且不太优雅。直接用ZwQuerySystemInformati ...

啊学习到了,话说ZwDuplicateObject这个api还是第一次见到:D
网上面一些博客都是讲的应用层用DeviceIoControl来控制和驱动的通信来直接读写磁盘,同时并没有那篇博客指明如何用驱动程序来进行直接读写文件对应区域的磁盘,我百度api也没有查到相关的。tangptr大佬这么一说感觉是个很大的工程。

watermelon 发表于 2019-1-30 22:11:05

tangptr@126.com 发表于 2019-1-30 21:51
你枚举并结束进程是为了解锁文件被进程的占用?搞得太繁琐了,而且不太优雅。直接用ZwQuerySystemInformati ...

刚才百度了一下ZwDuplicateObject,发现的确有人用这个api进行强删文件的操作。

唐凌 发表于 2019-1-31 01:46:55

watermelon 发表于 2019-1-30 22:08
啊学习到了,话说ZwDuplicateObject这个api还是第一次见到
网上面一些博客都是讲的应用层用DeviceIoCon ...

如果搞直接磁盘操作的话,可以搜关键词:
IoAllocateIrp
IoCallDriver
...
太多了说不下去了
另外重点是解析文件系统,不是用啥API了。

watermelon 发表于 2019-1-31 12:26:30

tangptr@126.com 发表于 2019-1-31 01:46
如果搞直接磁盘操作的话,可以搜关键词:
IoAllocateIrp
IoCallDriver


:-O 哦哦

watermelon 发表于 2019-1-31 12:39:36

tangptr@126.com 发表于 2019-1-31 01:46
如果搞直接磁盘操作的话,可以搜关键词:
IoAllocateIrp
IoCallDriver


话说小弟在文中第8条发现遍历进程时候发现会有较多的同名字的进程,但是有些同名字的进程用内存清零法没有办法结束,我当时是跳过去没有办法结束的进程,转而去结束另一个同名字的进程,直到它被结束掉。

比如说有一个QQProtect.exe的应用程序,我在windbg上用!process 0 0看的时候有4个同名字的进程,并且每个进程的pid都不一样,而前3个较低pid的进程没有办法结束,最后一个QQProtect.exe进程可以结束,并且结束后QQ退出掉了。那tangptr大佬感觉那前3个QQProtect.exe是怎么回事呢?

唐凌 发表于 2019-1-31 13:24:45

watermelon 发表于 2019-1-31 12:39
话说小弟在文中第8条发现遍历进程时候发现会有较多的同名字的进程,但是有些同名字的进程用内存清零法没 ...

你咋也玩起这种肮脏的玩意了。。话说我这看过去只有一个QQProtect.exe。

我已经在和流氓软件的对肛中弃坑了,对于这是咋回事,我没啥感觉,基本上大概可能也许应该是你算法不好。
或者你试试取消对Intel VT-x/AMD-V的虚拟化看看?软件可以通过cr3-load-exit实现对KeAttachProcess的拦截。腾讯还玩不玩硬件虚拟化这茬我现在已经不知道了。
另外我直接在任务管理器里点击关闭是可以直接关掉的

watermelon 发表于 2019-1-31 13:39:46

tangptr@126.com 发表于 2019-1-31 13:24
你咋也玩起这种肮脏的玩意了。。话说我这看过去只有一个QQProtect.exe。

我已经在和流氓软件的对肛中弃 ...

好的好的,我也是为了测试程序“内存清零”来弄得,测试了QQProtect.exe,360tray.exe,360Safe.exe来的。感谢tangptr大佬指导!
页: [1]
查看完整版本: 【C语言】文件擦写删除操作