最近在浏览一篇文章的时候,偶然看见了一篇由readteam工具开发人员写的开发日志,较为详细地描述了自己是如何绕过AV,并且将部分代码贴了出来,直接从 c++ 反转 Ntdll.dll 和硬编码系统调用。

  • 反编译NtCreateFile 和 NtWriteFile 函数并找到系统调用。
  • 创建函数原型
  • 对函数系统调用进行硬编码。
  • 使用 main 和 All done 中的功能。

反编译NtCreateFile 和NtWriteFile 函数并找到系统调用

首先打开您的 IDA 并从 c:\windows\syswow64\ntdll.dll 拖放 Ntdll.dll,然后确保您导入 idb 文件。
下一步转到 Exports 点击并搜索 Ntcreatefile函数

mov eax, 55h ; 'U' ; NtCreateFile
mov edx, offset _Wow64SystemServiceCall@0 ; Wow64SystemServiceCall()
call edx ; Wow64SystemServiceCall() ; Wow64SystemServiceCall()
retn 2Ch ; ','

move eax,55h 其中55 是 NtCreateFile 函数的系统调用号,因为我们是 64 位系统,我们的应用程序是 86 位,所以我们需要切换到 wow64 位。我们需要对这个 asm 代码做一些更改,要做的使用 call dword ptr fs :[0xC0] 将它手动切换到syswow64,所以对应的代码如下:

mov eax,55h
call dword ptr fs:[0xC0] ; wow64cpu.dll!X86SwitchTo64BitMode
retn 2Ch

现在我们有了 NtcreateFile 的 asm 代码,现在对 NtwriteFile 函数执行相同的操作。

因为我们正在使用 Nt 函数并且我们正在硬编码,所以我们需要注意的每一件事,我们要注意我们要写入它的文件路径。如果我们使用 Winapi 并且需要使用 CreateFileACreateFileW 创建文件,则路径将是这样的:c:\users\mose3c\Desktop\my.jpg
并且 winapi 函数将为您完成所有事情,例如将路径转换为 ​​Nt Path 。如果您打开 ApiMonotring 并跟踪 CreateFile 函数,您可以仔细看到这一点,您将知道该函数为您做了什么。
现在我们的 Nt 路径将是这样的: \??\c:\users\mose3c\Desktop\my.jpg,否则无法正常工作。

创建函数原型

typedef _Return_type_success_(return >= 0) LONG NTSTATUS;
typedef struct _STRING { USHORT Length; USHORT MaximumLength; PCHAR Buffer; } STRING;
typedef STRING* PSTRING;
typedef STRING ANSI_STRING;
typedef PSTRING PANSI_STRING;

typedef struct _UNICODE_STRING
{
USHORT Length;
USHORT MaximumLength;
PWSTR  Buffer;
} UNICODE_STRING, * PUNICODE_STRING;
typedef struct _OBJECT_ATTRIBUTES
{
ULONG          Length;
HANDLE          RootDirectory;
PUNICODE_STRING ObjectName;
ULONG          Attributes;
PVOID          SecurityDescriptor;
PVOID          SecurityQualityOfService;
} OBJECT_ATTRIBUTES, * POBJECT_ATTRIBUTES;
typedef struct _IO_STATUS_BLOCK
{
union
{
NTSTATUS Status;
VOID* Pointer;
};
ULONG_PTR Information;
} IO_STATUS_BLOCK, * PIO_STATUS_BLOCK;

typedef NTSTATUS(NTAPI* _RtlGetVersion)(PRTL_OSVERSIONINFOW lpVersionInformation);

typedef void (NTAPI* _RtlInitUnicodeString)(PUNICODE_STRING DestinationString, PCWSTR SourceString);
typedef DWORD(NTAPI* _RtlAnsiStringToUnicodeString)(PUNICODE_STRING, PANSI_STRING, BOOL);
typedef VOID(NTAPI* _RtlInitString)(PSTRING DestinationString, char* SourceString);
_RtlAnsiStringToUnicodeString RtlAnsiStringToUnicodeString = (_RtlAnsiStringToUnicodeString)GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlAnsiStringToUnicodeString");
_RtlInitUnicodeString RtlInitUnicodeString = (_RtlInitUnicodeString)GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlInitUnicodeString");
_RtlInitString RtlInitString = (_RtlInitString)GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlInitString");


