Гайд Пишем простой чит на Phasmophobia

Начинающий
Начинающий
Статус
Оффлайн
Регистрация
7 Янв 2025
Сообщения
25
Реакции
1
Итак, я занимаюсь взломом данной игры уже несколько месяцев и написал хороший чит. Поэтому сегодня мы разберем как написать простой чит на данную игры который просто получит тип призрака. Для начала разберемся, что под капотом у игры: Unity, x64, Il2cpp, нет античит системы, только проверка файлов и некоторых RPC, но нам это не помешает никак.

Использовать я буду:
Пожалуйста, авторизуйтесь для просмотра ссылки.
- дампер (буду называть дальше просто дампер)
Пожалуйста, авторизуйтесь для просмотра ссылки.
- реверс дампа
Visual Studio 2022 - кодинг (буду называть дальше просто vs22)
Вы можете использовать, то что вам будет удобно

Для начала получим файл Assembly-CSharp.dll для этого мы откроем дампер и укажем что ему нужно.
n5xL10z.png
Нажимаем на Press start or... и у нас идёт процесс дампа. После в папке output\Phasmophobia_dumped\ переходим в папку DummyDll и ищем Assembly-CSharp.dll она нам и нужна. Первый шаг уже позади.

Теперь откроем его в DnSpy и развернем ветку "-" в ней мы ищем GhostAI это наш призрак. У нас есть много Fields:
Fields GhostAI:
Expand Collapse Copy
{
    // Token: 0x040005FF RID: 1535
    [Token(Token = "0x40005FF")]
    [Il2CppDummyDll.FieldOffset(Offset = "0x28")]
    internal readonly ണഭശഴഠഞയഷഹ ജതബവധനരഫണ;

    // Token: 0x04000600 RID: 1536
    [Token(Token = "0x4000600")]
    [Il2CppDummyDll.FieldOffset(Offset = "0x30")]
    public GhostAI.ഢരനഡഴയഩഺഥ ഹതഺഫധവധ\u0D3Bജ;

    // Token: 0x04000601 RID: 1537
    [Token(Token = "0x4000601")]
    [Il2CppDummyDll.FieldOffset(Offset = "0x38")]
    public GhostInfo ഡറലഷഺഹണഺയ;

    // Token: 0x04000602 RID: 1538
    [Token(Token = "0x4000602")]
    [Il2CppDummyDll.FieldOffset(Offset = "0x40")]
    public NavMeshAgent മഢടടതഺലശഺ;

    // Token: 0x04000603 RID: 1539
    [Token(Token = "0x4000603")]
    [Il2CppDummyDll.FieldOffset(Offset = "0x48")]
    public GhostAudio ഠടഷളനലമഞറ;

    // Token: 0x04000604 RID: 1540
    [Token(Token = "0x4000604")]
    [Il2CppDummyDll.FieldOffset(Offset = "0x50")]
    public GhostInteraction തഷഢധഠണതശഹ;

    // Token: 0x04000605 RID: 1541
    [Token(Token = "0x4000605")]
    [Il2CppDummyDll.FieldOffset(Offset = "0x58")]
    public GhostActivity ടഫഴദളരമഥഥ;

    // Token: 0x04000606 RID: 1542
    [Token(Token = "0x4000606")]
    [Il2CppDummyDll.FieldOffset(Offset = "0x60")]
    [HideInInspector]
    public GhostModel ഥ\u0D3Bറഩഩഹതപഠ;

    // Token: 0x04000607 RID: 1543
    [Token(Token = "0x4000607")]
    [Il2CppDummyDll.FieldOffset(Offset = "0x68")]
    [SerializeField]
    private GhostModel halloweenModel;

    // Token: 0x04000608 RID: 1544
    [Token(Token = "0x4000608")]
    [Il2CppDummyDll.FieldOffset(Offset = "0x70")]
    [SerializeField]
    private GhostModel holidayModel;

    // Token: 0x04000609 RID: 1545
    [Token(Token = "0x4000609")]
    [Il2CppDummyDll.FieldOffset(Offset = "0x78")]
    [SerializeField]
    private GhostModel easterModel;

    // Token: 0x0400060A RID: 1546
    [Token(Token = "0x400060A")]
    [Il2CppDummyDll.FieldOffset(Offset = "0x80")]
    public GhostModel[] നഭഝവസശവശധ;

    // Token: 0x0400060B RID: 1547
    [Token(Token = "0x400060B")]
    [Il2CppDummyDll.FieldOffset(Offset = "0x88")]
    public GhostModel[] ശധഹഷവഹഞഢത;

    // Token: 0x0400060C RID: 1548
    [Token(Token = "0x400060C")]
    [Il2CppDummyDll.FieldOffset(Offset = "0x90")]
    [HideInInspector]
    public ShadowCastingMode ദപണറടഹഹളഴ;

    // Token: 0x0400060D RID: 1549
    [Token(Token = "0x400060D")]
    [Il2CppDummyDll.FieldOffset(Offset = "0x98")]
    [HideInInspector]
    public List<Vector3> ണഺപഺഺ\u0D3Bരജമ;

    // Token: 0x0400060E RID: 1550
    [Token(Token = "0x400060E")]
    [Il2CppDummyDll.FieldOffset(Offset = "0xA0")]
    private float രവഫവറ\u0D3Bനജഩ;

    // Token: 0x0400060F RID: 1551
    [Token(Token = "0x400060F")]
    [Il2CppDummyDll.FieldOffset(Offset = "0xA4")]
    private float ബഝഡഩഫഠഞ\u0D3Bത;

    // Token: 0x04000610 RID: 1552
    [Token(Token = "0x4000610")]
    [Il2CppDummyDll.FieldOffset(Offset = "0xA8")]
    public LOSSensor ദഭതബഡ\u0D3Bഫലഠ;

    // Token: 0x04000611 RID: 1553
    [Token(Token = "0x4000611")]
    [Il2CppDummyDll.FieldOffset(Offset = "0xB0")]
    [HideInInspector]
    public bool ഢധളഺഡഷദസണ;

    // Token: 0x04000612 RID: 1554
    [Token(Token = "0x4000612")]
    [Il2CppDummyDll.FieldOffset(Offset = "0xB8")]
    public Transform റവ\u0D3Bഺഝദധഠഺ;

    // Token: 0x04000613 RID: 1555
    [Token(Token = "0x4000613")]
    [Il2CppDummyDll.FieldOffset(Offset = "0xC0")]
    public Transform ശധര\u0D3Bസവഠഴഴ;

    // Token: 0x04000614 RID: 1556
    [Token(Token = "0x4000614")]
    [Il2CppDummyDll.FieldOffset(Offset = "0xC8")]
    public Transform മണണപന\u0D3Bലമറ;

    // Token: 0x04000615 RID: 1557
    [Token(Token = "0x4000615")]
    [Il2CppDummyDll.FieldOffset(Offset = "0xD0")]
    [HideInInspector]
    public float മഠശഹവളണണ\u0D3B;

    // Token: 0x04000616 RID: 1558
    [Token(Token = "0x4000616")]
    [Il2CppDummyDll.FieldOffset(Offset = "0xD4")]
    [HideInInspector]
    public float ലശറളഢമശഺല;

    // Token: 0x04000617 RID: 1559
    [Token(Token = "0x4000617")]
    [Il2CppDummyDll.FieldOffset(Offset = "0xD8")]
    [HideInInspector]
    public float ബഞഡഺഭഺഥരഡ;

    // Token: 0x04000618 RID: 1560
    [Token(Token = "0x4000618")]
    [Il2CppDummyDll.FieldOffset(Offset = "0xDC")]
    [HideInInspector]
    public float ഩമളയജവഢവന;

    // Token: 0x04000619 RID: 1561
    [Token(Token = "0x4000619")]
    [Il2CppDummyDll.FieldOffset(Offset = "0xE0")]
    [HideInInspector]
    public bool ററ\u0D3Bഝരമഭടധ;

    // Token: 0x0400061A RID: 1562
    [Token(Token = "0x400061A")]
    [Il2CppDummyDll.FieldOffset(Offset = "0xE1")]
    [HideInInspector]
    public bool ഢണമഩഞലഡഷഴ;

    // Token: 0x0400061B RID: 1563
    [Token(Token = "0x400061B")]
    [Il2CppDummyDll.FieldOffset(Offset = "0xE4")]
    [HideInInspector]
    public Vector3 ഭഢജജഞളധജഹ;

    // Token: 0x0400061C RID: 1564
    [Token(Token = "0x400061C")]
    [Il2CppDummyDll.FieldOffset(Offset = "0xF0")]
    public GameObject വരഠബഥമമഢഫ;

    // Token: 0x0400061D RID: 1565
    [Token(Token = "0x400061D")]
    [Il2CppDummyDll.FieldOffset(Offset = "0xF8")]
    [HideInInspector]
    public bool ബദബപദനതശദ;

    // Token: 0x0400061E RID: 1566
    [Token(Token = "0x400061E")]
    [Il2CppDummyDll.FieldOffset(Offset = "0xF9")]
    [HideInInspector]
    public bool റഫഴരനഡഩണഩ;

    // Token: 0x0400061F RID: 1567
    [Token(Token = "0x400061F")]
    [Il2CppDummyDll.FieldOffset(Offset = "0xFA")]
    [HideInInspector]
    public bool നതലഝ\u0D3Bലപതഢ;

    // Token: 0x04000620 RID: 1568
    [Token(Token = "0x4000620")]
    [Il2CppDummyDll.FieldOffset(Offset = "0xFB")]
    [HideInInspector]
    public bool ബഠദബയധഩരപ;

    // Token: 0x04000621 RID: 1569
    [Token(Token = "0x4000621")]
    [Il2CppDummyDll.FieldOffset(Offset = "0x100")]
    [HideInInspector]
    public WhiteSage ഝളഡവശണജടറ;

    // Token: 0x04000622 RID: 1570
    [Token(Token = "0x4000622")]
    [Il2CppDummyDll.FieldOffset(Offset = "0x108")]
    private float ഡഢനഢത\u0D3Bടലവ;

    // Token: 0x04000623 RID: 1571
    [Token(Token = "0x4000623")]
    [Il2CppDummyDll.FieldOffset(Offset = "0x10C")]
    [HideInInspector]
    public bool ഩ\u0D3B\u0D3Bഡമഥഥനധ;

    // Token: 0x04000624 RID: 1572
    [Token(Token = "0x4000624")]
    [Il2CppDummyDll.FieldOffset(Offset = "0x10D")]
    [HideInInspector]
    public bool \u0D3Bഥസഥയഩധഞഩ;

    // Token: 0x04000625 RID: 1573
    [Token(Token = "0x4000625")]
    [Il2CppDummyDll.FieldOffset(Offset = "0x10E")]
    [HideInInspector]
    public bool റടഢതബയമജഝ;

    // Token: 0x04000626 RID: 1574
    [Token(Token = "0x4000626")]
    [Il2CppDummyDll.FieldOffset(Offset = "0x110")]
    [HideInInspector]
    public Player ഞഺനഫഺയബഢള;

    // Token: 0x04000627 RID: 1575
    [Token(Token = "0x4000627")]
    [Il2CppDummyDll.FieldOffset(Offset = "0x118")]
    [HideInInspector]
    public int ഫയസമഹണഺഞഫ;

    // Token: 0x04000628 RID: 1576
    [Token(Token = "0x4000628")]
    [Il2CppDummyDll.FieldOffset(Offset = "0x11C")]
    [HideInInspector]
    public Vector3 ളജരനശരഢ\u0D3Bട;

    // Token: 0x04000629 RID: 1577
    [Token(Token = "0x4000629")]
    [Il2CppDummyDll.FieldOffset(Offset = "0x128")]
    private readonly float[] ടയഹവസപവഝട;

    // Token: 0x0400062A RID: 1578
    [Token(Token = "0x400062A")]
    [Il2CppDummyDll.FieldOffset(Offset = "0x130")]
    private readonly float[] ഡഝശരഩഷഷബഷ;

    // Token: 0x0400062B RID: 1579
    [Token(Token = "0x400062B")]
    [Il2CppDummyDll.FieldOffset(Offset = "0x138")]
    private readonly float[] \u0D3Bതഫരഭടറണധ;

    // Token: 0x0400062C RID: 1580
    [Token(Token = "0x400062C")]
    [Il2CppDummyDll.FieldOffset(Offset = "0x140")]
    private int ടധസഥണഝജധദ;

    // Token: 0x0400062D RID: 1581
    [Token(Token = "0x400062D")]
    [Il2CppDummyDll.FieldOffset(Offset = "0x144")]
    private int മ\u0D3Bനധലളഠഹദ;

    // Token: 0x0400062E RID: 1582
    [Token(Token = "0x400062E")]
    [Il2CppDummyDll.FieldOffset(Offset = "0x148")]
    private int ടദറളഫഡഝധപ;

    // Token: 0x0400062F RID: 1583
    [Token(Token = "0x400062F")]
    [Il2CppDummyDll.FieldOffset(Offset = "0x14C")]
    private int ഥഫഥറധരരലഡ;

    // Token: 0x04000630 RID: 1584
    [Token(Token = "0x4000630")]
    [Il2CppDummyDll.FieldOffset(Offset = "0x150")]
    private int ഞസപശതടളഢശ;

    // Token: 0x04000631 RID: 1585
    [Token(Token = "0x4000631")]
    [Il2CppDummyDll.FieldOffset(Offset = "0x158")]
    private readonly float[] ഝലളശലരതധപ;

    // Token: 0x04000632 RID: 1586
    [Token(Token = "0x4000632")]
    [Il2CppDummyDll.FieldOffset(Offset = "0x160")]
    private readonly float[] ബ\u0D3Bഡയഠമടഡസ;

    // Token: 0x04000633 RID: 1587
    [Token(Token = "0x4000633")]
    [Il2CppDummyDll.FieldOffset(Offset = "0x168")]
    private readonly float[] ഺസലമഭണദതഝ;

    // Token: 0x04000634 RID: 1588
    [Token(Token = "0x4000634")]
    [Il2CppDummyDll.FieldOffset(Offset = "0x170")]
    private float ഩനപഹഷഴഩധഹ;

    // Token: 0x04000635 RID: 1589
    [Token(Token = "0x4000635")]
    [Il2CppDummyDll.FieldOffset(Offset = "0x178")]
    private readonly int[] ഡയസപഴവഭത\u0D3B;

    // Token: 0x020000E8 RID: 232
    [Token(Token = "0x20000E8")]
    public enum ഢരനഡഴയഩഺഥ
    {
        // Token: 0x04000637 RID: 1591
        [Token(Token = "0x4000637")]
        idle,
        // Token: 0x04000638 RID: 1592
        [Token(Token = "0x4000638")]
        wander,
        // Token: 0x04000639 RID: 1593
        [Token(Token = "0x4000639")]
        hunting,
        // Token: 0x0400063A RID: 1594
        [Token(Token = "0x400063A")]
        favouriteRoom,
        // Token: 0x0400063B RID: 1595
        [Token(Token = "0x400063B")]
        light,
        // Token: 0x0400063C RID: 1596
        [Token(Token = "0x400063C")]
        door,
        // Token: 0x0400063D RID: 1597
        [Token(Token = "0x400063D")]
        throwing,
        // Token: 0x0400063E RID: 1598
        [Token(Token = "0x400063E")]
        fusebox,
        // Token: 0x0400063F RID: 1599
        [Token(Token = "0x400063F")]
        appear,
        // Token: 0x04000640 RID: 1600
        [Token(Token = "0x4000640")]
        doorKnock,
        // Token: 0x04000641 RID: 1601
        [Token(Token = "0x4000641")]
        windowKnock,
        // Token: 0x04000642 RID: 1602
        [Token(Token = "0x4000642")]
        carAlarm,
        // Token: 0x04000643 RID: 1603
        [Token(Token = "0x4000643")]
        flicker,
        // Token: 0x04000644 RID: 1604
        [Token(Token = "0x4000644")]
        cctv,
        // Token: 0x04000645 RID: 1605
        [Token(Token = "0x4000645")]
        randomEvent,
        // Token: 0x04000646 RID: 1606
        [Token(Token = "0x4000646")]
        GhostAbility,
        // Token: 0x04000647 RID: 1607
        [Token(Token = "0x4000647")]
        mannequin,
        // Token: 0x04000648 RID: 1608
        [Token(Token = "0x4000648")]
        teleportObject,
        // Token: 0x04000649 RID: 1609
        [Token(Token = "0x4000649")]
        interact,
        // Token: 0x0400064A RID: 1610
        [Token(Token = "0x400064A")]
        summoningCircle,
        // Token: 0x0400064B RID: 1611
        [Token(Token = "0x400064B")]
        musicBox,
        // Token: 0x0400064C RID: 1612
        [Token(Token = "0x400064C")]
        dots,
        // Token: 0x0400064D RID: 1613
        [Token(Token = "0x400064D")]
        salt,
        // Token: 0x0400064E RID: 1614
        [Token(Token = "0x400064E")]
        ignite
    }
}

