Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add signature scanning #58

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions docs/auto-splitters.md
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,18 @@ end

* Cheat Engine is a tool that allows you to easily find Addresses and Pointer Paths for those Addresses, so you don't need to debug the game to figure out the structure of the memory.

## sig_scan
sig_scan performs a signature/pattern scan on the provided IDA-style byte array and optional integer offset and returns the found address

Example:
`signature = sig_scan("89 5C 24 ?? 89 44 24 ?? 74 ?? 48 8D 15", 4)`

Returns:
`14123ce19`

* Note: Until the address is found, sig_scan returns a 0.
* Note: Signature scanning is an expensive action. So, once an address has been found, it's recommended to reassign the sig_scan variable with the result of the sig_scan function to stop the scanning.

## getPID
* Returns the current PID

Expand Down
3 changes: 3 additions & 0 deletions src/auto-splitter.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "memory.h"
#include "process.h"
#include "settings.h"
#include "signature.h"

char auto_splitter_file[PATH_MAX];
int refresh_rate = 60;
Expand Down Expand Up @@ -323,6 +324,8 @@ void run_auto_splitter()
lua_setglobal(L, "process");
lua_pushcfunction(L, read_address);
lua_setglobal(L, "readAddress");
lua_pushcfunction(L, find_signature);
lua_setglobal(L, "sig_scan");
lua_pushcfunction(L, getPid);
lua_setglobal(L, "getPID");

Expand Down
178 changes: 178 additions & 0 deletions src/signature.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/uio.h>
#include <unistd.h>

#include "memory.h"
#include "process.h"
#include "signature.h"

#include <luajit.h>

extern game_process process;

MemoryRegion* get_memory_regions(pid_t pid, int* count)
{
char maps_path[256];
sprintf(maps_path, "/proc/%d/maps", pid);
FILE* maps_file = fopen(maps_path, "r");
if (!maps_file) {
perror("Failed to open maps file");
return NULL;
}

MemoryRegion* regions = NULL;
int capacity = 0;
*count = 0;

char line[256];
while (fgets(line, sizeof(line), maps_file)) {
if (*count >= capacity) {
capacity = capacity == 0 ? 10 : capacity * 2;
regions = (MemoryRegion*)realloc(regions, capacity * sizeof(MemoryRegion));
if (!regions) {
perror("Failed to allocate memory for regions");
fclose(maps_file);
return NULL;
}
}

uintptr_t start, end;
if (sscanf(line, "%lx-%lx", &start, &end) != 2) {
continue; // Skip lines that don't match the expected format
}
regions[*count].start = start;
regions[*count].end = end;
(*count)++;
}

fclose(maps_file);
return regions;
}

bool match_pattern(const uint8_t* data, const int* pattern, size_t pattern_size)
{
for (size_t i = 0; i < pattern_size; ++i) {
if (pattern[i] != -1 && data[i] != pattern[i])
EXtremeExploit marked this conversation as resolved.
Show resolved Hide resolved
return false;
}
return true;
}

int* convert_signature(const char* signature, size_t* pattern_size)
{
char* signature_copy = strdup(signature);
if (!signature_copy) {
return NULL;
}

char* token = strtok(signature_copy, " ");
size_t size = 0;
size_t capacity = 10;
int* pattern = (int*)malloc(capacity * sizeof(int));
if (!pattern) {
free(signature_copy);
return NULL;
}

while (token != NULL) {
if (size >= capacity) {
capacity *= 2;
int* temp = (int*)realloc(pattern, capacity * sizeof(int));
if (!temp) {
free(pattern);
free(signature_copy);
return NULL;
}
pattern = temp;
}

if (strcmp(token, "??") == 0)
pattern[size] = -1;
EXtremeExploit marked this conversation as resolved.
Show resolved Hide resolved
else
pattern[size] = strtol(token, NULL, 16);
size++;
token = strtok(NULL, " ");
}

free(signature_copy);
*pattern_size = size;
return pattern;
}

bool validate_process_memory(pid_t pid, uintptr_t address, void* buffer, size_t size)
{
struct iovec local_iov = { buffer, size };
struct iovec remote_iov = { (void*)address, size };
ssize_t nread = process_vm_readv(pid, &local_iov, 1, &remote_iov, 1, 0);

return nread == size;
}

int find_signature(lua_State* L)
{
pid_t p_pid = process.pid;
const char* signature = lua_tostring(L, 1);
int offset = lua_tointeger(L, 2); // Get the offset as an integer directly

size_t pattern_length;
int* pattern = convert_signature(signature, &pattern_length);
if (!pattern) {
lua_pushinteger(L, 0);
return 1;
}

int regions_count = 0;
MemoryRegion* regions = get_memory_regions(p_pid, &regions_count);
if (!regions) {
free(pattern);
lua_pushinteger(L, 0);
return 1;
}

for (int i = 0; i < regions_count; i++) {
MemoryRegion region = regions[i];
ssize_t region_size = region.end - region.start;
uint8_t* buffer = (uint8_t*)malloc(region_size);
if (!buffer) {
free(pattern);
free(regions);
lua_pushinteger(L, 0);
return 1;
}

if (!validate_process_memory(p_pid, region.start, buffer, region_size)) {
// printf("Failed to read memory region: %lx-%lx\n", region.start, region.end);
free(buffer);
continue; // Continue to next region
}

for (size_t j = 0; j <= region_size - pattern_length; ++j) {
if (match_pattern(buffer + j, pattern, pattern_length)) {
uintptr_t result = region.start + j + offset; // Apply the offset here
char hex_str[20]; // Buffer to hold the hexadecimal string representation
sprintf(hex_str, "%lx", result); // Convert result to hexadecimal string

free(buffer);
free(pattern);
free(regions);

lua_pushstring(L, hex_str); // Push the hexadecimal string onto the Lua stack
Copy link
Collaborator

@EXtremeExploit EXtremeExploit Jul 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Im kinda confused, the function returns a string that contains the address in hexadecimal, but the result in the example is used as an argument for readAddress which accepts numbers, not strings. Is it casting it to a number by some miracle?

return 1; // Return the number of values pushed onto the stack
}
}

free(buffer);
}

free(pattern);
free(regions);

lua_pushinteger(L, 0); // Push 0 if no match is found in any region
return 1; // Return the number of values pushed onto the stack
}
21 changes: 21 additions & 0 deletions src/signature.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#ifndef __SIGNATURE_H__
#define __SIGNATURE_H__

#include <luajit.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>

typedef struct {
uintptr_t start;
uintptr_t end;
} MemoryRegion;

MemoryRegion* get_memory_regions(pid_t pid, int* count);
bool match_pattern(const uint8_t* data, const int* pattern, size_t pattern_size);
int* convert_signature(const char* signature, size_t* pattern_size);
bool validate_process_memory(pid_t pid, uintptr_t address, void* buffer, size_t size);
int find_signature(lua_State* L);

#endif /* __SIGNATURE_H__ */