Files
ps5debug-NG/debugger/source/scan.c
T
OpenSourcereR-dev 14fc8c16b5 v1.2.7: fix console crash on "between" rescan (NULL deref in async scan)
Rescanning with a "between (A to B)" comparison crashed the console
(SceShellCore SIGSEGV -> kernel panic): proc_scan_count_handle passed
prevValue = NULL, but the async "between" comparator reads its upper bound
from prevValue. Long-standing since v1.2.2; only the async rescan + between
combo triggers it.

- scan.c: for "between", point prev at the upper bound in the scan-value
  buffer (pattern + value_length), as proc_scan_start_handle already does.
- scan_compare.c: harden both comparators - cmpType 4-10 return false instead
  of dereferencing a NULL prev/extra operand.
- Strip stray comments re-introduced this session (proc.c/scan.c/scan_compare.c)
  to match the public-release convention.

Bump version 1.2.6 to 1.2.7.
2026-06-03 13:07:18 +00:00

675 lines
22 KiB
C

// SPDX-License-Identifier: GPL-3.0-only
#include "protocol.h"
#include "sdk_shim.h"
#include "net.h"
#include "proc.h"
#include "kern_rw_fast.h"
#include <ps5/kernel.h>
static int vmmap_offs_initialized = 0;
static uint64_t vmmap_nentries_adj = 0;
static uint64_t vmmap_name_adj = 0;
static void vmmap_init_offsets(void) {
uint32_t fw = kernel_get_fw_version();
int write_adj = ((fw & 0xffff0000u) >= 0x6000000u) ? 1 : 0;
if (write_adj) {
vmmap_nentries_adj = 8;
vmmap_name_adj = 0xE;
}
vmmap_offs_initialized = 1;
}
int sys_proc_vm_map(uint32_t pid, void **out_maps, int *out_count) {
if (out_maps) *out_maps = NULL;
if (out_count) *out_count = 0;
if ((int32_t)pid <= 0) return 1;
if (!vmmap_offs_initialized) vmmap_init_offsets();
intptr_t kproc = kernel_get_proc_fast((pid_t)pid);
if (!kproc) return 1;
intptr_t vmspace_kaddr = 0;
if (kernel_copyout_fast(kproc + 0x200, &vmspace_kaddr, sizeof(vmspace_kaddr)) != 0) return 1;
uint8_t vmspace_buf[0x350];
memset(vmspace_buf, 0, sizeof(vmspace_buf));
if (kernel_copyout_fast(vmspace_kaddr, vmspace_buf, sizeof(vmspace_buf)) != 0) return 1;
int32_t n_entries = *(int32_t *)(vmspace_buf + 0x1A8 + (uintptr_t)vmmap_nentries_adj);
if (n_entries <= 0) return 1;
void *out_buf = malloc((size_t)n_entries * sizeof(struct proc_vm_map_entry));
if (!out_buf) return 1;
intptr_t cur_entry = *(intptr_t *)(vmspace_buf + 8);
uint8_t *out_p = (uint8_t *)out_buf;
uint8_t *out_end = out_p + (size_t)n_entries * sizeof(struct proc_vm_map_entry);
while (cur_entry != 0) {
uint8_t entry_buf[0x1C0];
memset(entry_buf, 0, sizeof(entry_buf));
if (kernel_copyout_fast(cur_entry, entry_buf, sizeof(entry_buf)) != 0) {
free(out_buf);
return 1;
}
*(uint64_t *)(out_p + 0x20) = *(uint64_t *)(entry_buf + 0x20);
*(uint64_t *)(out_p + 0x28) = *(uint64_t *)(entry_buf + 0x28);
*(uint64_t *)(out_p + 0x30) = *(uint64_t *)(entry_buf + 0x58);
uint8_t prot_byte = entry_buf[0x64] & 0x0F;
if (prot_byte == 4) prot_byte = 5;
*(uint16_t *)(out_p + 0x38) = (uint16_t)prot_byte;
memcpy(out_p, entry_buf + 0x142 + (uintptr_t)vmmap_name_adj, 0x20);
cur_entry = *(intptr_t *)(entry_buf + 8);
out_p += sizeof(struct proc_vm_map_entry);
if (out_p >= out_end) break;
}
if (out_maps) *out_maps = out_buf;
if (out_count) *out_count = n_entries;
return 0;
}
int proc_scan_handle(int fd, struct cmd_packet *packet) {
struct cmd_proc_scan_packet *sp = (struct cmd_proc_scan_packet *)packet->data;
if (!sp) {
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
uint64_t value_length = proc_scan_getSizeOfValueType(sp->valueType);
if (value_length == 0) value_length = sp->lenData;
uint8_t *data = (uint8_t *)net_alloc_buffer(sp->lenData);
if (!data) {
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
net_send_int32(fd, CMD_SUCCESS);
net_recv_all(fd, data, sp->lenData, 1);
void *maps = NULL;
int count = 0;
if (sys_proc_vm_map(sp->pid, &maps, &count) != 0) {
net_send_int32(fd, CMD_ERROR);
free(data);
return 1;
}
net_send_int32(fd, CMD_SUCCESS);
const uint8_t *extra = (sp->lenData == value_length) ? NULL : &data[value_length];
uint8_t *scan_buf = (uint8_t *)net_alloc_buffer(0x4000);
if (count > 0 && scan_buf) {
struct proc_vm_map_entry *entry = (struct proc_vm_map_entry *)maps;
for (int i = 0; i < count; i++,
entry = (struct proc_vm_map_entry *)
((uint8_t *)entry + sizeof(*entry))) {
if (!(entry->prot & 1)) continue;
uint64_t start = entry->start;
uint64_t section_len = entry->end - start;
if (!section_len) continue;
for (uint64_t j = 0; j < section_len; j += value_length) {
uint64_t off = j & 0x3FFF;
if (j == 0 || off == 0) {
proc_read_mem(sp->pid, start, 0x4000, scan_buf);
off = j & 0x3FFF;
}
uint64_t cur_addr = start + j;
if (proc_scan_legacy_compareValues(sp->compareType, sp->valueType,
value_length, data,
&scan_buf[off], extra)) {
net_send_all(fd, &cur_addr, 8);
}
}
}
}
uint64_t endflag = 0xFFFFFFFFFFFFFFFFull;
net_send_all(fd, &endflag, 8);
if (scan_buf) free(scan_buf);
if (maps) free(maps);
free(data);
return 0;
}
int proc_scan_aob_handle(int fd, struct cmd_packet *packet) {
struct cmd_proc_scan_aob_packet *sp = (struct cmd_proc_scan_aob_packet *)packet->data;
if (!sp || sp->pattern_length == 0) {
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
uint32_t plen = sp->pattern_length;
uint64_t step_unit = proc_scan_getSizeOfValueType(10);
if (step_unit == 0) step_unit = plen;
uint8_t *pattern = (uint8_t *)net_alloc_buffer(plen);
if (!pattern) {
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
uint8_t *mask = (uint8_t *)net_alloc_buffer((uint32_t)step_unit);
if (!mask) {
free(pattern);
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
net_send_int32(fd, CMD_SUCCESS);
net_recv_all(fd, pattern, plen, 1);
net_recv_all(fd, mask, (int)step_unit, 1);
uint64_t chunk_size = 0x100000;
if (chunk_size % step_unit != 0) chunk_size = (chunk_size / step_unit) * step_unit;
uint8_t *read_buf = (uint8_t *)net_alloc_buffer(chunk_size);
if (!read_buf) {
free(pattern);
free(mask);
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
net_send_int32(fd, CMD_SUCCESS);
uint64_t address = sp->address;
uint64_t remaining = sp->length;
uint64_t match_count = 0;
uint64_t match_addr = 0;
uint8_t stop_flag = sp->stop_flag;
uint8_t max_matches = sp->max_matches;
int stop_unique = (stop_flag == 1);
int invalidated = 0;
int found_done = 0;
while (remaining > 0) {
uint64_t to_read = (chunk_size < remaining) ? chunk_size : remaining;
memset(read_buf, 0, to_read);
proc_read_mem(sp->pid, address, to_read, read_buf);
uint64_t limit = (to_read >= plen) ? to_read - plen : 0;
for (uint64_t j = 0; j <= limit; j++) {
if (aob_match(plen, pattern, &read_buf[j], 0, mask)) {
match_count++;
if (match_count == max_matches) {
match_addr = address + j;
if (!stop_unique) { found_done = 1; break; }
} else if (match_count > max_matches && stop_unique) {
match_addr = 0;
invalidated = 1;
break;
}
}
}
if (found_done || invalidated) break;
if (chunk_size >= remaining) break;
uint64_t advance = chunk_size - plen + 1;
address += advance;
remaining = (remaining > advance) ? remaining - advance : 0;
}
net_send_all(fd, &match_addr, 8);
free(read_buf);
free(pattern);
free(mask);
net_send_int32(fd, CMD_SUCCESS);
return 0;
}
int proc_scan_aob_multi_handle(int fd, struct cmd_packet *packet) {
struct cmd_proc_scan_aob_multi_packet *mp =
(struct cmd_proc_scan_aob_multi_packet *)packet->data;
if (!mp || mp->patterns_length == 0) {
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
uint32_t patterns_length = mp->patterns_length;
uint8_t *blob = (uint8_t *)net_alloc_buffer(patterns_length);
if (!blob) {
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
net_send_int32(fd, CMD_SUCCESS);
net_recv_all(fd, blob, patterns_length, 1);
uint16_t pat_count = 0;
uint64_t max_plen = 1;
{
uint32_t cursor = 0;
while (cursor + 5 <= patterns_length) {
uint32_t plen;
memcpy(&plen, blob + cursor + 1, 4);
cursor += 5 + 2 * plen;
if (plen > max_plen) max_plen = plen;
pat_count++;
}
}
uint32_t output_size = (uint32_t)pat_count * 8u;
uint8_t *read_buf = (uint8_t *)net_alloc_buffer(0x100000);
void *output = read_buf ? net_alloc_buffer(output_size) : NULL;
uint64_t *match_counts = output ? (uint64_t *)net_alloc_buffer((uint32_t)pat_count * 8u) : NULL;
if (!read_buf || !output || !match_counts) {
if (read_buf) free(read_buf);
if (output) free(output);
if (match_counts) free(match_counts);
free(blob);
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
memset(output, 0, output_size);
memset(match_counts, 0, (size_t)pat_count * 8u);
net_send_int32(fd, CMD_SUCCESS);
uint64_t address = mp->address;
uint64_t remaining = mp->length;
uint8_t stop_flag = mp->stop_flag;
int stop_unique = (stop_flag == 1);
uint16_t found_count = 0;
uint16_t invalidated_count = 0;
if (remaining > 0) {
for (;;) {
uint64_t to_read = (remaining < 0x100000) ? remaining : 0x100000;
memset(read_buf, 0, to_read);
proc_read_mem(mp->pid, address, to_read, read_buf);
uint32_t cursor = 0;
int outer_break = 0;
for (uint16_t pi = 0; pi < pat_count; pi++) {
uint8_t target_count = blob[cursor];
uint32_t plen;
memcpy(&plen, blob + cursor + 1, 4);
const uint8_t *pat = blob + cursor + 5;
const uint8_t *msk = pat + plen;
uint64_t limit = (to_read >= plen) ? to_read - plen : 0;
for (uint64_t j = 0; j <= limit; j++) {
if (aob_match(plen, pat, &read_buf[j], 0, msk)) {
match_counts[pi]++;
if (match_counts[pi] == target_count) {
uint64_t cur_addr = address + j;
memcpy((uint8_t *)output + (size_t)pi * 8u, &cur_addr, 8);
found_count++;
if (!stop_unique) break;
} else if (stop_unique &&
match_counts[pi] == (uint64_t)target_count + 1u) {
uint64_t zero = 0;
memcpy((uint8_t *)output + (size_t)pi * 8u, &zero, 8);
invalidated_count++;
break;
}
}
}
cursor += 5 + 2u * plen;
if (!stop_unique && found_count >= pat_count) { outer_break = 1; break; }
if ( stop_unique && invalidated_count >= pat_count) { outer_break = 1; break; }
}
if (outer_break) break;
if (remaining <= 0x100000) break;
uint64_t advance = (uint64_t)0x100001 - max_plen;
address += advance;
remaining = (remaining > advance) ? remaining - advance : 0;
}
}
net_send_all(fd, output, (int)output_size);
free(match_counts);
free(output);
free(read_buf);
free(blob);
net_send_int32(fd, CMD_SUCCESS);
return 0;
}
int proc_scan_start_handle(int fd, struct cmd_packet *packet) {
if (!(g_proc_auth_state & 2)) {
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
struct cmd_proc_scan_start_packet *sp = (struct cmd_proc_scan_start_packet *)packet->data;
if (!sp) {
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
if (sp->compareType > 12 || sp->lenData == 0) {
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
int needs_scan_value = ((1u << sp->compareType) & 0x114Fu) != 0;
int is_between = (sp->compareType == 4);
int is_arrbytes = (sp->valueType == 10);
if (is_between) needs_scan_value = 1;
uint64_t value_length = proc_scan_getSizeOfValueType(sp->valueType);
if (value_length == 0) value_length = sp->lenData;
if (value_length == 0) {
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
uint8_t *pattern = NULL;
if (needs_scan_value && sp->lenData) {
pattern = (uint8_t *)net_alloc_buffer(sp->lenData);
if (!pattern) {
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
}
net_send_int32(fd, CMD_SUCCESS);
if (pattern) {
net_recv_all(fd, pattern, (int)sp->lenData, 1);
}
uint8_t *mask = NULL;
if (is_arrbytes) {
mask = (uint8_t *)net_alloc_buffer((uint32_t)value_length);
if (!mask) {
if (pattern) free(pattern);
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
net_recv_all(fd, mask, (int)value_length, 1);
}
uint64_t step = sp->alignment ? sp->alignment : value_length;
uint64_t chunk_size = 0x100000ULL;
if (chunk_size % value_length != 0) {
chunk_size = (chunk_size / value_length) * value_length;
}
uint8_t *read_buf = (uint8_t *)net_alloc_buffer(chunk_size);
uint8_t *result_buf = (uint8_t *)net_alloc_buffer(0x40000);
if (!read_buf || !result_buf) {
if (read_buf) free(read_buf);
if (result_buf) free(result_buf);
if (pattern) free(pattern);
if (mask) free(mask);
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
net_send_int32(fd, CMD_SUCCESS);
uint64_t addr = sp->address;
uint64_t remaining = sp->length;
uint64_t result_len = 0;
uint64_t flush_thresh = 0x3FFE8ULL - value_length;
const void *prev_for_between = is_between ? (pattern + value_length) : NULL;
while (remaining > 0) {
uint64_t to_read = (remaining > chunk_size) ? chunk_size : remaining;
memset(read_buf, 0, to_read);
proc_read_mem(sp->pid, addr, to_read, read_buf);
uint64_t limit = (to_read >= value_length) ? to_read - value_length : 0;
for (uint64_t j = 0; j <= limit; j += step) {
if (proc_scan_compareValues(sp->compareType, sp->valueType, value_length,
pattern, &read_buf[j], prev_for_between, mask)) {
if (result_len > flush_thresh) {
*(uint64_t *)result_buf = result_len;
net_send_all(fd, result_buf, (int)(result_len + 8));
result_len = 0;
}
uint32_t offset = (uint32_t)((addr + j) - sp->address);
memcpy(result_buf + 8 + result_len, &offset, 4);
memcpy(result_buf + 8 + result_len + 4, &read_buf[j], value_length);
result_len += 4 + value_length;
}
}
uint64_t advance = to_read + step - value_length;
if (advance == 0 || advance > to_read) advance = to_read;
addr += advance;
remaining = (remaining > advance) ? remaining - advance : 0;
}
if (result_len) {
*(uint64_t *)result_buf = result_len;
net_send_all(fd, result_buf, (int)(result_len + 8));
}
uint64_t sentinel = 0xFFFFFFFFFFFFFFFFULL;
net_send_all(fd, &sentinel, 8);
free(read_buf);
free(result_buf);
if (pattern) free(pattern);
if (mask) free(mask);
net_send_int32(fd, CMD_SUCCESS);
return 0;
}
int proc_scan_count_handle(int fd, struct cmd_packet *packet) {
if (!(g_proc_auth_state & 2)) {
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
struct cmd_proc_scan_count_packet *cp = (struct cmd_proc_scan_count_packet *)packet->data;
if (!cp) {
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
if (cp->compareType > 12) {
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
int needs_value_flag = g_cmptype_needs_value [cp->compareType] != 0;
int needs_extra_flag = g_cmptype_needs_extra [cp->compareType] != 0;
int needs_previous_flag = g_cmptype_needs_previous[cp->compareType] != 0;
int needs_scan_value = needs_value_flag || needs_extra_flag;
int is_arrbytes = (cp->valueType == 10);
int is_between = (cp->compareType == 4);
uint64_t value_length = proc_scan_getSizeOfValueType(cp->valueType);
if (value_length == 0) value_length = cp->lenData;
if (value_length == 0 || value_length > 0x1000) {
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
uint8_t *pattern = NULL;
uint8_t *mask = NULL;
if (needs_scan_value && cp->lenData) {
pattern = (uint8_t *)net_alloc_buffer(cp->lenData);
if (!pattern) {
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
}
if (is_arrbytes) {
mask = (uint8_t *)net_alloc_buffer((uint32_t)value_length);
if (!mask) {
if (pattern) free(pattern);
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
}
uint8_t *chunk_buf = (uint8_t *)net_alloc_buffer(0x40000);
uint8_t *result_buf = (uint8_t *)net_alloc_buffer(0x40000);
uint8_t *mem_buf = (uint8_t *)net_alloc_buffer(0x100000);
if (!chunk_buf || !result_buf || !mem_buf) {
if (chunk_buf) free(chunk_buf);
if (result_buf) free(result_buf);
if (mem_buf) free(mem_buf);
if (pattern) free(pattern);
if (mask) free(mask);
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
net_send_int32(fd, CMD_SUCCESS);
if (pattern) net_recv_all(fd, pattern, (int)cp->lenData, 1);
if (mask) net_recv_all(fd, mask, (int)value_length, 1);
const uint8_t *between_hi = (is_between && pattern && cp->lenData >= 2 * value_length)
? (pattern + value_length) : NULL;
int includes_prev = needs_previous_flag;
uint64_t entry_size = includes_prev ? (4 + value_length) : 4;
uint64_t flush_thresh = 0x3FFE8ULL - 2 * value_length;
for (;;) {
uint32_t chunk_len = 0;
net_recv_all(fd, &chunk_len, 4, 1);
if (chunk_len == 0xFFFFFFFFu) break;
if (chunk_len == 0) {
uint64_t per_chunk_sentinel = 0xFFFFFFFFFFFFFFFFULL;
net_send_all(fd, &per_chunk_sentinel, 8);
continue;
}
if (chunk_len > 0x40000u) break;
net_recv_all(fd, chunk_buf, (int)chunk_len, 1);
uint64_t last_entry_addr = 0;
if (chunk_len >= entry_size) {
uint32_t last_offset;
memcpy(&last_offset, chunk_buf + chunk_len - entry_size, 4);
last_entry_addr = cp->base_address + last_offset;
}
uint64_t window_start = 0;
uint64_t window_end = 0;
uint64_t result_len = 0;
for (uint64_t off = 0; off + entry_size <= chunk_len; off += entry_size) {
uint32_t entry_offset;
memcpy(&entry_offset, chunk_buf + off, 4);
const uint8_t *prev_value_ptr = includes_prev ? (chunk_buf + off + 4) : between_hi;
uint64_t addr = cp->base_address + entry_offset;
if (addr >= window_end) {
uint64_t span = (last_entry_addr >= addr)
? (last_entry_addr + value_length) - addr
: value_length;
uint32_t read_size = (span > 0x100000ULL) ? 0x100000u : (uint32_t)span;
memset(mem_buf, 0, read_size);
proc_read_mem(cp->pid, addr, (uint64_t)read_size, mem_buf);
window_start = addr;
window_end = addr + read_size;
}
const uint8_t *mem_value_ptr = mem_buf + (addr - window_start);
if (proc_scan_compareValues(cp->compareType, cp->valueType, value_length,
pattern, mem_value_ptr, prev_value_ptr, mask)) {
if (result_len > flush_thresh) {
*(uint64_t *)result_buf = result_len;
net_send_all(fd, result_buf, (int)(result_len + 8));
result_len = 0;
}
memcpy(result_buf + 8 + result_len, &entry_offset, 4);
memcpy(result_buf + 8 + result_len + 4, mem_value_ptr, value_length);
result_len += 4 + value_length;
}
}
if (result_len) {
*(uint64_t *)result_buf = result_len;
net_send_all(fd, result_buf, (int)(result_len + 8));
}
uint64_t per_chunk_sentinel = 0xFFFFFFFFFFFFFFFFULL;
net_send_all(fd, &per_chunk_sentinel, 8);
}
free(chunk_buf);
free(result_buf);
free(mem_buf);
if (pattern) free(pattern);
if (mask) free(mask);
net_send_int32(fd, CMD_SUCCESS);
return 0;
}
int proc_scan_get_handle(int fd, struct cmd_packet *packet) {
if (!(g_proc_auth_state & 2)) {
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
struct cmd_proc_scan_get_packet *gp = (struct cmd_proc_scan_get_packet *)packet->data;
if (!gp) {
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
uint32_t entries_len = gp->count * 12u;
uint8_t *entries = (uint8_t *)net_alloc_buffer(entries_len);
if (!entries) {
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
net_send_int32(fd, CMD_SUCCESS);
net_recv_all(fd, entries, (int)entries_len, 1);
uint8_t *buf = (uint8_t *)net_alloc_buffer(0x100000);
if (!buf) {
free(entries);
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
for (uint32_t i = 0; i < gp->count; i++) {
uint64_t addr;
uint32_t length32;
memcpy(&addr, entries + 12u * i, 8);
memcpy(&length32, entries + 12u * i + 8u, 4);
uint64_t length = length32;
while (length > 0) {
uint64_t to_read = (length > 0x100000ULL) ? 0x100000ULL : length;
memset(buf, 0, to_read);
proc_read_mem(gp->pid, addr, to_read, buf);
net_send_all(fd, buf, (int)to_read);
addr += to_read;
length -= to_read;
}
}
uint64_t sentinel = 0xFFFFFFFFFFFFFFFFULL;
net_send_all(fd, &sentinel, 8);
free(entries);
free(buf);
return 0;
}