Я выделил 12-15 строки. Они нам и нужны точнее нам нужен GhostInfo имя уже говорит об том, что мы ищем (почти). Теперь посмотрим на его поля:
Fields GhostInfo:
Expand Collapse Copy
{
    // Token: 0x0400067F RID: 1663
    [Token(Token = "0x400067F")]
    [FieldOffset(Offset = "0x28")]
    [HideInInspector]
    public ബളലയഴഥഡഡഝ ദഺസജണനഞപയ;

    // Token: 0x04000680 RID: 1664
    [Token(Token = "0x4000680")]
    [FieldOffset(Offset = "0x68")]
    [SerializeField]
    private GhostAI ghost;

    // Token: 0x04000681 RID: 1665
    [Token(Token = "0x4000681")]
    [FieldOffset(Offset = "0x70")]
    [HideInInspector]
    public LevelRoom \u0D3Bപഡദതളഹയശ;

    // Token: 0x04000682 RID: 1666
    [Token(Token = "0x4000682")]
    [FieldOffset(Offset = "0x78")]
    [HideInInspector]
    public float ണരളഭറഥഡജധ;

    // Token: 0x04000683 RID: 1667
    [Token(Token = "0x4000683")]
    [FieldOffset(Offset = "0x7C")]
    private bool ഡലഫഠസ\u0D3Bറപമ;
}

