Skip to content

Commit bb14dc9

Browse files
author
Josh Lospinoso
committed
Windows x86 POC
1 parent d486ed9 commit bb14dc9

7 files changed

+423
-1
lines changed

Gargoyle.sln

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio 14
4+
VisualStudioVersion = 14.0.25420.1
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Gargoyle", "Gargoyle.vcxproj", "{76CDDD0D-EA56-4AA9-9B94-41390A34AB23}"
7+
EndProject
8+
Global
9+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
10+
Debug|x86 = Debug|x86
11+
Release|x86 = Release|x86
12+
EndGlobalSection
13+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
14+
{76CDDD0D-EA56-4AA9-9B94-41390A34AB23}.Debug|x86.ActiveCfg = Debug|Win32
15+
{76CDDD0D-EA56-4AA9-9B94-41390A34AB23}.Debug|x86.Build.0 = Debug|Win32
16+
{76CDDD0D-EA56-4AA9-9B94-41390A34AB23}.Release|x86.ActiveCfg = Release|Win32
17+
{76CDDD0D-EA56-4AA9-9B94-41390A34AB23}.Release|x86.Build.0 = Release|Win32
18+
EndGlobalSection
19+
GlobalSection(SolutionProperties) = preSolution
20+
HideSolutionNode = FALSE
21+
EndGlobalSection
22+
EndGlobal

Gargoyle.vcxproj

+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<ItemGroup Label="ProjectConfigurations">
4+
<ProjectConfiguration Include="Debug|Win32">
5+
<Configuration>Debug</Configuration>
6+
<Platform>Win32</Platform>
7+
</ProjectConfiguration>
8+
<ProjectConfiguration Include="Release|Win32">
9+
<Configuration>Release</Configuration>
10+
<Platform>Win32</Platform>
11+
</ProjectConfiguration>
12+
</ItemGroup>
13+
<PropertyGroup Label="Globals">
14+
<ProjectGuid>{76CDDD0D-EA56-4AA9-9B94-41390A34AB23}</ProjectGuid>
15+
<Keyword>Win32Proj</Keyword>
16+
<RootNamespace>Gargoyle</RootNamespace>
17+
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
18+
</PropertyGroup>
19+
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
20+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
21+
<ConfigurationType>Application</ConfigurationType>
22+
<UseDebugLibraries>true</UseDebugLibraries>
23+
<PlatformToolset>v140</PlatformToolset>
24+
<CharacterSet>Unicode</CharacterSet>
25+
</PropertyGroup>
26+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
27+
<ConfigurationType>Application</ConfigurationType>
28+
<UseDebugLibraries>false</UseDebugLibraries>
29+
<PlatformToolset>v140</PlatformToolset>
30+
<WholeProgramOptimization>true</WholeProgramOptimization>
31+
<CharacterSet>Unicode</CharacterSet>
32+
</PropertyGroup>
33+
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
34+
<ItemGroup>
35+
<CustomBuild Include="setup.nasm">
36+
<FileType>Document</FileType>
37+
<Message>Assembling setup.nasm</Message>
38+
<Command>nasm -f bin setup.nasm -o $(Configuration)\setup.pic</Command>
39+
<Outputs>$(Configuration)\setup.pic</Outputs>
40+
</CustomBuild>
41+
<CustomBuild Include="gadget.nasm">
42+
<FileType>Document</FileType>
43+
<Message>Assembling gadget.nasm</Message>
44+
<Command>nasm -f bin gadget.nasm -o $(Configuration)\gadget.pic</Command>
45+
<Outputs>$(Configuration)\gadget.pic</Outputs>
46+
</CustomBuild>
47+
</ItemGroup>
48+
<ImportGroup Label="ExtensionSettings">
49+
</ImportGroup>
50+
<ImportGroup Label="Shared">
51+
</ImportGroup>
52+
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
53+
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
54+
</ImportGroup>
55+
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
56+
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
57+
</ImportGroup>
58+
<PropertyGroup Label="UserMacros" />
59+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
60+
<LinkIncremental>true</LinkIncremental>
61+
</PropertyGroup>
62+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
63+
<LinkIncremental>false</LinkIncremental>
64+
</PropertyGroup>
65+
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
66+
<ClCompile>
67+
<PrecompiledHeader>
68+
</PrecompiledHeader>
69+
<WarningLevel>Level3</WarningLevel>
70+
<Optimization>Disabled</Optimization>
71+
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
72+
</ClCompile>
73+
<Link>
74+
<SubSystem>Console</SubSystem>
75+
<GenerateDebugInformation>true</GenerateDebugInformation>
76+
</Link>
77+
</ItemDefinitionGroup>
78+
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
79+
<ClCompile>
80+
<WarningLevel>Level3</WarningLevel>
81+
<PrecompiledHeader>
82+
</PrecompiledHeader>
83+
<Optimization>MaxSpeed</Optimization>
84+
<FunctionLevelLinking>true</FunctionLevelLinking>
85+
<IntrinsicFunctions>true</IntrinsicFunctions>
86+
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
87+
</ClCompile>
88+
<Link>
89+
<SubSystem>Console</SubSystem>
90+
<EnableCOMDATFolding>true</EnableCOMDATFolding>
91+
<OptimizeReferences>true</OptimizeReferences>
92+
<GenerateDebugInformation>true</GenerateDebugInformation>
93+
</Link>
94+
</ItemDefinitionGroup>
95+
<ItemGroup>
96+
<ClCompile Include="main.cpp" />
97+
</ItemGroup>
98+
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
99+
<ImportGroup Label="ExtensionTargets">
100+
</ImportGroup>
101+
</Project>

