1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<//
// TutorialMode.h
// Cart Collect
//
// Created by Starla Insigna on 8/10/11.
// Copyright 2011 Four Island. All rights reserved.
//
#import "GameMode.h"
#import "TutorialBubble.h"
#import "FallingObjectDelegate.h"
@interface TutorialMode : GameMode <FallingObjectDelegate> {
TutorialBubble* currentTutorial;
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */#include "Memory.h"
#include <psapi.h>
#include <tlhelp32.h>
#include <iostream>
#include <string>
#include <cassert>
#undef PROCESSENTRY32
#undef Process32Next
Memory::Memory(const std::wstring& processName) : _processName(processName) {}
Memory::~Memory() {
if (_threadActive) {
_threadActive = false;
_thread.join();
}
if (_handle != nullptr) {
for (uintptr_t addr : _allocations) VirtualFreeEx(_handle, (void*)addr, 0, MEM_RELEASE);
CloseHandle(_handle);
}
}
void Memory::StartHeartbeat(HWND window, std::chrono::milliseconds beat) {
if (_threadActive) return;
_threadActive = true;
_thread = std::thread([sharedThis = shared_from_this(), window, beat]{
while (sharedThis->_threadActive) {
sharedThis->Heartbeat(window);
std::this_thread::sleep_for(beat);
}
});
_thread.detach();
}
void Memory::Heartbeat(HWND window) {
if (!_handle && !Initialize()) {
// Couldn't initialize, definitely not running
PostMessage(window, WM_COMMAND, HEARTBEAT, (LPARAM)ProcStatus::NotRunning);
return;
}
DWORD exitCode = 0;
assert(_handle);
GetExitCodeProcess(_handle, &exitCode);
if (exitCode != STILL_ACTIVE) {
// Process has exited, clean up.
_computedAddresses.clear();
_handle = NULL;
PostMessage(window, WM_COMMAND, HEARTBEAT, (LPARAM)ProcStatus::NotRunning);
return;
}
#if GLOBALS == 0x5B28C0
int currentFrame = ReadData<int>({0x5BE3B0}, 1)[0];
#elif GLOBALS == 0x62D0A0
int currentFrame = ReadData<int>({0x63954C}, 1)[0];
#endif
int frameDelta = currentFrame - _previousFrame;
_previousFrame = currentFrame;
if (frameDelta < 0 && currentFrame < 250) {
// Some addresses (e.g. Entity Manager) may get re-allocated on newgame.
_computedAddresses.clear();
PostMessage(window, WM_COMMAND, HEARTBEAT, (LPARAM)ProcStatus::NewGame);
return;
}
// TODO: Some way to return ProcStatus::Randomized vs ProcStatus::NotRandomized vs ProcStatus::DeRandomized;
PostMessage(window, WM_COMMAND, HEARTBEAT, (LPARAM)ProcStatus::Running);
}
[[nodiscard]]
bool Memory::Initialize() {
// First, get the handle of the process
PROCESSENTRY32W entry;
entry.dwSize = sizeof(entry);
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
while (Process32NextW(snapshot, &entry)) {
if (_processName == entry.szExeFile) {
_handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, entry.th32ProcessID);
break;
}
}
if (!_handle) {
std::cerr << "Couldn't find " << _processName.c_str() << ", is it open?" << std::endl;
return false;
}
// Next, get the process base address
DWORD numModules;
std::vector<HMODULE> moduleList(1024);
EnumProcessModulesEx(_handle, &moduleList[0], static_cast<DWORD>(moduleList.size()), &numModules, 3);
std::wstring name(64, '\0');
for (DWORD i = 0; i < numModules / sizeof(HMODULE); i++) {
int length = GetModuleBaseNameW(_handle, moduleList[i], &name[0], static_cast<DWORD>(name.size()));
name.resize(length);
if (_processName == name) {
_baseAddress = (uintptr_t)moduleList[i];
break;
}
}
if (_baseAddress == 0) {
std::cerr << "Couldn't locate base address" << std::endl;
return false;
}
return true;
}
void Memory::AddSigScan(const std::vector<byte>& scanBytes, const std::function<void(int index)>& scanFunc)
{
_sigScans[scanBytes] = {scanFunc, false};
}
int find(const std::vector<byte> &data, const std::vector<byte>& search, size_t startIndex = 0) {
for (size_t i=startIndex; i<data.size() - search.size(); i++) {
bool match = true;
for (size_t j=0; j<search.size(); j++) {
if (data[i+j] == search[j]) {
continue;
}
match = false;
break;
}
if (match) return static_cast<int>(i);
}
return -1;
}
int Memory::ExecuteSigScans()
{
for (int i=0; i<0x200000; i+=0x1000) {
std::vector<byte> data = ReadData<byte>({i}, 0x1100);
for (auto& [scanBytes, sigScan] : _sigScans) {
if (sigScan.found) continue;
int index = find(data, scanBytes);
if (index == -1) continue;
sigScan.scanFunc(i + index);
sigScan.found = true;
}
}
int notFound = 0;
for (auto it : _sigScans) {
if (it.second.found == false) notFound++;
}
return notFound;
}
void Memory::ThrowError() {
std::wstring message(256, '\0');
DWORD error = GetLastError();
int length = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, error, 1024, &message[0], static_cast<DWORD>(message.size()), nullptr);
message.resize(length);
#ifndef NDEBUG
MessageBox(NULL, message.c_str(), L"Please tell darkid about this", MB_OK);
#endif
}
void* Memory::ComputeOffset(std::vector<int> offsets) {
// Leave off the last offset, since it will be either read/write, and may not be of type uintptr_t.
int final_offset = offsets.back();
offsets.pop_back();
uintptr_t cumulativeAddress = _baseAddress;
for (const int offset : offsets) {
cumulativeAddress += offset;
const auto search = _computedAddresses.find(cumulativeAddress);
// This is an issue with re-randomization. Always. Just disable it in debug mode!
#ifdef NDEBUG
if (search == std::end(_computedAddresses)) {
#endif
// If the address is not yet computed, then compute it.
uintptr_t computedAddress = 0;
if (bool result = !ReadProcessMemory(_handle, reinterpret_cast<LPVOID>(cumulativeAddress), &computedAddress, sizeof(uintptr_t), NULL)) {
ThrowError();
}
if (computedAddress == 0) { // Attempting to dereference a nullptr
ThrowError();
}
_computedAddresses[cumulativeAddress] = computedAddress;
#ifdef NDEBUG
}
#endif
cumulativeAddress = _computedAddresses[cumulativeAddress];
}
return reinterpret_cast<void*>(cumulativeAddress + final_offset);
}
|