Я выделил 2-6 поле. Оно зашифровано, но мы не обращаем на это внимание. Я пользуюсь 0.11.0.0 версией которая не зашифрована узнал что это GhostTraits и у него такая структура:
Structure GhostTraits:
Expand Collapse Copy
using System;
using System.Collections.Generic;
using Il2CppDummyDll;
using UnityEngine;

// Token: 0x02000157 RID: 343
[Token(Token = "0x2000157")]
[SerializeField]
public struct GhostTraits
{
    // Token: 0x04000881 RID: 2177
    [Token(Token = "0x4000881")]
    [FieldOffset(Offset = "0x0")]
    public GhostTraits.Type initialGhostType;

    // Token: 0x04000882 RID: 2178
    [Token(Token = "0x4000882")]
    [FieldOffset(Offset = "0x4")]
    public GhostTraits.Type ghostType;

    // Token: 0x04000883 RID: 2179
    [Token(Token = "0x4000883")]
    [FieldOffset(Offset = "0x8")]
    public List<evidenceType> evidences;

    // Token: 0x04000884 RID: 2180
    [Token(Token = "0x4000884")]
    [FieldOffset(Offset = "0x10")]
    public List<evidenceType> fullEvidences;

    // Token: 0x04000885 RID: 2181
    [Token(Token = "0x4000885")]
    [FieldOffset(Offset = "0x18")]
    public int ghostAge;