Gargoyle.vcxproj.filters

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<ItemGroup>
4+
<Filter Include="Source Files">
5+
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
6+
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
7+
</Filter>
8+
<Filter Include="Header Files">
9+
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
10+
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
11+
</Filter>
12+
<Filter Include="Resource Files">
13+
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
14+
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
15+
</Filter>
16+
</ItemGroup>
17+
<ItemGroup>
18+
<ClCompile Include="main.cpp">
19+
<Filter>Source Files</Filter>
20+
</ClCompile>
21+
</ItemGroup>
22+
<ItemGroup>
23+
<CustomBuild Include="setup.nasm" />
24+
</ItemGroup>
25+
</Project>

README.md

+27-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,32 @@
44
# How it works
55
![gargoyle infographic](https://github.com/JLospinoso/gargoyle/raw/master/infographic.png)
66

7-
# Building from source
7+
# Build from source
8+
9+
*gargoyle* is only implemented for 32-bit Windows (64-bit Windows on Windows is fine). You must have the following installed:
10+
11+
* [Visual Studio](https://www.visualstudio.com/downloads/): 2015 Community is tested, but it may work for other versions.
12+
* [Netwide Assembler](http://www.nasm.us/pub/nasm/releasebuilds/?C=M;O=D) v2.12.02 x64 is tested, but it may work for other versions. Make sure `nasm.exe` is on your path.
13+
14+
Clone *gargoyle*:
15+
16+
```sh
17+
git clone https://github.com/JLospinoso/gargoyle.git
18+
```
19+
20+
Open `Gargoyle.sln`, build, and run. There is some harness code in `main.cpp` that configures the following three components:
21+
22+
* *gargoyle* stack trampoline, stack, and configuration (read/write memory on the heap)
23+
* *gargoyle* position independent code (PIC) that receives the ROP gadget/stack trampoline and runs arbitrary code
24+
* A ROP gadget. If you have `mshtml.dll`, *gargoyle* will load it into memory and use it. If it is not available, you will have to tell *gargoyle* to allocate its own (3-byte) ROP gadget on the heap:
25+
26+
```cpp
27+
// main.cpp
28+
auto use_mshtml{ true };
29+
auto gadget_memory = get_gadget(use_mshtml, gadget_pic_path);
30+
```
31+
32+
Every 5 seconds, gargoyle will pop up an empty message box then unmark itself executable. For fun, use [Sysinternals's excellent VMMap tool](https://technet.microsoft.com/en-us/sysinternals/vmmap.aspx) to examine when *gargoyle*'s PIC is executable. If a message box is active, *gargoyle* will be executable. If it is not, *gargoyle* should not be executable. The PIC's address is printed to `stdout` just before the harness calls into the PIC.
833
934
# More information
35+
Blog post coming soon at [lospi.net](https://jlospinoso.github.io/).

gadget.nasm

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
BITS 32
2+
3+
pop eax
4+
pop esp
5+
ret

main.cpp

+129
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
#include <vector>
2+
#include <tuple>
3+
#include <fstream>
4+
#include <iostream>
5+
#include "Windows.h"
6+
#include <psapi.h>
7+
8+
using namespace std;
9+
10+
namespace {
11+
typedef void(*callable)(void*);
12+
constexpr DWORD invocation_interval_ms = 5 * 1000;
13+
constexpr size_t stack_size = 0x10000;
14+
15+
struct SetupConfiguration {
16+
uint32_t initialized;
17+
void* setup_address;
18+
uint32_t setup_length;
19+
void* VirtualProtectEx;
20+
void* WaitForSingleObjectEx;
21+
void* CreateWaitableTimer;
22+
void* SetWaitableTimer;
23+
void* MessageBox;
24+
void* tramp_addr;
25+
void* sleep_handle;
26+
uint32_t interval;
27+
void* target;
28+
uint8_t shadow[8];
29+
};
30+
31+
struct StackTrampoline {
32+
void* VirtualProtectEx;
33+
void* return_address;
34+
void* current_process;
35+
void* address;
36+
uint32_t size;
37+
uint32_t protections;
38+
void* old_protections_ptr;
39+
uint32_t old_protections;
40+
void* setup_config;
41+
};
42+
43+
struct Workspace {
44+
SetupConfiguration config;
45+
uint8_t stack[stack_size];
46+
StackTrampoline tramp;
47+
};
48+
}
49+
50+
Workspace& allocate_workspace() {
51+
auto result = VirtualAllocEx(GetCurrentProcess(), nullptr, sizeof(Workspace), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
52+
if (!result) throw runtime_error("Couldn't VirtualAllocEx: " + GetLastError());
53+
RtlSecureZeroMemory(result, sizeof(Workspace));
54+
return *static_cast<Workspace*>(result);
55+
}
56+
57+
tuple<void*, size_t> allocate_pic(const string& filename) {
58+
fstream file_stream{ filename, fstream::in | fstream::ate | fstream::binary };
59+
if (!file_stream) throw runtime_error("Couldn't open " + filename);
60+
auto pic_size = static_cast<size_t>(file_stream.tellg());
61+
file_stream.seekg(0, fstream::beg);
62+
auto pic = VirtualAllocEx(GetCurrentProcess(), nullptr, pic_size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
63+
if (!pic) throw runtime_error("Couldn't VirtualAllocEx: " + GetLastError());
64+
file_stream.read(static_cast<char*>(pic), pic_size);
65+
file_stream.close();
66+
DWORD old_protection;
67+
auto prot_result = VirtualProtectEx(GetCurrentProcess(), pic, pic_size, PAGE_EXECUTE_READ, &old_protection);
68+
if (!prot_result) throw runtime_error("Couldn't VirtualProtectEx: " + GetLastError());
69+
return { pic, pic_size };
70+
}
71+
72+
void* get_gadget(bool use_mshtml, const string& gadget_pic_path) {
73+
if (use_mshtml) {
74+
auto mshtml_base = reinterpret_cast<uint8_t*>(LoadLibraryA("mshtml.dll"));
75+
return mshtml_base + 7165405;
76+
} else {
77+
void* memory; size_t size;
78+
tie(memory, size) = allocate_pic(gadget_pic_path);
79+
return memory;
80+
}
81+
}
82+
83+
void launch(const string& setup_pic_path, const string& gadget_pic_path) {
84+
void* setup_memory; size_t setup_size;
85+
tie(setup_memory, setup_size) = allocate_pic(setup_pic_path);
86+
87+
auto use_mshtml{ true };
88+
auto gadget_memory = get_gadget(use_mshtml, gadget_pic_path);
89+
90+
auto& scratch_memory = allocate_workspace();
91+
auto& config = scratch_memory.config;
92+
auto& tramp = scratch_memory.tramp;
93+
94+
tramp.old_protections_ptr = &tramp.old_protections;
95+
tramp.protections = PAGE_EXECUTE_READ;
96+
tramp.current_process = GetCurrentProcess();
97+
tramp.VirtualProtectEx = VirtualProtectEx;
98+
tramp.size = static_cast<uint32_t>(setup_size);
99+
tramp.address = setup_memory;
100+
tramp.return_address = setup_memory;
101+
tramp.setup_config = &config;
102+
103+
config.setup_address = setup_memory;
104+
config.setup_length = static_cast<uint32_t>(setup_size);
105+
config.VirtualProtectEx = VirtualProtectEx;
106+
config.WaitForSingleObjectEx = WaitForSingleObjectEx;
107+
config.CreateWaitableTimer = CreateWaitableTimerW;
108+
config.SetWaitableTimer = SetWaitableTimer;
109+
config.MessageBox = MessageBox;
110+
config.tramp_addr = &tramp;
111+
config.interval = invocation_interval_ms;
112+
config.target = gadget_memory;
113+
114+
printf("Gargoyle PIC located at --> %p\n", setup_memory);
115+
printf("ROP gadget located at ----> %p\n", gadget_memory);
116+
printf("Scratch memory located ---> %p\n", &scratch_memory);
117+
printf("Top of stack -------------> %p\n", &scratch_memory.stack);
118+
printf("Trampoline cast location -> %p\n", &scratch_memory.tramp);
119+
120+
reinterpret_cast<callable>(setup_memory)(&config);
121+
}
122+
123+
int main() {
124+
try {
125+
launch("setup.pic", "gadget.pic");
126+
} catch (exception& e) {
127+
cerr << "Exception caught: " << e.what() << endl;
128+
}
129+
}

0 commit comments

Comments
 (0)