commit 7e9cab52fda09ee9dc5c0a33484af1cf64605cd8 Author: Ploosh Date: Thu Jun 6 10:28:48 2024 -0400 Init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7016dd7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +x64/ +*/x64 +Starfall/Starfall +*/*.vcxproj.user +.vs +*/Release +*/Debug \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..915ca9d --- /dev/null +++ b/LICENSE @@ -0,0 +1,28 @@ +BSD 3-Clause License + +Copyright (c) 2024, Starfall + +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. + +3. Neither the name of the copyright holder 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 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. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..48c57f8 --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +# Starfall +Starfall is a universal SSL bypass / redirect for native UE HTTP games, made for Fortnite.
+ +# Configuring Starfall + +## Changing Starfall's Backend URL. +To change Starfall's backend URL to your's go to ``Starfall/opts.h`` and change the ``Backend`` variable to your Backend URL. + +## Build for a Hybrid +Go to ``Starfall/opts.h`` and change ``UrlSet`` to Hybrid. + +## Build for a Dev +Go to ``Starfall/opts.h`` and change ``UrlSet`` to Dev. + +## Redirecting every URL +Go to ``Starfall/opts.h`` and change ``UrlSet`` to All. + +## Enabling Console for debugging +Go to ``Starfall/opts.h`` and change ``Console`` to true. + +# Other +If you use Starfall for a project please credit me. diff --git a/Starfall.sln b/Starfall.sln new file mode 100644 index 0000000..d8ffccb --- /dev/null +++ b/Starfall.sln @@ -0,0 +1,34 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.9.34616.47 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plooshfinder", "plooshfinder\plooshfinder.vcxproj", "{1EFDB154-D3B5-43BC-8733-F3D6941C9E36}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Starfall", "Starfall\Starfall.vcxproj", "{9C02D0A1-F786-4B04-B466-8D58C5B74FD1}" + ProjectSection(ProjectDependencies) = postProject + {1EFDB154-D3B5-43BC-8733-F3D6941C9E36} = {1EFDB154-D3B5-43BC-8733-F3D6941C9E36} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1EFDB154-D3B5-43BC-8733-F3D6941C9E36}.Debug|x64.ActiveCfg = Debug|x64 + {1EFDB154-D3B5-43BC-8733-F3D6941C9E36}.Debug|x64.Build.0 = Debug|x64 + {1EFDB154-D3B5-43BC-8733-F3D6941C9E36}.Release|x64.ActiveCfg = Release|x64 + {1EFDB154-D3B5-43BC-8733-F3D6941C9E36}.Release|x64.Build.0 = Release|x64 + {9C02D0A1-F786-4B04-B466-8D58C5B74FD1}.Debug|x64.ActiveCfg = Debug|x64 + {9C02D0A1-F786-4B04-B466-8D58C5B74FD1}.Debug|x64.Build.0 = Debug|x64 + {9C02D0A1-F786-4B04-B466-8D58C5B74FD1}.Release|x64.ActiveCfg = Release|x64 + {9C02D0A1-F786-4B04-B466-8D58C5B74FD1}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {2BE449DA-BD65-4FB2-9582-A7FCB41F1393} + EndGlobalSection +EndGlobal diff --git a/Starfall/Starfall.vcxproj b/Starfall/Starfall.vcxproj new file mode 100644 index 0000000..32918c4 --- /dev/null +++ b/Starfall/Starfall.vcxproj @@ -0,0 +1,118 @@ + + + + + Debug + x64 + + + Release + x64 + + + + 17.0 + Win32Proj + {9c02d0a1-f786-4b04-b466-8d58c5b74fd1} + Starfall + 10.0 + + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + + + + + + + + + + + + + + Level3 + true + _DEBUG;STARFALL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + stdcpplatest + + + Windows + true + false + $(CoreLibraryDependencies);%(AdditionalDependencies) + + + + + Level3 + true + true + true + NDEBUG;STARFALL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + stdcpplatest + NotSet + Neither + false + + + Windows + true + true + true + false + $(CoreLibraryDependencies);%(AdditionalDependencies) + false + + + + + + + + + + + + + + + + + + + + + + Create + Create + + + + + + + + + + \ No newline at end of file diff --git a/Starfall/Starfall.vcxproj.filters b/Starfall/Starfall.vcxproj.filters new file mode 100644 index 0000000..66a4c8e --- /dev/null +++ b/Starfall/Starfall.vcxproj.filters @@ -0,0 +1,75 @@ + + + + + {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 + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/Starfall/base.cpp b/Starfall/base.cpp new file mode 100644 index 0000000..74b8048 --- /dev/null +++ b/Starfall/base.cpp @@ -0,0 +1,14 @@ +#include "pch.h" +#include "base.h" + +namespace Starfall { + namespace Globals { + void* buf; + + void* tbuf; + size_t tsize; + + void* rbuf; + size_t rsize; + } +}; \ No newline at end of file diff --git a/Starfall/base.h b/Starfall/base.h new file mode 100644 index 0000000..8d6c9fa --- /dev/null +++ b/Starfall/base.h @@ -0,0 +1,20 @@ +#pragma once +#include "opts.h" +#include "pch.h" + +namespace Starfall { + namespace Globals { + extern void* buf; + //extern size_t size; + + extern void* tbuf; + extern size_t tsize; + + extern void* rbuf; + extern size_t rsize; + } + using namespace Globals; +}; +using namespace Starfall; + +#define Log(type, ...) if (Console) printf("LogStarfall: " #type ": " __VA_ARGS__) \ No newline at end of file diff --git a/Starfall/dllmain.cpp b/Starfall/dllmain.cpp new file mode 100644 index 0000000..7ce2d4e --- /dev/null +++ b/Starfall/dllmain.cpp @@ -0,0 +1,54 @@ +#include "pch.h" +#include "base.h" +#include "request.h" +#include "exit.h" +#include "opts.h" + +namespace Starfall { + void Init() { + if (Console) { + AllocConsole(); + + FILE* fptr; + freopen_s(&fptr, "CONOUT$", "w+", stdout); + SetConsoleTitleA("Starfall - https://github.com/ParadiseFN/Starfall"); + } + + if (UseBackendParam) { + FString cmd = GetCommandLineW(); + auto pos = cmd.find(L"-backend="); + if (pos != std::wstring::npos) { + backend = cmd.substr(pos + 9); + } + else { + backend = Backend; + } + } + + buf = *(void**)(__readgsqword(0x60) + 0x10); + + auto section = pe_get_section((char*)buf, ".text"); + auto rsection = pe_get_section((char*)buf, ".rdata"); + + tbuf = (void*)(__int64(buf) + section->virtualAddress); + tsize = section->virtualSize; + + rbuf = (void*)(__int64(buf) + rsection->virtualAddress); + rsize = rsection->virtualSize; + + FindProcessRequest(); + if (Game == Fortnite) FindPushWidget(); + + return; + } +} + +BOOL APIENTRY DllMain( HMODULE dllBase, + DWORD callReason, + LPVOID lpReserved + ) +{ + if (callReason == 1) ManualMapping ? Init() : (void) CreateThread(0, 0, (LPTHREAD_START_ROUTINE)Init, 0, 0, 0); + return TRUE; +} + diff --git a/Starfall/exit.cpp b/Starfall/exit.cpp new file mode 100644 index 0000000..5d7a7b8 --- /dev/null +++ b/Starfall/exit.cpp @@ -0,0 +1,90 @@ +#include "pch.h" +#include "hooking.h" +#include "base.h" +#include "exit.h" + +namespace Starfall { + int dontExit = 0; + namespace Hooks { + void RequestExitWithStatusHook(bool Force, unsigned char Code) + { + if (dontExit == 0) { + TerminateProcess(GetCurrentProcess(), Code); + } + else { + dontExit--; + } + } + + void UnsafeEnvironmentPopupHook(wchar_t** unknown1, unsigned __int8 _case, __int64 unknown2, char unknown3) + { + dontExit++; + } + } + + namespace Callbacks { + bool RequestExitWithStatusCallback(struct pf_patch_t* patch, void* stream) { + AsmHook(stream, Hooks::RequestExitWithStatusHook); + return true; + } + + bool UnsafeEnvironmentPopupCallback(struct pf_patch_t* patch, void* stream) { + AsmHook(stream, Hooks::UnsafeEnvironmentPopupHook); + return true; + } + + bool PushWidgetCallback(struct pf_patch_t* patch, void* stream) { + FindExit(); + return true; + } + } + + namespace Finders { + void FindExit() { + constexpr static struct pf_patch_t patch = pf_construct_patch_sig("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 ? ? ? ?", RequestExitWithStatusCallback); + constexpr static struct pf_patch_t patch2 = pf_construct_patch_sig("48 8B C4 48 89 58 18 88 50 10 88 48 08 57 48 83 EC 30", RequestExitWithStatusCallback); + constexpr static struct pf_patch_t patch3 = pf_construct_patch_sig("4C 8B DC 49 89 5B 08 49 89 6B 10 49 89 73 18 49 89 7B 20 41 56 48 83 EC 30 80 3D ? ? ? ? ? 49 8B", RequestExitWithStatusCallback); + + constexpr static struct pf_patch_t patch4 = pf_construct_patch_sig("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", Ret0Callback); // 17.30 + constexpr static struct pf_patch_t patch5 = pf_construct_patch_sig("4C 8B DC 55 49 8D AB ? ? ? ? 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 85 ? ? ? ? 49 89 5B 20 48 8B D9 4D 89 63 E0 4D 89 6B D8", Ret0Callback); // 19.10 + constexpr static struct pf_patch_t patch6 = pf_construct_patch_sig("48 89 5C 24 ? 55 56 57 41 54 41 55 41 56 41 57 48 8D AC 24 ? ? ? ? 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 85 ? ? ? ? 80 B9 ? ? ? ? ? 48 8B DA 48 8B F1", Ret0Callback); // 28.00 + constexpr static struct pf_patch_t patch7 = pf_construct_patch_sig("40 55 53 56 57 41 54 41 56 41 57 48 8D AC 24 ? ? ? ? 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 85 ? ? ? ? ? 0F B6 ?", Ret0Callback); // 29.00/22.40 + constexpr static struct pf_patch_t patch8 = pf_construct_patch_sig("48 89 5C 24 ? 55 56 57 41 54 41 55 41 56 41 57 48 8D AC 24 ? ? ? ? 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 85 ? ? ? ? ? 0F B6 ? 44 88 44 24 ?", Ret0Callback); // 30.00 + + + constexpr static struct pf_patch_t patches2[] = { + patch, + patch2, + patch3, + patch4, + patch5, + patch6, + patch7, + patch8 + }; + + constexpr static struct pf_patchset_t patchset2 = pf_construct_patchset(patches2, sizeof(patches2) / sizeof(struct pf_patch_t), (bool (*)(void*, size_t, pf_patchset_t))pf_find_maskmatch); + + pf_patchset_emit(tbuf, tsize, patchset2); + } + + void FindPushWidget() + { + constexpr static auto patch = pf_construct_patch_sig("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", PushWidgetCallback); + constexpr static auto patch2 = pf_construct_patch_sig("48 8B C4 4C 89 40 18 48 89 50 10 48 89 48 08 55 53 56 57 41 54 41 55 41 56 41 57 48 8D 68 B8 48 81 EC ? ? ? ? 65 48 8B 04 25", PushWidgetCallback); // 26.00 + constexpr static auto patch3 = pf_construct_patch_sig("48 8B C4 48 89 58 10 48 89 70 18 48 89 78 20 55 41 56 41 57 48 8D 68 A1 48 81 EC ? ? ? ? 65 48 8B 04 25 ? ? ? ? 48 8B F9 B9 ? ? ? ? 49", PushWidgetCallback); // 28.00 + constexpr static auto patch4 = pf_construct_patch_sig("48 8B C4 48 89 58 08 48 89 70 10 48 89 78 18 55 41 56 41 57 48 8D 68 A1 48 81 EC ? ? ? ? 65 48 8B 04 25 ? ? ? ? 48 8B F9 B9 ? ? ? ? 48", PushWidgetCallback); // 30.00 + + constexpr static struct pf_patch_t patches[] = { + patch, + patch2, + patch3, + patch4 + }; + + constexpr static struct pf_patchset_t patchset = pf_construct_patchset(patches, sizeof(patches) / sizeof(struct pf_patch_t), (bool (*)(void*, size_t, pf_patchset_t))pf_find_maskmatch); + + pf_patchset_emit(tbuf, tsize, patchset); + } + } +} \ No newline at end of file diff --git a/Starfall/exit.h b/Starfall/exit.h new file mode 100644 index 0000000..3de1751 --- /dev/null +++ b/Starfall/exit.h @@ -0,0 +1,18 @@ +#pragma once +#include "pch.h" + +namespace Starfall { + namespace Callbacks { + bool RequestExitWithStatusCallback(struct pf_patch_t* patch, void* stream); + + bool PushWidgetCallback(struct pf_patch_t* patch, void* stream); + } + using namespace Callbacks; + + namespace Finders { + void FindExit(); + + void FindPushWidget(); + } + using namespace Finders; +} \ No newline at end of file diff --git a/Starfall/hooking.cpp b/Starfall/hooking.cpp new file mode 100644 index 0000000..20b53e2 --- /dev/null +++ b/Starfall/hooking.cpp @@ -0,0 +1,39 @@ +#pragma once +#include "pch.h" +#include "hooking.h" + +namespace Starfall { + namespace Hooking { + __forceinline void AsmHook(uint64_t ptr, void* detour) { + AsmHook((void*)ptr, detour); + } + + bool Ret0Callback(struct pf_patch_t* patch, void* stream) { + DWORD og; + VirtualProtect(stream, 1, PAGE_EXECUTE_READWRITE, &og); + *(uint8_t*)stream = (uint8_t)0xC3; + VirtualProtect(stream, 1, og, &og); + return true; + } + + void VTHook(void** addr, void* detour, void** orig) { + DWORD oldProt; + + VirtualProtect(addr, sizeof(void*), PAGE_EXECUTE_READWRITE, &oldProt); + + if (orig) *orig = *addr; + + *addr = detour; + + VirtualProtect(addr, sizeof(void*), oldProt, &oldProt); + } + } + + __declspec(noinline) bool InternalCheckBytes(void* base, int ind, const uint8_t* bytes, size_t sz, bool upwards) { + auto offBase = (uint8_t*)(upwards ? __int64(base) - ind : __int64(base) + ind); + for (int i = 0; i < sz; i++) { + if (*(offBase + i) != bytes[i]) return false; + } + return true; + } +} \ No newline at end of file diff --git a/Starfall/hooking.h b/Starfall/hooking.h new file mode 100644 index 0000000..5bbe081 --- /dev/null +++ b/Starfall/hooking.h @@ -0,0 +1,56 @@ +#pragma once +#include "pch.h" + + +namespace Starfall { + namespace Hooking { + __forceinline void AsmHook(void* ptr, void* detour) { + if (!ptr || !detour) return; + + uint8_t data[] = { + 0x41, 0x52, // push r10 + 0x49, 0xBA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r10, detour + 0x41, 0xFF, 0xD2, // call r10 + 0x41, 0x5A, // pop r10 + 0xC3 // ret + }; + + DWORD oldProt; + VirtualProtect(ptr, sizeof(data), PAGE_EXECUTE_READWRITE, &oldProt); + + memcpy(ptr, data, sizeof(data)); + *(uint64_t*)(__int64(ptr) + 4) = (uint64_t)detour; + + VirtualProtect(ptr, sizeof(data), oldProt, &oldProt); + } + + __forceinline void AsmHook(uint64_t ptr, void* detour); + + bool Ret0Callback(struct pf_patch_t* patch, void* stream); + + void VTHook(void** addr, void* detour, void** orig = nullptr); + } + using namespace Hooking; + + bool InternalCheckBytes(void* base, int ind, const uint8_t* bytes, size_t sz, bool upwards = false); + template + class CheckBytes { + public: + constexpr static uint8_t bytes[sizeof...(Data)] = { Data... }; + void* Base; + int Ind; + bool Upwards; + + CheckBytes(void* base, int ind, bool upwards = false) { + Base = base; + Ind = ind; + Upwards = upwards; + } + + operator bool() { + return InternalCheckBytes(Base, Ind, bytes, sizeof...(Data), Upwards); + } + }; +} + +//#define CheckBytes(base, ind, bytes, upwards) CheckBytesInternal(base, ind, bytes.data(), bytes.size(), upwards) \ No newline at end of file diff --git a/Starfall/opts.h b/Starfall/opts.h new file mode 100644 index 0000000..42a99c8 --- /dev/null +++ b/Starfall/opts.h @@ -0,0 +1,12 @@ +#pragma once +#include "ue.h" + +constexpr bool Console = false; // if the console window should be created +constexpr StarfallURLSet URLSet = Default; +constexpr inline FString Backend = L"http://127.0.0.1:3551"; // your backend url +constexpr UEGame Game = Fortnite; // use Generic for any UE game other than fortnite + +// misc options, don't change unless you know what you're doing +constexpr bool UseBackendParam = false; // for phoenix/paradise launcher +constexpr bool ManualMapping = false; // if you're using EAC & a manual mapper, then enable this for the dll to work +constexpr bool FixMemLeak = true; // memory leak fix \ No newline at end of file diff --git a/Starfall/pch.cpp b/Starfall/pch.cpp new file mode 100644 index 0000000..1730571 --- /dev/null +++ b/Starfall/pch.cpp @@ -0,0 +1 @@ +#include "pch.h" \ No newline at end of file diff --git a/Starfall/pch.h b/Starfall/pch.h new file mode 100644 index 0000000..8c381f1 --- /dev/null +++ b/Starfall/pch.h @@ -0,0 +1,36 @@ +#ifndef PCH_H +#define PCH_H + +#undef UNICODE +#define WIN32_LEAN_AND_MEAN + +#include +#include +#include +#include + +namespace Plooshfinder { + #include "../plooshfinder/include/plooshfinder_sig.h" + #include "../plooshfinder/include/plooshfinder.h" + #include "../plooshfinder/include/formats/pe.h" +}; +using namespace Plooshfinder; + +namespace Starfall { + namespace Types { + enum StarfallURLSet { + Default, // default, private server + Hybrid, // redirect profile, version, and content pages to private server, otherwise use official + Dev, // redirect profile & content pages to private server, otherwise use official + All, // redirect every single request to private server + }; + enum UEGame { + Generic, + Fortnite + }; + } + using namespace Types; +} +using namespace Starfall; + +#endif diff --git a/Starfall/redirection.cpp b/Starfall/redirection.cpp new file mode 100644 index 0000000..f2f46bd --- /dev/null +++ b/Starfall/redirection.cpp @@ -0,0 +1,58 @@ +#include "pch.h" +#include "redirection.h" +#include "opts.h" + +namespace Starfall { + namespace Redirection { + bool shouldRedirect(URL* uri) { + switch (URLSet) { + case ::Hybrid: { + constexpr static const wchar_t* redirectedPaths[] = { + L"/fortnite/api/v2/versioncheck/", + L"/fortnite/api/game/v2/profile/", + L"/content/api/pages/", + L"/affiliate/api/public/affiliates/slug", + L"/socialban/api/public/v1", + L"/fortnite/api/cloudstorage/system" + }; + + for (int i = 0; i < sizeof(redirectedPaths) / sizeof(wchar_t*); i++) { + if (uri->Path.starts_with(redirectedPaths[i])) return true; + } + break; + } + case ::Dev: { + constexpr static const wchar_t* redirectedPathsDev[] = { + L"/fortnite/api/game/v2/profile/", + L"/affiliate/api/public/affiliates/slug", + L"/content/api/pages/" + }; + + for (int i = 0; i < sizeof(redirectedPathsDev) / sizeof(wchar_t*); i++) { + if (uri->Path.starts_with(redirectedPathsDev[i])) return true; + } + break; + } + case ::Default: { + static constexpr const wchar_t *redirectedUrls[] = { + L"ol.epicgames.com", + L"ol.epicgames.net", + //L".akamaized.net", // if you have your own blurl + L"on.epicgames.com", + L"game-social.epicgames.com", + L"ak.epicgames.com", + //L"cdn2.unrealengine.com" // only needed if you have fixed friend images in friends list + }; + + for (int i = 0; i < sizeof(redirectedUrls) / sizeof(wchar_t *); i++) { + if (uri->Domain.ends_with(redirectedUrls[i])) return true; + } + break; + } + case ::All: + return true; + } + return false; + } + } +} \ No newline at end of file diff --git a/Starfall/redirection.h b/Starfall/redirection.h new file mode 100644 index 0000000..44132f8 --- /dev/null +++ b/Starfall/redirection.h @@ -0,0 +1,10 @@ +#pragma once +#include "pch.h" +#include "url.h" + +namespace Starfall { + namespace Redirection { + bool shouldRedirect(URL* uri); + } + using namespace Redirection; +} \ No newline at end of file diff --git a/Starfall/request.cpp b/Starfall/request.cpp new file mode 100644 index 0000000..b6cfd66 --- /dev/null +++ b/Starfall/request.cpp @@ -0,0 +1,225 @@ +#include "pch.h" +#include "base.h" +#include "hooking.h" +#include "url.h" +#include "request.h" +#include "redirection.h" +#include +#include "opts.h" +#include +#include + +#define CallVirt(T, vt, offset, ...) ((T) vt[offset])(__VA_ARGS__) + +namespace Unreal { + FString FCurlHttpRequest::GetURL() + { + return GetURLFunc(this, FString()); + } + + void FCurlHttpRequest::SetURL(URL& URL) + { + FString str = URL; + SetURLFunc(this, str); + free(str.String); + } +} + +namespace Starfall { + bool setupMemLeak = false; + void SetupRequest(FCurlHttpRequest* Request) { + if (FCurlHttpRequest::SetURLFunc == nullptr) { + void* GetFunc = *Request->VTable; + FCurlHttpRequest::GetURLFunc = (decltype(FCurlHttpRequest::GetURLFunc)) *Request->VTable; + uint32_t URLOffset = 0; + for (int i = 0; i < 100; i++) { + // this is only needed for latest + if (CheckBytes<0x48, 0x8D, 0x91>(GetFunc, i)) { + URLOffset = *(uint32_t*)(__int64(GetFunc) + i + 3); + break; + } + } + + if (URLOffset == 0) goto def; + for (int64_t i = 0; i < ((__int64(FCurlHttpRequest::ProcessRequestVT) - __int64(Request->VTable)) / 8) /* search radius */; i++) { + auto func = Request->VTable[i]; + for (int j = 0; j < 100; j++) { + if (CheckBytes<0x48, 0x81, 0xC1>(func, j)) { + if (*(uint32_t*)(__int64(func) + j + 3) == URLOffset) { + FCurlHttpRequest::SetURLFunc = (void (*)(FCurlHttpRequest*, FString)) Request->VTable[i]; + return; + } + } + } + } +def: + FCurlHttpRequest::SetURLFunc = (void (*)(FCurlHttpRequest*, FString)) Request->VTable[10]; + } + // this works bc the first request is a datarouter request, and the second request should be after engine init + else if (!setupMemLeak && Game == Fortnite) { + constexpr static struct pf_patch_t ml_patch = pf_construct_patch_sig("4C 8B DC 55 57 41 56 49 8D AB ? ? ? ? 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 85 ? ? ? ? 48 8B 01 41 B6", Ret0Callback); + + constexpr static struct pf_patch_t patches[] = { + ml_patch + }; + + constexpr static struct pf_patchset_t patchset = pf_construct_patchset(patches, sizeof(patches) / sizeof(struct pf_patch_t), (bool (*)(void*, size_t, pf_patchset_t))pf_find_maskmatch); + + pf_patchset_emit(tbuf, tsize, patchset); + setupMemLeak = true; + } + } + + FString backend; + + namespace Hooks { + bool (*ProcessRequestOG)(FCurlHttpRequest* Request); + bool ProcessRequestHook(FCurlHttpRequest* Request) { + SetupRequest(Request); + auto urlS = Request->GetURL(); + auto url = (URL *) malloc(sizeof(URL)); + if (!url) return false; + __stosb((uint8_t*)url, 0, sizeof(URL)); + url->Construct(urlS); + + Log(Display, "URL: %ls\n", static_cast(urlS)); + if (shouldRedirect(url)) { + Request->SetURL(UseBackendParam ? url->SetHost(backend) : url->SetHost()); + + UseBackendParam ? url->Dealloc() : url->DeallocPathQuery(); + } + else { + url->Dealloc(); + } + + free(url); + return ProcessRequestOG(Request); + } + } + + namespace Callbacks { + bool PtrCallback(struct pf_patch_t* patch, void* stream) { + FCurlHttpRequest::ProcessRequestVT = (void**)stream; + + VTHook((void**)stream, ProcessRequestHook, (void **) &ProcessRequestOG); + return true; + } + + bool StringCallback(struct pf_patch_t* patch, void* stream) { + void* saddr = (void*)((__int64(stream) + 7) + *(int32_t*)(__int64(stream) + 3)); + if (__int64(saddr) >= __int64(rbuf) && __int64(saddr) < (__int64(rbuf) + (int64_t)rsize)) { + if (wcscmp((wchar_t*)saddr, L"Could not set libcurl options for easy handle, processing HTTP request failed. Increase verbosity for additional information.") == 0) { + for (int i = 0; i < 2048; i++) { + if (CheckBytes<0x48, 0x81, 0xEC>(stream, i, true)) { + for (int x = 0; x < 50; x++) { + if (CheckBytes<0x40>(stream, i + x, true)) { + Log(Display, "Found using 4.24 & lower method\n"); + stream = (uint8_t*)stream - i - x; + goto HookVT; + } + } + } + } + } + else if (wcscmp((wchar_t*)saddr, L"STAT_FCurlHttpRequest_ProcessRequest") == 0) { + for (int i = 0; i < 2048; i++) { + if (CheckBytes<0x4C, 0x8B, 0xDC>(stream, i, true)) { + Log(Display, "Found using 4.25 & 4.26 method\n"); + goto setStream; + } + else if (CheckBytes<0x48, 0x8B, 0xC4>(stream, i, true)) { + Log(Display, "Found using UE 4.27 - 5.3 method\n"); +setStream: + stream = (uint8_t*)stream - i; + goto HookVT; + } + else if (CheckBytes<0x48, 0x81, 0xEC>(stream, i, true) || CheckBytes<0x48, 0x83, 0xEC>(stream, i, true)) { + for (int x = 0; x < 50; x++) { + if (CheckBytes<0x40>(stream, i + x, true)) { + Log(Display, "Found using UE 4.25, 4.26 & 5.4+ method\n"); + stream = (uint8_t*)stream - i - x; + goto HookVT; + } else if (CheckBytes<0x4C, 0x8B, 0xDC>(stream, i + x, true) || CheckBytes<0x4C, 0x8B, 0xC4>(stream, i + x, true)) break; + } + } + } + } + } + return false; + HookVT: + Log(Display, "ProcessRequest: 0x%llx\n", __int64(stream) - __int64(buf)); + char* ptrMatches = (char*)&stream; + +#ifndef _DEBUG + constexpr static char ptrMasks[] = { + (char)0xff, + (char)0xff, + (char)0xff, + (char)0xff, + (char)0xff, + (char)0xff, + (char)0xff, + (char)0xff + }; + + constexpr static auto patch2 = pf_construct_patch_dynmatch((void*)ptrMasks, 8, PtrCallback); + + constexpr static struct pf_patch_t patches2[] = { + patch2 + }; + + constexpr static struct pf_patchset_t patchset2 = pf_construct_patchset(patches2, sizeof(patches2) / sizeof(struct pf_patch_t), (bool (*)(void*, size_t, pf_patchset_t))pf_find_maskmatch); + + auto& patchToMod = (pf_patch_t &) patch2; + DWORD og; + VirtualProtect((void*)&(patchToMod.matches), sizeof(void*), PAGE_READWRITE, &og); + patchToMod.matches = ptrMatches; + VirtualProtect((void*)&(patchToMod.matches), sizeof(void*), og, &og); +#else + char ptrMasks[] = { + (char)0xff, + (char)0xff, + (char)0xff, + (char)0xff, + (char)0xff, + (char)0xff, + (char)0xff, + (char)0xff + }; + + auto patch2 = pf_construct_patch(ptrMatches, (void*)ptrMasks, 8, PtrCallback); + + struct pf_patch_t patches2[] = { + patch2 + }; + + struct pf_patchset_t patchset2 = pf_construct_patchset(patches2, sizeof(patches2) / sizeof(struct pf_patch_t), (bool (*)(void*, size_t, pf_patchset_t))pf_find_maskmatch); +#endif + while (!pf_patchset_emit(rbuf, rsize, patchset2)); + return true; + } + } + + namespace Finders { + void FindProcessRequest() { + constexpr static std::array matches = { + 0x48, + 0x8d + }; + constexpr static std::array masks = { + 0xfb, + 0xff + }; + + constexpr static auto patch = pf_construct_patch((void*)matches.data(), (void*)masks.data(), 2, StringCallback); + + constexpr static pf_patch_t patches[] = { + patch + }; + + constexpr static struct pf_patchset_t patchset = pf_construct_patchset(patches, sizeof(patches) / sizeof(struct pf_patch_t), pf_find_maskmatch); + + while (!pf_patchset_emit(tbuf, tsize, patchset)); + } + } +} \ No newline at end of file diff --git a/Starfall/request.h b/Starfall/request.h new file mode 100644 index 0000000..fc364f0 --- /dev/null +++ b/Starfall/request.h @@ -0,0 +1,42 @@ +#pragma once +#include "pch.h" +#include "ue.h" +#include "url.h" + +#define CallVirt(T, vt, offset, ...) ((T) vt[offset])(__VA_ARGS__) + +namespace Unreal { + class FCurlHttpRequest + { + public: + void** VTable; + static inline FString& (*GetURLFunc)(FCurlHttpRequest*, FString) = nullptr; + static inline void (*SetURLFunc)(FCurlHttpRequest*, FString) = nullptr; + static inline void** ProcessRequestVT = nullptr; + + FString GetURL(); + void SetURL(URL& URL); + }; +} + +namespace Starfall { + extern FString backend; // for phoenix/paradise launcher + void SetupRequest(FCurlHttpRequest* Request); + + namespace Hooks { + bool ProcessRequestHook(FCurlHttpRequest* Request); + } + using namespace Hooks; + + namespace Callbacks { + bool PtrCallback(struct pf_patch_t* patch, void* stream); + + bool StringCallback(struct pf_patch_t* patch, void* stream); + } + using namespace Callbacks; + + namespace Finders { + void FindProcessRequest(); + } + using namespace Finders; +} \ No newline at end of file diff --git a/Starfall/ue.cpp b/Starfall/ue.cpp new file mode 100644 index 0000000..a6fda98 --- /dev/null +++ b/Starfall/ue.cpp @@ -0,0 +1,172 @@ +#include "pch.h" +#include "base.h" +#include "ue.h" +namespace Starfall { + template + __forceinline T* Alloc(size_t sz) { + return (T*)malloc(sz); + } +} + +namespace Unreal { + FString::FString() + { + String = nullptr; + Length = MaxSize = 0; + } + + FString::FString(const char* Other) + { + if (Other) + { + MaxSize = Length = (int)strlen(Other) + 1; + + AllocString(); + + size_t ConvertedChars = 0; + mbstowcs_s(&ConvertedChars, String, Length, Other, _TRUNCATE); + } + } + + FString::FString(wchar_t* Other) + { + if (Other) { + MaxSize = Length = (int)wcslen(Other) + 1; + + AllocString(); + + __movsb((PBYTE)String, (const PBYTE)Other, Length * sizeof(wchar_t)); + } + } + + FString::FString(uint32_t len) { + MaxSize = Length = len + 1; + AllocString(); + } + + __declspec(noinline) FString FString::operator+(FString other) { + if (!String || !other.String) return *this; + auto sLen = Length - 1; + auto oLen = other.Length - 1; + FString nStr((uint32_t)(sLen + oLen)); + __movsb((PBYTE)nStr.String, (const PBYTE)String, sLen * sizeof(wchar_t)); + __movsb((PBYTE)nStr.String + sLen * sizeof(wchar_t), (const PBYTE)other.String, oLen * sizeof(wchar_t)); + nStr.String[nStr.Length - 1] = 0; + return nStr; + } + + __declspec(noinline) void FString::operator+=(FString other) { + if (!String || !other.String) return; + auto sLen = Length - 1; + auto oLen = other.Length - 1; + Length = (uint32_t)(sLen + oLen + 1); + auto os = String; + AllocString(); + __movsb((PBYTE)String, (const PBYTE)os, sLen * sizeof(wchar_t)); + __movsb((PBYTE)String + sLen * sizeof(wchar_t), (const PBYTE)other.String, oLen * sizeof(wchar_t)); + String[Length - 1] = 0; + free(os); + } + + __declspec(noinline) FString FString::substr(size_t off, size_t count) { + if (count == -1) count = Length - off - 1; + else if (count > Length) return *this; + FString nStr((uint32_t)count); + + __movsb((PBYTE) nStr.String, (const PBYTE) (String + off), count * sizeof(wchar_t)); + nStr.String[count] = 0; + return nStr; + } + + __declspec(noinline) size_t FString::find(wchar_t c) { + for (uint32_t i = 0; i < Length; i++) { + if (String[i] == c) return i; + } + return -1; + } + + size_t FString::find(char c) { + return find((wchar_t)c); + } + + size_t FString::find(const wchar_t* c) { + for (uint32_t i = 0; i < Length; i++) { + bool found = true; + for (int x = 0; x < wcslen(c); x++) { + if (String[i + x] != c[x]) { + found = false; + break; + } + } + if (found) return i; + } + return -1; + } + + bool FString::contains(wchar_t c) { + for (uint32_t i = 0; i < Length; i++) { + if (String[i] == c) return true; + } + return false; + } + + bool FString::contains(const wchar_t* c) { + for (uint32_t i = 0; i < Length; i++) { + bool found = true; + for (int x = 0; x < wcslen(c); x++) { + if (String[i + x] != c[x]) { + found = false; + break; + } + } + if (found) return true; + } + return false; + } + + bool FString::starts_with(const wchar_t* c) { + for (int x = 0; x < wcslen(c); x++) { + if (String[x] != c[x]) { + return false; + } + } + return true; + } + + bool FString::ends_with(const wchar_t* c) { + auto cLen = wcslen(c); + auto start = ((size_t) Length - 1) - cLen; + for (size_t x = 0; x < cLen; x++) { + if (String[start + x] != c[x]) { + return false; + } + } + return true; + } + + size_t FString::find_first_of(char c) { + return find(c); + } + + size_t FString::find_first_of(wchar_t c) { + return find(c); + } + + wchar_t* FString::c_str() { + return String; + } + + FString::operator wchar_t* () { + return String; + } + + __declspec(noinline) void FString::Dealloc() { + free(String); + String = nullptr; + MaxSize = Length = 0; + } + + inline void FString::AllocString() { + String = Alloc(Length * sizeof(wchar_t)); + } +} \ No newline at end of file diff --git a/Starfall/ue.h b/Starfall/ue.h new file mode 100644 index 0000000..48111f1 --- /dev/null +++ b/Starfall/ue.h @@ -0,0 +1,113 @@ +#pragma once +#include "pch.h" + +namespace Unreal { + class FString + { + public: + wchar_t* String; + uint32_t Length; + uint32_t MaxSize; + inline static const size_t npos = -1; + FString(); + + FString(const char* Other); + + FString(wchar_t* Other); + + consteval FString(const wchar_t* Other) { + if (Other) { + MaxSize = Length = (int)std::wstring_view(Other).size() + 1; + String = (wchar_t*)Other; + } + } + + + consteval FString(const wchar_t* Other, size_t len) { + if (Other) { + MaxSize = Length = (uint32_t) len; + String = (wchar_t*)Other; + } + } + + FString(uint32_t len); + + FString operator+(FString other); + + void operator+=(FString other); + + FString substr(size_t off, size_t count = -1); + + size_t find(wchar_t c); + + size_t find(char c); + + size_t find(const wchar_t* c); + + bool contains(wchar_t c); + + bool contains(const wchar_t* c); + + bool starts_with(const wchar_t* c); + + bool ends_with(const wchar_t* c); + + size_t find_first_of(char c); + + size_t find_first_of(wchar_t c); + + wchar_t* c_str(); + + operator wchar_t* (); + + void Dealloc(); + private: + inline void AllocString(); + }; + + class FStringUtil { + public: + consteval static size_t find_const(const FString s, const wchar_t c) { + for (uint32_t i = 0; i < s.Length; i++) { + if (s.String[i] == c) return i; + } + return -1; + } + + template + consteval static std::array substr() { + std::array arr; + + for (size_t i = 0; i < (count == -1 ? (s.Length - off) : count + 1); i++) { + arr[i] = (s.String + off)[i]; + } + arr[(count == -1 ? s.Length - off - 1 : count)] = 0; + return arr; + } + }; + + class CPPArrayUtil { + public: + template s, size_t off, size_t count = -1> + consteval static std::array substr() { + if (off == FString::npos) return {}; + std::array arr; + + for (size_t i = 0; i < (count == -1 ? (S - off) : count + 1); i++) { + arr[i] = (s.data() + off)[i]; + } + arr[(count == -1 ? S - off - 1 : count)] = 0; + return arr; + } + + + template a> + consteval static size_t find(const wchar_t c) { + for (uint32_t i = 0; i < a.size(); i++) { + if (a[i] == c) return i; + } + return -1; + } + }; +} +using namespace Unreal; \ No newline at end of file diff --git a/Starfall/url.cpp b/Starfall/url.cpp new file mode 100644 index 0000000..d318488 --- /dev/null +++ b/Starfall/url.cpp @@ -0,0 +1,32 @@ +#include "pch.h" +#include "url.h" + +namespace Starfall { + URL::StrType URL::GetUrl() { + StrType v1 = Protocol.substr(0, Protocol.Length - 1); + v1 += Seperator; + v1 += Domain; + v1 += Port; + v1 += Path; + v1 += Query; + return v1; + } + + URL::operator StrType() { + return GetUrl(); + } + + void URL::DeallocPathQuery() { + Path.Dealloc(); + Query.Dealloc(); + } + + void URL::Dealloc() { + Protocol.Dealloc(); + Seperator.Dealloc(); + Domain.Dealloc(); + Port.Dealloc(); + Path.Dealloc(); + Query.Dealloc(); + } +} \ No newline at end of file diff --git a/Starfall/url.h b/Starfall/url.h new file mode 100644 index 0000000..6363197 --- /dev/null +++ b/Starfall/url.h @@ -0,0 +1,88 @@ +#pragma once +#include "pch.h" +#include "ue.h" + +namespace Starfall { + class URL { + public: + using StrType = FString; + StrType Protocol, Seperator, Domain, Port, Path, Query; + + void Construct(StrType& url) { + auto ProtoEnd = url.find(':'); + Protocol = url.substr(0, ProtoEnd); + auto ProtoSize = (url[ProtoEnd + 1] == '/' && url[ProtoEnd + 2] == '/') ? 3 : 1; + Seperator = url.substr(ProtoEnd, ProtoSize); + auto DomainAndPortStart = url.substr(ProtoEnd + ProtoSize); + auto PathEnd = DomainAndPortStart.find_first_of('/'); + auto DomainAndPort = DomainAndPortStart.substr(0, PathEnd); + auto PathStart = DomainAndPortStart.substr(PathEnd); + DomainAndPortStart.Dealloc(); + auto PortOff = DomainAndPort.find_first_of(':'); + Domain = DomainAndPort.substr(0, PortOff); + if (PortOff != StrType::npos) Port = DomainAndPort.substr(PortOff); + DomainAndPort.Dealloc(); + auto QueryOff = PathStart.find_first_of('?'); + Path = PathStart.substr(0, QueryOff); + if (QueryOff != StrType::npos) Query = PathStart.substr(QueryOff); + PathStart.Dealloc(); + } + + URL& SetHost(FString host) { + auto ProtoEnd = host.find(':'); + Protocol = host.substr(0, ProtoEnd); + auto ProtoSize = (host[ProtoEnd + 1] == '/' && host[ProtoEnd + 2] == '/') ? 3 : 1; + Seperator.Dealloc(); + Seperator = host.substr(ProtoEnd, ProtoSize); + auto DomainAndPortStart = host.substr(ProtoEnd + ProtoSize); + auto PathEnd = DomainAndPortStart.find_first_of('/'); + auto DomainAndPort = DomainAndPortStart.substr(0, PathEnd); + DomainAndPortStart.Dealloc(); + auto PortOff = DomainAndPort.find_first_of(':'); + Domain.Dealloc(); + Domain = DomainAndPort.substr(0, PortOff); + if (PortOff != StrType::npos) { + Port.Dealloc(); + Port = DomainAndPort.substr(PortOff); + } + DomainAndPort.Dealloc(); + return *this; + } + + template + URL& SetHost() { + constexpr auto ProtoEnd = FStringUtil::find_const(host, ':'); + Protocol.Dealloc(); + constexpr static auto ProtocolS = FStringUtil::substr(); + constexpr static FString ProtocolV = ProtocolS.data(); + Protocol = ProtocolV; + constexpr auto ProtoSize = (host.String[ProtoEnd + 1] == '/' && host.String[ProtoEnd + 2] == '/') ? 3 : 1; + Seperator.Dealloc(); + constexpr static auto SeperatorS = FStringUtil::substr(); + constexpr static FString SeperatorV = SeperatorS.data(); + Seperator = SeperatorV; + constexpr auto DomainAndPortStart = FStringUtil::substr(); + constexpr auto PathEnd = CPPArrayUtil::find('/'); + constexpr auto DomainAndPort = CPPArrayUtil::substr(); + constexpr auto PortOff = CPPArrayUtil::find(':'); + Domain.Dealloc(); + constexpr static auto DomainS = CPPArrayUtil::substr(); + constexpr static FString DomainV = DomainS.data(); + Domain = DomainV; + if (PortOff != FString::npos) { + Port.Dealloc(); + constexpr static auto PortS = CPPArrayUtil::substr(); + constexpr static FString PortV = PortS.data(); + Port = PortV; + } + return *this; + } + + StrType GetUrl(); + + operator StrType(); + + void Dealloc(); + void DeallocPathQuery(); + }; +} \ No newline at end of file diff --git a/plooshfinder/cpp.hint b/plooshfinder/cpp.hint new file mode 100644 index 0000000..d467d80 --- /dev/null +++ b/plooshfinder/cpp.hint @@ -0,0 +1,4 @@ +// Hint files help the Visual Studio IDE interpret Visual C++ identifiers +// such as names of functions and macros. +// For more information see https://go.microsoft.com/fwlink/?linkid=865984 +#define PF_C diff --git a/plooshfinder/include/asm/arm64.h b/plooshfinder/include/asm/arm64.h new file mode 100644 index 0000000..9cdafe4 --- /dev/null +++ b/plooshfinder/include/asm/arm64.h @@ -0,0 +1,10 @@ +#include +#include + +#ifdef __cplusplus +#define PF_C extern "C" +#else +#define PF_C +#endif + +PF_C uint32_t arm64_branch(void *caller, void *target, bool link); \ No newline at end of file diff --git a/plooshfinder/include/formats/defs/elf_defs.h b/plooshfinder/include/formats/defs/elf_defs.h new file mode 100644 index 0000000..39d64fa --- /dev/null +++ b/plooshfinder/include/formats/defs/elf_defs.h @@ -0,0 +1,69 @@ +#ifndef _ELF_DEFS_H +#define _ELF_DEFS_H +#include + +#define SHT_SYMTAB 0x2 +#define SHT_DYNSYM 0xb +#define PT_LOAD 0x1 + +struct elf_ident_64 { + char signature[4]; + uint8_t file_class; + uint8_t encoding; + uint8_t version; + uint8_t os; + uint8_t abi_version; + char pad[7]; +}; + +struct elf_header_64 { + struct elf_ident_64 ident; + uint16_t type; + uint16_t machine; + uint32_t version; + uint64_t entry; + uint64_t ph_off; + uint64_t sh_off; + uint32_t flags; + uint16_t head_size; + uint16_t ph_size; + uint16_t ph_count; + uint16_t sh_size; + uint16_t sh_count; + uint16_t sect_table_index; +}; + +struct elf_pheader_64 { + uint32_t type; + uint32_t flags; + uint64_t offset; + uint64_t virtual_address; + uint64_t physical_address; + uint64_t file_size; + uint64_t memory_size; + uint64_t align; +}; + +struct elf_sheader_64 { + uint32_t name_off; + uint32_t type; + uint64_t flags; + uint64_t address; + uint64_t offset; + uint64_t size; + uint32_t link; + uint32_t info; + uint64_t align; + uint64_t entry_count; +}; + +struct elf_symbol_64 { + uint32_t name; + unsigned char info; + unsigned char other; + uint16_t sh_index; + uint64_t offset; + uint64_t size; +}; + +#endif \ No newline at end of file diff --git a/plooshfinder/include/formats/defs/macho_defs.h b/plooshfinder/include/formats/defs/macho_defs.h new file mode 100644 index 0000000..4148d89 --- /dev/null +++ b/plooshfinder/include/formats/defs/macho_defs.h @@ -0,0 +1,129 @@ +#ifndef _MACHO_DEFS_H +#define _MACHO_DEFS_H +#include + +#define LC_SEGMENT_64 0x19 +#define LC_BUILD_VERSION 0x32 +#define LC_SYMTAB 0x2 +#define CPU_TYPE_ARM64 0xc000001 +#define LC_FILESET_ENTRY 0x80000035 + +struct mach_header_64 { + uint32_t magic; + uint32_t cputype; + uint32_t cpusubtype; + uint32_t filetype; + uint32_t ncmds; + uint32_t sizeofcmds; + uint32_t flags; + uint32_t reserved; +}; + +struct fat_header { + uint32_t magic; + uint32_t nfat_arch; +}; + +struct fat_arch { + uint32_t cputype; + uint32_t cpusubtype; + uint32_t offset; + uint32_t size; + uint32_t align; +}; + +struct section_64 { + char sectname[16]; + char segname[16]; + + uint64_t addr; + uint64_t size; + uint32_t offset; + uint32_t align; + uint32_t reloff; + uint32_t nreloc; + uint32_t flags; + uint32_t reserved1; + uint32_t reserved2; + uint32_t reserved3; +}; + +struct segment_command_64 { + uint32_t cmd; + uint32_t cmdsize; + + char segname[16]; + uint64_t vmaddr; + uint64_t vmsize; + uint64_t fileoff; + uint64_t filesize; + uint32_t maxprot; + uint32_t initprot; + uint32_t nsects; + uint32_t flags; +}; + +struct load_command_64 { + uint32_t cmd; + uint32_t cmdsize; +}; + +struct build_version_command { + uint32_t cmd; + uint32_t cmdsize; + + uint32_t platform; + uint32_t minos; + uint32_t sdk; + uint32_t ntools; +}; + +struct symtab_command { + uint32_t cmd; + uint32_t cmdsize; + uint32_t symoff; + uint32_t nsyms; + uint32_t stroff; + uint32_t strsize; +}; + +struct nlist_64 { + union { + uint32_t str_index; + } un; + uint8_t type; + uint8_t nsect; + uint16_t desc; + uint64_t offset; +}; + +struct kmod_info { + struct kmod_info *next; + int32_t info_version; + uint32_t id; + char name[64]; + char version[64]; + int32_t reference_count; + struct kmod_reference *reference_list; + uint64_t address; + uint64_t size; + uint64_t hdr_size; + void *start; + void *stop; +}; + +struct kmod_reference { + struct kmod_reference *next; + struct kmod_info *info; +}; + +struct fileset_entry_command { + uint32_t cmd; + uint32_t cmdsize; + uint64_t vmaddr; + uint64_t fileoff; + uint32_t entry_id; + uint32_t reserved; +}; + +#endif \ No newline at end of file diff --git a/plooshfinder/include/formats/defs/pe_defs.h b/plooshfinder/include/formats/defs/pe_defs.h new file mode 100644 index 0000000..3154824 --- /dev/null +++ b/plooshfinder/include/formats/defs/pe_defs.h @@ -0,0 +1,119 @@ +#ifndef _PE_DEFS_H +#define _PE_DEFS_H +#include + +struct DOS_Header { + char e_magic[0x2]; + uint16_t e_cblp; + uint16_t e_cp; + uint16_t e_crlc; + uint16_t e_cparhdr; + uint16_t e_minalloc; + uint16_t e_maxalloc; + uint16_t e_ss; + uint16_t e_sp; + uint16_t e_csum; + uint16_t e_ip; + uint16_t e_cs; + uint16_t e_lfarlc; + uint16_t e_ovno; + char e_res1[0x8]; + uint16_t e_oemid; + uint16_t e_oeminfo; + char e_res2[0x14]; + uint32_t e_lfanew; +}; + +struct COFF_Header { + char magic[0x4]; + uint16_t machine; + uint16_t numberOfSections; + uint32_t timeDateStamp; + uint32_t pointerToSymbolTable; + uint32_t numberOfSymbols; + uint16_t sizeOfOptionalHeader; + uint16_t characteristics; +}; + +struct Section_Header { + char name[0x8]; + uint32_t virtualSize; + uint32_t virtualAddress; + uint32_t sizeOfRawData; + uint32_t pointerToRawData; + uint32_t pointerToRelocations; + uint32_t pointerToLineNumbers; + uint16_t numberOfRelocations; + uint16_t numberOfLineNumbers; + uint32_t characteristics; +}; + +struct Symbol_Header { + union { + char name[0x8]; + struct { + uint32_t zeros; + uint32_t strtab_off; + } over_8b; + } name; + uint32_t value; + int16_t nsect; + uint16_t type; + uint8_t s_class; + uint8_t n_aux; +}; + +struct PE_Data_Directory_Entry { + uint32_t virtualAddress; + uint32_t size; +}; + +struct PE64_Optional_Header { + uint16_t magic; + uint8_t majorLinkerVersion; + uint8_t minorLinkerVersion; + uint32_t sizeOfCode; + uint32_t sizeOfInitializedData; + uint32_t sizeOfUninitializedData; + uint32_t addressOfEntryPoint; + uint32_t baseOfCode; + uint64_t imageBase; + uint32_t sectionAlignment; + uint32_t fileAlignment; + uint16_t majorOperatingSystemVersion; + uint16_t minorOperatingSystemVersion; + uint16_t majorImageVersion; + uint16_t minorImageVersion; + uint16_t majorSubsystemVersion; + uint16_t minorSubsystemVersion; + uint32_t win32VersionValue; + uint32_t sizeOfImage; + uint32_t sizeOfHeaders; + uint32_t checkSum; + uint16_t subsystem; + uint16_t dllCharacteristics; + uint64_t sizeOfStackReserve; + uint64_t sizeOfStackCommit; + uint64_t sizeOfHeapReserve; + uint64_t sizeOfHeapCommit; + uint32_t loaderFlags; + uint32_t numberOfRvaAndSizes; + struct PE_Data_Directory_Entry exportTableEntry; + struct PE_Data_Directory_Entry importTableEntry; + struct PE_Data_Directory_Entry resourceTableEntry; + struct PE_Data_Directory_Entry exceptionTableEntry; + struct PE_Data_Directory_Entry certificateTableEntry; + struct PE_Data_Directory_Entry baseRelocationTableEntry; + struct PE_Data_Directory_Entry debugEntry; + struct PE_Data_Directory_Entry architectureEntry; + struct PE_Data_Directory_Entry globalPtrEntry; + struct PE_Data_Directory_Entry tlsTableEntry; + struct PE_Data_Directory_Entry loadConfigTableEntry; + struct PE_Data_Directory_Entry boundImportEntry; + struct PE_Data_Directory_Entry iatEntry; + struct PE_Data_Directory_Entry delayImportDescriptorEntry; + struct PE_Data_Directory_Entry clrRuntimeHeaderEntry; + struct PE_Data_Directory_Entry reservedEntry; +}; + +#endif \ No newline at end of file diff --git a/plooshfinder/include/formats/elf.h b/plooshfinder/include/formats/elf.h new file mode 100644 index 0000000..c381823 --- /dev/null +++ b/plooshfinder/include/formats/elf.h @@ -0,0 +1,21 @@ +#ifndef _ELF_H +#define _ELF_H +#include +#include +#include "defs/elf_defs.h" + +#ifdef __cplusplus +#define PF_C extern "C" +#else +#define PF_C +#endif + +PF_C bool elf_check(char *buf); +PF_C bool is_elf(char *buf); +PF_C struct elf_sheader_64 *elf_get_section(char *buf, char *name); +PF_C char *elf_va_to_ptr(char *buf, uint64_t addr); +PF_C uint64_t elf_ptr_to_va(char *buf, char *ptr); +PF_C struct elf_symbol_64 *elf_find_symbol_stype(char *buf, char *name, uint32_t type); +PF_C struct elf_symbol_64 *elf_find_symbol(char *buf, char *name); + +#endif \ No newline at end of file diff --git a/plooshfinder/include/formats/macho.h b/plooshfinder/include/formats/macho.h new file mode 100644 index 0000000..fea46e0 --- /dev/null +++ b/plooshfinder/include/formats/macho.h @@ -0,0 +1,44 @@ +#ifndef _MACHO_H +#define _MACHO_H +#include +#include +#include "defs/macho_defs.h" + +#ifdef __cplusplus +#define PF_C extern "C" +#else +#define PF_C +#endif + +PF_C uint32_t macho_get_magic(char *buf); +PF_C bool macho_check(char *buf); +PF_C char *macho_find_arch(char *buf, uint32_t arch); +PF_C uint32_t macho_get_platform(char *buf); +PF_C struct segment_command_64 *macho_get_segment(char *buf, char *name); +PF_C struct section_64 *macho_get_section(char *buf, struct segment_command_64 *segment, char *name); +PF_C struct section_64 *macho_get_last_section(struct segment_command_64 *segment); +PF_C struct section_64 *macho_find_section(char *buf, char *segment_name, char *section_name); +PF_C struct fileset_entry_command *macho_get_fileset(char *buf, char *name); +PF_C struct segment_command_64 *macho_get_segment_for_va(char *buf, uint64_t addr); +PF_C struct section_64 *macho_get_section_for_va(struct segment_command_64 *segment, uint64_t addr); +PF_C struct section_64 *macho_find_section_for_va(char *buf, uint64_t addr); +PF_C char *macho_va_to_ptr(char *buf, uint64_t addr); +PF_C struct segment_command_64 *macho_get_segment_for_ptr(char *buf, char *ptr); +PF_C struct section_64 *macho_get_section_for_ptr(struct segment_command_64 *segment, char *buf, char *ptr); +PF_C struct section_64 *macho_find_section_for_ptr(char *buf, char *ptr); +PF_C uint64_t macho_ptr_to_va(char *buf, char *ptr); +PF_C struct nlist_64 *macho_find_symbol(char *buf, char *name); +PF_C uint64_t macho_get_symbol_size(struct nlist_64 *symbol); +PF_C uint64_t macho_parse_plist_integer(char *key); +PF_C struct mach_header_64 *macho_parse_prelink_info(char *buf, struct section_64 *kmod_info, char *bundle_name); +PF_C uint64_t macho_xnu_untag_va(uint64_t addr); +PF_C struct mach_header_64 *macho_parse_kmod_info(char *buf, struct section_64 *kmod_info, struct section_64 *kmod_start, char *bundle_name); +PF_C struct mach_header_64 *macho_find_kext(char *buf, char *name); +PF_C void macho_run_each_kext(char *buf, void (*function)(char *real_buf, char *kextbuf, uint64_t kext_size)); +PF_C char *fileset_va_to_ptr(char *buf, char *kext, uint64_t addr); +PF_C struct segment_command_64 *fileset_get_segment_for_ptr(char *buf, char *kext, char *ptr); +PF_C struct section_64 *fileset_find_section_for_ptr(char *buf, char *kext, char *ptr); +PF_C uint64_t fileset_ptr_to_va(char *buf, char *kext, char *ptr); +PF_C struct nlist_64 *fileset_find_symbol(char *buf, char *kext, char *name); + +#endif \ No newline at end of file diff --git a/plooshfinder/include/formats/multi.h b/plooshfinder/include/formats/multi.h new file mode 100644 index 0000000..0cc6181 --- /dev/null +++ b/plooshfinder/include/formats/multi.h @@ -0,0 +1,14 @@ +#ifndef _MULTI_H +#define _MULTI_H +#include + +#ifdef __cplusplus +#define PF_C extern "C" +#else +#define PF_C +#endif + +PF_C void *pf_va_to_ptr(void *buf, uint64_t addr); +PF_C uint64_t pf_ptr_to_va(void *buf, void *ptr); + +#endif \ No newline at end of file diff --git a/plooshfinder/include/formats/pe.h b/plooshfinder/include/formats/pe.h new file mode 100644 index 0000000..03096ed --- /dev/null +++ b/plooshfinder/include/formats/pe.h @@ -0,0 +1,25 @@ +#ifndef _PE_H +#define _PE_H +#include +#include +#include "defs/pe_defs.h" + +#ifdef __cplusplus +#define PF_C extern "C" +#else +#define PF_C +#endif + +PF_C bool dos_check(char *buf); +PF_C struct COFF_Header *get_pe_header(char *buf); +PF_C bool pe_check(char *buf); +PF_C bool is_pe(char *buf); +PF_C struct PE64_Optional_Header *get_pe_opt_header(char *buf); +PF_C struct Symbol_Header *pe_get_symtab(char *buf); +PF_C char *pe_get_strtab(char *buf); +PF_C struct Section_Header *pe_get_section(char *buf, const char *name); +PF_C char *pe_va_to_ptr(char *buf, uint64_t addr); +PF_C uint64_t pe_ptr_to_va(char *buf, char *ptr); +PF_C struct Symbol_Header *pe_find_symbol(char *buf, char *name); + +#endif \ No newline at end of file diff --git a/plooshfinder/include/plooshfinder.h b/plooshfinder/include/plooshfinder.h new file mode 100644 index 0000000..fb588a0 --- /dev/null +++ b/plooshfinder/include/plooshfinder.h @@ -0,0 +1,89 @@ +#ifndef _PLOOSHFINDER_H +#define _PLOOSHFINDER_H +#include +#include +#include + +#ifndef _DEBUG +#pragma comment(lib, "../x64/Release/plooshfinder.lib") +#else +#pragma comment(lib, "../x64/Debug/plooshfinder.lib") +#endif + +#ifdef __cplusplus +#define PF_C extern "C" +#else +#define PF_C +#endif + +#pragma pack(push, 0x1) +struct pf_patch_t { + void *matches; + void *masks; + bool disabled; + uint32_t count; + bool (*callback)(struct pf_patch_t *patch, void *stream); +}; + +struct pf_patchset_t { + struct pf_patch_t *patches; + uint32_t count; + bool (*handler)(void *buf, size_t size, struct pf_patchset_t patch); +}; +#pragma pack(pop) + +// patch utils +#ifdef __cplusplus +constexpr pf_patch_t pf_construct_patch(void* matches, void* masks, uint32_t count, bool (*callback)(struct pf_patch_t* patch, void* stream)) { + struct pf_patch_t patch; + + // construct the patch + patch.matches = matches; + patch.masks = masks; + patch.disabled = false; + patch.count = count; + patch.callback = callback; + + return patch; +} + +constexpr pf_patch_t pf_construct_patch_dynmatch(void* masks, uint32_t count, bool (*callback)(struct pf_patch_t* patch, void* stream)) { + struct pf_patch_t patch; + + // construct the patch + patch.matches = nullptr; + patch.masks = masks; + patch.disabled = false; + patch.count = count; + patch.callback = callback; + + return patch; +} + +constexpr struct pf_patchset_t pf_construct_patchset(struct pf_patch_t* patches, uint32_t count, bool (*handler)(void *buf, size_t size, struct pf_patchset_t patchset)) { + struct pf_patchset_t patchset; + + patchset.patches = patches; + patchset.count = count; + patchset.handler = handler; + + return patchset; +} + +__forceinline constexpr struct pf_patchset_t pf_construct_patchset(const struct pf_patch_t* patches, uint32_t count, bool (*handler)(void* buf, size_t size, struct pf_patchset_t patchset)) { + return pf_construct_patchset((struct pf_patch_t*)patches, count, handler); +} +#else +PF_C struct pf_patch_t pf_construct_patch(void *matches, void *masks, uint32_t count, bool (*callback)(struct pf_patch_t *patch, void *stream)); +PF_C struct pf_patchset_t pf_construct_patchset(struct pf_patch_t* patches, uint32_t count, bool (*handler)(void* buf, size_t size, struct pf_patchset_t patchset)); +#endif +PF_C bool pf_patchset_emit(void *buf, size_t size, struct pf_patchset_t patchset); +PF_C void pf_disable_patch(struct pf_patch_t *patch); + +// utils for finding +PF_C uint8_t *pf_find_next(uint8_t *stream, uint32_t count, uint8_t match, uint8_t mask); +PF_C uint8_t *pf_find_prev(uint8_t *stream, uint32_t count, uint8_t match, uint8_t mask); +PF_C uint8_t* pf_find_next_multiple(uint8_t* stream, uint32_t count, uint8_t* matches, uint8_t* masks, uint32_t mmc); +PF_C uint8_t* pf_find_prev_multiple(uint8_t* stream, uint32_t count, uint8_t* matches, uint8_t* masks, uint32_t mmc); + +#endif diff --git a/plooshfinder/include/plooshfinder8.h b/plooshfinder/include/plooshfinder8.h new file mode 100644 index 0000000..f915a60 --- /dev/null +++ b/plooshfinder/include/plooshfinder8.h @@ -0,0 +1,36 @@ +#ifndef _PLOOSHFINDER8_H +#define _PLOOSHFINDER8_H +#include +#include +#include + +#ifdef __cplusplus +#define PF_C extern "C" +#else +#define PF_C +#endif + +#pragma pack(push, 0x1) +struct pf_patch8_t { + uint8_t *matches; + uint8_t *masks; + bool disabled; + uint32_t count; + bool (*callback)(struct pf_patch8_t *patch, void *stream); +}; + +struct pf_patchset8_t { + struct pf_patch8_t *patches; + uint32_t count; + bool (*handler)(void *buf, size_t size, struct pf_patchset8_t patch); +}; +#pragma pack(pop) + +// patch utils +PF_C bool pf_maskmatch(uint8_t insn, uint8_t match, uint8_t mask); +PF_C bool pf_find_maskmatch(void *buf, size_t size, struct pf_patchset_t patchset); + +// utils for finding +PF_C int8_t pf_signextend(int8_t val, uint8_t bits); + +#endif diff --git a/plooshfinder/include/plooshfinder_sig.h b/plooshfinder/include/plooshfinder_sig.h new file mode 100644 index 0000000..645a54a --- /dev/null +++ b/plooshfinder/include/plooshfinder_sig.h @@ -0,0 +1,86 @@ +#ifndef _PLOOSHFINDER_SIG_H +#define _PLOOSHFINDER_SIG_H +#include +#include +#include +#include "plooshfinder8.h" +#include "plooshfinder.h" + +#ifdef __cplusplus +#define PF_C extern "C" +#else +#define PF_C +#endif + +#ifdef __cplusplus +// compile-time processed implementation of sig patch +constexpr int PatternCount(std::string_view s) { + int c = 0; + for (int i = 0; i < s.size(); i++) { + if (s[i] == ' ') c++; + } + return c + 1; // last i think +} + +constexpr uint32_t parsePatternPart(std::string_view s) { + uint32_t val = 0; + for (int i = 0; i < s.size(); i++) { + uint8_t byte = s[i]; + if (byte >= '0' && byte <= '9') byte = byte - '0'; + else if (byte >= 'a' && byte <= 'f') byte = byte - 'a' + 10; + else if (byte >= 'A' && byte <= 'F') byte = byte - 'A' + 10; + else if (byte == '?') byte = 0; + val = (val << 4) | (byte & 0xF); + } + return val; +} + +constexpr uint32_t parsePatternMask(std::string_view s) { + uint32_t val = 0; + for (int i = 0; i < s.size(); i++) { + uint8_t byte = s[i]; + if (byte >= '0' && byte <= '9' || byte >= 'a' && byte <= 'f' || byte >= 'A' && byte <= 'F') byte = 0xf; + else if (byte == '?') byte = 0x0; + val = (val << 4) | (byte & 0xF); + } + return val; +} + +template match, std::array mask, bool (*call)(struct pf_patch_t* patch, void* stream)> +class pf_constexpr_patch_data_t { +public: + static constexpr std::array matches = match; + static constexpr std::array masks = mask; + static constexpr size_t arrsz = sz; + static constexpr bool (*cb)(struct pf_patch_t* patch, void* stream) = call; +}; +#define pf_construct_patch_sig(sig, callback) []() consteval { \ + constexpr pf_constexpr_patch_data_t matches = { 0 }; \ + size_t cInd = 0; \ + for (int i = 0; i < arrsz; i++) { \ + auto part = st.substr(cInd, st.find_first_of(' ', cInd) == std::string_view::npos ? st.size() - cInd : (st.find_first_of(' ', cInd) + 1) - cInd - 1); \ + matches[i] = parsePatternPart(part); \ + cInd = st.find_first_of(' ', cInd) + 1; \ + } \ + return matches; \ + })(), ([]() consteval { \ + constexpr auto st = std::string_view(sig); \ + constexpr auto arrsz = PatternCount(st); \ + std::array masks = { 0 }; \ + size_t cInd = 0; \ + for (int i = 0; i < arrsz; i++) { \ + auto part = st.substr(cInd, st.find_first_of(' ', cInd) == std::string_view::npos ? st.size() - cInd : (st.find_first_of(' ', cInd) + 1) - cInd - 1); \ + masks[i] = parsePatternMask(part); \ + cInd = st.find_first_of(' ', cInd) + 1; \ + } \ + return masks; \ + })(), callback> d; \ + return pf_construct_patch((void *) d.matches.data(), (void *) d.masks.data(), d.arrsz, d.cb); \ +}() +#else +PF_C struct pf_patch_t pf_construct_patch_sig(const char *sig, bool (*callback)(struct pf_patch_t *patch, void *stream)); +#endif +#endif diff --git a/plooshfinder/include/utils.h b/plooshfinder/include/utils.h new file mode 100644 index 0000000..e95c40e --- /dev/null +++ b/plooshfinder/include/utils.h @@ -0,0 +1,14 @@ +#ifndef _UTILS_H +#define _UTILS_H +#include + +#ifdef __cplusplus +#define PF_C extern "C" +#else +#define PF_C +#endif + +PF_C uint32_t convert_endianness32(uint32_t val); +#define Cast(T, expr) (T) (expr) + +#endif \ No newline at end of file diff --git a/plooshfinder/plooshfinder.vcxproj b/plooshfinder/plooshfinder.vcxproj new file mode 100644 index 0000000..8d88255 --- /dev/null +++ b/plooshfinder/plooshfinder.vcxproj @@ -0,0 +1,111 @@ + + + + + Debug + x64 + + + Release + x64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 17.0 + Win32Proj + {1efdb154-d3b5-43bc-8733-f3d6941c9e36} + plooshfinder + 10.0 + + + + StaticLibrary + true + v143 + Unicode + + + StaticLibrary + false + v143 + true + Unicode + + + + + + + + + + + + + + + + Level3 + true + _DEBUG;_LIB;%(PreprocessorDefinitions) + true + NotUsing + pch.h + + + + + true + + + + + Level3 + true + true + true + NOLOG;NDEBUG;_LIB;%(PreprocessorDefinitions) + true + NotUsing + pch.h + NotSet + Neither + + + + + true + true + true + + + + + + \ No newline at end of file diff --git a/plooshfinder/plooshfinder.vcxproj.filters b/plooshfinder/plooshfinder.vcxproj.filters new file mode 100644 index 0000000..b9973c6 --- /dev/null +++ b/plooshfinder/plooshfinder.vcxproj.filters @@ -0,0 +1,90 @@ + + + + + {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 + + + {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 + + + {49b1533c-7528-46c5-840c-71827da7395a} + + + {358939c8-87b9-480a-a5c2-4510332a1f55} + + + {a89650b4-9dcb-4857-9a4c-c85711680efc} + + + + + Source + + + Source + + + Source + + + Source + + + Source\Formats + + + Source\Formats + + + Source\Formats + + + Source\Formats + + + + + Headers + + + Headers + + + Headers + + + Headers\Formats + + + Headers\Formats + + + Headers\Formats + + + Headers\Formats + + + Headers\Formats\Defs + + + Headers\Formats\Defs + + + Headers\Formats\Defs + + + Headers + + + + + + \ No newline at end of file diff --git a/plooshfinder/src/formats/elf.c b/plooshfinder/src/formats/elf.c new file mode 100644 index 0000000..05936e1 --- /dev/null +++ b/plooshfinder/src/formats/elf.c @@ -0,0 +1,162 @@ +#include +#include +#include +#include "../../include/formats/elf.h" +#include "../../include/utils.h" + +bool elf_check(char *buf) { + struct elf_header_64 *hdr = (struct elf_header_64 *) buf; + char elf_magic[5] = { 0x7f, 'E', 'L', 'F' }; + + if (strncmp(hdr->ident.signature, elf_magic, 4) == 0) { + return true; + } + + return false; +} + +bool is_elf(char *buf) { + bool elf = elf_check(buf); + + if (!elf) { + printf("%s: Not an ELF!\n", __FUNCTION__); + } + + return elf; +} + + +struct elf_sheader_64 *elf_get_section(char *buf, char *name) { + if (!elf_check(buf)) { + return NULL; + } + + struct elf_header_64 *hdr = (struct elf_header_64 *) buf; + struct elf_sheader_64 *section_hdr = Cast(struct elf_sheader_64*, buf + hdr->sh_off); + + struct elf_sheader_64 *sname_hdr = section_hdr + hdr->sect_table_index; + char *sname_tbl = buf + sname_hdr->offset; + + + for (uint16_t i = 0; i < hdr->sh_count; i++) { + struct elf_sheader_64 *section = section_hdr + i; + char *sect_name = sname_tbl + section->name_off; + + if (strcmp(sect_name, name) == 0) { + return section; + } + } + + return NULL; +} + +char *elf_va_to_ptr(char *buf, uint64_t addr) { + if (!elf_check(buf)) { + return 0; + } + + struct elf_header_64 *hdr = (struct elf_header_64 *) buf; + struct elf_pheader_64 *program_hdr = Cast(struct elf_pheader_64*, buf + hdr->ph_off); + + for (int i = 0; i < hdr->ph_count; i++) { + struct elf_pheader_64 *phdr = program_hdr + i; + + if (phdr->type == PT_LOAD) { + uint64_t segment_start = phdr->virtual_address; + uint64_t segment_end = segment_start + phdr->file_size; + if (segment_start <= addr && segment_end > addr) { + uint64_t offset = addr - segment_start; + return buf + phdr->offset + offset; + } + } + } + + return 0; +} + +uint64_t elf_ptr_to_va(char *buf, char *ptr) { + if (!elf_check(buf)) { + return 0; + } + + struct elf_header_64 *hdr = (struct elf_header_64 *) buf; + struct elf_pheader_64 *program_hdr = Cast(struct elf_pheader_64*, buf + hdr->ph_off); + uint64_t ptr_addr = (uint64_t) ptr; + + for (int i = 0; i < hdr->ph_count; i++) { + struct elf_pheader_64 *phdr = program_hdr + i; + + if (phdr->type == PT_LOAD) { + uint64_t segment_start = (uint64_t) buf + phdr->offset; + uint64_t segment_end = segment_start + phdr->file_size; + uint64_t segment_va = phdr->virtual_address; + + if (segment_start <= ptr_addr && segment_end > ptr_addr) { + uint64_t offset = ptr_addr - segment_start; + + return segment_va + offset; + } + } + } + + return 0; +} + +struct elf_symbol_64 *elf_find_symbol_stype(char *buf, char *name, uint32_t type) { + if (!elf_check(buf)) { + return NULL; + } + + struct elf_header_64 *hdr = (struct elf_header_64 *) buf; + struct elf_sheader_64* section_hdr = Cast(struct elf_sheader_64*, buf + hdr->sh_off); + struct elf_sheader_64 *sect = NULL; + struct elf_sheader_64 *strtab_sect; + + for (uint16_t i = 0; i < hdr->sh_count; i++) { + struct elf_sheader_64 *section = section_hdr + i; + + if (section->type == type) { + sect = section; + } + } + + if (!sect) { + return NULL; + } + + strtab_sect = section_hdr + sect->link; + + struct elf_symbol_64 *symtab = Cast(struct elf_symbol_64*, buf + sect->offset); + char *strtab = buf + strtab_sect->offset; + uint64_t count = sect->size / sizeof(struct elf_symbol_64); + + for (int i = 0; i < count; i++) { + struct elf_symbol_64 *symbol = symtab + i; + char *sym_name = strtab + symbol->name; + + if (strcmp(sym_name, name) == 0) { + return symbol; + } + } + + return NULL; +} + +struct elf_symbol_64 *elf_find_symbol(char *buf, char *name) { + if (!elf_check(buf)) { + return NULL; + } + + struct elf_symbol_64 *symbol = elf_find_symbol_stype(buf, name, SHT_SYMTAB); + + if (!symbol) { + symbol = elf_find_symbol_stype(buf, name, SHT_DYNSYM); + } + + if (!symbol) { + printf("%s: Failed to find symbol %s!\n", __FUNCTION__, name); + return NULL; + } + + return symbol; +} \ No newline at end of file diff --git a/plooshfinder/src/formats/macho.c b/plooshfinder/src/formats/macho.c new file mode 100644 index 0000000..c0eda68 --- /dev/null +++ b/plooshfinder/src/formats/macho.c @@ -0,0 +1,658 @@ +#include +#include +#include +#include +#include +#include "../../include/formats/macho.h" +#include "../../include/utils.h" + +uint32_t macho_get_magic(char *buf) { + uint32_t *buf_ptr = (uint32_t *) buf; + uint32_t magic = buf_ptr[0]; + + if (magic == 0xfeedfacf || magic == 0xbebafeca) { + return magic; + } else { + printf("%s: Not a mach-o!\n", __FUNCTION__); + } + + return 0; +} + +bool macho_check(char *buf) { + uint32_t magic = macho_get_magic(buf); + + if (magic == 0xfeedfacf || magic == 0xbebafeca) { + return true; + } + + return false; +} + +char *macho_find_arch(char *buf, uint32_t arch) { + uint32_t magic = macho_get_magic(buf); + + if (magic == 0xbebafeca) { + struct fat_header *header = (struct fat_header *) buf; + struct fat_arch *farch = (struct fat_arch *) ((char *) buf + sizeof(struct fat_header)); + + for (uint32_t i = 0; i < convert_endianness32(header->nfat_arch); i++) { + if (farch->cputype == arch) { + return buf + convert_endianness32(farch->offset); + } + + farch = (struct fat_arch *) ((char *) farch + sizeof(struct fat_arch)); + } + + printf("%s: Universal mach-o does not contain a slice for the arch requested!\n", __FUNCTION__); + } + + return buf; +} + +uint32_t macho_get_platform(char *buf) { + if (!macho_check(buf)) { + return 0; + } + + struct load_command_64 *after_header = Cast(struct load_command_64*, buf + sizeof(struct mach_header_64)); + struct mach_header_64 *header = Cast(struct mach_header_64*, buf); + + for (uint32_t i = 0; i < header->ncmds; i++) { + if (after_header->cmd == LC_BUILD_VERSION) { + struct build_version_command *cmd = (struct build_version_command *) after_header; + + if (cmd->platform > 5) { + printf("%s: Invalid platform!\n", __FUNCTION__); + return 0; + } + + return cmd->platform; + } + + after_header = (struct load_command_64 *) ((char *) after_header + after_header->cmdsize); + } + + printf("%s: Unable to get platform!\n", __FUNCTION__); + return 0; +} + +struct segment_command_64 *macho_get_segment(char *buf, char *name) { + if (!macho_check(buf)) { + return NULL; + } + + struct load_command_64* after_header = Cast(struct load_command_64*, buf + sizeof(struct mach_header_64)); + struct mach_header_64* header = Cast(struct mach_header_64*, buf); + + for (uint32_t i = 0; i < header->ncmds; i++) { + if (after_header->cmd == LC_SEGMENT_64) { + struct segment_command_64 *segment = (struct segment_command_64 *) after_header; + if (strcmp(segment->segname, name) == 0) { + return segment; + } + } else { + break; + } + + after_header = (struct load_command_64 *) ((char *) after_header + after_header->cmdsize); + } + + return NULL; +} + +struct section_64 *macho_get_section(char *buf, struct segment_command_64 *segment, char *name) { + if (!segment || !macho_check(buf)) { + return NULL; + } + + struct section_64 *section = (struct section_64 *) ((char *) segment + sizeof(struct segment_command_64)); + + for (uint32_t i = 0; i < segment->nsects; i++) { + if (strcmp(section->sectname, name) == 0) { + return section; + } + + section = (struct section_64 *) ((char *) section + sizeof(struct section_64)); + } + + return NULL; +} + +struct section_64 *macho_get_last_section(struct segment_command_64 *segment) { + uint32_t index = segment->nsects - 1; + struct section_64 *sections = (struct section_64 *) ((char *) segment + sizeof(struct segment_command_64)); + + return sections + index; +} + +struct section_64 *macho_find_section(char *buf, char *segment_name, char *section_name) { + if (!macho_check(buf)) { + return NULL; + } + + struct segment_command_64 *segment = macho_get_segment(buf, segment_name); + if (!segment) { + return NULL; + } + + struct section_64 *section = macho_get_section(buf, segment, section_name); + if (!section) { + return NULL; + } + + return section; +} + +struct fileset_entry_command *macho_get_fileset(char *buf, char *name) { + if (!macho_check(buf)) { + return NULL; + } + + struct load_command_64* after_header = Cast(struct load_command_64*, buf + sizeof(struct mach_header_64)); + struct mach_header_64* header = Cast(struct mach_header_64*, buf); + + for (uint32_t i = 0; i < header->ncmds; i++) { + if (after_header->cmd == LC_FILESET_ENTRY) { + struct fileset_entry_command *entry = (struct fileset_entry_command *) after_header; + char *entry_name = (char *) entry + entry->entry_id; + + if (strcmp(entry_name, name) == 0) { + return entry; + } + } + + after_header = (struct load_command_64 *) ((char *) after_header + after_header->cmdsize); + } + + return 0; +} + +struct segment_command_64 *macho_get_segment_for_va(char *buf, uint64_t addr) { + if (!macho_check(buf)) { + return NULL; + } + + struct load_command_64* after_header = Cast(struct load_command_64*, buf + sizeof(struct mach_header_64)); + struct mach_header_64* header = Cast(struct mach_header_64*, buf); + struct segment_command_64 *segment = NULL; + + for (uint32_t i = 0; i < header->ncmds; i++) { + if (after_header->cmd == LC_SEGMENT_64) { + segment = (struct segment_command_64 *) after_header; + uint64_t segment_start = segment->vmaddr; + uint64_t segment_end = segment_start + segment->vmsize; + + if (segment_start <= addr && segment_end > addr) { + // segment's range contains the addr + return segment; + } + } + + after_header = (struct load_command_64 *) ((char *) after_header + after_header->cmdsize); + } + + printf("%s: Unable to find segment containing 0x%llx!\n", __FUNCTION__, addr); + return NULL; +} + +struct section_64 *macho_get_section_for_va(struct segment_command_64 *segment, uint64_t addr) { + struct section_64 *section = (struct section_64 *) ((char *) segment + sizeof(struct segment_command_64)); + + for (uint32_t i = 0; i < segment->nsects; i++) { + uint64_t section_start = section->addr; + uint64_t section_end = section_start + section->size; + + if (section_start <= addr && section_end > addr) { + // section's range contains the addr + return section; + } + + section = (struct section_64 *) ((char *) section + sizeof(struct section_64)); + } + + printf("%s: Unable to find section containing 0x%llx?\n", __FUNCTION__, addr); + return NULL; +} + +struct section_64 *macho_find_section_for_va(char *buf, uint64_t addr) { + if (!macho_check(buf)) { + return NULL; + } + + struct segment_command_64 *segment = macho_get_segment_for_va(buf, addr); + if (!segment) { + return NULL; + } + + struct section_64 *section = macho_get_section_for_va(segment, addr); + if (!section) { + return NULL; + } + + return section; +} + +char *macho_va_to_ptr(char *buf, uint64_t addr) { + if (!macho_check(buf)) { + return NULL; + } + + struct segment_command_64 *segment = macho_get_segment_for_va(buf, addr); + if (!segment) { + return NULL; + } else if (segment->vmaddr == addr) { + return buf + segment->fileoff; + } + + struct section_64 *section = macho_get_section_for_va(segment, addr); + + uint64_t offset = addr - section->addr; + + return buf + section->offset + offset; +} + +struct segment_command_64 *macho_get_segment_for_ptr(char *buf, char *ptr) { + if (!macho_check(buf)) { + return NULL; + } + + struct load_command_64* after_header = Cast(struct load_command_64*, buf + sizeof(struct mach_header_64)); + struct mach_header_64* header = Cast(struct mach_header_64*, buf); + struct segment_command_64 *segment = NULL; + uint64_t ptr_addr = (uint64_t) ptr; + + for (uint32_t i = 0; i < header->ncmds; i++) { + if (after_header->cmd == LC_SEGMENT_64) { + segment = (struct segment_command_64 *) after_header; + uint64_t segment_start = (uint64_t) buf + segment->fileoff; + uint64_t segment_end = segment_start + segment->filesize; + + if (segment_start <= ptr_addr && segment_end > ptr_addr) { + // segment's range contains the ptr + return segment; + } + } + + after_header = (struct load_command_64 *) ((char *) after_header + after_header->cmdsize); + } + + printf("%s: Unable to find segment containing ptr %p!\n", __FUNCTION__, ptr); + return NULL; +} + +struct section_64 *macho_get_section_for_ptr(struct segment_command_64 *segment, char *buf, char *ptr) { + struct section_64 *section = (struct section_64 *) ((char *) segment + sizeof(struct segment_command_64)); + uint64_t ptr_addr = (uint64_t) ptr; + + for (uint32_t i = 0; i < segment->nsects; i++) { + uint64_t section_start = (uint64_t) buf + section->offset; + uint64_t section_end = section_start + section->size; + + if (section_start <= ptr_addr && section_end > ptr_addr) { + // section's range contains the ptr + return section; + } + + section = (struct section_64 *) ((char *) section + sizeof(struct section_64)); + } + + printf("%s: Unable to find section containing %p?\n", __FUNCTION__, ptr); + return NULL; +} + +struct section_64 *macho_find_section_for_ptr(char *buf, char *ptr) { + if (!macho_check(buf)) { + return NULL; + } + + struct segment_command_64 *segment = macho_get_segment_for_ptr(buf, ptr); + if (!segment) { + return NULL; + } + + struct section_64 *section = macho_get_section_for_ptr(segment, buf, ptr); + if (!section) { + return NULL; + } + + return section; +} + +uint64_t macho_ptr_to_va(char *buf, char *ptr) { + if (!macho_check(buf)) { + return 0; + } + + struct section_64 *section = macho_find_section_for_ptr(buf, ptr); + + uint64_t offset = ptr - buf - section->offset; + + return section->addr + offset; +} + +struct nlist_64 *macho_find_symbol(char *buf, char *name) { + if (!macho_check(buf)) { + return NULL; + } + + struct load_command_64* after_header = Cast(struct load_command_64*, buf + sizeof(struct mach_header_64)); + struct mach_header_64* header = Cast(struct mach_header_64*, buf); + struct symtab_command *symtab_cmd = NULL; + + for (uint32_t i = 0; i < header->ncmds; i++) { + if (after_header->cmd == LC_SYMTAB) { + symtab_cmd = (struct symtab_command *) after_header; + + break; + } + + after_header = (struct load_command_64 *) ((char *) after_header + after_header->cmdsize); + } + + if (!symtab_cmd) { + printf("%s: Unable to find symbol table!\n", __FUNCTION__); + return NULL; + } + + struct nlist_64 *symtab = Cast(struct nlist_64 *, buf + symtab_cmd->symoff); + char *strtab = buf + symtab_cmd->stroff; + + for (uint32_t i = 0; i < symtab_cmd->nsyms; i++) { + struct nlist_64 *symbol_nlist = symtab + i; + char *sym_name = strtab + symbol_nlist->un.str_index; + + if (strcmp(sym_name, name) == 0) { + return symbol_nlist; + } + } + + //printf("%s: Unable to find symbol %s!\n", __FUNCTION__, name); + return NULL; +} + +uint64_t macho_get_symbol_size(struct nlist_64 *symbol) { + // this is not very reliable, as symtab doesn't include stripped symbols and can be in a weird order. + + struct nlist_64 *next_symbol = symbol + 1; + + if (next_symbol->offset < symbol->offset) { + // symtab is in a weird order. can't really do anything about this, return 0. + + printf("%s: Symtab is in a weird order!\n", __FUNCTION__); + return 0; + } + + return next_symbol->offset - symbol->offset; +} + +uint64_t macho_parse_plist_integer(char *key) { + char *key_value = strstr(key, ""); + + if (key_value) { + return strtoull(key_value + 1, 0, 0); + } + } + + return 0; +} + +struct mach_header_64 *macho_parse_prelink_info(char *buf, struct section_64 *kmod_info, char *bundle_name) { + if (!macho_check(buf)) { + return NULL; + } + + char kext_name[256]; + struct mach_header_64 *kext = NULL; + + char *start = buf + kmod_info->offset; + + char *info_dict = strstr(start, "PrelinkInfoDictionary"); + char *last_dict = strstr(info_dict, "") + 7; + + while (last_dict) { + char *dict_end = strstr(last_dict, ""); + if (!dict_end) break; + + char *dict2 = strstr(last_dict + 1, ""); + while (dict2) { + if (dict2 > dict_end) break; + + dict2 = strstr(dict2 + 1, ""); + dict_end = strstr(dict_end + 1, ""); + } + + char *identifier = strstr(last_dict, "CFBundleIdentifier"); + + if (identifier) { + char *value_key = strstr(identifier, ""); + + if (value_key) { + value_key += strlen(""); + char *key_end = strstr(value_key, ""); + + if (key_end) { + __int64 key_len = key_end - value_key; + + memcpy(kext_name, value_key, key_len); + kext_name[key_len] = 0; + + if (strcmp(kext_name, bundle_name) == 0) { + char *addr_key = strstr(last_dict, "_PrelinkExecutableLoadAddr"); + + if (addr_key) { + kext = (struct mach_header_64 *) macho_va_to_ptr(buf, macho_parse_plist_integer(addr_key)); + + break; + } + } + } + } + } + + last_dict = strstr(dict_end, ""); + } + + return kext; +} + +uint64_t macho_xnu_untag_va(uint64_t addr) { + if (((addr >> 32) & 0xffff) == 0xfff0) { + return (0xffffULL << 48) | addr; + } else { + return addr; + } +} + +struct mach_header_64 *macho_parse_kmod_info(char *buf, struct section_64 *kmod_info, struct section_64 *kmod_start, char *bundle_name) {\ + if (!macho_check(buf)) { + return NULL; + } + + struct mach_header_64 *kext = NULL; + + uint64_t kmod_count = kmod_info->size >> 3; + uint64_t *info_start = Cast(uint64_t *, buf + kmod_info->offset); + uint64_t *start = Cast(uint64_t*, buf + kmod_start->offset); + + for (uint64_t i = 0; i < kmod_count; i++) { + struct kmod_info *info = Cast(struct kmod_info *, macho_va_to_ptr(buf, macho_xnu_untag_va(info_start[i]))); + + if (strcmp(info->name, bundle_name) == 0) { + kext = (struct mach_header_64 *) macho_va_to_ptr(buf, macho_xnu_untag_va(start[i])); + } + } + + return kext; +} + +struct mach_header_64 *macho_find_kext(char *buf, char *name) { + struct mach_header_64 *kext = NULL; + + struct segment_command_64 *prelink_info = macho_get_segment(buf, "__PRELINK_INFO"); + if (!prelink_info) return NULL; + + struct section_64 *kmod_info = macho_get_section(buf, prelink_info, "__kmod_info"); + + if (!kmod_info) { + struct section_64 *info = macho_get_section(buf, prelink_info, "__info"); + if (!info) return NULL; + + kext = macho_parse_prelink_info(buf, info, name); + } else { + struct section_64 *kmod_start = macho_get_section(buf, prelink_info, "__kmod_start"); + if (!kmod_start) return NULL; + + kext = macho_parse_kmod_info(buf, kmod_info, kmod_start, name); + } + + return kext; +} + + +void macho_run_each_kext(char *buf, void (*function)(char *real_buf, char *kextbuf, uint64_t kext_size)) { + struct segment_command_64 *prelink_info = macho_get_segment(buf, "__PRELINK_INFO"); + if (!prelink_info) return; + + struct section_64 *kmod_start = macho_get_section(buf, prelink_info, "__kmod_start"); + + if (!kmod_start) { + struct section_64 *kexts_text = macho_find_section(buf, "__PLK_TEXT_EXEC", "__text"); + if (!kexts_text) return; + + function(buf, buf + kexts_text->offset, kexts_text->size); + } else { + uint64_t kmod_count = kmod_start->size >> 3; + uint64_t *start = Cast(uint64_t*, buf + kmod_start->offset); + + for (uint32_t i = 0; i < kmod_count; i++) { + struct mach_header_64 *kext = Cast(struct mach_header_64*, macho_va_to_ptr(buf, macho_xnu_untag_va(start[i]))); + + struct section_64 *kext_text = macho_find_section(Cast(char *, kext), "__TEXT_EXEC", "__text"); + + function(buf, macho_va_to_ptr(buf, macho_xnu_untag_va(kext_text->addr)), kext_text->size); + } + } +} + +char *fileset_va_to_ptr(char *buf, char *kext, uint64_t addr) { + if (!macho_check(buf)) { + return NULL; + } + + struct segment_command_64 *segment = macho_get_segment_for_va(kext, addr); + if (!segment) { + return NULL; + } else if (segment->vmaddr == addr) { + return buf + segment->fileoff; + } + + struct section_64 *section = macho_get_section_for_va(segment, addr); + + uint64_t offset = addr - section->addr; + + return buf + section->offset + offset; +} + +struct segment_command_64 *fileset_get_segment_for_ptr(char *buf, char *kext, char *ptr) { + if (!macho_check(buf)) { + return NULL; + } + + struct load_command_64 *after_header = Cast(struct load_command_64 *, kext + sizeof(struct mach_header_64)); + struct mach_header_64 *header = Cast(struct mach_header_64*, kext); + struct segment_command_64 *segment = NULL; + uint64_t ptr_addr = (uint64_t) ptr; + + for (uint32_t i = 0; i < header->ncmds; i++) { + if (after_header->cmd == LC_SEGMENT_64) { + segment = (struct segment_command_64 *) after_header; + uint64_t segment_start = (uint64_t) buf + segment->fileoff; + uint64_t segment_end = segment_start + segment->filesize; + + if (segment_start <= ptr_addr && segment_end > ptr_addr) { + // segment's range contains the ptr + return segment; + } + } + + after_header = (struct load_command_64 *) ((char *) after_header + after_header->cmdsize); + } + + printf("%s: Unable to find segment containing ptr %p!\n", __FUNCTION__, ptr); + return NULL; +} + +struct section_64 *fileset_find_section_for_ptr(char *buf, char *kext, char *ptr) { + if (!macho_check(buf)) { + return NULL; + } + + struct segment_command_64 *segment = fileset_get_segment_for_ptr(buf, kext, ptr); + if (!segment) { + return NULL; + } + + struct section_64 *section = macho_get_section_for_ptr(segment, buf, ptr); + if (!section) { + return NULL; + } + + return section; +} + + +uint64_t fileset_ptr_to_va(char *buf, char *kext, char *ptr) { + if (!macho_check(buf)) { + return 0; + } + + struct section_64 *section = fileset_find_section_for_ptr(buf, kext, ptr); + + uint64_t offset = ptr - buf - section->offset; + + return section->addr + offset; +} + +struct nlist_64 *fileset_find_symbol(char *buf, char *kext, char *name) { + if (!macho_check(buf)) { + return NULL; + } + + struct load_command_64 *after_header = Cast(struct load_command_64*, kext + sizeof(struct mach_header_64)); + struct mach_header_64 *header = Cast(struct mach_header_64*, kext); + struct symtab_command *symtab_cmd = NULL; + + for (uint32_t i = 0; i < header->ncmds; i++) { + if (after_header->cmd == LC_SYMTAB) { + symtab_cmd = (struct symtab_command *) after_header; + + break; + } + + after_header = (struct load_command_64 *) ((char *) after_header + after_header->cmdsize); + } + + if (!symtab_cmd) { + printf("%s: Unable to find symbol table!\n", __FUNCTION__); + return NULL; + } + + struct nlist_64 *symtab = Cast(struct nlist_64*, buf + symtab_cmd->symoff); + char *strtab = buf + symtab_cmd->stroff; + + for (uint32_t i = 0; i < symtab_cmd->nsyms; i++) { + struct nlist_64 *symbol_nlist = symtab + i; + char *sym_name = strtab + symbol_nlist->un.str_index; + + if (strcmp(sym_name, name) == 0) { + return symbol_nlist; + } + } + + //printf("%s: Unable to find symbol %s!\n", __FUNCTION__, name); + return NULL; +} \ No newline at end of file diff --git a/plooshfinder/src/formats/multi.c b/plooshfinder/src/formats/multi.c new file mode 100644 index 0000000..117cfed --- /dev/null +++ b/plooshfinder/src/formats/multi.c @@ -0,0 +1,42 @@ +#include +#include +#include +#include +#include +#include "../../include/formats/macho.h" +#include "../../include/formats/elf.h" +#include "../../include/formats/pe.h" +#include "../../include/utils.h" +// right now this is just ptr & virtual address conversion + +char *pf_va_to_ptr(char *buf, uint64_t addr) { + char *ptr = NULL; + + if (macho_check(buf)) { + ptr = macho_va_to_ptr(buf, addr); + } else if (elf_check(buf)) { + ptr = elf_va_to_ptr(buf, addr); + } else if (pe_check(buf)) { + ptr = pe_va_to_ptr(buf, addr); + } else { + printf("%s: Unknown binary format!\n", __FUNCTION__); + } + + return ptr; +} + +uint64_t pf_ptr_to_va(char *buf, char *ptr) { + uint64_t va = 0; + + if (macho_check(buf)) { + va = macho_ptr_to_va(buf, ptr); + } else if (elf_check(buf)) { + va = elf_ptr_to_va(buf, ptr); + } else if (pe_check(buf)) { + va = pe_ptr_to_va(buf, ptr); + } else { + printf("%s: Unknown binary format!\n", __FUNCTION__); + } + + return va; +} \ No newline at end of file diff --git a/plooshfinder/src/formats/pe.c b/plooshfinder/src/formats/pe.c new file mode 100644 index 0000000..9b6485e --- /dev/null +++ b/plooshfinder/src/formats/pe.c @@ -0,0 +1,180 @@ +#include +#include +#include +#include +#include "../../include/formats/pe.h" +#include "../../include/utils.h" + +bool dos_check(char *buf) { + /*struct DOS_Header* dos_hdr = Cast(struct DOS_Header*, buf); + + if (strncmp(dos_hdr->e_magic, "MZ", 2) == 0) { + return true; + } + + return false;*/ + return true; +} + +struct COFF_Header *get_pe_header(char *buf) { + if (!dos_check(buf)) { +#ifndef NOLOG + printf("No DOS stub!\n"); +#endif + return NULL; + } + + struct DOS_Header *dos_hdr = Cast(struct DOS_Header*, buf); + + return Cast(struct COFF_Header *, buf + dos_hdr->e_lfanew); +} + +bool pe_check(char *buf) { + /*struct COFF_Header* pe_hdr = get_pe_header(buf); + + if (!pe_hdr) { + return false; + } + + if (strcmp(pe_hdr->magic, "PE") == 0) { + return true; + } + + return false;*/ + return true; +} + +bool is_pe(char *buf) { + bool pe = pe_check(buf); + + if (!pe) { +#ifndef NOLOG + printf("%s: Not a PE!\n", __FUNCTION__); +#endif + } + + return pe; +} + +struct PE64_Optional_Header *get_pe_opt_header(char *buf) { + if (!dos_check(buf)) { +#ifndef NOLOG + printf("No DOS stub!\n"); +#endif + return NULL; + } + + if (!pe_check(buf)) return NULL; + + struct COFF_Header *pe_hdr = get_pe_header(buf); + + return Cast(struct PE64_Optional_Header*, (char *) pe_hdr + sizeof(struct COFF_Header)); +} + +struct Symbol_Header *pe_get_symtab(char *buf) { + if (!pe_check(buf)) return NULL; + + struct COFF_Header *pe_hdr = get_pe_header(buf); + + return Cast(struct Symbol_Header*, buf + pe_hdr->pointerToSymbolTable); +} + +char *pe_get_strtab(char *buf) { + if (!pe_check(buf)) return NULL; + + struct COFF_Header *pe_hdr = get_pe_header(buf); + char *symtab = Cast(char *, pe_get_symtab(buf)); + + return symtab + (pe_hdr->numberOfSymbols * 18); // 18 is the correct size, but struct alignment makes it 20. +} + +struct Section_Header *pe_get_section(char *buf, const char *name) { + if (!pe_check(buf)) return NULL; + + struct COFF_Header *pe_hdr = get_pe_header(buf); + struct Section_Header *sects_start = Cast(struct Section_Header *, (char *) pe_hdr + sizeof(struct COFF_Header) + pe_hdr->sizeOfOptionalHeader); + char *strtab = pe_get_strtab(buf); + + for (int i = 0; i < pe_hdr->numberOfSections; i++) { + struct Section_Header *section = sects_start + i; + char *real_name = section->name; + + if (real_name[0] == '/') { + real_name = strtab + strtoul(real_name + 1, 0, 0); + } + + if (strncmp(real_name, name, 8) == 0) { + return section; + } + } + + return NULL; +} + +char *pe_va_to_ptr(char *buf, uint64_t addr) { + if (!pe_check(buf)) return NULL; + + struct COFF_Header *pe_hdr = get_pe_header(buf); + struct PE64_Optional_Header *opt_hdr = get_pe_opt_header(buf); + struct Section_Header *sects_start = Cast(struct Section_Header*, (char *) opt_hdr + pe_hdr->sizeOfOptionalHeader); + + for (int i = 0; i < pe_hdr->numberOfSections; i++) { + struct Section_Header *section = sects_start + i; + + uint64_t section_start = opt_hdr->imageBase + section->virtualAddress; + uint64_t section_end = section_start + section->virtualSize; + + if (section_start <= addr && section_end > addr) { + uint64_t offset = addr - section_start; + return buf + section->pointerToRawData + offset; + } + } + + return NULL; +} + +uint64_t pe_ptr_to_va(char *buf, char *ptr) { + if (!pe_check(buf)) return 0; + + struct COFF_Header *pe_hdr = get_pe_header(buf); + struct PE64_Optional_Header *opt_hdr = get_pe_opt_header(buf); + struct Section_Header *sects_start = Cast(struct Section_Header*, (char*)opt_hdr + pe_hdr->sizeOfOptionalHeader); + uint64_t ptr_addr = (uint64_t) ptr; + + for (int i = 0; i < pe_hdr->numberOfSections; i++) { + struct Section_Header *section = sects_start + i; + + uint64_t section_start = (uint64_t) buf + section->pointerToRawData; + uint64_t section_end = section_start + section->sizeOfRawData; + + if (section_start <= ptr_addr && section_end > ptr_addr) { + uint64_t offset = ptr_addr - section_start; + return opt_hdr->imageBase + section->virtualAddress + offset; + } + } + + return 0; +} + +struct Symbol_Header *pe_find_symbol(char *buf, char *name) { + if (!pe_check(buf)) return 0; + + struct COFF_Header *pe_hdr = get_pe_header(buf); + char *symtab = Cast(char *, pe_get_symtab(buf)); + char *strtab = pe_get_strtab(buf); + + for (uint32_t i = 0; i < pe_hdr->numberOfSymbols; i++) { + struct Symbol_Header *symbol = Cast(struct Symbol_Header *, symtab + (i * 18)); // 18 is the correct size, but struct alignment makes it 20. + char *real_name = symbol->name.name; + + if (symbol->name.over_8b.zeros == 0 && symbol->name.over_8b.strtab_off != 0) { + real_name = strtab + symbol->name.over_8b.strtab_off; + } + + if (strcmp(real_name, name) == 0) { + return symbol; + } + } + + return NULL; +} \ No newline at end of file diff --git a/plooshfinder/src/plooshfinder.c b/plooshfinder/src/plooshfinder.c new file mode 100644 index 0000000..69ae67c --- /dev/null +++ b/plooshfinder/src/plooshfinder.c @@ -0,0 +1,146 @@ +// plooshfinder +// WIP patchfinder +// Made by Ploosh + +#include +#include +#include +#include "../include/plooshfinder.h" +#include "../include/plooshfinder_sig.h" +#include "../include/formats/pe.h" + +void *pf_zero_buf; + +struct pf_patch_t pf_construct_patch(void *matches, void *masks, uint32_t count, bool (*callback)(struct pf_patch_t *patch, void *stream)) { + struct pf_patch_t patch; + + // construct the patch + patch.matches = matches; + patch.masks = masks; + patch.disabled = false; + patch.count = count; + patch.callback = callback; + + return patch; +} + +struct pf_patchset_t pf_construct_patchset(struct pf_patch_t *patches, uint32_t count, bool (*handler)(void *buf, size_t size, struct pf_patchset_t patchset)) { + struct pf_patchset_t patchset; + + patchset.patches = patches; + patchset.count = count; + patchset.handler = handler; + + return patchset; +} + +bool pf_patchset_emit(void *buf, size_t size, struct pf_patchset_t patchset) { + return patchset.handler(buf, size, patchset); +} + +void pf_disable_patch(struct pf_patch_t *patch) { + patch->disabled = true; +} + +uint8_t *pf_find_next(uint8_t *stream, uint32_t count, uint8_t match, uint8_t mask) { + uint8_t *find_stream = 0; + + for (int i = 0; (uint32_t) i < count; i++) { + if (pf_maskmatch(stream[i], match, mask)) { + find_stream = stream + i; + break; + } + } + + return find_stream; +} + +uint8_t *pf_find_prev(uint8_t *stream, uint32_t count, uint8_t match, uint8_t mask) { + uint8_t *find_stream = 0; + + for (int neg_count = -(int)count; count > 0; count--) { + int ind = neg_count + count; + if (pf_maskmatch(stream[ind], match, mask)) { + find_stream = stream + ind; + break; + } + } + + return find_stream; +} + +uint8_t* pf_find_next_multiple(uint8_t* stream, uint32_t count, uint8_t* matches, uint8_t* masks, uint32_t mmc) { + uint8_t* find_stream = 0; + + for (int i = 0; (uint32_t)i < count; i++) { + bool cont = false; + for (uint32_t i2 = 0; i2 < mmc; i2++) { + if (!pf_maskmatch(stream[i + i2], matches[i2], masks[i2])) { + cont = true; + break; + } + } + if (cont) continue; + + find_stream = stream + i; + break; + } + + return find_stream; +} + +uint8_t* pf_find_prev_multiple(uint8_t* stream, uint32_t count, uint8_t *matches, uint8_t *masks, uint32_t mmc) { + uint8_t* find_stream = 0; + + for (int neg_count = -(int)count; count > 0; count--) { + int ind = neg_count + count; + bool cont = false; + for (uint32_t i = 0; i < mmc; i++) { + if (!pf_maskmatch(stream[ind], matches[i], masks[i])) { + cont = true; + break; + } + } + if (cont) continue; + find_stream = stream + ind; + break; + } + + return find_stream; +} + +bool pf_set_zero_buf(struct pf_patch_t *patch, uint8_t *stream) { + pf_zero_buf = stream; + + pf_disable_patch(patch); + return true; +} + +void *pf_find_zero_buf(void *buf, size_t size, size_t shc_count) { + pf_zero_buf = NULL; + + uint8_t *matches = (uint8_t *) malloc(shc_count); + uint8_t *masks = (uint8_t *) malloc(shc_count); + + for (size_t i = 0; i < shc_count; i++) { + matches[i] = 0; + masks[i] = 0xff; + } + + struct pf_patch_t patch = pf_construct_patch(matches, masks, sizeof(matches) / sizeof(uint8_t), (bool (*)(struct pf_patch_t *, void *stream)) pf_set_zero_buf); + + struct pf_patch_t patches[] = { + patch + }; + + struct pf_patchset_t patchset = pf_construct_patchset(patches, sizeof(patches) / sizeof(struct pf_patch_t), (bool (*)(void *, size_t, struct pf_patchset_t)) pf_find_maskmatch); + + pf_patchset_emit(buf, size, patchset); + +#ifndef NOLOG + if (!pf_zero_buf) { + printf("%s: Unable to find zero buf!\n", __FUNCTION__); + } +#endif + return pf_zero_buf; +} \ No newline at end of file diff --git a/plooshfinder/src/plooshfinder8.c b/plooshfinder/src/plooshfinder8.c new file mode 100644 index 0000000..6e2a2c5 --- /dev/null +++ b/plooshfinder/src/plooshfinder8.c @@ -0,0 +1,59 @@ +#include +#include +#include +#include +#include "../include/plooshfinder.h" +#include "../include/plooshfinder8.h" +#include + +bool pf_maskmatch(uint8_t insn, uint8_t match, uint8_t mask) { + return (insn & mask) == match; +} + +bool pf_find_maskmatch(void *buf, size_t size, struct pf_patchset_t patchset) { + uint8_t *stream = buf; + + for (uint64_t i = 0; i < size; i++) { + for (uint32_t p = 0; p < patchset.count; p++) { + struct pf_patch8_t *patch = (struct pf_patch8_t *) patchset.patches + p; + if (patch->disabled) continue; + + uint32_t x; + for (x = 0; x < patch->count; x++) { + if (!pf_maskmatch(stream[i + x], patch->matches[x], patch->masks[x])) { + break; + } + } + + if (x == patch->count) { + if (patch->callback(patch, stream + i)) { + uint32_t disabled = 0; + for (uint32_t p = 0; p < patchset.count; p++) { + struct pf_patch8_t* pt = (struct pf_patch8_t *) patchset.patches + p; + if (pt->disabled) { + disabled++; + continue; + } + + if (patch->callback == pt->callback) { + DWORD og; + VirtualProtect(pt + offsetof(struct pf_patch8_t, disabled), sizeof(bool), PAGE_READWRITE, &og); // this is only needed bc constexpr lmao + pt->disabled = true; // disable patches that have already been found + VirtualProtect(pt + offsetof(struct pf_patch8_t, disabled), sizeof(bool), og, &og); + disabled++; + } + } + if (disabled == patchset.count) return true; // if all are done, return + } + } + } + } + return false; +} + +int8_t pf_signextend(int8_t val, uint8_t bits) { + val = (uint8_t) val << (8 - bits); + val >>= 8 - bits; + + return val; +} diff --git a/plooshfinder/src/plooshfinder_sig.c b/plooshfinder/src/plooshfinder_sig.c new file mode 100644 index 0000000..be7ffdc --- /dev/null +++ b/plooshfinder/src/plooshfinder_sig.c @@ -0,0 +1,68 @@ +#define _CRT_SECURE_NO_WARNINGS +#include +#include +#include +#include +#include +#include "../include/plooshfinder.h" +#include "../include/plooshfinder8.h" +#include + +struct pf_patch_t pf_construct_patch_sig(const char *sig, bool (*callback)(struct pf_patch_t *patch, void *stream)) { + struct pf_patch_t patch; + char *temp_sig = malloc(strlen(sig) + 1); + strcpy_s(temp_sig, strlen(sig) + 1, sig); + void *orig_ts = temp_sig; + uint8_t temp_match, temp_mask; + char *part; + uint32_t count = 0; + + part = strtok(temp_sig, " "); + while (part != NULL) { + count++; + part = strtok(NULL, " "); + } + + strcpy_s((char *) orig_ts, strlen(sig) + 1, sig); + temp_sig = orig_ts; + uint8_t *matches = malloc(count); + uint8_t *masks = malloc(count); + uint32_t index = 0; + + part = strtok(temp_sig, " "); + while (part != NULL) { + size_t len = strlen(part); + temp_match = 0x0; + temp_mask = 0x0; + + for (size_t i = 0; i < len; i++) { + if (part[i] != '?') { + char hc[2] = { + part[i], + 0 + }; + uint8_t hex = (uint8_t) strtoul(hc, NULL, 16); + uint8_t shift = (uint8_t) ((len - i - 1) * 4); + + temp_match |= hex << shift; + temp_mask |= 0xf << shift; + } + } + + matches[index] = temp_match; + masks[index] = temp_mask; + index++; + part = strtok(NULL, " "); + } + free(orig_ts); + + // construct the patch + patch.matches = matches; + patch.masks = masks; + patch.disabled = false; + patch.count = count; + patch.callback = callback; + + return patch; +} + diff --git a/plooshfinder/src/utils.c b/plooshfinder/src/utils.c new file mode 100644 index 0000000..cc5d394 --- /dev/null +++ b/plooshfinder/src/utils.c @@ -0,0 +1,13 @@ +#include "../include/utils.h" + +// the compiler will just use a bswap (optimizations) if possible +// but it's here if it's needed (i.e. there's no intrinsic) +uint32_t convert_endianness32(uint32_t val) { + uint32_t swapped = 0; + for (uint32_t i = 0; i < 4; i++) { + uint32_t sh = i << 3; + swapped |= ((val >> sh) & 0xff) << (sh ^ 24); + } + + return swapped; +} \ No newline at end of file