diff --git a/ - Copy.gitignore b/ - Copy.gitignore
deleted file mode 100644
index 9e5d671..0000000
--- a/ - Copy.gitignore
+++ /dev/null
@@ -1,387 +0,0 @@
-## 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/master/VisualStudio.gitignore
-
-# User-specific files
-*.rsuser
-*.suo
-*.user
-*.userosscache
-*.sln.docstates
-
-# User-specific files (MonoDevelop/Xamarin Studio)
-*.userprefs
-
-# Mono auto generated files
-mono_crash.*
-
-# Build results
-[Dd]ebug/
-[Dd]ebugPublic/
-[Rr]elease/
-[Rr]eleases/
-x64/
-x86/
-[Ww][Ii][Nn]32/
-[Aa][Rr][Mm]/
-[Aa][Rr][Mm]64/
-bld/
-[Bb]in/
-[Oo]bj/
-[Oo]ut/
-[Ll]og/
-[Ll]ogs/
-
-# 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.*
-
-# NUnit
-*.VisualState.xml
-TestResult.xml
-nunit-*.xml
-
-# 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
-*.iobj
-*.pch
-*.pdb
-*.ipdb
-*.pgc
-*.pgd
-*.rsp
-*.sbr
-*.tlb
-*.tli
-*.tlh
-*.tmp
-*.tmp_proj
-*_wpftmp.csproj
-*.log
-*.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_*
-.*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 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
-
-# NVidia Nsight GPU debugger configuration file
-*.nvuser
-
-# MFractors (Xamarin productivity tool) working folder
-.mfractor/
-
-# Local History for Visual Studio
-.localhistory/
-
-# 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/Cobalt/v17/Browse.VC.db
-.vs/Cobalt/v17/.suo
-.vs/Cobalt/v17/fileList.bin
-.vs/Cobalt/v17/ipch/AutoPCH/389a1fa1f6dcd16f/DLLMAIN.ipch
-.vs/Cobalt/v17/ipch/AutoPCH/6b05e7771d6ded15/UTIL.ipch
-Cobalt/x64/Release/Cobalt.tlog/CL.read.1.tlog
-Cobalt/x64/Release/Cobalt.tlog/link.read.1.tlog
-Cobalt/x64/Release/Cobalt.tlog/CL.write.1.tlog
-Cobalte/x64/Release/vc143.pdb
-x64/Release/Cobalt.pdb
-Cobalt/x64/Release/Cobalt.log
-Cobalt/x64/Release/Cobalt.ipdb
-Cobalt/x64/Release/Cobalt.iobj
-.vs/Cobalt/v17/Browse.VC.db
-.vs/Cobalt/v17/.suo
-.vs/Cobalt/v17/.suo
-*.db
-*.bin
-*.ipch
-*.ipdb
-*.iobj
-*.tlog
-*.ifc
-*.pdb
diff --git a/Cobalt/Cobalt.vcxproj b/Cobalt/Cobalt.vcxproj
new file mode 100644
index 0000000..de707e4
--- /dev/null
+++ b/Cobalt/Cobalt.vcxproj
@@ -0,0 +1,165 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 16.0
+ Win32Proj
+ {355fb80e-3008-430e-a074-cdb93819396f}
+ Cobalt
+ 10.0
+
+
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Level3
+ true
+ WIN32;_DEBUG;COBALT_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
+ true
+ Use
+ pch.h
+
+
+ Windows
+ true
+ false
+
+
+
+
+ Level3
+ true
+ true
+ true
+ WIN32;NDEBUG;COBALT_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
+ true
+ Use
+ pch.h
+
+
+ Windows
+ true
+ true
+ true
+ false
+
+
+
+
+ Level3
+ true
+ _DEBUG;COBALT_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
+ true
+ Use
+ pch.h
+ stdcpplatest
+
+
+ Windows
+ true
+ false
+
+
+
+
+ Level3
+ true
+ true
+ true
+ NDEBUG;COBALT_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
+ true
+ NotUsing
+ pch.h
+ stdcpplatest
+
+
+ Windows
+ true
+ true
+ true
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
+
\ No newline at end of file
diff --git a/Cobalt/Cobalt.vcxproj.filters b/Cobalt/Cobalt.vcxproj.filters
new file mode 100644
index 0000000..e4570f9
--- /dev/null
+++ b/Cobalt/Cobalt.vcxproj.filters
@@ -0,0 +1,39 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
+
+ Source Files
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Cobalt/README.md b/Cobalt/README.md
new file mode 100644
index 0000000..71790af
--- /dev/null
+++ b/Cobalt/README.md
@@ -0,0 +1,2 @@
+# Cobalt
+ SSL Bypass for Fortnite
diff --git a/Cobalt/curldefinitions.h b/Cobalt/curldefinitions.h
new file mode 100644
index 0000000..308aa50
--- /dev/null
+++ b/Cobalt/curldefinitions.h
@@ -0,0 +1,146 @@
+#pragma once
+
+//got these definitions from reversing curl
+#define CURLOPT_URL 0x2712
+#define CURLOPT_NOPROXY 0x27C1
+#define CURLOPT_SSL_VERIFYPEER 0x40
+#define CURLOPT_SSL_VERIFYHOST 0x51
+#define CURLOPT_PINNEDPUBLICKEY 0x27F6
+#define CURLOPT_POSTFIELDS 0x271F
+
+//got from curl includes
+typedef enum {
+ CURLE_OK = 0,
+ CURLE_UNSUPPORTED_PROTOCOL, /* 1 */
+ CURLE_FAILED_INIT, /* 2 */
+ CURLE_URL_MALFORMAT, /* 3 */
+ CURLE_NOT_BUILT_IN, /* 4 - [was obsoleted in August 2007 for
+ 7.17.0, reused in April 2011 for 7.21.5] */
+ CURLE_COULDNT_RESOLVE_PROXY, /* 5 */
+ CURLE_COULDNT_RESOLVE_HOST, /* 6 */
+ CURLE_COULDNT_CONNECT, /* 7 */
+ CURLE_WEIRD_SERVER_REPLY, /* 8 */
+ CURLE_REMOTE_ACCESS_DENIED, /* 9 a service was denied by the server
+ due to lack of access - when login fails
+ this is not returned. */
+ CURLE_FTP_ACCEPT_FAILED, /* 10 - [was obsoleted in April 2006 for
+ 7.15.4, reused in Dec 2011 for 7.24.0]*/
+ CURLE_FTP_WEIRD_PASS_REPLY, /* 11 */
+ CURLE_FTP_ACCEPT_TIMEOUT, /* 12 - timeout occurred accepting server
+ [was obsoleted in August 2007 for 7.17.0,
+ reused in Dec 2011 for 7.24.0]*/
+ CURLE_FTP_WEIRD_PASV_REPLY, /* 13 */
+ CURLE_FTP_WEIRD_227_FORMAT, /* 14 */
+ CURLE_FTP_CANT_GET_HOST, /* 15 */
+ CURLE_HTTP2, /* 16 - A problem in the http2 framing layer.
+ [was obsoleted in August 2007 for 7.17.0,
+ reused in July 2014 for 7.38.0] */
+ CURLE_FTP_COULDNT_SET_TYPE, /* 17 */
+ CURLE_PARTIAL_FILE, /* 18 */
+ CURLE_FTP_COULDNT_RETR_FILE, /* 19 */
+ CURLE_OBSOLETE20, /* 20 - NOT USED */
+ CURLE_QUOTE_ERROR, /* 21 - quote command failure */
+ CURLE_HTTP_RETURNED_ERROR, /* 22 */
+ CURLE_WRITE_ERROR, /* 23 */
+ CURLE_OBSOLETE24, /* 24 - NOT USED */
+ CURLE_UPLOAD_FAILED, /* 25 - failed upload "command" */
+ CURLE_READ_ERROR, /* 26 - couldn't open/read from file */
+ CURLE_OUT_OF_MEMORY, /* 27 */
+ /* Note: CURLE_OUT_OF_MEMORY may sometimes indicate a conversion error
+ instead of a memory allocation error if CURL_DOES_CONVERSIONS
+ is defined
+ */
+ CURLE_OPERATION_TIMEDOUT, /* 28 - the timeout time was reached */
+ CURLE_OBSOLETE29, /* 29 - NOT USED */
+ CURLE_FTP_PORT_FAILED, /* 30 - FTP PORT operation failed */
+ CURLE_FTP_COULDNT_USE_REST, /* 31 - the REST command failed */
+ CURLE_OBSOLETE32, /* 32 - NOT USED */
+ CURLE_RANGE_ERROR, /* 33 - RANGE "command" didn't work */
+ CURLE_HTTP_POST_ERROR, /* 34 */
+ CURLE_SSL_CONNECT_ERROR, /* 35 - wrong when connecting with SSL */
+ CURLE_BAD_DOWNLOAD_RESUME, /* 36 - couldn't resume download */
+ CURLE_FILE_COULDNT_READ_FILE, /* 37 */
+ CURLE_LDAP_CANNOT_BIND, /* 38 */
+ CURLE_LDAP_SEARCH_FAILED, /* 39 */
+ CURLE_OBSOLETE40, /* 40 - NOT USED */
+ CURLE_FUNCTION_NOT_FOUND, /* 41 - NOT USED starting with 7.53.0 */
+ CURLE_ABORTED_BY_CALLBACK, /* 42 */
+ CURLE_BAD_FUNCTION_ARGUMENT, /* 43 */
+ CURLE_OBSOLETE44, /* 44 - NOT USED */
+ CURLE_INTERFACE_FAILED, /* 45 - CURLOPT_INTERFACE failed */
+ CURLE_OBSOLETE46, /* 46 - NOT USED */
+ CURLE_TOO_MANY_REDIRECTS, /* 47 - catch endless re-direct loops */
+ CURLE_UNKNOWN_OPTION, /* 48 - User specified an unknown option */
+ CURLE_TELNET_OPTION_SYNTAX, /* 49 - Malformed telnet option */
+ CURLE_OBSOLETE50, /* 50 - NOT USED */
+ CURLE_OBSOLETE51, /* 51 - NOT USED */
+ CURLE_GOT_NOTHING, /* 52 - when this is a specific error */
+ CURLE_SSL_ENGINE_NOTFOUND, /* 53 - SSL crypto engine not found */
+ CURLE_SSL_ENGINE_SETFAILED, /* 54 - can not set SSL crypto engine as
+ default */
+ CURLE_SEND_ERROR, /* 55 - failed sending network data */
+ CURLE_RECV_ERROR, /* 56 - failure in receiving network data */
+ CURLE_OBSOLETE57, /* 57 - NOT IN USE */
+ CURLE_SSL_CERTPROBLEM, /* 58 - problem with the local certificate */
+ CURLE_SSL_CIPHER, /* 59 - couldn't use specified cipher */
+ CURLE_PEER_FAILED_VERIFICATION, /* 60 - peer's certificate or fingerprint
+ wasn't verified fine */
+ CURLE_BAD_CONTENT_ENCODING, /* 61 - Unrecognized/bad encoding */
+ CURLE_LDAP_INVALID_URL, /* 62 - Invalid LDAP URL */
+ CURLE_FILESIZE_EXCEEDED, /* 63 - Maximum file size exceeded */
+ CURLE_USE_SSL_FAILED, /* 64 - Requested FTP SSL level failed */
+ CURLE_SEND_FAIL_REWIND, /* 65 - Sending the data requires a rewind
+ that failed */
+ CURLE_SSL_ENGINE_INITFAILED, /* 66 - failed to initialise ENGINE */
+ CURLE_LOGIN_DENIED, /* 67 - user, password or similar was not
+ accepted and we failed to login */
+ CURLE_TFTP_NOTFOUND, /* 68 - file not found on server */
+ CURLE_TFTP_PERM, /* 69 - permission problem on server */
+ CURLE_REMOTE_DISK_FULL, /* 70 - out of disk space on server */
+ CURLE_TFTP_ILLEGAL, /* 71 - Illegal TFTP operation */
+ CURLE_TFTP_UNKNOWNID, /* 72 - Unknown transfer ID */
+ CURLE_REMOTE_FILE_EXISTS, /* 73 - File already exists */
+ CURLE_TFTP_NOSUCHUSER, /* 74 - No such user */
+ CURLE_CONV_FAILED, /* 75 - conversion failed */
+ CURLE_CONV_REQD, /* 76 - caller must register conversion
+ callbacks using curl_easy_setopt options
+ CURLOPT_CONV_FROM_NETWORK_FUNCTION,
+ CURLOPT_CONV_TO_NETWORK_FUNCTION, and
+ CURLOPT_CONV_FROM_UTF8_FUNCTION */
+ CURLE_SSL_CACERT_BADFILE, /* 77 - could not load CACERT file, missing
+ or wrong format */
+ CURLE_REMOTE_FILE_NOT_FOUND, /* 78 - remote file not found */
+ CURLE_SSH, /* 79 - error from the SSH layer, somewhat
+ generic so the error message will be of
+ interest when this has happened */
+
+ CURLE_SSL_SHUTDOWN_FAILED, /* 80 - Failed to shut down the SSL
+ connection */
+ CURLE_AGAIN, /* 81 - socket is not ready for send/recv,
+ wait till it's ready and try again (Added
+ in 7.18.2) */
+ CURLE_SSL_CRL_BADFILE, /* 82 - could not load CRL file, missing or
+ wrong format (Added in 7.19.0) */
+ CURLE_SSL_ISSUER_ERROR, /* 83 - Issuer check failed. (Added in
+ 7.19.0) */
+ CURLE_FTP_PRET_FAILED, /* 84 - a PRET command failed */
+ CURLE_RTSP_CSEQ_ERROR, /* 85 - mismatch of RTSP CSeq numbers */
+ CURLE_RTSP_SESSION_ERROR, /* 86 - mismatch of RTSP Session Ids */
+ CURLE_FTP_BAD_FILE_LIST, /* 87 - unable to parse FTP file list */
+ CURLE_CHUNK_FAILED, /* 88 - chunk callback reported error */
+ CURLE_NO_CONNECTION_AVAILABLE, /* 89 - No connection available, the
+ session will be queued */
+ CURLE_SSL_PINNEDPUBKEYNOTMATCH, /* 90 - specified pinned public key did not
+ match */
+ CURLE_SSL_INVALIDCERTSTATUS, /* 91 - invalid certificate status */
+ CURLE_HTTP2_STREAM, /* 92 - stream error in HTTP/2 framing layer
+ */
+ CURLE_RECURSIVE_API_CALL, /* 93 - an api function was called from
+ inside a callback */
+ CURLE_AUTH_ERROR, /* 94 - an authentication function returned an
+ error */
+ CURLE_HTTP3, /* 95 - An HTTP/3 layer problem */
+ CURL_LAST /* never use! */
+} CURLcode;
+
+typedef void CURL;
\ No newline at end of file
diff --git a/Cobalt/curlhook.h b/Cobalt/curlhook.h
new file mode 100644
index 0000000..4c95b3a
--- /dev/null
+++ b/Cobalt/curlhook.h
@@ -0,0 +1,74 @@
+#pragma once
+
+#include
+#include
+
+#include "curldefinitions.h"
+#include "memcury.h"
+
+// #define HYBRID_ENABLED
+
+auto (*curl_easy_setopt_original)(CURL* Curl, uintptr_t opt, ...)->CURLcode;
+
+std::string FNhost = "127.0.0.1:3551";
+
+//This routine is used for setting up curl. we will be hijacking this to change the values.
+auto Hijacked_curl_easy_setopt(CURL* Curl, uintptr_t opt, va_list info) -> CURLcode
+{
+ int OFF = 0;
+ switch (opt)
+ {
+ case CURLOPT_NOPROXY:
+ return curl_easy_setopt_original(Curl, opt, "");
+ break;
+
+ case CURLOPT_SSL_VERIFYPEER:
+ return curl_easy_setopt_original(Curl, opt, OFF);
+ break;
+ case CURLOPT_SSL_VERIFYHOST:
+ return curl_easy_setopt_original(Curl, opt, OFF);
+ break;
+ case CURLOPT_PINNEDPUBLICKEY:
+ return CURLcode::CURLE_OK;
+ break;
+ case CURLOPT_URL:
+ std::string url = info;
+ std::regex Host(("(.*).ol.epicgames.com"));
+#ifdef HYBRID_ENABLED
+ if (std::regex_search(info, std::regex(("/fortnite/api/cloudstorage/system")))) {
+ url = std::regex_replace(info, Host, FNhost);
+ }
+ else if (std::regex_search(info, std::regex(("/fortnite/api/v2/versioncheck/")))) {
+ url = std::regex_replace(info, Host, FNhost);
+ }
+ else if (std::regex_search(info, std::regex(("/fortnite/api/game/v2/profile")))) {
+ url = std::regex_replace(info, Host, FNhost);
+ }
+ else if (std::regex_search(info, std::regex(("/content/api/pages/fortnite-game")))) {
+ url = std::regex_replace(info, Host, FNhost);
+ }
+ else if (std::regex_search(info, std::regex(("/affiliate/api/public/affiliates/slug")))) {
+ url = std::regex_replace(info, Host, FNhost);
+ }
+ else if (std::regex_search(info, std::regex(("/socialban/api/public/v1")))) {
+ url = std::regex_replace(info, Host, FNhost);
+ }
+ /* else if (std::regex_search(info, std::regex(ENC("player.platform")))) { // idk if this even works
+ if (version == S13)
+ {
+ url = std::regex_replace(info, Host, "IOS");
+ Log(ENC("Changed Windows to IOS"));
+ }
+ } */
+#else
+ if (std::regex_search(info, Host)) {
+ url = std::regex_replace(info, Host, FNhost);
+ }
+#endif
+ return curl_easy_setopt_original(Curl, opt, url.c_str());
+ break;
+ }
+ return curl_easy_setopt_original(Curl, opt, info);
+}
+
+__int64 sigscan(const std::string& Str) { return Memcury::Scanner::FindPattern(Str.c_str()).Get(); }
\ No newline at end of file
diff --git a/Cobalt/dllmain.cpp b/Cobalt/dllmain.cpp
new file mode 100644
index 0000000..3324c8f
--- /dev/null
+++ b/Cobalt/dllmain.cpp
@@ -0,0 +1,109 @@
+#include
+#include
+#include
+#include "curlhook.h"
+#include "exithook.h"
+
+#define DetoursEasy(address, hook) \
+ DetourTransactionBegin(); \
+ DetourUpdateThread(GetCurrentThread()); \
+ DetourAttach(reinterpret_cast(&address), hook); \
+ DetourTransactionCommit();
+
+auto FindPushWidget()
+{
+ return sigscan("48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 57 48 83 EC 30 48 8B E9 49 8B D9 48 8D 0D ? ? ? ? 49 8B F8 48 8B F2 E8 ? ? ? ? 4C 8B CF 48 89 5C 24 ? 4C 8B C6 48 8B D5 48 8B 48 78");
+}
+
+bool InitializeCurlHook()
+{
+ auto CurlEasySetOptAddr = sigscan("89 54 24 10 4C 89 44 24 18 4C 89 4C 24 20 48 83 EC 28 48 85 C9 75 08 8D 41 2B 48 83 C4 28 C3 4C");
+
+ if (!CurlEasySetOptAddr)
+ {
+ CurlEasySetOptAddr = sigscan("89 54 24 10 4C 89 44 24 18 4C 89 4C 24 20 48 83 EC 28 48 85 C9 75 08 8D 41 2B 48 83 C4 28 C3 4C");
+ }
+
+ if (!CurlEasySetOptAddr)
+ {
+ std::cout << "Failed to find CurlEasySetOptAddr!";
+ return false;
+ }
+
+ curl_easy_setopt_original = decltype(curl_easy_setopt_original)(CurlEasySetOptAddr);
+
+ if (FindPushWidget())
+ {
+ DetoursEasy(curl_easy_setopt_original, Hijacked_curl_easy_setopt);
+ }
+ else
+ {
+ Memcury::VEHHook::AddHook(curl_easy_setopt_original, Hijacked_curl_easy_setopt);
+ }
+
+ return true;
+}
+
+void InitializeExitHook()
+{
+ if (!FindPushWidget())
+ return;
+
+ auto UnsafeEnvironmentPopupAddr = sigscan("4C 8B DC 55 49 8D AB ? ? ? ? 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 85 ? ? ? ? 49 89 73 F0 49 89 7B E8 48 8B F9 4D 89 63 E0 4D 8B E0 4D 89 6B D8");
+
+ if (!UnsafeEnvironmentPopupAddr)
+ {
+ UnsafeEnvironmentPopupAddr = sigscan("4C 8B DC 55 49 8D AB ? ? ? ? 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 85 ? ? ? ?");
+ }
+
+ auto RequestExitWithStatusAddr = sigscan("48 89 5C 24 ? 57 48 83 EC 40 41 B9 ? ? ? ? 0F B6 F9 44 38 0D ? ? ? ? 0F B6 DA 72 24 89 5C 24 30 48 8D 05 ? ? ? ? 89 7C 24 28 4C 8D 05 ? ? ? ? 33 D2 48 89 44 24 ? 33 C9 E8 ? ? ? ?");
+
+ if (!RequestExitWithStatusAddr)
+ {
+ RequestExitWithStatusAddr = sigscan("48 8B C4 48 89 58 18 88 50 10 88 48 08 57 48 83 EC 30");
+ }
+
+ DetoursEasy(UnsafeEnvironmentPopupAddr, UnsafeEnvironmentPopupHook);
+ DetoursEasy(RequestExitWithStatusAddr, RequestExitWithStatusHook);
+}
+
+DWORD WINAPI Main(LPVOID)
+{
+ AllocConsole();
+
+ FILE* fptr;
+ freopen_s(&fptr, "CONOUT$", "w+", stdout);
+
+ bool curlResult = InitializeCurlHook();
+ InitializeExitHook();
+
+ bool result = curlResult;
+
+ if (result)
+ {
+ std::cout << "Cobalt v0.1 initialized sucessfully.";
+ }
+ else
+ {
+ MessageBoxA(0, "Failed to initialize!", "Cobalt", MB_ICONERROR);
+ }
+
+ return 0;
+}
+
+BOOL APIENTRY DllMain( HMODULE hModule,
+ DWORD ul_reason_for_call,
+ LPVOID lpReserved
+ )
+{
+ switch (ul_reason_for_call)
+ {
+ case DLL_PROCESS_ATTACH:
+ CreateThread(0, 0, Main, 0, 0, 0);
+ break;
+ case DLL_PROCESS_DETACH:
+ break;
+ }
+ return TRUE;
+}
+
diff --git a/Cobalt/exithook.h b/Cobalt/exithook.h
new file mode 100644
index 0000000..4485dbe
--- /dev/null
+++ b/Cobalt/exithook.h
@@ -0,0 +1,14 @@
+#pragma once
+
+void (*RequestExitWithStatus)(bool Force, unsigned char Code);
+void RequestExitWithStatusHook(bool Force, unsigned char Code)
+{
+ // printf("[VEH] RequestExitWithStatus Call Forced: %i ReturnCode: %u\n", Force, Code);
+}
+
+void (*UnsafeEnvironmentPopup)(wchar_t** unknown1, unsigned __int8 _case, __int64 unknown2, char unknown3);
+
+void UnsafeEnvironmentPopupHook(wchar_t** unknown1, unsigned __int8 _case, __int64 unknown2, char unknown3)
+{
+ // printf("[VEH]
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#pragma comment(lib, "Dbghelp.lib")
+
+#define MemcuryAssert(cond) \
+ if (!(cond)) \
+ { \
+ MessageBoxA(nullptr, #cond, __FUNCTION__, MB_ICONERROR | MB_OK); \
+ Memcury::Safety::FreezeCurrentThread(); \
+ }
+
+#define MemcuryAssertM(cond, msg) \
+ if (!(cond)) \
+ { \
+ MessageBoxA(nullptr, msg, __FUNCTION__, MB_ICONERROR | MB_OK); \
+ Memcury::Safety::FreezeCurrentThread(); \
+ }
+
+#define MemcuryThrow(msg) \
+ MessageBoxA(nullptr, msg, __FUNCTION__, MB_ICONERROR | MB_OK); \
+ Memcury::Safety::FreezeCurrentThread();
+
+namespace Memcury
+{
+ extern "C" IMAGE_DOS_HEADER __ImageBase;
+
+ inline auto GetCurrentModule() -> HMODULE
+ {
+ return reinterpret_cast(&__ImageBase);
+ }
+
+ namespace Util
+ {
+ template
+ constexpr static auto IsInRange(T value, T min, T max) -> bool
+ {
+ return value >= min && value < max;
+ }
+
+ constexpr auto StrHash(const char* str, int h = 0) -> unsigned int
+ {
+ return !str[h] ? 5381 : (StrHash(str, h + 1) * 33) ^ str[h];
+ }
+
+ inline auto IsSamePage(void* A, void* B) -> bool
+ {
+ MEMORY_BASIC_INFORMATION InfoA;
+ if (!VirtualQuery(A, &InfoA, sizeof(InfoA)))
+ {
+ return true;
+ }
+
+ MEMORY_BASIC_INFORMATION InfoB;
+ if (!VirtualQuery(B, &InfoB, sizeof(InfoB)))
+ {
+ return true;
+ }
+
+ return InfoA.BaseAddress == InfoB.BaseAddress;
+ }
+
+ inline auto GetModuleStartAndEnd() -> std::pair
+ {
+ auto HModule = GetCurrentModule();
+ auto NTHeaders = reinterpret_cast((uintptr_t)HModule + reinterpret_cast((uintptr_t)HModule)->e_lfanew);
+
+ uintptr_t dllStart = (uintptr_t)HModule;
+ uintptr_t dllEnd = (uintptr_t)HModule + NTHeaders->OptionalHeader.SizeOfImage;
+
+ return { dllStart, dllEnd };
+ }
+
+ inline auto CopyToClipboard(std::string str)
+ {
+ auto mem = GlobalAlloc(GMEM_FIXED, str.size() + 1);
+ memcpy(mem, str.c_str(), str.size() + 1);
+
+ OpenClipboard(nullptr);
+ EmptyClipboard();
+ SetClipboardData(CF_TEXT, mem);
+ CloseClipboard();
+
+ GlobalFree(mem);
+ }
+ }
+
+ namespace Safety
+ {
+ enum class ExceptionMode
+ {
+ None,
+ CatchDllExceptionsOnly,
+ CatchAllExceptions
+ };
+
+ static auto FreezeCurrentThread() -> void
+ {
+ SuspendThread(GetCurrentThread());
+ }
+
+ static auto PrintStack(CONTEXT* ctx) -> void
+ {
+ STACKFRAME64 stack;
+ memset(&stack, 0, sizeof(STACKFRAME64));
+
+ auto process = GetCurrentProcess();
+ auto thread = GetCurrentThread();
+
+ SymInitialize(process, NULL, TRUE);
+
+ bool result;
+ DWORD64 displacement = 0;
+
+ char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)]{ 0 };
+ char name[256]{ 0 };
+ char module[256]{ 0 };
+
+ PSYMBOL_INFO symbolInfo = (PSYMBOL_INFO)buffer;
+
+ for (ULONG frame = 0;; frame++)
+ {
+ result = StackWalk64(
+ IMAGE_FILE_MACHINE_AMD64,
+ process,
+ thread,
+ &stack,
+ ctx,
+ NULL,
+ SymFunctionTableAccess64,
+ SymGetModuleBase64,
+ NULL);
+
+ if (!result)
+ break;
+
+ symbolInfo->SizeOfStruct = sizeof(SYMBOL_INFO);
+ symbolInfo->MaxNameLen = MAX_SYM_NAME;
+ SymFromAddr(process, (ULONG64)stack.AddrPC.Offset, &displacement, symbolInfo);
+
+ HMODULE hModule = NULL;
+ lstrcpyA(module, "");
+ GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (const wchar_t*)(stack.AddrPC.Offset), &hModule);
+
+ if (hModule != NULL)
+ GetModuleFileNameA(hModule, module, 256);
+
+ printf("[%lu] Name: %s - Address: %p - Module: %s\n", frame, symbolInfo->Name, (void*)symbolInfo->Address, module);
+ }
+ }
+
+ template
+ auto MemcuryGlobalHandler(EXCEPTION_POINTERS* ExceptionInfo) -> long
+ {
+ auto [dllStart, dllEnd] = Util::GetModuleStartAndEnd();
+
+ if constexpr (mode == ExceptionMode::CatchDllExceptionsOnly)
+ {
+ if (!Util::IsInRange(ExceptionInfo->ContextRecord->Rip, dllStart, dllEnd))
+ {
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+ }
+
+ auto message = std::format("Memcury caught an exception at [{:x}]\nPress Yes if you want the address to be copied to your clipboard", ExceptionInfo->ContextRecord->Rip);
+ if (MessageBoxA(nullptr, message.c_str(), "Error", MB_ICONERROR | MB_YESNO) == IDYES)
+ {
+ std::string clip = std::format("{:x}", ExceptionInfo->ContextRecord->Rip);
+ Util::CopyToClipboard(clip);
+ }
+
+ PrintStack(ExceptionInfo->ContextRecord);
+
+ FreezeCurrentThread();
+
+ return EXCEPTION_EXECUTE_HANDLER;
+ }
+
+ template
+ static auto SetExceptionMode() -> void
+ {
+ SetUnhandledExceptionFilter(MemcuryGlobalHandler);
+ }
+ }
+
+ namespace Globals
+ {
+ constexpr const bool bLogging = true;
+
+ inline const char* moduleName = nullptr;
+ }
+
+ namespace ASM
+ {
+ //@todo: this whole namespace needs a rework, should somehow make this more modern and less ugly.
+ enum MNEMONIC : uint8_t
+ {
+ JMP_REL8 = 0xEB,
+ JMP_REL32 = 0xE9,
+ JMP_EAX = 0xE0,
+ CALL = 0xE8,
+ LEA = 0x8D,
+ CDQ = 0x99,
+ CMOVL = 0x4C,
+ CMOVS = 0x48,
+ CMOVNS = 0x49,
+ NOP = 0x90,
+ INT3 = 0xCC,
+ RETN_REL8 = 0xC2,
+ RETN = 0xC3,
+ NONE = 0x00
+ };
+
+ constexpr int SIZE_OF_JMP_RELATIVE_INSTRUCTION = 5;
+ constexpr int SIZE_OF_JMP_ABSLOUTE_INSTRUCTION = 13;
+
+ constexpr auto MnemonicToString(MNEMONIC e) -> const char*
+ {
+ switch (e)
+ {
+ case JMP_REL8:
+ return "JMP_REL8";
+ case JMP_REL32:
+ return "JMP_REL32";
+ case JMP_EAX:
+ return "JMP_EAX";
+ case CALL:
+ return "CALL";
+ case LEA:
+ return "LEA";
+ case CDQ:
+ return "CDQ";
+ case CMOVL:
+ return "CMOVL";
+ case CMOVS:
+ return "CMOVS";
+ case CMOVNS:
+ return "CMOVNS";
+ case NOP:
+ return "NOP";
+ case INT3:
+ return "INT3";
+ case RETN_REL8:
+ return "RETN_REL8";
+ case RETN:
+ return "RETN";
+ case NONE:
+ return "NONE";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ constexpr auto Mnemonic(const char* s) -> MNEMONIC
+ {
+ switch (Util::StrHash(s))
+ {
+ case Util::StrHash("JMP_REL8"):
+ return JMP_REL8;
+ case Util::StrHash("JMP_REL32"):
+ return JMP_REL32;
+ case Util::StrHash("JMP_EAX"):
+ return JMP_EAX;
+ case Util::StrHash("CALL"):
+ return CALL;
+ case Util::StrHash("LEA"):
+ return LEA;
+ case Util::StrHash("CDQ"):
+ return CDQ;
+ case Util::StrHash("CMOVL"):
+ return CMOVL;
+ case Util::StrHash("CMOVS"):
+ return CMOVS;
+ case Util::StrHash("CMOVNS"):
+ return CMOVNS;
+ case Util::StrHash("NOP"):
+ return NOP;
+ case Util::StrHash("INT3"):
+ return INT3;
+ case Util::StrHash("RETN_REL8"):
+ return RETN_REL8;
+ case Util::StrHash("RETN"):
+ return RETN;
+ default:
+ return NONE;
+ }
+ }
+
+ inline auto byteIsA(uint8_t byte, MNEMONIC opcode) -> bool
+ {
+ return byte == opcode;
+ }
+
+ inline auto byteIsAscii(uint8_t byte) -> bool
+ {
+ static constexpr bool isAscii[0x100] = {
+ false, false, false, false, false, false, false, false, false, true, true, false, false, true, false, false,
+ false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
+ true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, false,
+ false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false
+ };
+
+ return isAscii[byte];
+ }
+
+ inline bool isJump(uint8_t byte)
+ {
+ return byte >= 0x70 && byte <= 0x7F;
+ }
+
+ static auto pattern2bytes(const char* pattern) -> std::vector
+ {
+ auto bytes = std::vector{};
+ const auto start = const_cast(pattern);
+ const auto end = const_cast(pattern) + strlen(pattern);
+
+ for (auto current = start; current < end; ++current)
+ {
+ if (*current == '?')
+ {
+ ++current;
+ if (*current == '?')
+ ++current;
+ bytes.push_back(-1);
+ }
+ else
+ {
+ bytes.push_back(strtoul(current, ¤t, 16));
+ }
+ }
+ return bytes;
+ }
+ }
+
+ namespace PE
+ {
+ inline auto SetCurrentModule(const char* moduleName) -> void
+ {
+ Globals::moduleName = moduleName;
+ }
+
+ inline auto GetModuleBase() -> uintptr_t
+ {
+ return reinterpret_cast(GetModuleHandleA(Globals::moduleName));
+ }
+
+ inline auto GetDOSHeader() -> PIMAGE_DOS_HEADER
+ {
+ return reinterpret_cast(GetModuleBase());
+ }
+
+ inline auto GetNTHeaders() -> PIMAGE_NT_HEADERS
+ {
+ return reinterpret_cast(GetModuleBase() + GetDOSHeader()->e_lfanew);
+ }
+
+ class Address
+ {
+ uintptr_t _address;
+
+ public:
+ Address()
+ {
+ _address = 0;
+ }
+
+ Address(uintptr_t address)
+ : _address(address)
+ {
+ }
+
+ Address(void* address)
+ : _address(reinterpret_cast(address))
+ {
+ }
+
+ auto operator=(uintptr_t address) -> Address
+ {
+ _address = address;
+ return *this;
+ }
+
+ auto operator=(void* address) -> Address
+ {
+ _address = reinterpret_cast(address);
+ return *this;
+ }
+
+ auto operator+(uintptr_t offset) -> Address
+ {
+ return Address(_address + offset);
+ }
+
+ bool operator>(uintptr_t offset)
+ {
+ return _address > offset;
+ }
+
+ bool operator>(Address address)
+ {
+ return _address > address._address;
+ }
+
+ bool operator<(uintptr_t offset)
+ {
+ return _address < offset;
+ }
+
+ bool operator<(Address address)
+ {
+ return _address < address._address;
+ }
+
+ bool operator>=(uintptr_t offset)
+ {
+ return _address >= offset;
+ }
+
+ bool operator>=(Address address)
+ {
+ return _address >= address._address;
+ }
+
+ bool operator<=(uintptr_t offset)
+ {
+ return _address <= offset;
+ }
+
+ bool operator<=(Address address)
+ {
+ return _address <= address._address;
+ }
+
+ bool operator==(uintptr_t offset)
+ {
+ return _address == offset;
+ }
+
+ bool operator==(Address address)
+ {
+ return _address == address._address;
+ }
+
+ bool operator!=(uintptr_t offset)
+ {
+ return _address != offset;
+ }
+
+ bool operator!=(Address address)
+ {
+ return _address != address._address;
+ }
+
+ auto RelativeOffset(uint32_t offset) -> Address
+ {
+ _address = ((_address + offset + 4) + *(int32_t*)(_address + offset));
+ return *this;
+ }
+
+ auto AbsoluteOffset(uint32_t offset) -> Address
+ {
+ _address = _address + offset;
+ return *this;
+ }
+
+ auto Jump() -> Address
+ {
+ if (ASM::isJump(*reinterpret_cast(_address)))
+ {
+ UINT8 toSkip = *reinterpret_cast(_address + 1);
+ _address = _address + 2 + toSkip;
+ }
+
+ return *this;
+ }
+
+ auto Get() -> uintptr_t
+ {
+ return _address;
+ }
+
+ template
+ auto GetAs() -> T
+ {
+ return reinterpret_cast(_address);
+ }
+
+ auto IsValid() -> bool
+ {
+ return _address != 0;
+ }
+ };
+
+ class Section
+ {
+ public:
+ std::string sectionName;
+ IMAGE_SECTION_HEADER rawSection;
+
+ static auto GetAllSections() -> std::vector
+ {
+ std::vector sections;
+
+ auto sectionsSize = GetNTHeaders()->FileHeader.NumberOfSections;
+ auto section = IMAGE_FIRST_SECTION(GetNTHeaders());
+
+ for (WORD i = 0; i < sectionsSize; i++, section++)
+ {
+ auto secName = std::string((char*)section->Name);
+
+ sections.push_back({ secName, *section });
+ }
+
+ return sections;
+ }
+
+ static auto GetSection(std::string sectionName) -> Section
+ {
+ for (auto& section : GetAllSections())
+ {
+ if (section.sectionName == sectionName)
+ {
+ return section;
+ }
+ }
+
+ MemcuryThrow("Section not found");
+ return Section{};
+ }
+
+ auto GetSectionSize() -> uint32_t
+ {
+ return rawSection.Misc.VirtualSize;
+ }
+
+ auto GetSectionStart() -> Address
+ {
+ return Address(GetModuleBase() + rawSection.VirtualAddress);
+ }
+
+ auto GetSectionEnd() -> Address
+ {
+ return Address(GetSectionStart() + GetSectionSize());
+ }
+
+ auto isInSection(Address address) -> bool
+ {
+ return address >= GetSectionStart() && address < GetSectionEnd();
+ }
+ };
+ }
+
+ class Scanner
+ {
+ PE::Address _address;
+
+ public:
+ Scanner(PE::Address address)
+ : _address(address)
+ {
+ }
+
+ static auto SetTargetModule(const char* moduleName) -> void
+ {
+ PE::SetCurrentModule(moduleName);
+ }
+
+ static auto FindPatternEx(HANDLE handle, const char* pattern, const char* mask, uint64_t begin, uint64_t end) -> Scanner
+ {
+ auto scan = [](const char* pattern, const char* mask, char* begin, unsigned int size) -> char*
+ {
+ size_t patternLen = strlen(mask);
+ for (unsigned int i = 0; i < size - patternLen; i++)
+ {
+ bool found = true;
+ for (unsigned int j = 0; j < patternLen; j++)
+ {
+ if (mask[j] != '?' && pattern[j] != *(begin + i + j))
+ {
+ found = false;
+ break;
+ }
+ }
+
+ if (found)
+ return (begin + i);
+ }
+ return nullptr;
+ };
+
+ uint64_t match = NULL;
+ SIZE_T bytesRead;
+ char* buffer = nullptr;
+ MEMORY_BASIC_INFORMATION mbi = { 0 };
+
+ uint64_t curr = begin;
+
+ for (uint64_t curr = begin; curr < end; curr += mbi.RegionSize)
+ {
+ if (!VirtualQueryEx(handle, (void*)curr, &mbi, sizeof(mbi)))
+ continue;
+
+ if (mbi.State != MEM_COMMIT || mbi.Protect == PAGE_NOACCESS)
+ continue;
+
+ buffer = new char[mbi.RegionSize];
+
+ if (ReadProcessMemory(handle, mbi.BaseAddress, buffer, mbi.RegionSize, &bytesRead))
+ {
+ char* internalAddr = scan(pattern, mask, buffer, (unsigned int)bytesRead);
+
+ if (internalAddr != nullptr)
+ {
+ match = curr + (uint64_t)(internalAddr - buffer);
+ break;
+ }
+ }
+ }
+ delete[] buffer;
+
+ MemcuryAssertM(match != 0, "FindPatternEx return nullptr");
+
+ return Scanner(match);
+ }
+
+ static auto FindPatternEx(HANDLE handle, const char* sig) -> Scanner
+ {
+ char pattern[100];
+ char mask[100];
+
+ char lastChar = ' ';
+ unsigned int j = 0;
+
+ for (unsigned int i = 0; i < strlen(sig); i++)
+ {
+ if ((sig[i] == '?' || sig[i] == '*') && (lastChar != '?' && lastChar != '*'))
+ {
+ pattern[j] = mask[j] = '?';
+ j++;
+ }
+
+ else if (isspace(lastChar))
+ {
+ pattern[j] = lastChar = (char)strtol(&sig[i], 0, 16);
+ mask[j] = 'x';
+ j++;
+ }
+ lastChar = sig[i];
+ }
+ pattern[j] = mask[j] = '\0';
+
+ auto module = (uint64_t)GetModuleHandle(nullptr);
+
+ return FindPatternEx(handle, pattern, mask, module, module + Memcury::PE::GetNTHeaders()->OptionalHeader.SizeOfImage);
+ }
+
+ static auto FindPattern(const char* signature) -> Scanner
+ {
+ PE::Address add{ nullptr };
+
+ const auto sizeOfImage = PE::GetNTHeaders()->OptionalHeader.SizeOfImage;
+ auto patternBytes = ASM::pattern2bytes(signature);
+ const auto scanBytes = reinterpret_cast(PE::GetModuleBase());
+
+ const auto s = patternBytes.size();
+ const auto d = patternBytes.data();
+
+ for (auto i = 0ul; i < sizeOfImage - s; ++i)
+ {
+ bool found = true;
+ for (auto j = 0ul; j < s; ++j)
+ {
+ if (scanBytes[i + j] != d[j] && d[j] != -1)
+ {
+ found = false;
+ break;
+ }
+ }
+
+ if (found)
+ {
+ add = reinterpret_cast(&scanBytes[i]);
+ break;
+ }
+ }
+
+ // MemcuryAssertM(add != 0, "FindPattern return nullptr");
+
+ return Scanner(add);
+ }
+
+ static auto FindPointerRef(void* Pointer, int useRefNum = 0, bool bUseFirstResult = false) -> Scanner // credit me and ender
+ {
+ PE::Address add{ nullptr };
+
+ auto textSection = PE::Section::GetSection(".text");
+
+ const auto scanBytes = reinterpret_cast(textSection.GetSectionStart().Get());
+
+ int aa = 0;
+
+ // scan only text section
+ for (DWORD i = 0x0; i < textSection.GetSectionSize(); i++)
+ {
+ if ((scanBytes[i] == ASM::CMOVL || scanBytes[i] == ASM::CMOVS) && (scanBytes[i + 1] == ASM::LEA || scanBytes[i + 1] == 0x8B))
+ {
+ if (PE::Address(&scanBytes[i]).RelativeOffset(3).GetAs() == Pointer)
+ {
+ add = PE::Address(&scanBytes[i]);
+
+ // LOG_INFO(LogDev, "2add: 0x{:x}", add.Get() - __int64(GetModuleHandleW(0)));
+
+ if (bUseFirstResult)
+ return Scanner(add);
+
+ /* if (++aa > useRefNum)
+ break; */
+ }
+ }
+
+ if (scanBytes[i] == ASM::CALL)
+ {
+ if (PE::Address(&scanBytes[i]).RelativeOffset(1).GetAs() == Pointer)
+ {
+ add = PE::Address(&scanBytes[i]);
+
+ // LOG_INFO(LogDev, "1add: 0x{:x}", add.Get() - __int64(GetModuleHandleW(0)));
+
+ if (bUseFirstResult)
+ return Scanner(add);
+
+ /* if (++aa > useRefNum)
+ break; */
+ }
+ }
+ }
+
+ if (add == 0)
+ {
+ MessageBoxA(0, "FindPointerRef return nullptr", "Memcury", MB_OK);
+ }
+ else
+ {
+ // MessageBoxA(0, std::format("FindPointerRef return 0x{:x}", add.Get() - __int64(GetModuleHandleW(0))).c_str(), "Memcury", MB_OK);
+ }
+
+ return Scanner(add);
+ }
+
+
+ // Supports wide and normal strings both std and pointers
+ template
+ static auto FindStringRef(T string, bool bWarnIfNotFound = true, int useRefNum = 0, bool bIsInFunc = false) -> Scanner
+ {
+ PE::Address add{ nullptr };
+
+ constexpr auto bIsWide = std::is_same::value;
+ constexpr auto bIsChar = std::is_same::value;
+
+ constexpr auto bIsPtr = bIsWide || bIsChar;
+
+ auto textSection = PE::Section::GetSection(".text");
+ auto rdataSection = PE::Section::GetSection(".rdata");
+
+ const auto scanBytes = reinterpret_cast(textSection.GetSectionStart().Get());
+
+ int aa = 0;
+
+ // scan only text section
+ for (DWORD i = 0x0; i < textSection.GetSectionSize(); i++)
+ {
+ if ((scanBytes[i] == ASM::CMOVL || scanBytes[i] == ASM::CMOVS) && scanBytes[i + 1] == ASM::LEA)
+ {
+ auto stringAdd = PE::Address(&scanBytes[i]).RelativeOffset(3);
+
+ // Check if the string is in the .rdata section
+ if (rdataSection.isInSection(stringAdd))
+ {
+ auto strBytes = stringAdd.GetAs();
+
+ // Check if the first char is printable
+ if (ASM::byteIsAscii(strBytes[0]))
+ {
+ if constexpr (!bIsPtr)
+ {
+ // typedef T::value_type char_type;
+ using char_type = std::decay_t>;
+
+ auto lea = stringAdd.GetAs();
+
+ T leaT(lea);
+
+ if (leaT == string)
+ {
+ add = PE::Address(&scanBytes[i]);
+
+ if (++aa > useRefNum)
+ break;
+ }
+ }
+ else
+ {
+ auto lea = stringAdd.GetAs();
+
+ if constexpr (bIsWide)
+ {
+ if (wcscmp(string, lea) == 0)
+ {
+ add = PE::Address(&scanBytes[i]);
+
+ if (++aa > useRefNum)
+ break;
+ }
+ }
+ else
+ {
+ if (strcmp(string, lea) == 0)
+ {
+ add = PE::Address(&scanBytes[i]);
+
+ if (++aa > useRefNum)
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // MemcuryAssertM(add != 0, "FindStringRef return nullptr");
+
+ if (bWarnIfNotFound)
+ {
+ if (add == 0)
+ {
+ if constexpr (bIsWide)
+ {
+ std::wstring wstr = std::wstring(string);
+ // LOG_WARN(LogMemory, "Failed to find String {}", std::string(wstr.begin(), wstr.end()));
+
+ // auto aaa = (L"failed FindStringRef " + std::wstring(string));
+ // MessageBoxA(0, std::string(aaa.begin(), aaa.end()).c_str(), "Memcury", MB_ICONERROR);
+ }
+ else
+ {
+ // LOG_WARN(LogMemory, "Failed to find String {}", string);
+ // MessageBoxA(0, ("failed FindStringRef " + std::string(string)).c_str(), "Memcury", MB_ICONERROR);
+ }
+ }
+ }
+
+ if (add.Get())
+ {
+ if (bIsInFunc)
+ {
+ for (int i = 0; i < 300; i++)
+ {
+ if (*(uint8_t*)(add.Get() - i) == 0x48 && *(uint8_t*)(add.Get() - i + 1) == 0x83)
+ {
+ // MessageBoxA(0, std::format("0x{:x}", (__int64(add.Get() - i) - __int64(GetModuleHandleW(0)))).c_str(), "Memcury", MB_OK);
+
+ auto beginFunc = Scanner(add.Get() - i);
+
+ auto ref = FindPointerRef(beginFunc.GetAs());
+
+ return ref;
+ }
+ }
+ }
+ }
+
+ return Scanner(add);
+ }
+
+ auto Jump() -> Scanner
+ {
+ _address.Jump();
+ return *this;
+ }
+
+ auto ScanFor(std::vector opcodesToFind, bool forward = true, int toSkip = 0) -> Scanner
+ {
+ const auto scanBytes = _address.GetAs();
+
+ for (auto i = (forward ? 1 : -1); forward ? (i < 2048) : (i > -2048); forward ? i++ : i--)
+ {
+ bool found = true;
+
+ for (int k = 0; k < opcodesToFind.size() && found; k++)
+ {
+ if (opcodesToFind[k] == -1)
+ continue;
+ found = opcodesToFind[k] == scanBytes[i + k];
+ }
+
+ if (found)
+ {
+ _address = &scanBytes[i];
+ if (toSkip != 0)
+ {
+ return ScanFor(opcodesToFind, forward, toSkip - 1);
+ }
+
+ break;
+ }
+ }
+
+ return *this;
+ }
+
+ auto FindFunctionBoundary(bool forward = false) -> Scanner
+ {
+ const auto scanBytes = _address.GetAs();
+
+ for (auto i = (forward ? 1 : -1); forward ? (i < 2048) : (i > -2048); forward ? i++ : i--)
+ {
+ if ( // ASM::byteIsA(scanBytes[i], ASM::MNEMONIC::JMP_REL8) ||
+ // ASM::byteIsA(scanBytes[i], ASM::MNEMONIC::JMP_REL32) ||
+ // ASM::byteIsA(scanBytes[i], ASM::MNEMONIC::JMP_EAX) ||
+ ASM::byteIsA(scanBytes[i], ASM::MNEMONIC::RETN_REL8) || ASM::byteIsA(scanBytes[i], ASM::MNEMONIC::RETN) || ASM::byteIsA(scanBytes[i], ASM::MNEMONIC::INT3))
+ {
+ _address = (uintptr_t)&scanBytes[i + 1];
+ break;
+ }
+ }
+
+ return *this;
+ }
+
+ auto RelativeOffset(uint32_t offset) -> Scanner
+ {
+ _address.RelativeOffset(offset);
+
+ return *this;
+ }
+
+ auto AbsoluteOffset(uint32_t offset) -> Scanner
+ {
+ _address.AbsoluteOffset(offset);
+
+ return *this;
+ }
+
+ template
+ auto GetAs() -> T
+ {
+ return _address.GetAs();
+ }
+
+ auto Get() -> uintptr_t
+ {
+ return _address.Get();
+ }
+
+ auto IsValid() -> bool
+ {
+ return _address.IsValid();
+ }
+ };
+
+ /* Bad don't use it tbh... */
+ class TrampolineHook
+ {
+ void** originalFunctionPtr;
+ PE::Address originalFunction;
+ PE::Address hookFunction;
+ PE::Address allocatedPage;
+ std::vector restore;
+
+ void PointToCodeIfNot(PE::Address& ptr)
+ {
+ auto bytes = ptr.GetAs();
+
+ if (ASM::byteIsA(bytes[0], ASM::MNEMONIC::JMP_REL32))
+ {
+ ptr = bytes + 5 + *(int32_t*)&bytes[1];
+ }
+ }
+
+ void* AllocatePageNearAddress(void* targetAddr)
+ {
+ SYSTEM_INFO sysInfo;
+ GetSystemInfo(&sysInfo);
+ const uint64_t PAGE_SIZE = sysInfo.dwPageSize;
+
+ uint64_t startAddr = (uint64_t(targetAddr) & ~(PAGE_SIZE - 1)); // round down to nearest page boundary
+ uint64_t minAddr = min(startAddr - 0x7FFFFF00, (uint64_t)sysInfo.lpMinimumApplicationAddress);
+ uint64_t maxAddr = max(startAddr + 0x7FFFFF00, (uint64_t)sysInfo.lpMaximumApplicationAddress);
+
+ uint64_t startPage = (startAddr - (startAddr % PAGE_SIZE));
+
+ for (uint64_t pageOffset = 1; pageOffset; pageOffset++)
+ {
+ uint64_t byteOffset = pageOffset * PAGE_SIZE;
+ uint64_t highAddr = startPage + byteOffset;
+ uint64_t lowAddr = (startPage > byteOffset) ? startPage - byteOffset : 0;
+
+ bool needsExit = highAddr > maxAddr && lowAddr < minAddr;
+
+ if (highAddr < maxAddr)
+ {
+ void* outAddr = VirtualAlloc((void*)highAddr, PAGE_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
+ if (outAddr)
+ return outAddr;
+ }
+
+ if (lowAddr > minAddr)
+ {
+ void* outAddr = VirtualAlloc((void*)lowAddr, PAGE_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
+ if (outAddr != nullptr)
+ return outAddr;
+ }
+
+ if (needsExit)
+ {
+ break;
+ }
+ }
+
+ return nullptr;
+ }
+
+ void WriteAbsoluteJump(void* jumpLocation, void* destination)
+ {
+ uint8_t absJumpInstructions[] = {
+ ASM::Mnemonic("CMOVNS"), 0xBA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r10, addr
+ 0x41, 0xFF, 0xE2 // jmp r10
+ };
+
+ auto destination64 = (uint64_t)destination;
+ memcpy(&absJumpInstructions[2], &destination64, sizeof(destination64));
+ memcpy(jumpLocation, absJumpInstructions, sizeof(absJumpInstructions));
+ }
+
+ uintptr_t PrepareRestore()
+ {
+ /*
+ This is not a correct way to do it at all, since not all functions sub from the stack
+ This needs so much more tests, but it works for now.
+ */
+
+ Scanner scanner(originalFunction);
+ scanner.ScanFor({ 0x48, 0x83, 0xEC }); // sub rsp
+
+ auto restoreSize = scanner.Get() - originalFunction.Get();
+
+ MemcuryAssert(restoreSize > 0 && restoreSize < 0x100);
+
+ restore.reserve(restoreSize);
+ for (auto i = 0; i < restoreSize; i++)
+ {
+ restore.push_back(originalFunction.GetAs()[i]);
+ }
+
+ return restoreSize;
+ }
+
+ void WriteRestore()
+ {
+ auto restorePtr = allocatedPage + ASM::SIZE_OF_JMP_ABSLOUTE_INSTRUCTION + 2;
+
+ memcpy(restorePtr.GetAs(), restore.data(), restore.size());
+
+ *originalFunctionPtr = restorePtr.GetAs();
+
+ // Write a jump back to where the execution should resume
+ restorePtr.AbsoluteOffset((uint32_t)restore.size());
+
+ auto contuineExecution = originalFunction + restore.size();
+
+ WriteAbsoluteJump(restorePtr.GetAs(), contuineExecution.GetAs());
+ }
+
+ auto PrepareJMPInstruction(uint64_t dst)
+ {
+ uint8_t bytes[5] = { ASM::Mnemonic("JMP_REL32"), 0x0, 0x0, 0x0, 0x0 };
+
+ const uint64_t relAddr = dst - (originalFunction.Get() + ASM::SIZE_OF_JMP_RELATIVE_INSTRUCTION);
+ memcpy(bytes + 1, &relAddr, 4);
+
+ return std::move(bytes);
+ }
+
+ bool IsHooked()
+ {
+ return originalFunction.GetAs()[0] == ASM::Mnemonic("JMP_REL32");
+ }
+
+ public:
+ TrampolineHook(void** originalFunction, void* hookFunction)
+ {
+ this->originalFunctionPtr = originalFunction;
+
+ this->originalFunction = *originalFunction;
+ this->hookFunction = hookFunction;
+
+ PointToCodeIfNot(this->originalFunction);
+ PointToCodeIfNot(this->hookFunction);
+ };
+
+ bool Commit()
+ {
+ auto fnStart = originalFunction.GetAs();
+
+ auto restoreSize = PrepareRestore();
+
+ if (!allocatedPage.IsValid())
+ {
+ allocatedPage = AllocatePageNearAddress(fnStart);
+ }
+
+ memset(allocatedPage.GetAs(), ASM::MNEMONIC::INT3, 0x1000);
+
+ WriteAbsoluteJump(allocatedPage.GetAs(), hookFunction.GetAs());
+
+ DWORD oldProtect;
+ VirtualProtect(fnStart, 1024, PAGE_EXECUTE_READWRITE, &oldProtect);
+
+ auto jmpInstruction = PrepareJMPInstruction(allocatedPage.Get());
+
+ WriteRestore();
+
+ memset(fnStart, ASM::MNEMONIC::INT3, restoreSize);
+ memcpy(fnStart, jmpInstruction, ASM::SIZE_OF_JMP_RELATIVE_INSTRUCTION);
+
+ return true;
+ }
+
+ bool Revert()
+ {
+ auto fnStart = originalFunction.GetAs();
+
+ DWORD oldProtect;
+ VirtualProtect(fnStart, 1024, PAGE_EXECUTE_READWRITE, &oldProtect);
+
+ memcpy(fnStart, restore.data(), restore.size());
+
+ *originalFunctionPtr = originalFunction.GetAs();
+
+ // VirtualFree(allocatedPage.GetAs(), 0x1000, MEM_RELEASE);
+
+ return true;
+ }
+
+ auto Toggle()
+ {
+ if (IsHooked())
+ Revert();
+ else
+ Commit();
+
+ return IsHooked();
+ }
+ };
+
+ namespace VEHHook
+ {
+ struct HOOK_INFO
+ {
+ void* Original;
+ void* Detour;
+
+ HOOK_INFO(void* Original, void* Detour)
+ : Original(Original)
+ , Detour(Detour)
+ {
+ }
+ };
+
+ inline std::vector Hooks;
+ inline std::vector HookProtections;
+ inline HANDLE ExceptionHandler;
+
+ inline long Handler(EXCEPTION_POINTERS* Exception)
+ {
+ if (Exception->ExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION)
+ {
+ auto Itr = std::find_if(Hooks.begin(), Hooks.end(), [Rip = Exception->ContextRecord->Rip](const HOOK_INFO& Hook)
+ { return Hook.Original == (void*)Rip; });
+ if (Itr != Hooks.end())
+ {
+ Exception->ContextRecord->Rip = (uintptr_t)Itr->Detour;
+ }
+
+ Exception->ContextRecord->EFlags |= 0x100; // SINGLE_STEP_FLAG
+
+ return EXCEPTION_CONTINUE_EXECUTION;
+ }
+ else if (Exception->ExceptionRecord->ExceptionCode == STATUS_SINGLE_STEP)
+ {
+ // TODO: find a way to only vp the function that about to get executed
+ for (auto& Hook : Hooks)
+ {
+ DWORD dwOldProtect;
+ VirtualProtect(Hook.Original, 1, PAGE_EXECUTE_READ | PAGE_GUARD, &dwOldProtect);
+ }
+
+ return EXCEPTION_CONTINUE_EXECUTION;
+ }
+
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+
+ inline bool Init()
+ {
+ if (ExceptionHandler == nullptr)
+ {
+ ExceptionHandler = AddVectoredExceptionHandler(true, (PVECTORED_EXCEPTION_HANDLER)Handler);
+ }
+ return ExceptionHandler != nullptr;
+ }
+
+ inline bool AddHook(void* Target, void* Detour)
+ {
+ if (ExceptionHandler == nullptr)
+ {
+ return false;
+ }
+
+ if (Util::IsSamePage(Target, Detour))
+ {
+ return false;
+ }
+
+ if (!VirtualProtect(Target, 1, PAGE_EXECUTE_READ | PAGE_GUARD, &HookProtections.emplace_back()))
+ {
+ HookProtections.pop_back();
+ return false;
+ }
+
+ Hooks.emplace_back(Target, Detour);
+ return true;
+ }
+
+ inline bool RemoveHook(void* Original)
+ {
+ auto Itr = std::find_if(Hooks.begin(), Hooks.end(), [Original](const HOOK_INFO& Hook)
+ { return Hook.Original == Original; });
+
+ if (Itr == Hooks.end())
+ {
+ return false;
+ }
+
+ const auto ProtItr = HookProtections.begin() + std::distance(Hooks.begin(), Itr);
+ Hooks.erase(Itr);
+
+ DWORD dwOldProtect;
+ bool Ret = VirtualProtect(Original, 1, *ProtItr, &dwOldProtect);
+ HookProtections.erase(ProtItr);
+
+ return false;
+ }
+ }
+}
+
+inline uintptr_t FindFunctionCall(const wchar_t* Name, const std::vector& Bytes = std::vector{ 0x48, 0x89, 0x5C }, int skip = 0) // credit ender & me
+{
+ auto FunctionPtr = Memcury::Scanner::FindStringRef(Name, true, skip).ScanFor({ 0x48, 0x8D, 0x0D }).RelativeOffset(3).GetAs();
+
+ auto PtrRef = Memcury::Scanner::FindPointerRef(FunctionPtr);
+
+ /* if (!PtrRef.Get() || PtrRef.Get() == __int64(FunctionPtr))
+ {
+ std::wstring NameWStr = std::wstring(Name);
+ LOG_WARN(LogMemory, "Failed to find pointer reference for {}", std::string(NameWStr.begin(), NameWStr.end()));
+ return 0;
+ } */
+
+ return PtrRef.ScanFor(Bytes, false).Get();
+}
\ No newline at end of file
diff --git a/Cobalt/packages.config b/Cobalt/packages.config
new file mode 100644
index 0000000..31cfc2d
--- /dev/null
+++ b/Cobalt/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file