    // Token: 0x04000886 RID: 2182
    [Token(Token = "0x4000886")]
    [FieldOffset(Offset = "0x1C")]
    public bool isMale;

    // Token: 0x04000887 RID: 2183
    [Token(Token = "0x4000887")]
    [FieldOffset(Offset = "0x20")]
    public string GhostName;

    // Token: 0x04000888 RID: 2184
    [Token(Token = "0x4000888")]
    [FieldOffset(Offset = "0x28")]
    public int ghostFirstNameID;

    // Token: 0x04000889 RID: 2185
    [Token(Token = "0x4000889")]
    [FieldOffset(Offset = "0x2C")]
    public int ghostLastNameID;

    // Token: 0x0400088A RID: 2186
    [Token(Token = "0x400088A")]
    [FieldOffset(Offset = "0x30")]
    public bool isShy;

    // Token: 0x0400088B RID: 2187
    [Token(Token = "0x400088B")]
    [FieldOffset(Offset = "0x34")]
    public int deathLength;

    // Token: 0x0400088C RID: 2188
    [Token(Token = "0x400088C")]
    [FieldOffset(Offset = "0x38")]
    public int favouriteRoomID;

    // Token: 0x0400088D RID: 2189
    [Token(Token = "0x400088D")]
    [FieldOffset(Offset = "0x3C")]
    public bool isWhisper;

