{"version":"https://jsonfeed.org/version/1.1","title":"sipacid's blog","home_page_url":"https://sipacid.com/","feed_url":"https://sipacid.com/posts.rss","description":"Where I put my thoughts","language":"en","authors":[{"name":"sipacid","url":"https://sipacid.com"}],"items":[{"id":"https://sipacid.com/blog/writing-a-phasmophobia-cheat/","url":"https://sipacid.com/blog/writing-a-phasmophobia-cheat/","title":"writing a phasmophobia cheat","authors":[{"name":"sipacid","url":"https://sipacid.com"}],"content_html":"<p>okay, so you've decided you wanna cheat in a co-op horror game, fucking weirdo. anyway before we start we need some info about the game, like what game engine is it using, is it x64 or x86 and does it have an anticheat? phasmophobia is made in unity, x64 and does <strong>not</strong> have an anticheat which makes our job a lot easier.</p>\n<h2>setting up the project</h2>\n<p>ight, open up visual studio and create a new dll c++ project. i am kinda really fucking lazy so instead of writing our own hook functions we'll just use <a href=\"https://github.com/microsoft/Detours\">ms-detours</a> for this project. to add detours to our project head into <code>project properties -&gt; Linker -&gt; Input</code> then add your <code>detours.lib</code> file to additional dependencies. you'll also need to include the <code>detours.h</code> header file, <a href=\"https://github.com/Microsoft/Detours/wiki/FAQ#where-can-i-find-detourslib-and-detoursh\">how the fuck do i do this?</a>. also set the c++ language standard and the c language standard to <code>latest</code> or something similar.</p>\n<h2>getting function addresses and structs</h2>\n<p>phasmophobia uses something called il2cpp which means we can't just throw the dll in something like <a href=\"https://github.com/dnSpy/dnSpy\">dnspy</a> and look at the code. to get past this i normally would just use <a href=\"https://github.com/djkaty/Il2CppInspector\">il2cppinspector</a> but that project has been dead for quite some time and doesn't work with the newer unity version that phasmophobia is using, so we'll be using <a href=\"https://github.com/SamboyCoding/Cpp2IL\">cpp2il</a> instead.</p>\n<p>to use this, all you have to do is run this command:</p>\n<pre><code>.\\Cpp2IL.exe --game-path=&quot;C:\\Program Files (x86)\\Steam\\steamapps\\common\\Phasmophobia&quot; --parallel --analyze-all\n</code></pre>\n<p>or wherever you have installed your phasmophobia. once this is finished you can open up something like <a href=\"https://github.com/dnSpy/dnSpy\">dnspy</a>, i'll be using <a href=\"https://www.jetbrains.com/decompiler/\">jetbrains dotpeek</a> for this however.</p>\n<p>now that we have our decompiler open we can start looking for things we want, like the ghost type. most things will be located in the <code>Assembly-CSharp.dll</code> file, so open that up and look for the <code>GhostAI</code> class. to get the ghostai object we can hook the <code>GhostAI::Start</code> function.</p>\n<pre><code class=\"language-csharp\">[Address(RVA = &quot;0x158C940&quot;, Offset = &quot;0x158B740&quot;, VA = &quot;0x18158C940&quot;)]\nprivate void Start()\n{\n}\n</code></pre>\n<p>we can see that the function is located at base address + rva (<code>0x158F920</code>). that's great and all but how do we get the ghost type? below are all the fields of the <code>GhostAI</code> class.</p>\n<pre><code class=\"language-csharp\">[Token(Token = &quot;0x20000E9&quot;)]\npublic class GhostAI : MonoBehaviour\n{\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0x20&quot;)]\n  [Token(Token = &quot;0x40005D2&quot;)]\n  private readonly ഠദവടസളഠഺദ മപജജഫലസഞട;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0x28&quot;)]\n  [Token(Token = &quot;0x40005D3&quot;)]\n  public GhostAI.വബബരരറമഞഥ \\u0D3Bഺര\\u0D3Bപഠനളസ;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0x30&quot;)]\n  [Token(Token = &quot;0x40005D4&quot;)]\n  public PhotonView ശനടധലപണഴവ;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0x38&quot;)]\n  [Token(Token = &quot;0x40005D5&quot;)]\n  public GhostInfo ഝറഴജഴഫഹഥജ;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0x40&quot;)]\n  [Token(Token = &quot;0x40005D6&quot;)]\n  public NavMeshAgent ലഠഴഺമഫമ\\u0D3Bഠ;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0x48&quot;)]\n  [Token(Token = &quot;0x40005D7&quot;)]\n  public GhostAudio വദറതയല��മഝ;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0x50&quot;)]\n  [Token(Token = &quot;0x40005D8&quot;)]\n  public GhostInteraction ഷഥഴഡണഫഞഢഺ;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0x58&quot;)]\n  [Token(Token = &quot;0x40005D9&quot;)]\n  public GhostActivity യഞമഡശദദനണ;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0x60&quot;)]\n  [Token(Token = &quot;0x40005DA&quot;)]\n  [HideInInspector]\n  public GhostModel നഢനനഴബഞസത;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0x68&quot;)]\n  [Token(Token = &quot;0x40005DB&quot;)]\n  [SerializeField]\n  private GhostModel eventModel;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0x70&quot;)]\n  [Token(Token = &quot;0x40005DC&quot;)]\n  public GhostModel[] സയഠഝഷഠറഭബ;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0x78&quot;)]\n  [Token(Token = &quot;0x40005DD&quot;)]\n  public GhostModel[] നറഫളശഴശഠണ;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0x80&quot;)]\n  [Token(Token = &quot;0x40005DE&quot;)]\n  private bool \\u0D3Bടധഢദജഞയധ;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0x84&quot;)]\n  [Token(Token = &quot;0x40005DF&quot;)]\n  [HideInInspector]\n  public ShadowCastingMode തശപളഠളതഡവ;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0x88&quot;)]\n  [Token(Token = &quot;0x40005E0&quot;)]\n  [HideInInspector]\n  public List&lt;Vector3&gt; മനഥഴഴണഢഺജ;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0x90&quot;)]\n  [Token(Token = &quot;0x40005E1&quot;)]\n  private float ഭജ\\u0D3Bഢഴബശഹഫ;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0x98&quot;)]\n  [Token(Token = &quot;0x40005E2&quot;)]\n  public SanityDrainer \\u0D3Bഺഝഷബഷഩണഩ;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0xA0&quot;)]\n  [Token(Token = &quot;0x40005E3&quot;)]\n  [HideInInspector]\n  public bool ണഺശഥശഥധതറ;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0xA4&quot;)]\n  [Token(Token = &quot;0x40005E4&quot;)]\n  public LayerMask ജഥഭടജഞലഹപ;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0xA8&quot;)]\n  [Token(Token = &quot;0x40005E5&quot;)]\n  public Transform പനതദയഢഠശസ;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0xB0&quot;)]\n  [Token(Token = &quot;0x40005E6&quot;)]\n  public Transform ജളഠദഹളഝദഝ;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0xB8&quot;)]\n  [Token(Token = &quot;0x40005E7&quot;)]\n  public Transform ബഺപടഫബധസഭ;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0xC0&quot;)]\n  [Token(Token = &quot;0x40005E8&quot;)]\n  [HideInInspector]\n  public float പഫപഞഺദഴദഷ;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0xC4&quot;)]\n  [Token(Token = &quot;0x40005E9&quot;)]\n  [HideInInspector]\n  public float ഢറഴഝണബനഭസ;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0xC8&quot;)]\n  [Token(Token = &quot;0x40005EA&quot;)]\n  [HideInInspector]\n  public float ജ\\u0D3Bഹ\\u0D3Bമഭ\\u0D3Bശബ;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0xCC&quot;)]\n  [Token(Token = &quot;0x40005EB&quot;)]\n  [HideInInspector]\n  public bool ജയമടയഩപമഞ;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0xCD&quot;)]\n  [Token(Token = &quot;0x40005EC&quot;)]\n  [HideInInspector]\n  public bool ടലഡഹഫഢയമഩ;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0xD0&quot;)]\n  [Token(Token = &quot;0x40005ED&quot;)]\n  [HideInInspector]\n  public Vector3 ലന\\u0D3Bസതഷ\\u0D3Bഴഹ;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0xE0&quot;)]\n  [Token(Token = &quot;0x40005EE&quot;)]\n  public GameObject ധഺമലയളടണഥ;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0xE8&quot;)]\n  [Token(Token = &quot;0x40005EF&quot;)]\n  [HideInInspector]\n  public bool ഥനള\\u0D3B\\u0D3Bഝശവഭ;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0xE9&quot;)]\n  [Token(Token = &quot;0x40005F0&quot;)]\n  [HideInInspector]\n  public bool ഭമഫവഫസഢഞണ;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0xEA&quot;)]\n  [Token(Token = &quot;0x40005F1&quot;)]\n  [HideInInspector]\n  public bool ണഷഺവദഠഭഹസ;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0xEB&quot;)]\n  [Token(Token = &quot;0x40005F2&quot;)]\n  [HideInInspector]\n  public bool യഫവമളവറഺമ;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0xF0&quot;)]\n  [Token(Token = &quot;0x40005F3&quot;)]\n  [HideInInspector]\n  public WhiteSage ഷഹഴശരഥധഩപ;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0xF8&quot;)]\n  [Token(Token = &quot;0x40005F4&quot;)]\n  private float പഴളണഞഞഷഹഥ;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0xFC&quot;)]\n  [Token(Token = &quot;0x40005F5&quot;)]\n  [HideInInspector]\n  public bool ണഩഞബഹമഝഝന;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0xFD&quot;)]\n  [Token(Token = &quot;0x40005F6&quot;)]\n  [HideInInspector]\n  public bool ഷധ\\u0D3Bതതഩലളത;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0xFE&quot;)]\n  [Token(Token = &quot;0x40005F7&quot;)]\n  [HideInInspector]\n  public bool രഠ\\u0D3Bലതബബനജ;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0x100&quot;)]\n  [Token(Token = &quot;0x40005F8&quot;)]\n  [HideInInspector]\n  public Player ദഫഥഩഩഭഹഩമ;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0x108&quot;)]\n  [Token(Token = &quot;0x40005F9&quot;)]\n  [HideInInspector]\n  public int റഹവധനലഷ\\u0D3Bള;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0x10C&quot;)]\n  [Token(Token = &quot;0x40005FA&quot;)]\n  [HideInInspector]\n  public Vector3 ഭണഺവഢഞരഹ\\u0D3B;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0x118&quot;)]\n  [Token(Token = &quot;0x40005FB&quot;)]\n  private readonly float[] ജഡജസഠമററല;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0x120&quot;)]\n  [Token(Token = &quot;0x40005FC&quot;)]\n  private readonly float[] നസഩണഥവവരഭ;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0x128&quot;)]\n  [Token(Token = &quot;0x40005FD&quot;)]\n  private readonly float[] ഝയഩബതഥഥശയ;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0x130&quot;)]\n  [Token(Token = &quot;0x40005FE&quot;)]\n  private int ഡലടഫഡണഭഢഷ;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0x134&quot;)]\n  [Token(Token = &quot;0x40005FF&quot;)]\n  private int ധശഷണനരളളഹ;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0x138&quot;)]\n  [Token(Token = &quot;0x4000600&quot;)]\n  private int ഩഞബഴഝഥഺദധ;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0x13C&quot;)]\n  [Token(Token = &quot;0x4000601&quot;)]\n  private int മഹധശതദഫഫഷ;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0x140&quot;)]\n  [Token(Token = &quot;0x4000602&quot;)]\n  private int ഩധഝഭളഠയശര;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0x148&quot;)]\n  [Token(Token = &quot;0x4000603&quot;)]\n  private readonly float[] ണഭബധബശവ\\u0D3Bണ;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0x150&quot;)]\n  [Token(Token = &quot;0x4000604&quot;)]\n  private readonly float[] ഹഡ\\u0D3Bറനഫഫഝന;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0x158&quot;)]\n  [Token(Token = &quot;0x4000605&quot;)]\n  private readonly float[] വബശഠജണളഠഺ;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0x160&quot;)]\n  [Token(Token = &quot;0x4000606&quot;)]\n  private float ഩളഢരറരധ\\u0D3Bഴ;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0x168&quot;)]\n  [Token(Token = &quot;0x4000607&quot;)]\n  private readonly int[] ഭഢഝഭതഫദവ\\u0D3B;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0x170&quot;)]\n  [Token(Token = &quot;0x4000608&quot;)]\n  private readonly int[] ശയഹഡറഡഥടല;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0x178&quot;)]\n  [Token(Token = &quot;0x4000609&quot;)]\n  private readonly int[] ധലഢപബഡമഭല;\n  [Cpp2IlInjected.FieldOffset(Offset = &quot;0x180&quot;)]\n  [Token(Token = &quot;0x400060A&quot;)]\n  private readonly int[] \\u0D3Bണഫസഫയഠസന;\n}\n</code></pre>\n<p>ghost type is probably an enum but where is it? you can find it in the <code>GhostTraits</code> class, to get there, go the <code>GhostInfo</code> class then go the first type that is being used in the <code>GhostInfo</code> class, in this case it is <code>പദഴലടഫഺഷവ</code>.</p>\n<pre><code class=\"language-csharp\">[Token(Token = &quot;0x20000F7&quot;)]\npublic class GhostInfo : MonoBehaviourPun\n{\n  [FieldOffset(Offset = &quot;0x28&quot;)]\n  [Token(Token = &quot;0x4000652&quot;)]\n  [HideInInspector]\n  public പദഴലടഫഺഷവ ളവയഥസരഹഥട;\n  [FieldOffset(Offset = &quot;0x68&quot;)]\n  [Token(Token = &quot;0x4000653&quot;)]\n  [SerializeField]\n  private GhostAI ghost;\n  [FieldOffset(Offset = &quot;0x70&quot;)]\n  [Token(Token = &quot;0x4000654&quot;)]\n  [HideInInspector]\n  public LevelRoom ഹഷഹധതജഺലള;\n  [FieldOffset(Offset = &quot;0x78&quot;)]\n  [Token(Token = &quot;0x4000655&quot;)]\n  [HideInInspector]\n  public float ഫഥമതഹഷഫഩബ;\n  [FieldOffset(Offset = &quot;0x7C&quot;)]\n  [Token(Token = &quot;0x4000656&quot;)]\n  private bool ണടറളശഥസറപ;\n}\n</code></pre>\n<p>which will bring us to this struct, the first field (ബണജഷസഷണഞഴ) is the ghost type and the second one (ഹഭഴഞഴന\\u0d3bഥഝ) is the mimic type.</p>\n<pre><code class=\"language-csharp\">[Token(Token = &quot;0x200015E&quot;)]\n[SerializeField]\npublic struct പദഴലടഫഺഷവ\n{\n  [FieldOffset(Offset = &quot;0x0&quot;)]\n  [Token(Token = &quot;0x400086D&quot;)]\n  public പദഴലടഫഺഷവ.ഝഡശഭഡഡഷഴജ ബണജഷസഷണഞഴ;\n  [FieldOffset(Offset = &quot;0x4&quot;)]\n  [Token(Token = &quot;0x400086E&quot;)]\n  public പദഴലടഫഺഷവ.ഝഡശഭഡഡഷഴജ ഹഭഴഞഴന\\u0D3Bഥഝ;\n  [FieldOffset(Offset = &quot;0x8&quot;)]\n  [Token(Token = &quot;0x400086F&quot;)]\n  public List&lt;ദസഫനഠശഢപവ&gt; ഠഥളഡഠലഹമത;\n  [FieldOffset(Offset = &quot;0x10&quot;)]\n  [Token(Token = &quot;0x4000870&quot;)]\n  public List&lt;ദസഫനഠശഢപവ&gt; ഭടഞ\\u0D3Bറഝമനത;\n  [FieldOffset(Offset = &quot;0x18&quot;)]\n  [Token(Token = &quot;0x4000871&quot;)]\n  public int \\u0D3Bദഞന\\u0D3Bഫഺടഡ;\n  [FieldOffset(Offset = &quot;0x1C&quot;)]\n  [Token(Token = &quot;0x4000872&quot;)]\n  public bool ഥഭദടബശനദവ;\n  [FieldOffset(Offset = &quot;0x20&quot;)]\n  [Token(Token = &quot;0x4000873&quot;)]\n  public string തണവബഝഝഹവഹ;\n  [FieldOffset(Offset = &quot;0x28&quot;)]\n  [Token(Token = &quot;0x4000874&quot;)]\n  public int തഺറഡദഠഹരജ;\n  [FieldOffset(Offset = &quot;0x2C&quot;)]\n  [Token(Token = &quot;0x4000875&quot;)]\n  public int സയഡലരണഷദന;\n  [FieldOffset(Offset = &quot;0x30&quot;)]\n  [Token(Token = &quot;0x4000876&quot;)]\n  public bool ദജഷനജഭധജദ;\n  [FieldOffset(Offset = &quot;0x34&quot;)]\n  [Token(Token = &quot;0x4000877&quot;)]\n  public int ബഭഞപരയധഠണ;\n  [FieldOffset(Offset = &quot;0x38&quot;)]\n  [Token(Token = &quot;0x4000878&quot;)]\n  public int യലളനഹഝയതഠ;\n  [FieldOffset(Offset = &quot;0x3C&quot;)]\n  [Token(Token = &quot;0x4000879&quot;)]\n  public bool ല\\u0D3Bസഺഢവസളഫ;\n\n  [Token(Token = &quot;0x200015F&quot;)]\n  public enum ഝഡശഭഡഡഷഴജ\n  {\n    [Token(Token = &quot;0x400087B&quot;)] Spirit,\n    [Token(Token = &quot;0x400087C&quot;)] Wraith,\n    [Token(Token = &quot;0x400087D&quot;)] Phantom,\n    [Token(Token = &quot;0x400087E&quot;)] Poltergeist,\n    [Token(Token = &quot;0x400087F&quot;)] Banshee,\n    [Token(Token = &quot;0x4000880&quot;)] Jinn,\n    [Token(Token = &quot;0x4000881&quot;)] Mare,\n    [Token(Token = &quot;0x4000882&quot;)] Revenant,\n    [Token(Token = &quot;0x4000883&quot;)] Shade,\n    [Token(Token = &quot;0x4000884&quot;)] Demon,\n    [Token(Token = &quot;0x4000885&quot;)] Yurei,\n    [Token(Token = &quot;0x4000886&quot;)] Oni,\n    [Token(Token = &quot;0x4000887&quot;)] Yokai,\n    [Token(Token = &quot;0x4000888&quot;)] Hantu,\n    [Token(Token = &quot;0x4000889&quot;)] Goryo,\n    [Token(Token = &quot;0x400088A&quot;)] Myling,\n    [Token(Token = &quot;0x400088B&quot;)] Onryo,\n    [Token(Token = &quot;0x400088C&quot;)] TheTwins,\n    [Token(Token = &quot;0x400088D&quot;)] Raiju,\n    [Token(Token = &quot;0x400088E&quot;)] Obake,\n    [Token(Token = &quot;0x400088F&quot;)] Mimic,\n    [Token(Token = &quot;0x4000890&quot;)] Moroi,\n    [Token(Token = &quot;0x4000891&quot;)] Deogen,\n    [Token(Token = &quot;0x4000892&quot;)] Thaye,\n  }\n}\n</code></pre>\n<h2>creating our sdk</h2>\n<p>back in visual studio we are, let's start by creating a simple sdk that we can use. i'll create a new header file called <code>sdk.h</code> and add a base address and a macro to define functions. i'll also create another header file <code>GhostAI.h</code> and add the ghostai structs to it and define the ghostai::start function.\nbecause ghostai derives from monobehaviour and ghostinfo from monobehaviourpun we'll have to add those fields to the <code>GhostAIFields</code> struct too, i'll give you the <code>MonoBehaviour</code> and the <code>MonoBehaviourPun</code> structs. we won't need to add every field from the actual ghostai class, just the ones we need; so up until the ghostinfo field.</p>\n<pre><code class=\"language-cpp\">// sdk.h\n#pragma once\n\n#define DECLARE_FUNCTION_POINTER(NAME, TYPE, ADDRESS) \\\nusing NAME = TYPE; \\\ninline NAME NAME##_ptr = reinterpret_cast&lt;NAME&gt;(BASE_ADDRESS + ADDRESS);\n\nnamespace SDK\n{\n  const auto BASE_ADDRESS = reinterpret_cast&lt;uintptr_t&gt;(GetModuleHandleW(L&quot;GameAssembly.dll&quot;));\n}\n\n// MonoBehaviour.h\nnamespace SDK\n{\n  struct __declspec(align(8)) Object1Fields\n  {\n    void* m_CachedPtr;\n    void* m_CancellationTokenSource;\n  };\n\n  struct Component1Fields\n  {\n    Object1Fields _;\n  };\n\n  struct BehaviourFields\n  {\n    Component1Fields _;\n  };\n\n  struct MonoBehaviourFields\n  {\n    BehaviourFields _;\n  };\n\n  struct MonoBehaviour\n  {\n    void* Clazz; // MonoBehaviourClass\n    void* Monitor; // MonitorData\n    MonoBehaviourFields Fields;\n  };\n\n  struct MonoBehaviourPunFields\n  {\n    MonoBehaviourFields _;\n    void* pvCache;\n  };\n\n  struct MonoBehaviourPunCallbacksFields\n  {\n    MonoBehaviourPunFields _;\n  };\n}\n\n// GhostAI.h\n#pragma once\n#include &quot;sdk.h&quot;\n\nnamespace SDK\n{\n  struct GhostAIFields\n  {\n    MonoBehaviourFields _;\n    void* Field0;\n    int32_t Field1;\n    void* Field2;\n    GhostInfo* GhostInfo; // Only field we care about\n  };\n\n  struct GhostAI\n  {\n    void* Clazz; // GhostAI class\n    void* Monitor; // Monitor Data\n    GhostAIFields Fields;\n  };\n\n  DECLARE_FUNCTION_POINTER(GhostAI_Start, void(*)(GhostAI* ghostAI, void* methodInfo), 0x158C940);\n}\n</code></pre>\n<p>okay great, now we'll need to create the <code>GhostInfo</code> struct and the <code>GhostTrait</code> struct. don't forget to include them in the <code>sdk.h</code>. we will need every field from the <code>GhostTrait</code> struct because the <code>GhostType</code> field will only be valid after <code>Name</code> isn't a nullptr anymore.</p>\n<pre><code class=\"language-cpp\">// GhostInfo.h\n#pragma once\n#include &quot;sdk.h&quot;\n\nnamespace SDK\n{\n  struct GhostInfoFields\n  {\n    MonoBehaviourPunFields _;\n    GhostTraits GhostTraits; // very important 1!!!1\n    // don't care about other fields\n  };\n\n  struct GhostInfo\n  {\n    void* Clazz; // GhostInfo class\n    void* Monitor; // Monitor Data\n    GhostInfoFields Fields;\n  };\n}\n\n// GhostTraits.h\n#pragma once\n#include &quot;sdk.h&quot;\n\nnamespace SDK\n{\n  enum class GhostType: int32_t\n  {\n    Spirit,\n    Wraith,\n    Phantom,\n    Poltergeist,\n    Banshee,\n    Jinn,\n    Mare,\n    Revenant,\n    Shade,\n    Demon,\n    Yurei,\n    Oni,\n    Yokai,\n    Hantu,\n    Goryo,\n    Myling,\n    Onryo,\n    TheTwins,\n    Raiju,\n    Obake,\n    Mimic,\n    Moroi,\n    Deogen,\n    Thaye,\n  };\n\n  struct GhostTraits\n  {\n    GhostType GhostType_;\n    GhostType MimicType;\n    // don't care about the other ones\n    void* Field2;\n    void* Field3;\n    int32_t Field4;\n    bool Field5;\n    void* Name;\n    int32_t Field7;\n    int Field8;\n    bool Field9;\n    int32_t Field10;\n    int32_t Field11;\n    bool Field12;\n  };\n}\n</code></pre>\n<h2>hooking the ghostai::start function</h2>\n<p>back in our <code>dllmain.cpp</code> we can create our &quot;hackthread&quot;, define our <code>GhostAI_Start</code> function and hook it using detours.</p>\n<pre><code class=\"language-cpp\">// dllmain.cpp\nHMODULE hHackModule = nullptr;\nHANDLE hHackThread = nullptr;\n\nvoid hkGhostAI_Start(SDK::GhostAI* _ghostAI, void* methodInfo)\n{\n  // Calling original function\n  SDK::GhostAI_Start_ptr(_ghostAI, methodInfo);\n}\n\nDWORD WINAPI HackThread()\n{\n  // Detouring GhostAI.Start\n  DetourTransactionBegin();\n  DetourUpdateThread(GetCurrentThread());\n  DetourAttach(&amp;reinterpret_cast&lt;PVOID&amp;&gt;(SDK::GhostAI_Start_ptr), hkGhostAI_Start);\n  DetourTransactionCommit();\n\n  // No exiting the cheat yet\n  while (true)\n  {\n    if (GetAsyncKeyState(VK_END) &amp; 1)\n    {\n      break;\n    }\n\n    Sleep(100);\n  }\n\n  // Un-detouring GhostAI.Start\n  DetourTransactionBegin();\n  DetourUpdateThread(GetCurrentThread());\n  DetourDetach(&amp;reinterpret_cast&lt;PVOID&amp;&gt;(SDK::GhostAI_Start_ptr), hkGhostAI_Start);\n  DetourTransactionCommit();\n\n  CloseHandle(hHackThread);\n  FreeLibraryAndExitThread(hHackModule, 0);\n}\n\n\nBOOL APIENTRY DllMain(HMODULE hModule, DWORD ulReasonForCall, LPVOID lpReserved)\n{\n  if (ulReasonForCall == DLL_PROCESS_ATTACH)\n  {\n    hHackModule = hModule;\n    hHackThread = CreateThread(nullptr, 0, reinterpret_cast&lt;LPTHREAD_START_ROUTINE&gt;(HackThread), hModule, 0, nullptr);\n  }\n\n  return TRUE;\n}\n</code></pre>\n<h2>displaying the ghost type</h2>\n<p>woah almost there, now to display the ghost type i'll just add a simple console using <code>AllocConsole</code>. we'll also need to save the ghostai pointer to a global variable so we can use it.</p>\n<pre><code class=\"language-cpp\">// dllmain.cpp\nSDK::GhostAI* ghostAI = nullptr;\n\nvoid hkGhostAI_Start(SDK::GhostAI* _ghostAI, void* methodInfo)\n{\n  // Saving ghostAI pointer\n  ghostAI = _ghostAI;\n\n  // Calling original function\n  SDK::GhostAI_Start_ptr(ghostAI, methodInfo);\n}\n\nDWORD WINAPI HackThread()\n{\n  // Beep boop I'm a console\n  FILE* f;\n  AllocConsole();\n  freopen_s(&amp;f, &quot;CONOUT$&quot;, &quot;w&quot;, stdout);\n\n  // other code\n\n  // when exiting after removing hook\n  // Bye bye console\n  FreeConsole();\n}\n</code></pre>\n<p>cool, now that we can print stuff to a console, let's print the ghost type. to do this we'll need to convert the enum to a string, i'll just use a switch statement for this because i'm stupid. we will print via the hackthread while loop 'cause <code>ghostInfo</code> can be a nullptr.</p>\n<pre><code class=\"language-cpp\">// GhostTraits.h\ninline std::string GhostTypeToString(GhostType ghostType)\n{\n  switch (ghostType)\n  {\n  case GhostType::Spirit:\n    return &quot;Spirit&quot;;\n  case GhostType::Wraith:\n    return &quot;Wraith&quot;;\n  case GhostType::Phantom:\n    return &quot;Phantom&quot;;\n  case GhostType::Poltergeist:\n    return &quot;Poltergeist&quot;;\n  case GhostType::Banshee:\n    return &quot;Banshee&quot;;\n  case GhostType::Jinn:\n    return &quot;Jinn&quot;;\n  case GhostType::Mare:\n    return &quot;Mare&quot;;\n  case GhostType::Revenant:\n    return &quot;Revenant&quot;;\n  case GhostType::Shade:\n    return &quot;Shade&quot;;\n  case GhostType::Demon:\n    return &quot;Demon&quot;;\n  case GhostType::Yurei:\n    return &quot;Yurei&quot;;\n  case GhostType::Oni:\n    return &quot;Oni&quot;;\n  case GhostType::Yokai:\n    return &quot;Yokai&quot;;\n  case GhostType::Hantu:\n    return &quot;Hantu&quot;;\n  case GhostType::Goryo:\n    return &quot;Goryo&quot;;\n  case GhostType::Myling:\n    return &quot;Myling&quot;;\n  case GhostType::Onryo:\n    return &quot;Onryo&quot;;\n  case GhostType::TheTwins:\n    return &quot;The Twins&quot;;\n  case GhostType::Raiju:\n    return &quot;Raiju&quot;;\n  case GhostType::Obake:\n    return &quot;Obake&quot;;\n  case GhostType::Mimic:\n    return &quot;Mimic&quot;;\n  case GhostType::Moroi:\n    return &quot;Moroi&quot;;\n  case GhostType::Deogen:\n    return &quot;Deogen&quot;;\n  case GhostType::Thaye:\n    return &quot;Thaye&quot;;\n  default:\n    return &quot;Unknown&quot;;\n  }\n}\n\n// dllmain.cpp\nbool shouldPrint = false;\n\nvoid hkGhostAI_Start(SDK::GhostAI* _ghostAI, void* methodInfo)\n{\n  // Saving ghostAI pointer\n  ghostAI = _ghostAI;\n  shouldPrint = true;\n\n  // Calling original function\n  SDK::GhostAI_Start_ptr(ghostAI, methodInfo);\n}\n\nDWORD WINAPI HackThread()\n{\n    // code\n\n    while (true)\n    {\n      /// oooooo spooky ghost\n      if (shouldPrint &amp;&amp; ghostAI)\n      {\n        if (const auto ghostInfo = ghostAI-&gt;Fields.GhostInfo)\n        {\n          // if name is a nullptr then the ghost type isn't valid yet\n          if (ghostInfo-&gt;Fields.GhostTraits.Name)\n          {\n            const auto ghostType = ghostInfo-&gt;Fields.GhostTraits.GhostType_;\n            std::cout &lt;&lt; &quot;Ghost type: &quot; &lt;&lt; GhostTypeToString(ghostType) &lt;&lt; std::endl;\n            shouldPrint = false;\n          }\n        }\n      }\n\n      if (GetAsyncKeyState(VK_END) &amp; 1)\n      {\n          break;\n      }\n\n      Sleep(100);\n    }\n\n    // more code\n}\n</code></pre>\n<h2>anti-kick</h2>\n<p>during the development of <a href=\"https://sipacid.com/projects/asthmaphobia\">asthmaphobia</a> i found out that you can create an anti-kick hack by just hooking <code>ServerManager_KickPlayerNetworked</code> and then not calling the original function :) have fun.</p>\n<h2>conclusion</h2>\n<p>that's it, the basics for creating your own shitty phasmophobia cheat. source code for this project can be found on <a href=\"https://github.com/sipacid/phasmophobia-cheat\">github</a>. yes i know the code is bad, it isn't meant to be good code.</p>\n<style>\n  pre {\n    max-height: 400px;\n  }\n</style>\n","date_published":"2023-12-29T00:00:00.000Z"}]}