简单实现文件防删除,说简单是因为没有用很底层的技术,例如文件驱动之类。我只用最简单的方法实现了, 使用 ring3 的API hook 技术。
随着技术的发展这种技术已经过不了很多的主动防御技术了。主要是思路和方法和分析过程。(高手飘过)
ring3 下挂钩 API 基本上也就是修改导入表,和Inline hook 修改前5个字节这几种方法。挂钩Native API 没有什么区别,也就是多声明几个结构和变量类型。关于挂钩API 请参见:www.xfocus.net/articles/200403/681.html
文件删除的ring3 API 是DeleteFile, 此API 存在于kernel32.dll 中,用OD分析一下。(哪个都可以,IDA更不用说)
DeleteFileA 的反汇编代码:
7C80D2FB >/$ 8BFF mov edi, edi
7C80D2FD |. 55 push ebp
7C80D2FE |. 8BEC mov ebp, esp
7C80D300 |. FF75 08 push dword ptr [ebp+8]
7C80D303 |. E8 17790100 call 7C824C1F
7C80D308 |. 85C0 test eax, eax
7C80D30A |. 74 08 je short 7C80D314
7C80D30C |. FF70 04 push dword ptr [eax+4] ; /FileName
7C80D30F |. E8 3D170000 call DeleteFileW ; \DeleteFileW
7C80D314 |> 5D pop ebp
7C80D315 \. C2 0400 retn 4
可以得到一个流程 DeleteFileA --> DeleteFileW
DeleteFileW 的反汇编代码:
7C80EA51 > $ 8BFF mov edi, edi
7C80EA53 . 55 push ebp
7C80EA54 . 8BEC mov ebp, esp
7C80EA56 . 83EC 50 sub esp, 50
7C80EA59 . 56 push esi
7C80EA5A . 8D45 C8 lea eax, dword ptr [ebp-38]
7C80EA5D . 50 push eax
7C80EA5E . 33F6 xor esi, esi
7C80EA60 . 56 push esi
7C80EA61 . 8D45 E0 lea eax, dword ptr [ebp-20]
7C80EA64 . 50 push eax
7C80EA65 . FF75 08 push dword ptr [ebp+8]
7C80EA68 . C645 FF 00 mov byte ptr [ebp-1], 0
7C80EA6C . FF15 5411807C call dword ptr [<&ntdll.RtlDosPathNam>; ntdll.RtlDosPathNameToRelativeNtPathName_U
7C80EA72 . 84C0 test al, al
7C80EA74 . 0F84 FBCC0200 je 7C83B775
7C80EA7A . 8B45 E4 mov eax, dword ptr [ebp-1C]
7C80EA7D . 8945 F4 mov dword ptr [ebp-C], eax
7C80EA80 . 8B45 C8 mov eax, dword ptr [ebp-38]
7C80EA83 . 66:3BC6 cmp ax, si
7C80EA86 . 0F85 E9660200 jnz 7C835175
7C80EA8C . 8975 D0 mov dword ptr [ebp-30], esi
7C80EA8F > 8B45 D0 mov eax, dword ptr [ebp-30]
7C80EA92 . 53 push ebx
7C80EA93 . 57 push edi
7C80EA94 . 8945 B4 mov dword ptr [ebp-4C], eax
7C80EA97 . 8D45 E0 lea eax, dword ptr [ebp-20]
7C80EA9A . 8945 B8 mov dword ptr [ebp-48], eax
7C80EA9D . BF 40402000 mov edi, 204040
7C80EAA2 . 57 push edi
7C80EAA3 . 6A 07 push 7
7C80EAA5 . 8D45 E8 lea eax, dword ptr [ebp-18]
7C80EAA8 . 50 push eax
7C80EAA9 . 8D45 B0 lea eax, dword ptr [ebp-50]
7C80EAAC . 50 push eax
7C80EAAD . 68 80000100 push 10080 ; UNICODE "ocuments and
7C80EAB2 . 8D45 F8 lea eax, dword ptr [ebp-8]
7C80EAB5 . 8975 C0 mov dword ptr [ebp-40], esi
7C80EAB8 . 8975 C4 mov dword ptr [ebp-3C], esi
7C80EABB . 8B35 1410807C mov esi, dword ptr [<&ntdll.NtOpenFi>; ntdll.ZwOpenFile
7C80EAC1 . 50 push eax
7C80EAC2 . C745 B0 18000>mov dword ptr [ebp-50], 18
7C80EAC9 . C745 BC 40000>mov dword ptr [ebp-44], 40
7C80EAD0 . FFD6 call esi ; <&ntdll.NtOpenFile>
7C80EAD2 . 8BD8 mov ebx, eax
7C80EAD4 . 85DB test ebx, ebx
7C80EAD6 .^ 0F8C F5F4FFFF jl 7C80DFD1
7C80EADC . 6A 23 push 23 ; /InfoClass = FileAttributeTagInformation
7C80EADE . 6A 08 push 8 ; |Bufsize = 8
7C80EAE0 . 8D45 D8 lea eax, dword ptr [ebp-28] ; |
7C80EAE3 . 50 push eax ; |Buffer
7C80EAE4 . 8D45 E8 lea eax, dword ptr [ebp-18] ; |
7C80EAE7 . 50 push eax ; |pStatusBlock
7C80EAE8 . FF75 F8 push dword ptr [ebp-8] ; |hFile
7C80EAEB . FF15 1810807C call dword ptr [<&ntdll.NtQueryInform>; \ZwQueryInformationFile
7C80EAF1 . 8BD8 mov ebx, eax
7C80EAF3 . 85DB test ebx, ebx
7C80EAF5 . 0F8C D5CC0200 jl 7C83B7D0
7C80EAFB . 8B45 D8 mov eax, dword ptr [ebp-28]
7C80EAFE . 25 00040000 and eax, 400
7C80EB03 . 0F85 0ECD0200 jnz 7C83B817
7C80EB09 > 85C0 test eax, eax
7C80EB0B . 0F85 1CCD0200 jnz 7C83B82D
7C80EB11 > 8D45 C8 lea eax, dword ptr [ebp-38]
7C80EB14 . 50 push eax
7C80EB15 . FF15 5011807C call dword ptr [<&ntdll.RtlReleaseRel>; ntdll.RtlReleaseRelativeName
7C80EB1B . FF75 F4 push dword ptr [ebp-C]
7C80EB1E . 64:A1 1800000>mov eax, dword ptr fs:[18]
7C80EB24 . 8B40 30 mov eax, dword ptr [eax+30]
7C80EB27 . 6A 00 push 0
7C80EB29 . FF70 18 push dword ptr [eax+18]
7C80EB2C . FF15 1010807C call dword ptr [<&ntdll.RtlFreeHeap>] ; ntdll.RtlFreeHeap
7C80EB32 . 6A 0D push 0D ; /InfoClass = FileDispositionInformation
7C80EB34 . 6A 01 push 1 ; |Bufsize = 1
7C80EB36 . 8D45 0B lea eax, dword ptr [ebp+B] ; |
7C80EB39 . 50 push eax ; |Buffer
7C80EB3A . 8D45 E8 lea eax, dword ptr [ebp-18] ; |
7C80EB3D . 50 push eax ; |pStatusBlock
7C80EB3E . FF75 F8 push dword ptr [ebp-8] ; |hFile
7C80EB41 . C645 0B 01 mov byte ptr [ebp+B], 1 ; |
7C80EB45 . FF15 3010807C call dword ptr [<&ntdll.NtSetInformat>; \ntdll.ZwSetInformationFile
7C80EB4B . FF75 F8 push dword ptr [ebp-8] ; /Handle
7C80EB4E . 8BF0 mov esi, eax ; |
7C80EB50 . FF15 3810807C call dword ptr [<&ntdll.NtClose>] ; \ZwClose
7C80EB56 . 85F6 test esi, esi
7C80EB58 .^ 0F8C 1F91FFFF jl 7C807C7D
7C80EB5E . 33C0 xor eax, eax
7C80EB60 . 40 inc eax
7C80EB61 > 5F pop edi
7C80EB62 . 5B pop ebx
7C80EB63 > 5E pop esi
7C80EB64 . C9 leave
7C80EB65 . C2 0400 retn 4
得到一个流程 DeleteFileA --> DeleteFileW --> ntdll.ZwSetInformationFile,因此我们只要挂钩了ZwSetInformationFile 就可以简单实现目标了。这里写出了新的 ZwSetInformationFile 函数。
typedef LONG NTSTATUS;
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
#define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L)
#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
typedef struct _IO_STATUS_BLOCK {
DWORD Status;
ULONG Information;
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
typedef enum _FILE_INFORMATION_CLASS {
// end_wdm
FileDirectoryInformation = 1,
FileFullDirectoryInformation, // 2
FileBothDirectoryInformation, // 3
FileBasicInformation, // 4 wdm
FileStandardInformation, // 5 wdm
FileInternalInformation, // 6
FileEaInformation, // 7
FileAccessInformation, // 8
FileNameInformation, // 9
FileRenameInformation, // 10
FileLinkInformation, // 11
FileNamesInformation, // 12
FileDispositionInformation, // 13
FilePositionInformation, // 14 wdm
FileFullEaInformation, // 15
FileModeInformation, // 16
FileAlignmentInformation, // 17
FileAllInformation, // 18
FileAllocationInformation, // 19
FileEndOfFileInformation, // 20 wdm
FileAlternateNameInformation, // 21
FileStreamInformation, // 22
FilePipeInformation, // 23
FilePipeLocalInformation, // 24
FilePipeRemoteInformation, // 25
FileMailslotQueryInformation, // 26
FileMailslotSetInformation, // 27
FileCompressionInformation, // 28
FileObjectIdInformation, // 29
FileCompletionInformation, // 30
FileMoveClusterInformation, // 31
FileQuotaInformation, // 32
FileReparsePointInformation, // 33
FileNetworkOpenInformation, // 34
FileAttributeTagInformation, // 35
FileTrackingInformation, // 36
FileMaximumInformation
// begin_wdm
} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
typedef struct _FILE_NAME_INFORMATION {
ULONG FileNameLength;
WCHAR FileName[1];
} FILE_NAME_INFORMATION, *PFILE_NAME_INFORMATION; typedef NTSTATUS (__stdcall *ZWQUERYINFORMATIONFILE)(
IN HANDLE FileHandle,
OUT PIO_STATUS_BLOCK IoStatusBlock,
OUT PVOID FileInformation,
IN ULONG FileInformationLength,
IN FILE_INFORMATION_CLASS FileInformationClass
);
ZWQUERYINFORMATIONFILE ZwQueryInformationFile;
NTSTATUS __stdcall Hook_ZwSetInformationFile(IN HANDLE FileHandle,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN PVOID FileInformation,
IN ULONG FileInformationLength,
IN FILE_INFORMATION_CLASS FileInformationClass)
{
NTSTATUS ntstatus = STATUS_ACCESS_DENIED;
HMODULE hNtdll = GetModuleHandle("ntdll.dll");
ZwQueryInformationFile = (ZWQUERYINFORMATIONFILE)GetProcAddress(hNtdll, "ZwQueryInformationFile");
IO_STATUS_BLOCK ioStatus;
FILE_NAME_INFORMATION * psi = {0};
psi = (FILE_NAME_INFORMATION*)new WCHAR[sizeof(FILE_NAME_INFORMATION) + 1024];
memset(psi, 0, (sizeof(FILE_NAME_INFORMATION) + 1024)*2);
psi->FileNameLength = 1024;
ntstatus = ZwQueryInformationFile(FileHandle, &ioStatus, psi, sizeof(FILE_NAME_INFORMATION) + 1024 * sizeof(WCHAR), FileNameInformation);
if (ntstatus != STATUS_SUCCESS)
PrintZwError("ZwQueryInformationFile", ntstatus);
ntstatus = STATUS_ACCESS_DENIED;
if( wcsstr(psi->FileName, L"b.txt") == NULL)
ntstatus = ((PFZWSETINFORMATIONFILE)(PROC)g_ZwSetInformationFile)(FileHandle,IoStatusBlock,FileInformation,FileInformationLength,FileInformationClass);
delete psi;
return ntstatus;
} 这只是一个证明性代码,要实战的兄弟自己动点手吧。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课