    // Token: 0x02000158 RID: 344
    [Token(Token = "0x2000158")]
    public enum Type
    {
        // Token: 0x0400088F RID: 2191
        [Token(Token = "0x400088F")]
        Spirit,
        // Token: 0x04000890 RID: 2192
        [Token(Token = "0x4000890")]
        Wraith,
        // Token: 0x04000891 RID: 2193
        [Token(Token = "0x4000891")]
        Phantom,
        // Token: 0x04000892 RID: 2194
        [Token(Token = "0x4000892")]
        Poltergeist,
        // Token: 0x04000893 RID: 2195
        [Token(Token = "0x4000893")]
        Banshee,
        // Token: 0x04000894 RID: 2196
        [Token(Token = "0x4000894")]
        Jinn,
        // Token: 0x04000895 RID: 2197
        [Token(Token = "0x4000895")]
        Mare,
        // Token: 0x04000896 RID: 2198
        [Token(Token = "0x4000896")]
        Revenant,
        // Token: 0x04000897 RID: 2199
        [Token(Token = "0x4000897")]
        Shade,
        // Token: 0x04000898 RID: 2200
        [Token(Token = "0x4000898")]
        Demon,
        // Token: 0x04000899 RID: 2201
        [Token(Token = "0x4000899")]
        Yurei,
        // Token: 0x0400089A RID: 2202
        [Token(Token = "0x400089A")]
        Oni,
        // Token: 0x0400089B RID: 2203
        [Token(Token = "0x400089B")]
        Yokai,
        // Token: 0x0400089C RID: 2204
        [Token(Token = "0x400089C")]
        Hantu,
        // Token: 0x0400089D RID: 2205
        [Token(Token = "0x400089D")]
        Goryo,
        // Token: 0x0400089E RID: 2206
        [Token(Token = "0x400089E")]
        Myling,
        // Token: 0x0400089F RID: 2207
        [Token(Token = "0x400089F")]
        Onryo,
        // Token: 0x040008A0 RID: 2208
        [Token(Token = "0x40008A0")]
        TheTwins,
        // Token: 0x040008A1 RID: 2209
        [Token(Token = "0x40008A1")]
        Raiju,
        // Token: 0x040008A2 RID: 2210
        [Token(Token = "0x40008A2")]
        Obake,
        // Token: 0x040008A3 RID: 2211
        [Token(Token = "0x40008A3")]
        Mimic,
        // Token: 0x040008A4 RID: 2212
        [Token(Token = "0x40008A4")]
        Moroi,
        // Token: 0x040008A5 RID: 2213
        [Token(Token = "0x40008A5")]
        Deogen,
        // Token: 0x040008A6 RID: 2214
        [Token(Token = "0x40008A6")]
        Thaye
    }
}

