Начинающий
- Статус
- Оффлайн
- Регистрация
- 7 Янв 2025
- Сообщения
- 25
- Реакции
- 1
Итак, я занимаюсь взломом данной игры уже несколько месяцев и написал хороший чит. Поэтому сегодня мы разберем как написать простой чит на данную игры который просто получит тип призрака. Для начала разберемся, что под капотом у игры: Unity, x64, Il2cpp, нет античит системы, только проверка файлов и некоторых RPC, но нам это не помешает никак.
Использовать я буду:
Visual Studio 2022 - кодинг (буду называть дальше просто vs22)
Вы можете использовать, то что вам будет удобно
Для начала получим файл Assembly-CSharp.dll для этого мы откроем дампер и укажем что ему нужно.
Нажимаем на Press start or... и у нас идёт процесс дампа. После в папке output\Phasmophobia_dumped\ переходим в папку DummyDll и ищем Assembly-CSharp.dll она нам и нужна. Первый шаг уже позади.
Теперь откроем его в DnSpy и развернем ветку "-" в ней мы ищем GhostAI это наш призрак. У нас есть много Fields:
Я выделил 12-15 строки. Они нам и нужны точнее нам нужен GhostInfo имя уже говорит об том, что мы ищем (почти). Теперь посмотрим на его поля:
Я выделил 2-6 поле. Оно зашифровано, но мы не обращаем на это внимание. Я пользуюсь 0.11.0.0 версией которая не зашифрована узнал что это GhostTraits и у него такая структура:
Я выделил несколко полей. Они нам и нужны. Давай перейдем к написанию чита.
Создадим новый проект в 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:
Я просто сделал макрос который будем использовать, а так же подключил несколько наших файлов. Дальше же я пропишу файл MonoBehaviour.h.
В нём я просто добавил поля MonoBehaviour.
Теперь давайте же займёмся GhostTraits.h в нём я просто создам несколько структур и укажу поля и так же метод для конвертации Type > String
Ненужные нам поля я обозначал как FieldX т.к. мы их не будем юзать попросту. Но если вы хотите, то я оставил коментарии для того, чтобы вы понимали, что это за поле.
Давайте пропишем теперь GhostAI.h
Теперь нам осталось последнее и это GhostInfo.h, пропишем его быстренько:
Ну и теперь не забываем про dllmain.cpp где мы подключаем detours, ставим хук, ищем значение.
Я оставлял коменты дабы вам было проще понять мой код. Теперь мы можем собрать свой код в Release и попробовать заинжектить его в игру.
Как мы видим всё работает:
Исходный код я оставил тут:
Хочу сказать большое спасибо Evelien, так как я учился по её тутору. Без него я бы не смог понять взлом Phasmophobia ведь большинство строк зашифровано
Использовать я буду:
Пожалуйста, авторизуйтесь для просмотра ссылки.
- дампер (буду называть дальше просто дампер)
Пожалуйста, авторизуйтесь для просмотра ссылки.
- реверс дампаVisual Studio 2022 - кодинг (буду называть дальше просто vs22)
Вы можете использовать, то что вам будет удобно
Для начала получим файл Assembly-CSharp.dll для этого мы откроем дампер и укажем что ему нужно.
Теперь откроем его в DnSpy и развернем ветку "-" в ней мы ищем GhostAI это наш призрак. У нас есть много Fields:
Fields GhostAI:
{
// 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:
{
// 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:
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:
#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:
#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 _;
};
}
Теперь давайте же займёмся GhostTraits.h в нём я просто создам несколько структур и укажу поля и так же метод для конвертации Type > String
GhostTraits.h:
#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:
#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:
#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:
#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;
}
Как мы видим всё работает:
Пожалуйста, зарегистрируйтесь или авторизуйтесь, чтобы увидеть содержимое.
Исходный код я оставил тут:
Пожалуйста, авторизуйтесь для просмотра ссылки.
Хочу сказать большое спасибо Evelien, так как я учился по её тутору. Без него я бы не смог понять взлом Phasmophobia ведь большинство строк зашифровано