typedef VOID(NTAPI* PIO_APC_ROUTINE)(_In_ PVOID ApcContext, _In_ PIO_STATUS_BLOCK IoStatusBlock, _In_ ULONG Reserved);
typedef NTSTATUS(NTAPI* _NtCreateFile)(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PIO_STATUS_BLOCK IoStatusBlock, PLARGE_INTEGER AllocationSize OPTIONAL, ULONG FileAttributes, ULONG ShareAccess, ULONG CreateDisposition, ULONG CreateOptions, PVOID EaBuffer OPTIONAL, ULONG EaLength);
typedef NTSTATUS(NTAPI* _NtWriteFile)(HANDLE  FileHandle, HANDLE Event, PIO_APC_ROUTINE  ApcRoutine, PVOID  ApcContext, PIO_STATUS_BLOCK IoStatusBlock, PVOID  Buffer, ULONG Length, PLARGE_INTEGER  ByteOffset, PULONG  Key);
typedef NTSTATUS(NTAPI* _NtResumeThread)(HANDLE  ThreadHandle, PULONG  SuspendCount);
typedef NTSTATUS(NTAPI* _NtClose)(HANDLE Handle);

_NtCreateFile NtCreateFile;
_NtWriteFile  NtWriteFile;
_NtResumeThread NtResumeThread;
_NtClose NtClose;

now lets write our inline asm code .

_declspec(naked) NTSTATUS _stdcall WIN10_64_NtCreateFile(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PIO_STATUS_BLOCK IoStatusBlock, PLARGE_INTEGER AllocationSize OPTIONAL, ULONG FileAttributes, ULONG ShareAccess, ULONG CreateDisposition, ULONG CreateOptions, PVOID EaBuffer OPTIONAL, ULONG EaLength)
{
_asm
{
mov    eax, 55h
call    dword ptr fs : [0xC0]
retn    2Ch
}
}

_declspec(naked) NTSTATUS _stdcall WIN10_64_NtWriteFile(HANDLE  FileHandle, HANDLE Event, PIO_APC_ROUTINE  ApcRoutine, PVOID  ApcContext, PIO_STATUS_BLOCK IoStatusBlock, PVOID  Buffer, ULONG Length, PLARGE_INTEGER  ByteOffset, PULONG  Key)
{
_asm
{
mov  eax, 1A0008h
call  dword ptr fs : [0xC0]
retn  24h
}
}

做好这些后,接下来让我们进入我们的最主要的步骤:

我们需要将 WIN10_64_NtCreateFile = 的地址传递给 NtCreateFileWIN10_64_NtWriteFile 的地址传递给 NtWriteFile,例如以下代码

NtCreateFile  = &WIN10_64_NtCreateFile
NtWriteFile = &WIN10_64_NtWriteFile

ok . and now we will define attributes and others args but its not our tutorial will skip it and if you need any help just replay and i will try to help you .

NTSTATUS status;
UNICODE_STRING Ustring;
ANSI_STRING as;
HANDLE hFile;
IO_STATUS_BLOCK risb;
OBJECT_ATTRIBUTES robj;

char szDir[MAX_PATH];
strcpy(szDir, "\\??\\c:\users\mose3c\Desktop\my.txt");

as.Buffer = (char*)malloc(strlen(szDir) + 1);
strcpy(as.Buffer, szDir);
as.Length = as.MaximumLength = Ustring.MaximumLength = Ustring.Length = strlen(szDir);

// convert directory name from ANSI to UNICODE
_RtlAnsiStringToUnicodeString RtlAnsiStringToUnicodeString = (_RtlAnsiStringToUnicodeString)GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlAnsiStringToUnicodeString");
RtlAnsiStringToUnicodeString(&Ustring, &as, TRUE);


RtlFillMemory(&obj, sizeof(OBJECT_ATTRIBUTES), 0);
robj.Length = sizeof(obj);
robj.Attributes = OBJ_CASE_INSENSITIVE;
robj.RootDirectory = NULL;
robj.SecurityDescriptor = NULL;
robj.SecurityQualityOfService = NULL;
robj.ObjectName = &Ustring;

status = NtCreateFile(&hFile, FILE_GENERIC_READ | FILE_GENERIC_WRITE | SYNCHRONIZE, &obj, &risb, 0, FILE_ATTRIBUTE_NORMAL, 0, FILE_OVERWRITE_IF, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
if (!NT_SUCCESS(status))
      return 1 ;

IO_STATUS_BLOCK WFISB;
status = NtWriteFile(hFile, NULL, NULL, NULL, &WFISB, /*your Buffer here*/, BufLen, 0, NULL);

if (!NT_SUCCESS(status))
return 1;

搞定以上内容后,在WIN10就可以运行了,如果需要在Windows 7中运行,需要手动反编译Windows 7ntdll.dll,并且找到相关的API