Я выделил несколко полей. Они нам и нужны. Давай перейдем к написанию чита.

Создадим новый проект в vs22 назову его например Phasmophobia-hacking-tutorial это будет библиотека динамической компоновки на C++ (DLL)
Дальше создам простенькую структуру в корне:

sdk/
├─ GhostAI.h # Структура GhostAI
├─ GhostInfo.h # Структура GhostInfo
├─ GhostTraits.h # Структура GhostTraits
├─ MonoBehaviour.h # Структура MonoBehaviour
└─ sdk.h # Главный файл SDK
libraries/
└─ detours/
├─ include/ # Библиотека Detours файлы .h
└─ lib/ # Библиотека Detours файлы .lib и тд

(использовал ChatGPT (noad) для генерации данной схемы своей структуры)

Перенесу все данные файлы в vs22 дабы они отображались в ней и добавлю Detours в Свойства > Компоновщик > Дополнительные зависимости и не забываем про, то что нужен C++17 минимум я ставлю 20
Дальше напишу sdk.h:
sdk.h:
Expand Collapse Copy
#pragma once

#define DECLARE_FUNCTION_POINTER(NAME, TYPE, ADDRESS) \
using NAME = TYPE; \
inline NAME NAME##_ptr = reinterpret_cast<NAME>(BASE_ADDRESS + ADDRESS);

namespace SDK
{
    const auto BASE_ADDRESS = reinterpret_cast<uintptr_t>(GetModuleHandleW(L"GameAssembly.dll"));
}

#include "MonoBehaviour.h"
#include "GhostTraits.h"
#include "GhostInfo.h"
#include "GhostAI.h"

Я просто сделал макрос который будем использовать, а так же подключил несколько наших файлов. Дальше же я пропишу файл MonoBehaviour.h.
MonoBehaviour.h:
Expand Collapse Copy
#pragma once
#include "sdk.h"

namespace SDK
{
    struct __declspec(align(8)) Object1Fields
    {
        void* m_CachedPtr;
        void* m_CancellationTokenSource;
    };

    struct Component1Fields
    {
        Object1Fields _;
    };

    struct BehaviourFields
    {
        Component1Fields _;
    };

    struct MonoBehaviourFields
    {
        BehaviourFields _;
    };

    struct MonoBehaviour
    {
        void* Clazz;
        void* Monitor;
        MonoBehaviourFields Fields;
    };

    struct MonoBehaviourPunFields
    {
        MonoBehaviourFields _;
        void* pvCache;
    };

    struct MonoBehaviourPunCallbacksFields
    {
        MonoBehaviourPunFields _;
    };
}
В нём я просто добавил поля MonoBehaviour.

Теперь давайте же займёмся GhostTraits.h в нём я просто создам несколько структур и укажу поля и так же метод для конвертации Type > String
GhostTraits.h:
Expand Collapse Copy
#pragma once
#include <string>

#include "sdk.h"

namespace SDK
{
    enum class GhostType: int32_t
    {
        Spirit,
        Wraith,
        Phantom,
        Poltergeist,
        Banshee,
        Jinn,
        Mare,
        Revenant,
        Shade,
        Demon,
        Yurei,
        Oni,
        Yokai,
        Hantu,
        Goryo,
        Myling,
        Onryo,
        TheTwins,
        Raiju,
        Obake,
        Mimic,
        Moroi,
        Deogen,
        Thaye,
    };

    inline std::string GhostTypeToString(GhostType ghostType)
    {
        switch (ghostType)
        {
        case GhostType::Spirit:
            return "Spirit";
        case GhostType::Wraith:
            return "Wraith";
        case GhostType::Phantom:
            return "Phantom";
        case GhostType::Poltergeist:
            return "Poltergeist";
        case GhostType::Banshee:
            return "Banshee";
        case GhostType::Jinn:
            return "Jinn";
        case GhostType::Mare:
            return "Mare";
        case GhostType::Revenant:
            return "Revenant";
        case GhostType::Shade:
            return "Shade";
        case GhostType::Demon:
            return "Demon";
        case GhostType::Yurei:
            return "Yurei";
        case GhostType::Oni:
            return "Oni";
        case GhostType::Yokai:
            return "Yokai";
        case GhostType::Hantu:
            return "Hantu";
        case GhostType::Goryo:
            return "Goryo";
        case GhostType::Myling:
            return "Myling";
        case GhostType::Onryo:
            return "Onryo";
        case GhostType::TheTwins:
            return "The Twins";
        case GhostType::Raiju:
            return "Raiju";
        case GhostType::Obake:
            return "Obake";
        case GhostType::Mimic:
            return "Mimic";
        case GhostType::Moroi:
            return "Moroi";
        case GhostType::Deogen:
            return "Deogen";
        case GhostType::Thaye:
            return "Thaye";
        default:
            return "Unknown";
        }
    }

    struct GhostTraits
    {
        GhostType GhostType_;
        GhostType MimicType;
        void* Field2; // List evidences
        void* Field3; // List fullEvidences
        int32_t ghostAge;
        bool Field5; // isMale
        void* Name; // GhostName
        int32_t Field7; // ghostFirstNameID
        int Field8; // ghostLastNameID
        bool Field9; // isShy
        int32_t Field10; // deathLength
        int32_t Field11; // favouriteRoomID
        bool Field12; // isWhisper
    };
}

Ненужные нам поля я обозначал как FieldX т.к. мы их не будем юзать попросту. Но если вы хотите, то я оставил коментарии для того, чтобы вы понимали, что это за поле.

Давайте пропишем теперь GhostAI.h
GhostAI.h:
Expand Collapse Copy
#include "sdk.h"

namespace SDK

{

    struct GhostAIFields

    {

        MonoBehaviourFields _;
        void* Field0; // stateMachine
        void* Field1; // currentState
        char pad_0000[0x4]; // Добавляем смещение на 0x4 байта вперед т.к. в игре прыжок на 8 байтов, а у нас из-за этого может возникнуть ошибка если не делать выравнивание
        GhostInfo* GhostInfo;
        void* Field3; // NavMeshAgent
        // Больше нам и не нужно :)
    };



    struct GhostAI

    {
        void* Clazz;
        void* Monitor;
        GhostAIFields Fields;

    };



    DECLARE_FUNCTION_POINTER(GhostAI_Start, void(*)(GhostAI* instance, void* methodInfo), 0x1CCD6C0); // Обьявляем наш указатель 0X1CCD6C0 это наш оффсет на данную фукнцию: GhostAI.Start из игры
}

Теперь нам осталось последнее и это GhostInfo.h, пропишем его быстренько:

GhostInfo.h:
Expand Collapse Copy
#pragma once
#include "sdk.h"

namespace SDK
{
    struct GhostInfoFields
    {
        MonoBehaviourPunFields _;
        GhostTraits GhostTraits; // Он только нам нужен, но если вы хотите я оставил полную структуру ниже.
        void* ghost;
        void* favouriteRoom; // LevelRoom
        float activityMultiplier;
        bool hasSetEvidence;
    };

    struct GhostInfo
    {
        void* Clazz;
        void* Monitor;
        GhostInfoFields Fields;
    };
}

Ну и теперь не забываем про dllmain.cpp где мы подключаем detours, ставим хук, ищем значение.

dllmain.cpp:
Expand Collapse Copy
#include "pch.h"

#include <cstdio>
#include <iostream>
#include <ostream>
#include "libraries/detours/include/detours.h"

#include "sdk/sdk.h"

HMODULE g_hModuleDll = nullptr;
HANDLE g_hTutorialThread = nullptr;
SDK::GhostAI* g_Instance = nullptr;
bool g_IsInstance = false;

void Hook_GhostAI_Start(SDK::GhostAI* pGhostAI, void* methodInfo)
{
    g_Instance = pGhostAI; // берем наш instance
    g_IsInstance = true; // пишем что instance готов

    SDK::GhostAI_Start_ptr(pGhostAI, methodInfo); // вызываем ориг. функцию дабы не ломать игру
}

DWORD WINAPI TutorialThread(LPVOID lpParam)
{
    FILE* consoleFile = nullptr;
    AllocConsole(); // консолька
    freopen_s(&consoleFile, "CONOUT$", "w", stdout);
    DetourTransactionBegin();
    DetourUpdateThread(GetCurrentThread());
    DetourAttach(&reinterpret_cast<PVOID&>(SDK::GhostAI_Start_ptr), Hook_GhostAI_Start);
    DetourTransactionCommit();
    std::cout << "ready";


    while (true)
    {
        if (g_Instance && g_IsInstance)
        {
            if (const auto pGhostInfo = g_Instance->Fields.GhostInfo)
            {
                if (pGhostInfo->Fields.GhostTraits.Name)
                {
                    const auto ghostType = pGhostInfo->Fields.GhostTraits.GhostType_;
                    std::cout << "type: " << GhostTypeToString(ghostType) << std::endl;
                    g_IsInstance = false;
                }
            }
        }

        if (GetAsyncKeyState(VK_DELETE) & 1)
        {
            std::cout << "closed" << std::endl;
            break;
        }
        Sleep(100);
    }

    DetourTransactionBegin();
    DetourUpdateThread(GetCurrentThread());
    DetourDetach(&reinterpret_cast<PVOID&>(SDK::GhostAI_Start_ptr), Hook_GhostAI_Start);
    DetourTransactionCommit();
    FreeConsole();
    if (g_hTutorialThread)
    {
        CloseHandle(g_hTutorialThread);
        g_hTutorialThread = nullptr;
    }
    FreeLibraryAndExitThread(g_hModuleDll, 0);
    return 0;
}


BOOL APIENTRY DllMain(HMODULE hModule, DWORD ulReasonForCall, LPVOID lpReserved)
{
    if (ulReasonForCall == DLL_PROCESS_ATTACH)
    {
        g_hModuleDll = hModule;

        // создаём поток
        g_hTutorialThread = CreateThread(
            nullptr,
            0,
            TutorialThread,
            hModule,
            0,
            nullptr
        );
    }

    return TRUE;
}
Я оставлял коменты дабы вам было проще понять мой код. Теперь мы можем собрать свой код в Release и попробовать заинжектить его в игру.

Как мы видим всё работает:
bf1OVkO.png




Исходный код я оставил тут:
Пожалуйста, авторизуйтесь для просмотра ссылки.


Хочу сказать большое спасибо Evelien, так как я учился по её тутору. Без него я бы не смог понять взлом Phasmophobia ведь большинство строк зашифровано
 
Назад
Сверху Снизу