├── backend ├── scanmem │ ├── gui │ │ ├── README.md │ │ ├── icons │ │ │ ├── 48x48 │ │ │ │ └── GameConqueror.png │ │ │ ├── 72x72 │ │ │ │ └── GameConqueror.png │ │ │ ├── 128x128 │ │ │ │ └── GameConqueror.png │ │ │ └── Makefile.am │ │ ├── screenshot.png │ │ ├── GameConqueror_48x48.png │ │ ├── GameConqueror_72x72.png │ │ ├── GameConqueror_128x128.png │ │ ├── gameconqueror.in │ │ ├── org.scanmem.gameconqueror.desktop.in │ │ ├── consts.py.in │ │ ├── TODO │ │ ├── org.freedesktop.gameconqueror.policy.in.in │ │ ├── Makefile.am │ │ ├── README │ │ ├── gameconqueror.1 │ │ ├── org.scanmem.gameconqueror.metainfo.xml.in │ │ ├── scanmem.py │ │ └── misc.py │ ├── po │ │ ├── LINGUAS │ │ ├── POTFILES.skip │ │ └── POTFILES.in │ ├── NEWS │ ├── test │ │ ├── .gitignore │ │ ├── Makefile.am │ │ ├── sm_test.sh │ │ └── memfake.c │ ├── TODO │ ├── AUTHORS │ ├── .gitignore │ ├── autogen.sh │ ├── getline.h │ ├── menu.h │ ├── .travis.yml │ ├── interrupt.c │ ├── Makefile.am │ ├── readline.h │ ├── commands.h │ ├── interrupt.h │ ├── sets.h │ ├── readline.c │ ├── README │ ├── getline.c │ ├── README.md │ ├── show_message.h │ ├── maps.h │ ├── common.c │ ├── common.h │ ├── scanroutines.h │ ├── licence.h │ ├── endianness.h │ ├── scanmem.h │ ├── list.h │ ├── configure.ac │ ├── show_message.c │ ├── build_for_android.sh │ ├── menu.c │ ├── targetmem.c │ ├── lgpl-3.0.txt │ ├── value.h │ ├── ChangeLog │ ├── commands.c │ ├── scanmem.c │ ├── value.c │ ├── maps.c │ └── targetmem.h ├── entrypoint.sh └── Dockerfile ├── .gitmodules ├── image └── README │ └── 1664125159778.png ├── .vscode ├── defsettings.json ├── config.sh └── tasks.json ├── src ├── types.d.ts ├── util │ └── util.ts └── components │ └── NumpadInput.tsx ├── plugin.json ├── .gitignore ├── tsconfig.json ├── rollup.config.js ├── package.json └── README.md /backend/scanmem/gui/README.md: -------------------------------------------------------------------------------- 1 | README -------------------------------------------------------------------------------- /backend/scanmem/po/LINGUAS: -------------------------------------------------------------------------------- 1 | de 2 | es 3 | it 4 | ja 5 | ru 6 | sr_ME 7 | -------------------------------------------------------------------------------- /backend/scanmem/gui/icons/48x48/GameConqueror.png: -------------------------------------------------------------------------------- 1 | ../../GameConqueror_48x48.png -------------------------------------------------------------------------------- /backend/scanmem/gui/icons/72x72/GameConqueror.png: -------------------------------------------------------------------------------- 1 | ../../GameConqueror_72x72.png -------------------------------------------------------------------------------- /backend/scanmem/NEWS: -------------------------------------------------------------------------------- 1 | See https://github.com/scanmem/scanmem/releases for news. 2 | -------------------------------------------------------------------------------- /backend/scanmem/gui/icons/128x128/GameConqueror.png: -------------------------------------------------------------------------------- 1 | ../../GameConqueror_128x128.png -------------------------------------------------------------------------------- /backend/scanmem/po/POTFILES.skip: -------------------------------------------------------------------------------- 1 | gui/org.freedesktop.gameconqueror.policy.in 2 | -------------------------------------------------------------------------------- /backend/scanmem/test/.gitignore: -------------------------------------------------------------------------------- 1 | # memfake exe 2 | memfake 3 | 4 | # Test results 5 | *.log 6 | *.trs 7 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "backend/scanmem"] 2 | path = backend/scanmem 3 | url = https://github.com/scanmem/scanmem.git 4 | -------------------------------------------------------------------------------- /image/README/1664125159778.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CameronRedmore/memory-deck/HEAD/image/README/1664125159778.png -------------------------------------------------------------------------------- /backend/scanmem/gui/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CameronRedmore/memory-deck/HEAD/backend/scanmem/gui/screenshot.png -------------------------------------------------------------------------------- /backend/scanmem/gui/GameConqueror_48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CameronRedmore/memory-deck/HEAD/backend/scanmem/gui/GameConqueror_48x48.png -------------------------------------------------------------------------------- /backend/scanmem/gui/GameConqueror_72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CameronRedmore/memory-deck/HEAD/backend/scanmem/gui/GameConqueror_72x72.png -------------------------------------------------------------------------------- /backend/scanmem/gui/GameConqueror_128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CameronRedmore/memory-deck/HEAD/backend/scanmem/gui/GameConqueror_128x128.png -------------------------------------------------------------------------------- /backend/scanmem/test/Makefile.am: -------------------------------------------------------------------------------- 1 | TESTS = sm_test.sh 2 | check_PROGRAMS = memfake 3 | 4 | memfake_SOURCES = memfake.c 5 | memfake_CFLAGS = -std=gnu99 -Wall 6 | -------------------------------------------------------------------------------- /.vscode/defsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "deckip" : "0.0.0.0", 3 | "deckport" : "22", 4 | "deckpass" : "ssap", 5 | "deckkey" : "-i ${env:HOME}/.ssh/id_rsa", 6 | "deckdir" : "/home/deck" 7 | } -------------------------------------------------------------------------------- /backend/scanmem/gui/gameconqueror.in: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DATADIR=@PKGDATADIR@ 4 | 5 | PKEXEC=$(command -v "pkexec") 6 | if [ -n "$PKEXEC" ]; then 7 | $PKEXEC $DATADIR/GameConqueror.py "$@" 8 | else 9 | echo "install policykit!" 10 | fi 11 | -------------------------------------------------------------------------------- /backend/scanmem/po/POTFILES.in: -------------------------------------------------------------------------------- 1 | [type: gettext/glade]gui/GameConqueror.ui 2 | gui/GameConqueror.py 3 | gui/misc.py 4 | gui/org.scanmem.gameconqueror.desktop.in 5 | gui/org.freedesktop.gameconqueror.policy.in.in 6 | gui/org.scanmem.gameconqueror.metainfo.xml.in 7 | -------------------------------------------------------------------------------- /src/types.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.svg" { 2 | const content: string; 3 | export default content; 4 | } 5 | 6 | declare module "*.png" { 7 | const content: string; 8 | export default content; 9 | } 10 | 11 | declare module "*.jpg" { 12 | const content: string; 13 | export default content; 14 | } -------------------------------------------------------------------------------- /backend/scanmem/gui/org.scanmem.gameconqueror.desktop.in: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | _Name=Game Conqueror 3 | _Comment=A game hacking tool. A GUI front-end for scanmem. 4 | Exec=gameconqueror 5 | Terminal=false 6 | Type=Application 7 | Icon=GameConqueror 8 | Categories=Development;Utility; 9 | StartupNotify=true 10 | -------------------------------------------------------------------------------- /backend/scanmem/gui/consts.py.in: -------------------------------------------------------------------------------- 1 | # constants 2 | # should be defined by autotools 3 | 4 | VERSION = '@VERSION@' 5 | LIBDIR = '@LIBDIR@' 6 | LOCALEDIR = '@LOCALEDIR@' 7 | GETTEXT_PACKAGE = '@GETTEXT_PACKAGE@' 8 | PACKAGE_BUGREPORT = '@PACKAGE_BUGREPORT@' 9 | 10 | SETTINGS = {'scan_data_type':'int32' 11 | ,'lock_data_type':'int32' 12 | ,'search_scope' : 1 # normal 13 | } 14 | -------------------------------------------------------------------------------- /backend/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo "Container's IP address: `awk 'END{print $1}' /etc/hosts`" 5 | 6 | cd /backend/scanmem 7 | 8 | ./autogen.sh 9 | 10 | ./configure && make 11 | 12 | mkdir -p /backend/out 13 | 14 | cp -r /backend/scanmem/.libs/libscanmem.so.1.0.0 /backend/out 15 | cp -r /backend/scanmem/README.md /backend/out 16 | cp -r /backend/scanmem/lgpl-3.0.txt /backend/out 17 | cp -r /backend/scanmem/gpl-3.0.txt /backend/out 18 | -------------------------------------------------------------------------------- /plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Memory Deck", 3 | "author": "Camzie99 ", 4 | "contributors": [ 5 | "HeyItsWaters " 6 | ], 7 | "flags": ["debug", "root"], 8 | "publish": { 9 | "tags": ["cheats", "scanmem", "memory"], 10 | "description": "A simplistic scanmem wrapper for Decky. Enabling scanning for and editing values in memory.", 11 | "image": "https://raw.githubusercontent.com/HeyItsWaters/memory-deck/main/image/README/1664125159778.png" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/scanmem/gui/icons/Makefile.am: -------------------------------------------------------------------------------- 1 | dist_icon48_DATA = 48x48/GameConqueror.png 2 | dist_icon72_DATA = 72x72/GameConqueror.png 3 | dist_icon128_DATA = 128x128/GameConqueror.png 4 | EXTRA_DIST = $(icons48_DATA) $(icons72_DATA) $(icons128_DATA) 5 | 6 | icon48dir = $(datadir)/icons/hicolor/48x48/apps 7 | icon72dir = $(datadir)/icons/hicolor/72x72/apps 8 | icon128dir = $(datadir)/icons/hicolor/128x128/apps 9 | 10 | install-data-hook: 11 | @echo "Remember to run gtk-update-icon-cache if icons don't show up." 12 | -------------------------------------------------------------------------------- /backend/scanmem/TODO: -------------------------------------------------------------------------------- 1 | Current 2 | ======= 3 | * option: whether to search in readonly regions 4 | * reduce memory consumption (e.g. OVaMI padding) 5 | 6 | * see: https://github.com/scanmem/scanmem/issues for further TODOs 7 | 8 | Future 9 | ====== 10 | * use PT_IO on freebsd, instead of PT_READ/WRITE_D 11 | * add working freebsd support 12 | * in targetmem.c, check return value of allocate_enough_to_reach everywhere 13 | * search for values in files? (eg saved state) 14 | * macro support 15 | * automatically support zero and one based values. 16 | -------------------------------------------------------------------------------- /backend/scanmem/gui/TODO: -------------------------------------------------------------------------------- 1 | TODOs 2 | ===== 3 | 4 | - show error messages 5 | - different color for different addresses (heap, stack, module etc) 6 | - limit size of bytes read for memory editor 7 | - popup in memory editor -- like `add entry for this address` 8 | - icon in processlist 9 | - custom address region scan 10 | - different types of locking ('+' & '-') 11 | - plugin-like system to support different datatypes (or actually representations) 12 | - different representation (dec, hex ...) for cheatlist 13 | - reasonable parsing of simple math in several inputs 14 | -------------------------------------------------------------------------------- /backend/Dockerfile: -------------------------------------------------------------------------------- 1 | # we support images for building with a vanilla SteamOS base, 2 | # or versions with ootb support for rust or go 3 | # developers can also customize these images via this Dockerfile 4 | #FROM ghcr.io/steamdeckhomebrew/holo-toolchain-rust:latest 5 | #FROM ghcr.io/steamdeckhomebrew/holo-toolchain-go:latest 6 | FROM ghcr.io/steamdeckhomebrew/holo-base:latest 7 | 8 | # Install intltool for compiling scanmem 9 | RUN pacman -S intltool git --noconfirm 10 | 11 | # entrypoint.sh should always be located in the backend folder 12 | ENTRYPOINT [ "/backend/entrypoint.sh" ] -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | *.swp 10 | 11 | pids 12 | logs 13 | results 14 | tmp 15 | 16 | # Coverage reports 17 | coverage 18 | 19 | # API keys and secrets 20 | .env 21 | 22 | # Dependency directory 23 | node_modules 24 | bower_components 25 | 26 | # Editors 27 | .idea 28 | *.iml 29 | 30 | # OS metadata 31 | .DS_Store 32 | Thumbs.db 33 | 34 | # Ignore built ts files 35 | dist/ 36 | 37 | __pycache__/ 38 | 39 | /.yalc 40 | yalc.lock 41 | 42 | .vscode/settings.json 43 | 44 | # Ignore output folder 45 | 46 | backend/out 47 | bin 48 | cli/ 49 | -------------------------------------------------------------------------------- /.vscode/config.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]:-$0}"; )" &> /dev/null && pwd 2> /dev/null; )"; 3 | # printf "${SCRIPT_DIR}\n" 4 | # printf "$(dirname $0)\n" 5 | if ! [[ -e "${SCRIPT_DIR}/settings.json" ]]; then 6 | printf '.vscode/settings.json does not exist. Creating it with default settings. Exiting afterwards. Run your task again.\n\n' 7 | cp "${SCRIPT_DIR}/defsettings.json" "${SCRIPT_DIR}/settings.json" 8 | exit 1 9 | else 10 | printf '.vscode/settings.json does exist. Congrats.\n' 11 | printf 'Make sure to change settings.json to match your deck.\n' 12 | fi -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "dist", 4 | "module": "ESNext", 5 | "target": "ES2020", 6 | "jsx": "react", 7 | "jsxFactory": "window.SP_REACT.createElement", 8 | "declaration": false, 9 | "moduleResolution": "node", 10 | "noUnusedLocals": true, 11 | "noUnusedParameters": true, 12 | "esModuleInterop": true, 13 | "noImplicitReturns": true, 14 | "noImplicitThis": true, 15 | "noImplicitAny": true, 16 | "strict": true, 17 | "suppressImplicitAnyIndexErrors": true, 18 | "allowSyntheticDefaultImports": true, 19 | "skipLibCheck": true 20 | }, 21 | "include": ["src"], 22 | "exclude": ["node_modules"] 23 | } 24 | -------------------------------------------------------------------------------- /backend/scanmem/AUTHORS: -------------------------------------------------------------------------------- 1 | ## Release 0.17 2 | 3 | # Author list from `git shortlog -sen --no-merges` 4 | # Duplicates removed and Tavis put manually on top. 5 | 6 | Tavis Ormandy 7 | Lu Wang 8 | Andrea Stacchiotti 9 | Sebastian Parschauer 10 | Igor Gnatenko 11 | Mattias Münster 12 | Jonathan Pelletier 13 | Bijan Kazemi-Shirkadeh 14 | Eli Dupree 15 | 16 | 17 | # Current maintainers: 18 | 19 | Andrea Stacchiotti 20 | Sebastian Parschauer 21 | -------------------------------------------------------------------------------- /backend/scanmem/test/sm_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ev 3 | 4 | # Start memfake 5 | ./memfake 4 1 & 6 | memfake_pid=$! 7 | 8 | # Test runs 9 | 10 | test_sm () { 11 | ../scanmem -p $memfake_pid -e -c "$1" 12 | } 13 | 14 | test_sm "option scan_data_type int8;0;exit" 15 | test_sm "option scan_data_type int8;snapshot;exit" 16 | 17 | test_sm "option scan_data_type int8;snapshot;1;exit" 18 | test_sm "option scan_data_type int8;1;delete 0;1;exit" 19 | 20 | test_sm "option scan_data_type int;1;exit" 21 | test_sm "option scan_data_type float;1;exit" 22 | test_sm "option scan_data_type number;1;exit" 23 | 24 | huge_bytearray="" 25 | huge_string="" 26 | # 257 not a typo, forces full scan routine use 27 | for ((i=0; i < 257; i++)); do 28 | huge_bytearray+="00 ?? " 29 | huge_string+="a" 30 | done 31 | 32 | test_sm "option scan_data_type bytearray;${huge_bytearray};exit" 33 | test_sm "option scan_data_type string;\" ${huge_string};exit" 34 | 35 | # Clean up 36 | kill $memfake_pid 37 | -------------------------------------------------------------------------------- /backend/scanmem/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled program 2 | /scanmem 3 | 4 | # Auto-generated files 5 | *.o 6 | *.lo 7 | *.la 8 | .deps 9 | .libs 10 | m4 11 | autom4te.cache 12 | config.h* 13 | configure 14 | Makefile 15 | Makefile.in 16 | aclocal.m4 17 | compile 18 | config.guess 19 | config.log 20 | config.status 21 | config.sub 22 | depcomp 23 | install-sh 24 | libtool 25 | ltmain.sh 26 | missing 27 | py-compile 28 | stamp-h1 29 | tags 30 | test-driver 31 | 32 | # GameConqueror 33 | gui/org.scanmem.gameconqueror.metainfo.xml 34 | gui/org.scanmem.gameconqueror.desktop 35 | gui/consts.py 36 | gui/*.pyc 37 | gui/*.pyo 38 | gui/__pycache__ 39 | gui/gameconqueror 40 | gui/org.freedesktop.gameconqueror.policy 41 | gui/org.freedesktop.gameconqueror.policy.in 42 | 43 | # Translations 44 | po/.intltool-merge-cache 45 | po/GameConqueror.pot 46 | po/Makefile.in.in 47 | po/POTFILES 48 | po/*.gmo 49 | po/stamp-it 50 | 51 | # Android builds 52 | # libreadline and ncurses downloads and links 53 | readline-* 54 | ncurses-* 55 | readline 56 | ncurses 57 | -------------------------------------------------------------------------------- /backend/scanmem/gui/org.freedesktop.gameconqueror.policy.in.in: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | Scanmem 7 | https://github.com/scanmem/scanmem 8 | 9 | <_description>Run Game Conqueror 10 | <_message>Authentication is required to run Game Conqueror 11 | GameConqueror 12 | 13 | auth_admin_keep 14 | auth_admin_keep 15 | auth_admin_keep 16 | 17 | @PKGDATADIR@/GameConqueror.py 18 | true 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/util/util.ts: -------------------------------------------------------------------------------- 1 | import { useState, useEffect, SetStateAction, Dispatch } from 'react'; 2 | 3 | export const playSound = (sound: string) => { 4 | const audio = new Audio(sound); 5 | audio.play(); 6 | } 7 | 8 | //Function to use storage, and automatically store it in local storage 9 | export function useLocalStorageState (key: string, defaultValue: S | (() => S)): [S, Dispatch>] { 10 | const [state, setState] = useState(() => { 11 | const valueInLocalStorage = localStorage.getItem(key); 12 | if (valueInLocalStorage) { 13 | try 14 | { 15 | let value = JSON.parse(valueInLocalStorage); 16 | console.log(key, valueInLocalStorage); 17 | return value; 18 | } 19 | catch(ex) 20 | { 21 | return null; 22 | } 23 | } 24 | 25 | return typeof defaultValue === 'function' ? (defaultValue as Function)() : defaultValue; 26 | }); 27 | 28 | useEffect(() => { 29 | localStorage.setItem(key, JSON.stringify(state)); 30 | }, [key, state]); 31 | 32 | return [state, setState]; 33 | } 34 | -------------------------------------------------------------------------------- /backend/scanmem/autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | LIBTOOLIZE=libtoolize 4 | if [ "$(uname -s)" = "Darwin" ]; then 5 | LIBTOOLIZE=glibtoolize # install via brew 6 | fi 7 | 8 | echo "+ running $LIBTOOLIZE ..." 9 | $LIBTOOLIZE -c || { 10 | echo 11 | echo "$LIBTOOLIZE failed - check that it is present on system" 12 | exit 1 13 | } 14 | echo "+ running aclocal ..." 15 | aclocal -I m4 || { 16 | echo 17 | echo "aclocal failed - check that all needed development files"\ 18 | "are present on system" 19 | exit 1 20 | } 21 | echo "+ running autoheader ... " 22 | autoheader || { 23 | echo 24 | echo "autoheader failed" 25 | exit 1 26 | } 27 | echo "+ running autoconf ... " 28 | autoconf || { 29 | echo 30 | echo "autoconf failed" 31 | exit 1 32 | } 33 | echo "+ running intltoolize ..." 34 | intltoolize -f -c --automake || { 35 | echo 36 | echo "intltoolize failed - check that it is present on system" 37 | exit 1 38 | } 39 | echo "+ running automake ... " 40 | automake -c --add-missing || { 41 | echo 42 | echo "automake failed" 43 | exit 1 44 | } 45 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import commonjs from '@rollup/plugin-commonjs'; 2 | import json from '@rollup/plugin-json'; 3 | import { nodeResolve } from '@rollup/plugin-node-resolve'; 4 | import replace from '@rollup/plugin-replace'; 5 | import typescript from '@rollup/plugin-typescript'; 6 | import { defineConfig } from 'rollup'; 7 | import importAssets from 'rollup-plugin-import-assets'; 8 | 9 | import { name } from "./plugin.json"; 10 | 11 | export default defineConfig({ 12 | input: './src/index.tsx', 13 | plugins: [ 14 | commonjs(), 15 | nodeResolve(), 16 | typescript(), 17 | json(), 18 | replace({ 19 | preventAssignment: false, 20 | 'process.env.NODE_ENV': JSON.stringify('production'), 21 | }), 22 | importAssets({ 23 | publicPath: `http://127.0.0.1:1337/plugins/${name}/` 24 | }) 25 | ], 26 | context: 'window', 27 | external: ['react', 'react-dom', 'decky-frontend-lib'], 28 | output: { 29 | file: 'dist/index.js', 30 | globals: { 31 | react: 'SP_REACT', 32 | 'react-dom': 'SP_REACTDOM', 33 | 'decky-frontend-lib': 'DFL' 34 | }, 35 | format: 'iife', 36 | exports: 'default', 37 | }, 38 | }); 39 | -------------------------------------------------------------------------------- /backend/scanmem/gui/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = icons 2 | 3 | gameconquerordir=$(datadir)/gameconqueror 4 | python_PYTHON = scanmem.py consts.py misc.py hexview.py GameConqueror.py 5 | pythondir = $(gameconquerordir) 6 | dist_bin_SCRIPTS = gameconqueror 7 | dist_gameconqueror_DATA = GameConqueror.ui GameConqueror_128x128.png \ 8 | GameConqueror_72x72.png GameConqueror_48x48.png 9 | dist_desktop_in_files = org.scanmem.gameconqueror.desktop.in 10 | dist_desktop_DATA = $(dist_desktop_in_files:.desktop.in=.desktop) 11 | @INTLTOOL_DESKTOP_RULE@ 12 | dist_polkit_in_files = org.freedesktop.gameconqueror.policy.in 13 | dist_polkit_DATA = $(dist_polkit_in_files:.policy.in=.policy) 14 | @INTLTOOL_POLICY_RULE@ 15 | dist_appdata_in_files = org.scanmem.gameconqueror.metainfo.xml.in 16 | dist_appdata_DATA = $(dist_appdata_in_files:.xml.in=.xml) 17 | @INTLTOOL_XML_RULE@ 18 | dist_man_MANS = gameconqueror.1 19 | dist_doc_DATA = README TODO COPYING 20 | EXTRA_DIST = gameconqueror.in consts.py.in 21 | 22 | desktopdir=$(datadir)/applications 23 | polkitdir=$(datadir)/polkit-1/actions 24 | appdatadir=$(datadir)/metainfo 25 | 26 | install-data-hook: 27 | chmod +x $(DESTDIR)$(gameconquerordir)/GameConqueror.py 28 | -------------------------------------------------------------------------------- /backend/scanmem/getline.h: -------------------------------------------------------------------------------- 1 | /* 2 | Replace getline() with BSD specific fgetln(). 3 | 4 | Copyright (C) 2015 JianXiong Zhou 5 | Copyright (C) 2015 Jonathan Pelletier 6 | 7 | This file is part of libscanmem. 8 | 9 | This library is free software: you can redistribute it and/or modify 10 | it under the terms of the GNU Lesser General Public License as published 11 | by the Free Software Foundation; either version 3 of the License, or 12 | (at your option) any later version. 13 | 14 | This library is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | GNU Lesser General Public License for more details. 18 | 19 | You should have received a copy of the GNU Lesser General Public License 20 | along with this library. If not, see . 21 | */ 22 | 23 | #ifndef GETLINE_H 24 | #define GETLINE_H 25 | 26 | #include "config.h" 27 | 28 | #ifndef HAVE_GETLINE 29 | #include 30 | 31 | ssize_t getline(char **lineptr, size_t *n, FILE *stream); 32 | #endif 33 | 34 | #endif /* GETLINE_H */ 35 | -------------------------------------------------------------------------------- /backend/scanmem/gui/README: -------------------------------------------------------------------------------- 1 | # ![](https://raw.githubusercontent.com/scanmem/scanmem/master/gui/GameConqueror_48x48.png) GameConqueror 2 | 3 | Game Conqueror is a graphical game cheating tool under Linux, a frontend for scanmem. 4 | 5 | 6 | ## Requirements 7 | 8 | ``` 9 | libscanmem (latest version) 10 | Python (2.x / 3.x) 11 | PyGObject 12 | GTK+ 3 13 | policykit 14 | ``` 15 | 16 | ## Usage 17 | 18 | The interface is similar to the Windows-only Cheat Engine. 19 | 20 | - Enter a value to search in the Value entry, then press Enter or the scan buttons 21 | * Check supported syntax hovering over the '?' mark 22 | * You scan choose the search region/type via the dropdowns. 23 | * Number of matches will be shown in 'Found: XX' at topleft 24 | * Matches will be displayed in the left list if there are not too many 25 | - Add a candidate address to the list below by double-click on it or use the context-menu 26 | - There you can edit description, value, and lock it. 27 | 28 | ## Screenshot 29 | 30 | ![](https://raw.githubusercontent.com/scanmem/scanmem/master/gui/screenshot.png) 31 | 32 | ## Authors 33 | 34 | WANG Lu 35 | Andrea Stacchiotti 36 | 37 | ## License 38 | 39 | GPLv3 40 | -------------------------------------------------------------------------------- /backend/scanmem/gui/gameconqueror.1: -------------------------------------------------------------------------------- 1 | .TH gameconqueror 1 "2018-06-13" "gameconqueror-0.18" 2 | .SH NAME 3 | gameconqueror \- A GUI for scanmem, a game hacking tool. 4 | 5 | .SH SYNOPSIS 6 | .B gameconqueror 7 | .RB [options] 8 | .IR [target-program-pid] 9 | 10 | .SH DESCRIPTION 11 | .PP 12 | Scanmem is a simple interactive debugging utility for Linux, used to locate the 13 | address of a variable in a running process. This can be used for the analysis or 14 | modification of a hostile process on a compromised machine, for reverse 15 | engineering, or as a "pokefinder" to cheat at video games. 16 | .PP 17 | GameConqueror is a GUI for scanmem, aims to provide more features than scanmem, 18 | and a CheatEngine-like user-friendly interface. 19 | .PP 20 | 21 | .SH OPTIONS 22 | 23 | .TP 24 | .B "\-v, \-\-version" 25 | Print version and exit. 26 | 27 | .TP 28 | .B "\-h, \-\-help" 29 | Print a short description of command line options and exit. 30 | 31 | .TP 32 | .BI "\-s, \-\-search=" value 33 | Fill the search box with the given 34 | .IR value "." 35 | 36 | .SH HOMEPAGE 37 | https://github.com/scanmem/scanmem 38 | 39 | .SH AUTHORS 40 | 41 | WANG Lu 42 | .br 43 | Andrea Stacchiotti 44 | 45 | .SH SEE ALSO 46 | scanmem(1) 47 | -------------------------------------------------------------------------------- /backend/scanmem/menu.h: -------------------------------------------------------------------------------- 1 | /* 2 | Prompt and command completion. 3 | 4 | Copyright (C) 2017 Andrea Stacchiotti 5 | 6 | This file is part of scanmem. 7 | 8 | This program is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program. If not, see . 20 | */ 21 | 22 | #ifndef MENU_H 23 | #define MENU_H 24 | 25 | #include 26 | 27 | #include "scanmem.h" 28 | 29 | /* 30 | * getcommand() reads in a command using readline, and places a pointer to 31 | * the read string into *line, _which must be free'd by caller_. 32 | * returns true on success, or false on error. 33 | */ 34 | bool getcommand(globals_t *vars, char **line); 35 | 36 | #endif /* MENU_H */ 37 | -------------------------------------------------------------------------------- /backend/scanmem/.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | sudo: required 3 | dist: bionic 4 | 5 | env: 6 | global: 7 | # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created 8 | # via the "travis encrypt" command using the project repo's public key 9 | - secure: "C3Gd8W9U0yQIxnUkcKlzicB2iOeZ42NPpVXTOnigjoc2UoI4eVYlDjEZjccZAIn0wHUguircvBt0eyLD7cuNf2mTozJ21BG0NpMdYbG1n/aVchnJBr6rldb2X3kVmQCj50LQKm+aINbK1qSs56VIUwNepPiul+HPMV6sL5sQ5M0=" 10 | - ASAN_OPTIONS='halt_on_error=1' 11 | - UBSAN_OPTIONS='halt_on_error=1' 12 | 13 | before_install: 14 | - echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca- 15 | 16 | addons: 17 | apt: 18 | packages: 19 | - gcc binutils intltool 20 | # Coverity scan add-on, fires only when pushing to the `coverity_scan` branch 21 | coverity_scan: 22 | project: 23 | name: "scanmem/scanmem" 24 | description: "Build submitted via Travis CI" 25 | notification_email: andreastacchiotti@gmail.com 26 | build_command_prepend: "./autogen.sh && ./configure --enable-gui" 27 | build_command: "--fs-capture-search ./gui/ make" 28 | branch_pattern: coverity_scan 29 | 30 | script: 31 | - if [ "${COVERITY_SCAN_BRANCH}" == 1 ]; then exit ; fi 32 | - ./autogen.sh && ./configure --enable-gui 33 | - make CFLAGS='-O2 -fsanitize=address,undefined' 34 | # Testing requires `sudo` for `ptrace()` 35 | - sudo make check VERBOSE=1 36 | -------------------------------------------------------------------------------- /backend/scanmem/gui/org.scanmem.gameconqueror.metainfo.xml.in: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | org.scanmem.gameconqueror 5 | CC0-1.0 6 | GPL-3.0+ 7 | <_name>Game Conqueror 8 | <_summary>Game hacking tool. GUI front-end for scanmem. 9 | 10 | <_p> 11 | scanmem is a simple interactive debugging utility, used to locate the address 12 | of a variable in an executing process. This can be used for the analysis or 13 | modification of a hostile process on a compromised machine, reverse 14 | engineering, or as a "pokefinder" to cheat at video games. 15 | GameConqueror aims to provide a CheatEngine-alike interface for scanmem, 16 | it's user-friendly and easy to use. 17 | 18 | <_p>Features: 19 |
    20 | <_li>Locking on multiple variables 21 | <_li>Memory Viewer/Editor 22 | <_li>... many to be done 23 |
24 |
25 | 26 | 27 | 28 | https://raw.githubusercontent.com/scanmem/scanmem/master/gui/screenshot.png 29 | 30 | 31 | 32 | https://github.com/scanmem/scanmem 33 | i.gnatenko.brain@gmail.com 34 |
35 | -------------------------------------------------------------------------------- /backend/scanmem/interrupt.c: -------------------------------------------------------------------------------- 1 | /* 2 | Interrupt handling. 3 | 4 | This file is part of libscanmem. 5 | 6 | This library is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published 8 | by the Free Software Foundation; either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This library is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with this library. If not, see . 18 | */ 19 | 20 | #ifndef _GNU_SOURCE 21 | # define _GNU_SOURCE /* for sighandler_t */ 22 | #endif 23 | 24 | #include 25 | #include 26 | 27 | #include "scanmem.h" 28 | #include "interrupt.h" 29 | 30 | sigjmp_buf jmpbuf; /* used when aborting a command due to an interrupt */ 31 | sighandler_t oldsig; /* reinstalled before longjmp */ 32 | unsigned intr_used; 33 | 34 | /* signal handler used to handle an interrupt during commands */ 35 | void interrupted(int n) 36 | { 37 | (void) n; 38 | siglongjmp(jmpbuf, 1); 39 | } 40 | 41 | /* signal handler used to handle an interrupt during scans */ 42 | void interrupt_scan(int n) 43 | { 44 | (void) n; 45 | sm_set_stop_flag(true); 46 | } 47 | -------------------------------------------------------------------------------- /backend/scanmem/Makefile.am: -------------------------------------------------------------------------------- 1 | ACLOCAL_AMFLAGS = -I m4 2 | 3 | SUBDIRS = test 4 | 5 | if ENABLE_GUI 6 | SUBDIRS += po gui 7 | endif 8 | 9 | # '-O2 -g' are added by `configure`, unless the user overrides CFLAGS 10 | AM_CFLAGS = -std=gnu99 -Wall 11 | 12 | # Utilities library, statically linked in sm and libsm 13 | noinst_LTLIBRARIES = libutil.la 14 | libutil_la_SOURCES = common.h \ 15 | common.c \ 16 | show_message.c 17 | 18 | # libscanmem 19 | lib_LTLIBRARIES = libscanmem.la 20 | 21 | libscanmem_la_includedir = $(includedir)/scanmem 22 | 23 | libscanmem_la_include_HEADERS = commands.h \ 24 | list.h \ 25 | maps.h \ 26 | scanmem.h \ 27 | scanroutines.h \ 28 | show_message.h \ 29 | targetmem.h \ 30 | value.h 31 | 32 | libscanmem_la_SOURCES = commands.c \ 33 | ptrace.c \ 34 | handlers.h \ 35 | handlers.c \ 36 | interrupt.h \ 37 | interrupt.c \ 38 | licence.h \ 39 | maps.c \ 40 | scanmem.c \ 41 | scanroutines.c \ 42 | sets.h \ 43 | sets.c \ 44 | targetmem.c \ 45 | value.c 46 | 47 | if !HAVE_GETLINE 48 | libscanmem_la_SOURCES += getline.h \ 49 | getline.c 50 | endif 51 | 52 | libscanmem_la_LIBADD = libutil.la 53 | 54 | libscanmem_la_LDFLAGS = -version-info 1:0:0 \ 55 | -export-symbols-regex '^sm_' 56 | 57 | # scanmem CLI 58 | bin_PROGRAMS = scanmem 59 | 60 | scanmem_SOURCES = menu.h \ 61 | menu.c \ 62 | main.c 63 | 64 | if !WITH_READLINE 65 | scanmem_SOURCES += readline.h \ 66 | readline.c 67 | endif 68 | 69 | scanmem_LDADD = libutil.la libscanmem.la 70 | 71 | # Misc 72 | dist_man_MANS = scanmem.1 73 | dist_doc_DATA = README 74 | 75 | EXTRA_DIST = gpl-3.0.txt lgpl-3.0.txt 76 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "memory-deck", 3 | "version": "0.1.8", 4 | "description": "A Decky plugin to find and edit memory values from the Quick Access Menu", 5 | "scripts": { 6 | "build": "shx rm -rf dist && rollup -c", 7 | "watch": "rollup -c -w", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/CameronRedmore/memory-deck.git" 13 | }, 14 | "keywords": [ 15 | "decky", 16 | "plugin", 17 | "plugin-template", 18 | "steam-deck", 19 | "deck" 20 | ], 21 | "author": "Camzie99 ", 22 | "contributors": [ 23 | "HeyItsWaters " 24 | ], 25 | "license": "GPL-3.0", 26 | "bugs": { 27 | "url": "https://github.com/CameronRedmore/memory-deck/issues" 28 | }, 29 | "homepage": "https://github.com/CameronRedmore/memory-deck#readme", 30 | "devDependencies": { 31 | "@rollup/plugin-commonjs": "^21.1.0", 32 | "@rollup/plugin-json": "^4.1.0", 33 | "@rollup/plugin-node-resolve": "^13.3.0", 34 | "@rollup/plugin-replace": "^4.0.0", 35 | "@rollup/plugin-typescript": "^8.5.0", 36 | "@types/react": "16.14.0", 37 | "@types/webpack": "^5.28.1", 38 | "decky-frontend-lib": "~3.24.1", 39 | "rollup": "^2.79.1", 40 | "rollup-plugin-import-assets": "^1.1.1", 41 | "shx": "^0.3.4", 42 | "tslib": "^2.5.0", 43 | "typescript": "^4.9.5" 44 | }, 45 | "dependencies": { 46 | "react-icons": "^4.8.0" 47 | }, 48 | "pnpm": { 49 | "peerDependencyRules": { 50 | "ignoreMissing": [ 51 | "react", 52 | "react-dom", 53 | "decky-frontend-lib" 54 | ] 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /backend/scanmem/readline.h: -------------------------------------------------------------------------------- 1 | /* 2 | Replace libreadline. 3 | 4 | Copyright (C) 2015 Jonathan Pelletier 5 | 6 | This file is part of libscanmem. 7 | 8 | This library is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU Lesser General Public License as published 10 | by the Free Software Foundation; either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | This library is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU Lesser General Public License for more details. 17 | 18 | You should have received a copy of the GNU Lesser General Public License 19 | along with this library. If not, see . 20 | */ 21 | 22 | #ifndef READLINE_H 23 | #define READLINE_H 24 | 25 | typedef char *rl_compentry_func_t(const char *, int); 26 | typedef char **rl_completion_func_t(const char *, int, int); 27 | 28 | static char *rl_line_buffer = ""; 29 | 30 | extern int rl_attempted_completion_over; 31 | extern const char *rl_readline_name; 32 | extern rl_completion_func_t *rl_attempted_completion_function; 33 | 34 | char **rl_completion_matches(const char *text, rl_compentry_func_t 35 | *entry_function); 36 | char *readline(const char *prompt); 37 | 38 | /* History, all doing nothing */ 39 | void add_history(const char *line); 40 | int read_history (const char *filename); 41 | int write_history (const char *filename); 42 | int history_truncate_file (const char *filename, int nlines); 43 | 44 | #endif /* READLINE_H */ 45 | -------------------------------------------------------------------------------- /backend/scanmem/commands.h: -------------------------------------------------------------------------------- 1 | /* 2 | Registration and general execution of commands. 3 | 4 | This file is part of libscanmem. 5 | 6 | This library is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published 8 | by the Free Software Foundation; either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This library is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with this library. If not, see . 18 | */ 19 | 20 | #ifndef COMMANDS_H 21 | #define COMMANDS_H 22 | 23 | #include 24 | 25 | #include "scanmem.h" 26 | #include "list.h" 27 | 28 | 29 | typedef bool (*handler_ptr)(globals_t *vars, char **argv, unsigned argc); 30 | 31 | typedef struct { 32 | char *word; 33 | list_t *list; 34 | unsigned index; 35 | } completion_t; 36 | 37 | typedef struct { 38 | handler_ptr handler; 39 | char *command; 40 | char *shortdoc; 41 | char *longdoc; 42 | list_t *completions; 43 | unsigned complidx; 44 | } command_t; 45 | 46 | 47 | bool sm_registercommand(const char *command, handler_ptr handler, list_t *commands, 48 | char *shortdoc, char *longdoc, const char *complstr); 49 | bool sm_execcommand(globals_t *vars, const char *commandline); 50 | 51 | void sm_free_all_completions(list_t *commands); 52 | 53 | #endif /* COMMANDS_H */ 54 | -------------------------------------------------------------------------------- /backend/scanmem/interrupt.h: -------------------------------------------------------------------------------- 1 | /* 2 | Interrupt handling. 3 | 4 | This file is part of libscanmem. 5 | 6 | This library is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published 8 | by the Free Software Foundation; either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This library is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with this library. If not, see . 18 | */ 19 | 20 | #ifndef INTERRUPT_H 21 | #define INTERRUPT_H 22 | 23 | #ifndef _GNU_SOURCE 24 | # define _GNU_SOURCE /* for sighandler_t */ 25 | #endif 26 | 27 | #include 28 | #include 29 | 30 | extern sigjmp_buf jmpbuf; /* used when aborting a command due to an interrupt */ 31 | extern sighandler_t oldsig; /* reinstalled before longjmp */ 32 | extern unsigned intr_used; 33 | 34 | /* signal handler used to handle an interrupt during commands */ 35 | void interrupted(int); 36 | 37 | /* signal handler used to handle an interrupt during scans */ 38 | void interrupt_scan(int); 39 | 40 | #define INTERRUPTABLE() ((oldsig = signal(SIGINT, interrupted)), intr_used = 1, sigsetjmp(jmpbuf, 1)) 41 | #define INTERRUPTABLESCAN() ((oldsig = signal(SIGINT, interrupt_scan)), intr_used = 1) 42 | #define ENDINTERRUPTABLE() (intr_used ? ((void) signal(SIGINT, oldsig), intr_used = 0) : (intr_used = 0)) 43 | 44 | #endif /* INTERRUPT_H */ 45 | -------------------------------------------------------------------------------- /backend/scanmem/test/memfake.c: -------------------------------------------------------------------------------- 1 | /* 2 | Provide a simple program to run test scans on 3 | 4 | Copyright (C) 2017 Andrea Stacchiotti 5 | 6 | This library is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published 8 | by the Free Software Foundation; either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This library is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with this library. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | int main(int argc, char **argv) 28 | { 29 | size_t MB_to_allocate = 1; 30 | bool add_randomness = false; 31 | 32 | if (argc >= 2) MB_to_allocate = strtoul(argv[1], NULL, 10); 33 | if (argc >= 3) add_randomness = strtoul(argv[2], NULL, 10); 34 | if (argc >= 4) return 1; 35 | 36 | size_t array_size = MB_to_allocate * 1024 * 1024 / sizeof(int); 37 | 38 | int* array = calloc(array_size, sizeof(int)); 39 | assert(array != NULL); 40 | 41 | // Fill half with random values and leave an half of zeroes, if asked to 42 | if (add_randomness) { 43 | srand(time(NULL)); 44 | for (size_t i = 0; i < array_size/2; i++) { 45 | array[i] = rand(); 46 | } 47 | } 48 | 49 | pause(); 50 | 51 | free(array); 52 | return 0; 53 | } 54 | 55 | -------------------------------------------------------------------------------- /backend/scanmem/sets.h: -------------------------------------------------------------------------------- 1 | /* 2 | Create sets. 3 | 4 | Copyright (C) 2016-2017 Bijan Kazemi 5 | 6 | This file is part of libscanmem. 7 | 8 | This library is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU Lesser General Public License as published 10 | by the Free Software Foundation; either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | This library is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU Lesser General Public License for more details. 17 | 18 | You should have received a copy of the GNU Lesser General Public License 19 | along with this library. If not, see . 20 | */ 21 | 22 | #ifndef SETS_H 23 | #define SETS_H 24 | 25 | #include 26 | 27 | struct set { 28 | size_t size; /* size of set (used) */ 29 | size_t *buf; /* value buffer */ 30 | }; 31 | 32 | static inline void set_cleanup(struct set *set) 33 | { 34 | if (set) 35 | free(set->buf); 36 | } 37 | 38 | /* iterate over set from front (forwards) */ 39 | #define foreach_set_fw(i, set) \ 40 | for (size_t i = 0; i < (set)->size; i++) 41 | 42 | /* 43 | * iterate over set from back (backwards) 44 | * 45 | * NOTE: `_reserved_for_set_iteration_zzaw2_df_` is reserved, named randomly to avoid namespace crash 46 | */ 47 | #define foreach_set_bw(i, set) \ 48 | for (size_t _reserved_for_set_iteration_zzaw2_df_ = 0, i = (set)->size-1; \ 49 | _reserved_for_set_iteration_zzaw2_df_ < (set)->size; \ 50 | _reserved_for_set_iteration_zzaw2_df_++, i--) 51 | 52 | bool parse_uintset(const char *, struct set *, size_t); 53 | #endif /* SETS_H */ 54 | -------------------------------------------------------------------------------- /backend/scanmem/readline.c: -------------------------------------------------------------------------------- 1 | /* 2 | Replace libreadline. 3 | 4 | Copyright (C) 2015 Jonathan Pelletier 5 | 6 | This file is part of libscanmem. 7 | 8 | This library is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU Lesser General Public License as published 10 | by the Free Software Foundation; either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | This library is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU Lesser General Public License for more details. 17 | 18 | You should have received a copy of the GNU Lesser General Public License 19 | along with this library. If not, see . 20 | */ 21 | 22 | #include 23 | 24 | #include "readline.h" 25 | #include "getline.h" 26 | 27 | int rl_attempted_completion_over = 0; 28 | const char *rl_readline_name = "scanmem"; 29 | rl_completion_func_t *rl_attempted_completion_function = NULL; 30 | 31 | /* always return NULL to show that there are no completions */ 32 | char **rl_completion_matches(const char *text, rl_compentry_func_t 33 | *entry_function) 34 | { 35 | return NULL; 36 | } 37 | 38 | /* show the prompt, then allocate, read and 39 | return a line with getline() */ 40 | char *readline(const char *prompt) 41 | { 42 | char *line = NULL; 43 | size_t n = 0; 44 | ssize_t bytes_read; 45 | 46 | printf("%s", prompt); 47 | fflush(stdout); 48 | bytes_read = getline(&line, &n, stdin); 49 | if (bytes_read > 0) 50 | line[bytes_read - 1] = '\0'; /* remove the trailing newline */ 51 | 52 | return line; 53 | } 54 | 55 | /* don't maintain a history */ 56 | void add_history(const char *line) {} 57 | int read_history (const char *filename) { return 0; } 58 | int write_history (const char *filename) { return 0; } 59 | int history_truncate_file (const char *filename, int nlines) { return 0; } 60 | -------------------------------------------------------------------------------- /backend/scanmem/README: -------------------------------------------------------------------------------- 1 | # ![](https://raw.githubusercontent.com/scanmem/scanmem/master/gui/GameConqueror_72x72.png)scanmem & GameConqueror 2 | 3 | [![Build Status](https://travis-ci.org/scanmem/scanmem.svg?branch=master)](https://travis-ci.org/scanmem/scanmem) 4 | [![Coverity Status](https://scan.coverity.com/projects/8565/badge.svg?flat=1")](https://scan.coverity.com/projects/scanmem) 5 | 6 | scanmem is a debugging utility designed to isolate the address of an arbitrary 7 | variable in an executing process. scanmem simply needs to be told the pid of 8 | the process and the value of the variable at several different times. 9 | 10 | After several scans of the process, scanmem isolates the position of the 11 | variable and allows you to modify its value. 12 | 13 | ## GUI 14 | 15 | GameConqueror is a GUI front-end for scanmem, providing more features, such as: 16 | * Flexible syntax for searching 17 | * Easier and multiple variable locking 18 | * Better process finder 19 | * Memory browser/editor 20 | 21 | See [gui/README](gui/README) for more details. 22 | 23 | ## Requirements 24 | 25 | scanmem requires libreadline to read commands interactively, and `/proc` must be 26 | mounted. GameConqueror requirements are documented in [gui/README](gui/README). 27 | 28 | ## Documentation 29 | 30 | To read documentation: 31 | * `man scanmem` 32 | * `man gameconqueror` 33 | * `scanmem --help` 34 | * enter `help` at the scanmem prompt 35 | * use the interactive help of GameConqueror 36 | 37 | ## Build Requirements 38 | 39 | The build requires autotools-dev, libtool, libreadline-dev, intltool, and python. 40 | 41 | ## Build and Install 42 | 43 | To generate files required for the build: 44 | 45 | ./autogen.sh 46 | 47 | To build with GUI: 48 | 49 | ./configure --prefix=/usr --enable-gui && make 50 | sudo make install 51 | 52 | To build without GUI: 53 | 54 | ./configure --prefix=/usr && make 55 | sudo make install 56 | 57 | scanmem and GameConqueror use static paths to libscanmem. So executing 58 | `ldconfig` is not required. Consider setting `--libdir=/usr/lib/scanmem` or 59 | `--libdir=/usr/lib64/scanmem` to avoid that libscanmem is in a library 60 | search path. 61 | 62 | Run `./configure --help` for more details. 63 | 64 | ## Android Build 65 | 66 | You need a 67 | [standalone toolchain of Android NDK](https://developer.android.com/ndk/guides/standalone_toolchain.html#itc) 68 | (Advanced method) to build interactive capabilities for Android. 69 | For more information, run: 70 | 71 | ./build_for_android.sh help 72 | 73 | ## License: 74 | 75 | GPLv3, LGPLv3 for libscanmem 76 | -------------------------------------------------------------------------------- /backend/scanmem/getline.c: -------------------------------------------------------------------------------- 1 | /* 2 | Replace getline() with BSD specific fgetln(). 3 | 4 | Copyright (C) 2015 JianXiong Zhou 5 | Copyright (C) 2015 Jonathan Pelletier 6 | 7 | This file is part of libscanmem. 8 | 9 | This library is free software: you can redistribute it and/or modify 10 | it under the terms of the GNU Lesser General Public License as published 11 | by the Free Software Foundation; either version 3 of the License, or 12 | (at your option) any later version. 13 | 14 | This library is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | GNU Lesser General Public License for more details. 18 | 19 | You should have received a copy of the GNU Lesser General Public License 20 | along with this library. If not, see . 21 | */ 22 | 23 | #include "getline.h" 24 | 25 | #if !defined(HAVE_GETLINE) && defined(HAVE_FGETLN) 26 | #include 27 | #include 28 | #include 29 | 30 | /* 31 | * Avoid frequent malloc()/free() calls 32 | * (determined by getline() test on Linux) 33 | */ 34 | #define BUF_MIN 120 35 | 36 | ssize_t getline(char **lineptr, size_t *n, FILE *stream) 37 | { 38 | char *lptr; 39 | size_t len = 0; 40 | 41 | /* check for invalid arguments */ 42 | if (lineptr == NULL || n == NULL) { 43 | errno = EINVAL; 44 | return -1; 45 | } 46 | 47 | lptr = fgetln(stream, &len); 48 | if (lptr == NULL) { 49 | /* invalid stream */ 50 | errno = EINVAL; 51 | return -1; 52 | } 53 | 54 | /* 55 | * getline() returns a null byte ('\0') terminated C string, 56 | * but fgetln() returns characters without '\0' termination 57 | */ 58 | if (*lineptr == NULL) { 59 | *n = BUF_MIN; 60 | goto alloc_buf; 61 | } 62 | 63 | /* realloc the original pointer */ 64 | if (*n < len + 1) { 65 | free(*lineptr); 66 | 67 | *n = len + 1; 68 | alloc_buf: 69 | *lineptr = malloc(*n); 70 | if (*lineptr == NULL) { 71 | *n = 0; 72 | return -1; 73 | } 74 | } 75 | 76 | /* copy over the string */ 77 | memcpy(*lineptr, lptr, len); 78 | (*lineptr)[len] = '\0'; 79 | 80 | /* 81 | * getline() and fgetln() both return len including the 82 | * delimiter but without the null byte at the end 83 | */ 84 | return len; 85 | } 86 | 87 | #endif 88 | -------------------------------------------------------------------------------- /backend/scanmem/README.md: -------------------------------------------------------------------------------- 1 | # ![](https://raw.githubusercontent.com/scanmem/scanmem/master/gui/GameConqueror_72x72.png)scanmem & GameConqueror 2 | 3 | [![Build Status](https://travis-ci.org/scanmem/scanmem.svg?branch=master)](https://travis-ci.org/scanmem/scanmem) 4 | [![Coverity Status](https://scan.coverity.com/projects/8565/badge.svg?flat=1")](https://scan.coverity.com/projects/scanmem) 5 | 6 | scanmem is a debugging utility designed to isolate the address of an arbitrary 7 | variable in an executing process. scanmem simply needs to be told the pid of 8 | the process and the value of the variable at several different times. 9 | 10 | After several scans of the process, scanmem isolates the position of the 11 | variable and allows you to modify its value. 12 | 13 | ## GUI 14 | 15 | GameConqueror is a GUI front-end for scanmem, providing more features, such as: 16 | * Flexible syntax for searching 17 | * Easier and multiple variable locking 18 | * Better process finder 19 | * Memory browser/editor 20 | 21 | See [gui/README](gui/README) for more details. 22 | 23 | ## Requirements 24 | 25 | scanmem requires libreadline to read commands interactively, and `/proc` must be 26 | mounted. GameConqueror requirements are documented in [gui/README](gui/README). 27 | 28 | ## Documentation 29 | 30 | To read documentation: 31 | * `man scanmem` 32 | * `man gameconqueror` 33 | * `scanmem --help` 34 | * enter `help` at the scanmem prompt 35 | * use the interactive help of GameConqueror 36 | 37 | ## Build Requirements 38 | 39 | The build requires autotools-dev, libtool, libreadline-dev, intltool, and python. 40 | 41 | ## Build and Install 42 | 43 | To generate files required for the build: 44 | 45 | ./autogen.sh 46 | 47 | To build with GUI: 48 | 49 | ./configure --prefix=/usr --enable-gui && make 50 | sudo make install 51 | 52 | To build without GUI: 53 | 54 | ./configure --prefix=/usr && make 55 | sudo make install 56 | 57 | scanmem and GameConqueror use static paths to libscanmem. So executing 58 | `ldconfig` is not required. Consider setting `--libdir=/usr/lib/scanmem` or 59 | `--libdir=/usr/lib64/scanmem` to avoid that libscanmem is in a library 60 | search path. 61 | 62 | Run `./configure --help` for more details. 63 | 64 | ## Android Build 65 | 66 | You need a 67 | [standalone toolchain of Android NDK](https://developer.android.com/ndk/guides/standalone_toolchain.html#itc) 68 | (Advanced method) to build interactive capabilities for Android. 69 | For more information, run: 70 | 71 | ./build_for_android.sh help 72 | 73 | ## License: 74 | 75 | GPLv3, LGPLv3 for libscanmem 76 | -------------------------------------------------------------------------------- /backend/scanmem/show_message.h: -------------------------------------------------------------------------------- 1 | /* 2 | Message printing helper functions. 3 | 4 | Copyright (C) 2010 WANG Lu 5 | 6 | This file is part of libscanmem. 7 | 8 | This library is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU Lesser General Public License as published 10 | by the Free Software Foundation; either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | This library is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU Lesser General Public License for more details. 17 | 18 | You should have received a copy of the GNU Lesser General Public License 19 | along with this library. If not, see . 20 | */ 21 | 22 | /* 23 | * This file declares all types of output functions, in order to provide well-formatted messages that a front-end can understand. 24 | * 25 | * Basically, all data goes through stdout, and all messages (to user or front-end) go through stderr. 26 | * 27 | * In stderr: 28 | * all messages prefixed with 'error:' will be considered a fatal error; front-end may notify user to restart backend 29 | * all messages prefixed with 'warn:' will be considered a nonfatal error. 30 | * all messages prefixed with 'info:' will be ignored (by the front-end) 31 | * 32 | * To display messages to user only, use show_user; nothing will be prepended, and the message will be ignored if scanmem is running as a backend. 33 | */ 34 | 35 | #ifndef SHOW_MESSAGE_H 36 | #define SHOW_MESSAGE_H 37 | 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | /* prepend 'info: ', output to stderr */ 44 | void show_info(const char *fmt, ...); 45 | /* prepend 'error: ', output to stderr */ 46 | void show_error(const char *fmt, ...); 47 | /* prepend 'warn: ', output to stderr */ 48 | void show_warn(const char *fmt, ...); 49 | 50 | /* display message only when in debug mode */ 51 | void show_debug(const char *fmt, ...); 52 | 53 | /* display message only when not running as a backend */ 54 | void show_user(const char *fmt, ...); 55 | 56 | /* pager support routines */ 57 | FILE *get_pager(FILE *fallback_output); 58 | 59 | static inline void close_pager(FILE *pager) 60 | { 61 | if (pager != NULL && pager != stdout && pager != stderr) { 62 | if (pclose(pager) == -1 && errno != EPIPE) 63 | show_warn("pclose() error: %s\n", strerror(errno)); 64 | signal(SIGPIPE, SIG_DFL); 65 | } 66 | } 67 | 68 | #endif /* SHOW_MESSAGE_H */ 69 | -------------------------------------------------------------------------------- /backend/scanmem/maps.h: -------------------------------------------------------------------------------- 1 | /* 2 | Reading the data from /proc/pid/maps into a regions list. 3 | 4 | Copyright (C) 2006,2007,2009 Tavis Ormandy 5 | Copyright (C) 2009 Eli Dupree 6 | Copyright (C) 2009,2010 WANG Lu 7 | Copyright (C) 2014-2016 Sebastian Parschauer 8 | 9 | This file is part of libscanmem. 10 | 11 | This library is free software: you can redistribute it and/or modify 12 | it under the terms of the GNU Lesser General Public License as published 13 | by the Free Software Foundation; either version 3 of the License, or 14 | (at your option) any later version. 15 | 16 | This library is distributed in the hope that it will be useful, 17 | but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | GNU Lesser General Public License for more details. 20 | 21 | You should have received a copy of the GNU Lesser General Public License 22 | along with this library. If not, see . 23 | */ 24 | 25 | #ifndef MAPS_H 26 | #define MAPS_H 27 | 28 | #include 29 | #include 30 | 31 | #include "list.h" 32 | 33 | /* determine which regions we need */ 34 | typedef enum { 35 | REGION_ALL, /* All regions, including non-writable regions */ 36 | REGION_ALL_RW, /* each of them */ 37 | REGION_HEAP_STACK_EXECUTABLE, /* heap, stack, executable */ 38 | REGION_HEAP_STACK_EXECUTABLE_BSS /* heap, stack, executable, bss */ 39 | } region_scan_level_t; 40 | 41 | typedef enum { 42 | REGION_TYPE_MISC, 43 | REGION_TYPE_CODE, 44 | REGION_TYPE_EXE, 45 | REGION_TYPE_HEAP, 46 | REGION_TYPE_STACK 47 | } region_type_t; 48 | 49 | #define REGION_TYPE_NAMES { "misc", "code", "exe", "heap", "stack" } 50 | extern const char *region_type_names[]; 51 | 52 | /* a region obtained from /proc/pid/maps, these are searched for matches */ 53 | typedef struct { 54 | void *start; /* Start address. Hack: If HAVE_PROCMEM is defined, this is actually an (unsigned long) offset into /proc/{pid}/mem */ 55 | unsigned long size; /* size */ 56 | region_type_t type; 57 | unsigned long load_addr; /* e.g. load address of the executable */ 58 | struct __attribute__((packed)) { 59 | unsigned read:1; 60 | unsigned write:1; 61 | unsigned exec:1; 62 | unsigned shared:1; 63 | unsigned private:1; 64 | } flags; 65 | unsigned id; /* unique identifier */ 66 | char filename[1]; /* associated file, must be last */ 67 | } region_t; 68 | 69 | bool sm_readmaps(pid_t target, list_t *regions, region_scan_level_t region_scan_level); 70 | 71 | #endif /* MAPS_H */ 72 | -------------------------------------------------------------------------------- /backend/scanmem/common.c: -------------------------------------------------------------------------------- 1 | /* 2 | Common helper and utility functions 3 | 4 | Copyright (C) 2018 Sebastian Parschauer 5 | 6 | This file is part of libscanmem. 7 | 8 | This library is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU Lesser General Public License as published 10 | by the Free Software Foundation; either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | This library is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU Lesser General Public License for more details. 17 | 18 | You should have received a copy of the GNU Lesser General Public License 19 | along with this library. If not, see . 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "common.h" 28 | 29 | /* states returned by check_process() */ 30 | enum pstate { 31 | PROC_RUNNING, 32 | PROC_ERR, /* error during detection */ 33 | PROC_DEAD, 34 | PROC_ZOMBIE 35 | }; 36 | 37 | /* 38 | * We check if a process is running in /proc directly. 39 | * Also zombies are detected. 40 | * 41 | * Requirements: Linux kernel, mounted /proc 42 | * Assumption: (pid > 0) --> Please check your PID before! 43 | */ 44 | static enum pstate check_process(pid_t pid) 45 | { 46 | FILE *fp = NULL; 47 | char *line = NULL; 48 | size_t alloc_len = 0; 49 | char status = '\0'; 50 | char path_str[128] = "/proc/"; 51 | int pr_len, path_len = sizeof("/proc/") - 1; 52 | 53 | /* append $pid/status and check if file exists */ 54 | pr_len = sprintf((path_str + path_len), "%d/status", pid); 55 | if (pr_len <= 0) 56 | goto err; 57 | path_len += pr_len; 58 | 59 | fp = fopen(path_str, "r"); 60 | if (!fp) { 61 | if (errno != ENOENT) 62 | goto err; 63 | else 64 | return PROC_DEAD; 65 | } 66 | 67 | /* read process status */ 68 | while (getline(&line, &alloc_len, fp) != -1) { 69 | if (alloc_len <= sizeof("State:\t")) 70 | continue; 71 | if (strncmp(line, "State:\t", sizeof("State:\t") - 1) == 0) { 72 | status = line[sizeof("State:\t") - 1]; 73 | break; 74 | } 75 | } 76 | if (line) 77 | free(line); 78 | fclose(fp); 79 | 80 | if (status < 'A' || status > 'Z') 81 | goto err; 82 | 83 | /* zombies are not running - parent doesn't wait */ 84 | if (status == 'Z' || status == 'X') 85 | return PROC_ZOMBIE; 86 | return PROC_RUNNING; 87 | err: 88 | return PROC_ERR; 89 | } 90 | 91 | bool sm_process_is_dead(pid_t pid) 92 | { 93 | return (check_process(pid) != PROC_RUNNING); 94 | } 95 | -------------------------------------------------------------------------------- /backend/scanmem/common.h: -------------------------------------------------------------------------------- 1 | /* 2 | Common macros and helpers. 3 | 4 | Copyright (C) 2017 Andrea Stacchiotti 5 | 6 | This file is part of libscanmem. 7 | 8 | This library is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU Lesser General Public License as published 10 | by the Free Software Foundation; either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | This library is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU Lesser General Public License for more details. 17 | 18 | You should have received a copy of the GNU Lesser General Public License 19 | along with this library. If not, see . 20 | */ 21 | 22 | #ifndef COMMON_H 23 | #define COMMON_H 24 | 25 | #include 26 | #include 27 | 28 | #ifndef MIN 29 | # define MIN(a,b) ((a) < (b) ? (a) : (b)) 30 | #endif 31 | 32 | /* From `include/linux/compiler.h`, in the linux kernel: 33 | * Offers a simple interface to the expect builtin */ 34 | #ifdef __GNUC__ 35 | # define LIKELY(x) __builtin_expect(!!(x), 1) 36 | # define UNLIKELY(x) __builtin_expect(!!(x), 0) 37 | #else 38 | # define LIKELY(x) (x) 39 | # define UNLIKELY(x) (x) 40 | #endif 41 | 42 | /* from string.h in glibc for Android/BSD */ 43 | #ifndef strdupa 44 | # include 45 | # include 46 | # define strdupa(s) \ 47 | ({ \ 48 | const char *__old = (s); \ 49 | size_t __len = strlen(__old) + 1; \ 50 | char *__new = (char *) alloca(__len); \ 51 | (char *) memcpy(__new, __old, __len); \ 52 | }) 53 | #endif 54 | 55 | #ifndef strndupa 56 | # include 57 | # include 58 | # define strndupa(s, n) \ 59 | ({ \ 60 | const char *__old = (s); \ 61 | size_t __len = strnlen(__old, (n)); \ 62 | char *__new = (char *) alloca(__len + 1); \ 63 | __new[__len] = '\0'; \ 64 | (char *) memcpy(__new, __old, __len); \ 65 | }) 66 | #endif 67 | 68 | /* Use the best `getenv()` implementation we have */ 69 | #if HAVE_SECURE_GETENV 70 | # define util_getenv secure_getenv 71 | #else 72 | # define util_getenv getenv 73 | #endif 74 | 75 | /* Function declarations */ 76 | bool sm_process_is_dead(pid_t pid); 77 | 78 | #endif /* COMMON_H */ 79 | -------------------------------------------------------------------------------- /backend/scanmem/scanroutines.h: -------------------------------------------------------------------------------- 1 | /* 2 | Definition of routines of scanning for different data types. 3 | 4 | Copyright (C) 2009,2010 WANG Lu 5 | Copyright (C) 2015 Vyacheslav Shegai 6 | 7 | This file is part of libscanmem. 8 | 9 | This library is free software: you can redistribute it and/or modify 10 | it under the terms of the GNU Lesser General Public License as published 11 | by the Free Software Foundation; either version 3 of the License, or 12 | (at your option) any later version. 13 | 14 | This library is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | GNU Lesser General Public License for more details. 18 | 19 | You should have received a copy of the GNU Lesser General Public License 20 | along with this library. If not, see . 21 | */ 22 | 23 | #ifndef SCANROUTINES_H 24 | #define SCANROUTINES_H 25 | 26 | #include 27 | 28 | #include "value.h" 29 | 30 | typedef enum { 31 | ANYNUMBER, /* ANYINTEGER or ANYFLOAT */ 32 | ANYINTEGER, /* INTEGER of whatever width */ 33 | ANYFLOAT, /* FLOAT of whatever width */ 34 | INTEGER8, 35 | INTEGER16, 36 | INTEGER32, 37 | INTEGER64, 38 | FLOAT32, 39 | FLOAT64, 40 | BYTEARRAY, 41 | STRING 42 | } scan_data_type_t; 43 | 44 | typedef enum { 45 | MATCHANY, /* for snapshot */ 46 | /* following: compare with a given value */ 47 | MATCHEQUALTO, 48 | MATCHNOTEQUALTO, 49 | MATCHGREATERTHAN, 50 | MATCHLESSTHAN, 51 | MATCHRANGE, 52 | /* following: compare with the old value */ 53 | MATCHUPDATE, 54 | MATCHNOTCHANGED, 55 | MATCHCHANGED, 56 | MATCHINCREASED, 57 | MATCHDECREASED, 58 | /* following: compare with both given value and old value */ 59 | MATCHINCREASEDBY, 60 | MATCHDECREASEDBY 61 | } scan_match_type_t; 62 | 63 | 64 | /* Matches a memory area given by `memory_ptr` and `memlength` against `user_value` or `old_value` 65 | * (or both, depending on the matching type), stores the result into saveflags. 66 | * NOTE: saveflags must be set to 0, since only useful bits are set, but extra bits are not cleared! 67 | * Returns the number of bytes needed to store said match, 0 for not matched 68 | */ 69 | typedef unsigned int (*scan_routine_t)(const mem64_t *memory_ptr, size_t memlength, 70 | const value_t *old_value, const uservalue_t *user_value, match_flags *saveflags); 71 | extern scan_routine_t sm_scan_routine; 72 | 73 | /* 74 | * Choose the global scanroutine according to the given parameters, sm_scan_routine will be set. 75 | * Returns whether a proper routine has been found. 76 | */ 77 | bool sm_choose_scanroutine(scan_data_type_t dt, scan_match_type_t mt, const uservalue_t* uval, bool reverse_endianness); 78 | 79 | scan_routine_t sm_get_scanroutine(scan_data_type_t dt, scan_match_type_t mt, match_flags uflags, bool reverse_endianness); 80 | 81 | #endif /* SCANROUTINES_H */ 82 | -------------------------------------------------------------------------------- /backend/scanmem/licence.h: -------------------------------------------------------------------------------- 1 | /* 2 | Just some license texts. 3 | 4 | This file is part of libscanmem. 5 | 6 | This library is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published 8 | by the Free Software Foundation; either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This library is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with this library. If not, see . 18 | */ 19 | 20 | #ifndef LICENCE_H 21 | #define LICENCE_H 22 | 23 | #define SM_WARRANTY \ 24 | "15. Disclaimer of Warranty.\n\n" \ 25 | "THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\n" \ 26 | "APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\n" \ 27 | "HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\n" \ 28 | "OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\n" \ 29 | "THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n" \ 30 | "PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\n" \ 31 | "IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\n" \ 32 | "ALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n" \ 33 | "\n" \ 34 | "16. Limitation of Liability.\n\n" \ 35 | "IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\n" \ 36 | "WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\n" \ 37 | "THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\n" \ 38 | "GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\n" \ 39 | "USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\n" \ 40 | "DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\n" \ 41 | "PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\n" \ 42 | "EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\n" \ 43 | "SUCH DAMAGES.\n" 44 | 45 | 46 | #define SM_COPYING \ 47 | "This program is free software; you can redistribute it and/or modify\n" \ 48 | "it under the terms of the GNU General Public License as published by\n" \ 49 | "the Free Software Foundation; either version 3 of the License, or\n" \ 50 | "(at your option) any later version.\n" \ 51 | "\n" \ 52 | "This program is distributed in the hope that it will be useful,\n" \ 53 | "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" \ 54 | "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" \ 55 | "GNU General Public License for more details.\n" \ 56 | "\n" \ 57 | "You should have received a copy of the GNU General Public License\n" \ 58 | "along with this program; if not, see .\n" 59 | 60 | #endif /* LICENCE_H */ 61 | -------------------------------------------------------------------------------- /backend/scanmem/endianness.h: -------------------------------------------------------------------------------- 1 | /* 2 | Endianness conversion. 3 | 4 | Copyright (C) 2014 Hraban Luyat 5 | Copyright (C) 2015 Sebastian Parschauer 6 | 7 | This file is part of libscanmem. 8 | 9 | This library is free software: you can redistribute it and/or modify 10 | it under the terms of the GNU Lesser General Public License as published 11 | by the Free Software Foundation; either version 3 of the License, or 12 | (at your option) any later version. 13 | 14 | This library is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | GNU Lesser General Public License for more details. 18 | 19 | You should have received a copy of the GNU Lesser General Public License 20 | along with this library. If not, see . 21 | */ 22 | 23 | 24 | #ifndef ENDIANNESS_H 25 | #define ENDIANNESS_H 26 | 27 | #include "config.h" 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include "value.h" 35 | 36 | /* true if host is big endian */ 37 | #ifdef WORDS_BIGENDIAN 38 | static const bool big_endian = true; 39 | #else 40 | static const bool big_endian = false; 41 | #endif 42 | 43 | static inline uint8_t swap_bytes8(uint8_t i) 44 | { 45 | return i; 46 | } 47 | 48 | static inline uint16_t swap_bytes16(uint16_t i) 49 | { 50 | uint16_t res = i & 0xff; 51 | res <<= 8; 52 | res |= i >> 8; 53 | return res; 54 | } 55 | 56 | static inline uint32_t swap_bytes32(uint32_t i) 57 | { 58 | uint32_t res = swap_bytes16(i); 59 | res <<= 16; 60 | res |= swap_bytes16(i >> 16); 61 | return res; 62 | } 63 | 64 | static inline uint64_t swap_bytes64(uint64_t i) 65 | { 66 | uint64_t res = swap_bytes32(i); 67 | res <<= 32; 68 | res |= swap_bytes32(i >> 32); 69 | return res; 70 | } 71 | 72 | // swap endianness of 2, 4 or 8 byte word in-place. 73 | static inline void swap_bytes_var(void *p, size_t num) 74 | { 75 | switch (num) { 76 | case sizeof(uint16_t): ; // empty statement to cheat the compiler 77 | uint16_t i16 = swap_bytes16(*((uint16_t *)p)); 78 | memcpy(p, &i16, sizeof(uint16_t)); 79 | return; 80 | case sizeof(uint32_t): ; 81 | uint32_t i32 = swap_bytes32(*((uint32_t *)p)); 82 | memcpy(p, &i32, sizeof(uint32_t)); 83 | return; 84 | case sizeof(uint64_t): ; 85 | uint64_t i64 = swap_bytes64(*((uint64_t *)p)); 86 | memcpy(p, &i64, sizeof(uint64_t)); 87 | return; 88 | } 89 | assert(false); 90 | return; 91 | } 92 | 93 | static inline void fix_endianness(value_t *data_value, bool reverse_endianness) 94 | { 95 | if (!reverse_endianness) { 96 | return; 97 | } 98 | if (data_value->flags & flags_64b) { 99 | data_value->uint64_value = swap_bytes64(data_value->uint64_value); 100 | } else if (data_value->flags & flags_32b) { 101 | data_value->uint32_value = swap_bytes32(data_value->uint32_value); 102 | } else if (data_value->flags & flags_16b) { 103 | data_value->uint16_value = swap_bytes16(data_value->uint16_value); 104 | } 105 | return; 106 | } 107 | 108 | #endif /* ENDIANNESS_H */ 109 | -------------------------------------------------------------------------------- /backend/scanmem/scanmem.h: -------------------------------------------------------------------------------- 1 | /* 2 | Provide interfaces for front-ends. 3 | 4 | Copyright (C) 2006,2007,2009 Tavis Ormandy 5 | Copyright (C) 2009 Eli Dupree 6 | Copyright (C) 2009-2013 WANG Lu 7 | Copyright (C) 2016 Sebastian Parschauer 8 | 9 | This file is part of libscanmem. 10 | 11 | This library is free software: you can redistribute it and/or modify 12 | it under the terms of the GNU Lesser General Public License as published 13 | by the Free Software Foundation; either version 3 of the License, or 14 | (at your option) any later version. 15 | 16 | This library is distributed in the hope that it will be useful, 17 | but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | GNU Lesser General Public License for more details. 20 | 21 | You should have received a copy of the GNU Lesser General Public License 22 | along with this library. If not, see . 23 | */ 24 | 25 | #ifndef SCANMEM_H 26 | #define SCANMEM_H 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "scanroutines.h" 34 | #include "list.h" 35 | #include "maps.h" 36 | #include "value.h" 37 | #include "targetmem.h" 38 | 39 | 40 | /* global settings */ 41 | typedef struct { 42 | unsigned exit:1; 43 | pid_t target; 44 | matches_and_old_values_array *matches; 45 | unsigned long num_matches; 46 | double scan_progress; 47 | volatile bool stop_flag; 48 | list_t *regions; 49 | list_t *commands; /* command handlers */ 50 | const char *current_cmdline; /* the command being executed */ 51 | void (*printversion)(FILE *outfd); 52 | struct { 53 | unsigned short alignment; 54 | unsigned short debug; 55 | unsigned short backend; /* if 1, scanmem will work as a backend and 56 | output will be more machine-readable */ 57 | 58 | /* options that can be changed during runtime */ 59 | scan_data_type_t scan_data_type; 60 | region_scan_level_t region_scan_level; 61 | unsigned short dump_with_ascii; 62 | unsigned short reverse_endianness; 63 | unsigned short no_ptrace; 64 | } options; 65 | } globals_t; 66 | 67 | /* global settings */ 68 | extern globals_t sm_globals; 69 | 70 | bool sm_init(void); 71 | void sm_cleanup(void); 72 | void sm_printversion(FILE *outfd); 73 | void sm_set_backend(void); 74 | void sm_backend_exec_cmd(const char *commandline); 75 | unsigned long sm_get_num_matches(void); 76 | const char *sm_get_version(void); 77 | double sm_get_scan_progress(void); 78 | void sm_set_stop_flag(bool stop_flag); 79 | 80 | /* ptrace.c */ 81 | bool sm_detach(pid_t target); 82 | bool sm_setaddr(pid_t target, void *addr, const value_t *to); 83 | bool sm_checkmatches(globals_t *vars, scan_match_type_t match_type, 84 | const uservalue_t *uservalue); 85 | bool sm_searchregions(globals_t *vars, scan_match_type_t match_type, 86 | const uservalue_t *uservalue); 87 | bool sm_peekdata(const void *addr, uint16_t length, const mem64_t **result_ptr, size_t *memlength); 88 | bool sm_attach(pid_t target); 89 | bool sm_read_array(pid_t target, const void *addr, void *buf, size_t len); 90 | bool sm_write_array(pid_t target, void *addr, const void *data, size_t len); 91 | 92 | #endif /* SCANMEM_H */ 93 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | // OTHER 5 | { 6 | "label": "checkforsettings", 7 | "type": "shell", 8 | "group": "none", 9 | "detail": "Check that settings.json has been created", 10 | "command": "bash -c ${workspaceFolder}/.vscode/config.sh", 11 | "problemMatcher": [] 12 | }, 13 | // BUILD 14 | { 15 | "label": "pnpmsetup", 16 | "type": "shell", 17 | "group": "build", 18 | "detail": "Setup pnpm", 19 | "command": "pnpm i", 20 | "problemMatcher": [] 21 | }, 22 | { 23 | "label": "updatefrontendlib", 24 | "type": "shell", 25 | "group": "build", 26 | "detail": "Update deck-frontend-lib", 27 | "command": "pnpm update decky-frontend-lib --latest", 28 | "problemMatcher": [] 29 | }, 30 | { 31 | "label": "build", 32 | "type": "npm", 33 | "group": "build", 34 | "detail": "rollup -c", 35 | "script": "build", 36 | "path": "", 37 | "problemMatcher": [] 38 | }, 39 | { 40 | "label": "buildall", 41 | "group": "build", 42 | "detail": "Build decky-plugin-template", 43 | "dependsOrder": "sequence", 44 | "dependsOn": [ 45 | "pnpmsetup", 46 | "build" 47 | ], 48 | "problemMatcher": [] 49 | }, 50 | // DEPLOY 51 | { 52 | "label": "createfolders", 53 | "detail": "Create plugins folder in expected directory", 54 | "type": "shell", 55 | "group": "none", 56 | "dependsOn": [ 57 | "checkforsettings" 58 | ], 59 | "command": "ssh deck@${config:deckip} -p ${config:deckport} ${config:deckkey} 'mkdir -p ${config:deckdir}/homebrew/pluginloader && mkdir -p ${config:deckdir}/homebrew/plugins'", 60 | "problemMatcher": [] 61 | }, 62 | { 63 | "label": "deploy", 64 | "detail": "Deploy dev plugin to deck", 65 | "type": "shell", 66 | "group": "none", 67 | "dependsOn": [ 68 | "createfolders", 69 | "chmodfolders" 70 | ], 71 | "command": "rsync -azp --delete --chmod=D0755,F0755 --rsh='ssh -p ${config:deckport} ${config:deckkey}' --exclude='.git/' --exclude='.github/' --exclude='.vscode/' --exclude='node_modules/' --exclude='src/' --exclude='*.log' --exclude='.gitignore' . deck@${config:deckip}:${config:deckdir}/homebrew/plugins/${workspaceFolderBasename}", 72 | "problemMatcher": [] 73 | }, 74 | { 75 | "label": "chmodfolders", 76 | "detail": "chmods folders to prevent perms issues", 77 | "type": "shell", 78 | "group": "none", 79 | "command": "ssh deck@${config:deckip} -p ${config:deckport} ${config:deckkey} 'echo '${config:deckpass}' | sudo -S chmod -R ug+rw ${config:deckdir}/homebrew/'", 80 | "problemMatcher": [] 81 | }, 82 | { 83 | "label": "deployall", 84 | "dependsOrder": "sequence", 85 | "group": "none", 86 | "dependsOn": [ 87 | "deploy", 88 | "chmodfolders" 89 | ], 90 | "problemMatcher": [] 91 | }, 92 | // ALL-IN-ONE 93 | { 94 | "label": "allinone", 95 | "detail": "Build and deploy", 96 | "dependsOrder": "sequence", 97 | "group": "test", 98 | "dependsOn": [ 99 | "buildall", 100 | "deployall" 101 | ], 102 | "problemMatcher": [] 103 | } 104 | ] 105 | } 106 | -------------------------------------------------------------------------------- /backend/scanmem/list.h: -------------------------------------------------------------------------------- 1 | /* 2 | A very simple linked list implementation. 3 | 4 | Copyright (C) 2006,2007,2009 Tavis Ormandy 5 | 6 | This file is part of libscanmem. 7 | 8 | This library is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU Lesser General Public License as published 10 | by the Free Software Foundation; either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | This library is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU Lesser General Public License for more details. 17 | 18 | You should have received a copy of the GNU Lesser General Public License 19 | along with this library. If not, see . 20 | */ 21 | 22 | #ifndef LIST_H 23 | #define LIST_H 24 | 25 | #include 26 | 27 | typedef struct element { 28 | void *data; 29 | struct element *next; 30 | } element_t; 31 | 32 | typedef struct { 33 | size_t size; 34 | element_t *head; 35 | element_t *tail; 36 | } list_t; 37 | 38 | /* create a new list */ 39 | static inline list_t *l_init(void) 40 | { 41 | return calloc(1, sizeof(list_t)); 42 | } 43 | 44 | /* add a new element to the list */ 45 | static inline int l_append(list_t *list, element_t *element, void *data) 46 | { 47 | element_t *n = calloc(1, sizeof(element_t)); 48 | 49 | if (n == NULL) 50 | return -1; 51 | 52 | n->data = data; 53 | 54 | /* insert at head or tail */ 55 | if (element == NULL) { 56 | if (list->size == 0) { 57 | list->tail = n; 58 | } 59 | 60 | n->next = list->head; 61 | list->head = n; 62 | } else { 63 | 64 | /* insertion at the middle of a list */ 65 | if (element->next == NULL) { 66 | list->tail = n; 67 | } 68 | 69 | n->next = element->next; 70 | element->next = n; 71 | } 72 | 73 | list->size++; 74 | 75 | return 0; 76 | } 77 | 78 | /* remove the element at element->next */ 79 | static inline void l_remove(list_t *list, element_t *element, void **data) 80 | { 81 | element_t *o; 82 | 83 | /* remove from head */ 84 | if (element == NULL) { 85 | if (data) { 86 | *data = list->head->data; 87 | } 88 | 89 | o = list->head; 90 | 91 | list->head = o->next; 92 | 93 | if (list->size == 1) { 94 | list->tail = NULL; 95 | } 96 | } else { 97 | if (data) { 98 | *data = element->next->data; 99 | } 100 | 101 | o = element->next; 102 | 103 | 104 | if ((element->next = element->next->next) == NULL) { 105 | list->tail = element; 106 | } 107 | } 108 | 109 | if (data == NULL) 110 | free(o->data); 111 | 112 | free(o); 113 | 114 | list->size--; 115 | 116 | return; 117 | } 118 | 119 | /* remove the nth element from head */ 120 | static inline void l_remove_nth(list_t *list, size_t n, void **data) 121 | { 122 | element_t *np = list->head; 123 | 124 | /* traverse to correct element */ 125 | while (n--) { 126 | if ((np = np->next) == NULL) 127 | /* return */ abort(); 128 | } 129 | 130 | l_remove(list, np, data); 131 | } 132 | 133 | /* destroy the whole list */ 134 | static inline void l_destroy(list_t *list) 135 | { 136 | void *data; 137 | 138 | if (list == NULL) 139 | return; 140 | 141 | /* remove every element */ 142 | while (list->size) { 143 | l_remove(list, NULL, &data); 144 | free(data); 145 | } 146 | 147 | free(list); 148 | } 149 | 150 | /* concatenate list src with list dst */ 151 | static inline int l_concat(list_t *dst, list_t **src) 152 | { 153 | void *data; 154 | element_t *n; 155 | 156 | n = (*src)->head; 157 | 158 | while (n) { 159 | l_remove(*src, NULL, &data); 160 | if (l_append(dst, NULL, data) == -1) 161 | return -1; 162 | 163 | n = (*src)->head; 164 | } 165 | 166 | l_destroy(*src); 167 | 168 | *src = NULL; 169 | 170 | return 0; 171 | } 172 | 173 | #endif /* LIST_H */ 174 | -------------------------------------------------------------------------------- /backend/scanmem/gui/scanmem.py: -------------------------------------------------------------------------------- 1 | """ 2 | scanmem.py: python wrapper for libscanmem 3 | 4 | Copyright (C) 2010,2011,2013 Wang Lu 5 | Copyright (C) 2018 Sebastian Parschauer 6 | Copyright (C) 2020 Andrea Stacchiotti 7 | 8 | This program is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program. If not, see . 20 | """ 21 | 22 | import ctypes 23 | import os 24 | import re 25 | import sys 26 | import tempfile 27 | 28 | import misc 29 | 30 | class Scanmem(): 31 | """Wrapper for libscanmem.""" 32 | 33 | LIBRARY_FUNCS = { 34 | 'sm_init' : (ctypes.c_bool, ), 35 | 'sm_cleanup' : (None, ), 36 | 'sm_set_backend' : (None, ), 37 | 'sm_backend_exec_cmd' : (None, ctypes.c_char_p), 38 | 'sm_get_num_matches' : (ctypes.c_ulong, ), 39 | 'sm_get_version' : (ctypes.c_char_p, ), 40 | 'sm_get_scan_progress' : (ctypes.c_double, ), 41 | 'sm_set_stop_flag' : (None, ctypes.c_bool), 42 | 'sm_process_is_dead' : (ctypes.c_bool, ctypes.c_int32) 43 | } 44 | 45 | def __init__(self, libpath='libscanmem.so'): 46 | self._lib = ctypes.CDLL(libpath) 47 | self._init_lib_functions() 48 | self._lib.sm_set_backend() 49 | self._lib.sm_init() 50 | self.send_command('reset') 51 | self.version = misc.decode(self._lib.sm_get_version()) 52 | 53 | def _init_lib_functions(self): 54 | for k,v in Scanmem.LIBRARY_FUNCS.items(): 55 | f = getattr(self._lib, k) 56 | f.restype = v[0] 57 | f.argtypes = v[1:] 58 | 59 | def send_command(self, cmd, get_output = False): 60 | """ 61 | Execute command using libscanmem. 62 | This function is NOT thread safe, send only one command at a time. 63 | 64 | cmd: command to run 65 | get_output: if True, return in a string what libscanmem would print to stdout 66 | """ 67 | if get_output: 68 | with tempfile.TemporaryFile() as directed_file: 69 | backup_stdout_fileno = os.dup(sys.stdout.fileno()) 70 | os.dup2(directed_file.fileno(), sys.stdout.fileno()) 71 | 72 | self._lib.sm_backend_exec_cmd(ctypes.c_char_p(misc.encode(cmd))) 73 | 74 | os.dup2(backup_stdout_fileno, sys.stdout.fileno()) 75 | os.close(backup_stdout_fileno) 76 | directed_file.seek(0) 77 | return directed_file.read() 78 | else: 79 | self._lib.sm_backend_exec_cmd(ctypes.c_char_p(misc.encode(cmd))) 80 | 81 | def get_match_count(self): 82 | return self._lib.sm_get_num_matches() 83 | 84 | def get_scan_progress(self): 85 | return self._lib.sm_get_scan_progress() 86 | 87 | def set_stop_flag(self, stop_flag): 88 | """ 89 | Sets the flag to interrupt the current scan at the next opportunity 90 | """ 91 | self._lib.sm_set_stop_flag(stop_flag) 92 | 93 | def exit_cleanup(self): 94 | """ 95 | Frees resources allocated by libscanmem, should be called before disposing of this instance 96 | """ 97 | self._lib.sm_cleanup() 98 | 99 | def process_is_dead(self, pid): 100 | return self._lib.sm_process_is_dead(pid) 101 | 102 | def matches(self): 103 | """ 104 | Returns a generator of (match_id_str, addr_str, off_str, region_type, value, types_str) for each match, all strings. 105 | The function executes commands internally, it is NOT thread safe 106 | """ 107 | list_bytes = self.send_command('list', get_output=True) 108 | lines = filter(None, misc.decode(list_bytes).split('\n')) 109 | 110 | line_regex = re.compile(r'^\[ *(\d+)\] +([\da-f]+), +\d+ \+ +([\da-f]+), +(\w+), (.*), +\[([\w ]+)\]$') 111 | for line in lines: 112 | yield line_regex.match(line).groups() 113 | 114 | -------------------------------------------------------------------------------- /src/components/NumpadInput.tsx: -------------------------------------------------------------------------------- 1 | // Basic React component that renders a numeric input field 2 | 3 | import React, { useEffect, useState } from "react"; 4 | import { PanelSectionRow, gamepadDialogClasses, joinClassNames, DialogButton, Focusable } from "decky-frontend-lib"; 5 | 6 | import { playSound } from "../util/util"; 7 | 8 | const FieldWithSeparator = joinClassNames(gamepadDialogClasses.Field, gamepadDialogClasses.WithBottomSeparatorStandard); 9 | 10 | // NumericInputProps interface with value and onChange properties 11 | interface NumpadInputProps { 12 | value: string; 13 | onChange: (value: string) => void; 14 | label: string; 15 | } 16 | 17 | export const NumpadInput = (props: NumpadInputProps): JSX.Element => { 18 | const { label, value, onChange } = props; 19 | 20 | const [inputValue, setInputValue] = useState(value); 21 | 22 | const [active, setActive] = useState(true); 23 | 24 | useEffect(() => { 25 | if (active) { 26 | playSound("https://steamloopback.host/sounds/deck_ui_side_menu_fly_in.wav"); 27 | } 28 | else { 29 | playSound("https://steamloopback.host/sounds/deck_ui_side_menu_fly_out.wav"); 30 | } 31 | }, [active]); 32 | 33 | const enterDigit = (digit: string) => { 34 | //Ensure only one decimal point 35 | if (digit === "." && inputValue.includes(".")) { 36 | playSound("https://steamloopback.host/sounds/deck_ui_default_activation.wav"); 37 | return; 38 | } 39 | 40 | //Concat the digit to the current value 41 | let newValue = inputValue + digit; 42 | if (inputValue === "0") { 43 | if (digit === ".") { 44 | newValue = "0."; 45 | } 46 | else { 47 | newValue = digit; 48 | } 49 | } 50 | 51 | setInputValue(newValue); 52 | onChange(newValue); 53 | 54 | playSound("https://steamloopback.host/sounds/deck_ui_misc_10.wav"); 55 | } 56 | 57 | const backspace = () => { 58 | playSound("https://steamloopback.host/sounds/deck_ui_misc_10.wav"); 59 | if (inputValue.length > 1) { 60 | //Remove the last digit from the current value 61 | const newValue = inputValue.slice(0, -1); 62 | setInputValue(newValue); 63 | onChange(newValue); 64 | } 65 | else { 66 | //Clear the current value 67 | setInputValue("0"); 68 | onChange("0"); 69 | } 70 | } 71 | 72 | return ( 73 | 74 | 75 |
76 |
setActive(!active)} 79 | > 80 |
84 | {label} 85 |
86 |
90 | {inputValue} 91 |
92 |
93 |
94 |
95 | 96 | {/* If active */} 97 | {active && ( 98 | 99 | {/* Override min-width for DialogButtons */} 100 | 105 | 106 | {/* 3x4 Digit Grid */} 107 | 108 | enterDigit("7")}>7 109 | enterDigit("8")}>8 110 | enterDigit("9")}>9 111 | 112 | enterDigit("4")}>4 113 | enterDigit("5")}>5 114 | enterDigit("6")}>6 115 | 116 | enterDigit("1")}>1 117 | enterDigit("2")}>2 118 | enterDigit("3")}>3 119 | 120 | backspace()}>← 121 | enterDigit("0")}>0 122 | enterDigit(".")}>. 123 | 124 | 125 | )} 126 |
127 | ); 128 | } -------------------------------------------------------------------------------- /backend/scanmem/configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT([scanmem],[0.18~dev],[https://github.com/scanmem/scanmem]) 2 | AC_CONFIG_MACRO_DIR([m4]) 3 | AM_INIT_AUTOMAKE([-Wall -Werror -Wno-extra-portability foreign]) 4 | AC_CONFIG_SRCDIR([main.c]) 5 | AC_CONFIG_HEADER([config.h]) 6 | AC_USE_SYSTEM_EXTENSIONS 7 | AC_HEADER_STDBOOL 8 | 9 | LT_INIT 10 | 11 | IT_PROG_INTLTOOL 12 | AM_PROG_CC_C_O 13 | 14 | AC_CHECK_FUNCS(getline secure_getenv) 15 | 16 | if test "x$ac_cv_func_getline" = "xno"; then 17 | AC_CHECK_FUNCS(fgetln) 18 | if test "x$ac_cv_func_fgetln" = "xno"; then 19 | AC_MSG_ERROR([Cannot build without working getline().]) 20 | else 21 | AC_MSG_NOTICE([Using the fgetln()-based getline() replacement.]) 22 | fi 23 | fi 24 | AM_CONDITIONAL([HAVE_GETLINE], [test "x$ac_cv_func_getline" != "xno"]) 25 | 26 | AC_CHECK_HEADERS(fcntl.h limits.h stddef.h sys/ioctl.h sys/time.h) 27 | 28 | AC_FUNC_ALLOCA 29 | AC_FUNC_STRTOD 30 | 31 | AC_TYPE_INT8_T 32 | AC_TYPE_INT16_T 33 | AC_TYPE_INT32_T 34 | AC_TYPE_INT64_T 35 | AC_TYPE_UINT8_T 36 | AC_TYPE_UINT16_T 37 | AC_TYPE_UINT32_T 38 | AC_TYPE_UINT64_T 39 | AC_TYPE_OFF_T 40 | AC_TYPE_PID_T 41 | AC_TYPE_SIZE_T 42 | AC_TYPE_SSIZE_T 43 | 44 | AC_C_BIGENDIAN 45 | 46 | # Detect the host OS 47 | android="no" 48 | AC_CANONICAL_HOST 49 | case "$host_os" in 50 | *android*) 51 | android="yes" 52 | AC_MSG_NOTICE([Android detected]) 53 | ;; 54 | linux*) 55 | AC_MSG_NOTICE([Linux detected]) 56 | ;; 57 | *) 58 | AC_MSG_NOTICE([Your platform is not currently supported]) 59 | ;; 60 | esac 61 | 62 | # Allows disabling procmem support 63 | AC_ARG_ENABLE(procmem, [AS_HELP_STRING([--disable-procmem], 64 | [forcefully disable proc/pid/mem support])]) 65 | AS_IF([test "x$enable_procmem" = "xno"], [ 66 | AC_DEFINE(HAVE_PROCMEM, [0], [Enable /proc/pid/mem support]) 67 | ]) 68 | 69 | AS_IF([test "x$android" = "xno"], [ 70 | AS_IF([test "x$enable_procmem" != "xno"], [ 71 | # also need to check if the file is zero'ed (some hardened systems) 72 | AC_CHECK_FILE([/proc/self/maps], [], [ 73 | echo "This system does not seem to have /proc/pid/maps files." 74 | exit 1 75 | ]) 76 | 77 | # also need to check this file works 78 | AC_CHECK_FILE([/proc/self/mem], [ 79 | # LARGEFILE support required for this to work 80 | AC_SYS_LARGEFILE 81 | AC_DEFINE(HAVE_PROCMEM, [1], [Enable /proc/pid/mem support]) 82 | ],[ 83 | # This will hurt performance. 84 | echo "This system does not seem to have /proc/pid/mem files." 85 | echo "Falling back to ptrace() only support." 86 | AC_DEFINE(HAVE_PROCMEM, [0], [Enable /proc/pid/mem support]) 87 | ]) 88 | ]) 89 | # malloc optimizations without Android 90 | AC_FUNC_MALLOC 91 | AC_FUNC_REALLOC 92 | ], [ 93 | # supported on Android 94 | AC_SYS_LARGEFILE 95 | # /proc/pid/mem is there but reading interesting data fails 96 | AC_DEFINE(HAVE_PROCMEM, [0], [Enable /proc/pid/mem support]) 97 | ]) 98 | 99 | 100 | # Check for termcap and readline or bypass checking for the libraries. 101 | AC_ARG_WITH([readline], [AS_HELP_STRING([--without-readline], 102 | [build without readline])]) 103 | AM_CONDITIONAL([WITH_READLINE], [test "x$with_readline" != "xno"]) 104 | AS_IF([test "x$with_readline" != "xno"], [ 105 | # termcap is sometimes required by readline 106 | AC_CHECK_LIB([termcap], [tgetent], [], []) 107 | AC_CHECK_LIB([readline], [readline], [], [ 108 | echo "libreadline could not be found, which is required to continue." 109 | echo "The libreadline-dev package may be required." 110 | exit 1 111 | ]) 112 | ]) 113 | 114 | GETTEXT_PACKAGE=GameConqueror 115 | AC_SUBST(GETTEXT_PACKAGE) 116 | AC_DEFINE_UNQUOTED([GETTEXT_PACKAGE], ["$GETTEXT_PACKAGE"], 117 | [The domain to use with gettext]) 118 | 119 | AC_CONFIG_FILES([ 120 | Makefile 121 | test/Makefile 122 | po/Makefile.in 123 | ]) 124 | 125 | 126 | # copied from ubuntu-tweak 127 | 128 | dnl AS_AC_EXPAND(VAR, CONFIGURE_VAR) 129 | dnl 130 | dnl example 131 | dnl AS_AC_EXPAND(SYSCONFDIR, $sysconfdir) 132 | dnl will set SYSCONFDIR to /usr/local/etc if prefix=/usr/local 133 | 134 | AC_DEFUN([AS_AC_EXPAND], 135 | [ 136 | EXP_VAR=[$1] 137 | FROM_VAR=[$2] 138 | 139 | dnl first expand prefix and exec_prefix if necessary 140 | prefix_save=$prefix 141 | exec_prefix_save=$exec_prefix 142 | 143 | dnl if no prefix given, then use /usr/local, the default prefix 144 | if test "x$prefix" = "xNONE"; then 145 | prefix=$ac_default_prefix 146 | fi 147 | dnl if no exec_prefix given, then use prefix 148 | if test "x$exec_prefix" = "xNONE"; then 149 | exec_prefix=$prefix 150 | fi 151 | 152 | full_var="$FROM_VAR" 153 | dnl loop until it doesn't change anymore 154 | while true; do 155 | new_full_var="`eval echo $full_var`" 156 | if test "x$new_full_var" = "x$full_var"; then break; fi 157 | full_var=$new_full_var 158 | done 159 | 160 | dnl clean up 161 | full_var=$new_full_var 162 | AC_SUBST([$1], "$full_var") 163 | 164 | dnl restore prefix and exec_prefix 165 | prefix=$prefix_save 166 | exec_prefix=$exec_prefix_save 167 | ]) 168 | # end copy 169 | 170 | 171 | # GameConqueror configuration 172 | 173 | AC_ARG_ENABLE(gui, [AS_HELP_STRING([--enable-gui], 174 | [enable gameconqueror, the gui front-end of scanmem])]) 175 | AM_CONDITIONAL([ENABLE_GUI], [test "x$enable_gui" = "xyes"]) 176 | AS_IF([test "x$enable_gui" = "xyes"], [ 177 | AS_AC_EXPAND(PKGDATADIR, $datadir/gameconqueror) 178 | AS_AC_EXPAND(LOCALEDIR, $localedir) 179 | AS_AC_EXPAND(LIBDIR, $libdir) 180 | 181 | AM_PATH_PYTHON([2.7]) 182 | AC_CONFIG_FILES([ 183 | gui/Makefile 184 | gui/icons/Makefile 185 | gui/consts.py 186 | gui/gameconqueror 187 | gui/org.freedesktop.gameconqueror.policy.in 188 | ]) 189 | ]) 190 | 191 | AC_OUTPUT 192 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Memory Deck 2 | 3 | A scanmem wrapper in a [decky-loader](https://github.com/SteamDeckHomebrew/decky-loader) plugin. 4 | 5 | This plugin allows you to scan for, and edit values in memory. Akin to something like [Cheat Engine](https://cheatengine.org). 6 | 7 | ![Memory Deck](image/README/1664125159778.png) 8 | 9 | ## Warning 10 | This plugin directly manipulates memory. This can cause crashes, and other issues. Use at your own risk. 11 | 12 | ## Code Warning 13 | This plugin is a mess. 14 | I don't like C, I don't like Python and I don't like React. 15 | 16 | I openly welcome any PRs to clean up the code, refactor, and just make this... not so garbage! 17 | 18 | ## How to Use 19 | 20 | ### Selecting a Process 21 | 22 | When first opening Memory Deck, you will need to select a process. The plugin will automatically load a list of processes for you when opened. 23 | 24 | If you need to reload the process list for any reason, simply press the `Reload Process List` button. 25 | 26 | You will need to do this after closing / opening processes after Memory Deck has been opened. 27 | 28 | #### Changing Process 29 | 30 | After a process has been selected, you can change it by pressing the `Choose Another Process` button. 31 | 32 | ### Finding a Value 33 | 34 | To find a value in memory, a number of searches need to be performed in sequence. 35 | Each search should ideally change something to narrow the search down further and further, until eventually hopefully reaching just a single value (or at least a small enough list to manually check). 36 | 37 | #### Search Operators 38 | 39 | When searching for a value, you can use the following operators: 40 | 41 | | Operator | Description | 42 | | ------------ | --------------------------------------------------------------------------------- | 43 | | == | The value in memory *exactly matches* the search value. | 44 | | != | The value in memory *does not match* the search value. | 45 | | > | The value in memory is *greater* than the search value. | 46 | | < | The value in memory is *less* than the search value. | 47 | | Not Changed | The value in memory has *not* changed since the last search. | 48 | | Changed | The value in memory *has* changed since the last search. | 49 | | Increased | The value in memory has *increased* since the last search.  | 50 | | Decreased | The value in memory has *decreased* since the last search.  | 51 | | Increased By | The value in memory has *increased* by the search value since the last search.  | 52 | | Decreased By | The value in memory has *decreased* by the search value since the last search.  | 53 | | Any | Search for *all* values in memory. Only really useful for an "Unknown Initial Value" | 54 | 55 | Your first search should probably be a `==` search, as this will find all values that match the search value. But you may potentially want to use the `Any` search if you don't know what the initial value is. 56 | 57 | The `!=` operator could also technically be used here. But it's far more likely that you'll want to use `Any`. 58 | 59 | All other searches make use of the previous search results, so you will need to perform a search before you can use them. 60 | 61 | #### Running the Search 62 | 63 | Once you have entered your search value and operator. Press the `Search` button to run the search. 64 | 65 | This will ask the backend to run the scan you've selected. 66 | 67 | Once the process is complete, you will be able to see the number of matches found at the bottom of the QAM. 68 | 69 | Once values have been found, do something in-game that will change the value you're searching for. Then run another search. If you know the new value, you can use the `==` operator again. If you don't know the new value, you can use the other operators depending on the information you *do* know about the value. 70 | 71 | Keep repeating searches until there are less than *10* matches left. 72 | 73 | ### Editing Values 74 | Once there are less than 10 matches, a new section will appear at the bottom of the QAM. 75 | 76 | This section will list every match address, with a `Change` button. 77 | 78 | You can enter a new value with the `New Value` field, and press the `Change` button to change the value at that address. 79 | 80 | ### Resetting a Scan 81 | If you want to reset the scan, you can press the `Reset Scan` button at the top of the page. 82 | This will reset the scan and empty out all known values. This allows you to then start another initial scan. 83 | 84 | ## Installation 85 | Install [Decky Loader](https://github.com/SteamDeckHomebrew/decky-loader) using their instructions. 86 | You should then be able to find Memory Deck in the Decky store! 87 | 88 | ## Future Plans 89 | - [ ] Add range operator support 90 | - [ ] Add support for searching for strings 91 | 92 | - [ ] Allow saving of found addresses / game to enable easier editing of values later 93 | 94 | - [ ] Nicer found value list 95 | - [ ] Show variable type in found value list 96 | 97 | # License 98 | This project is licensed under the GPL-3.0 License - see the [LICENSE](LICENSE) file for details. 99 | 100 | This project uses `libscanmem` and includes source code for `scanmem` and `libscanmem`, licensed under the GPL-3.0 License and the LGPL-3.0 License respectively. 101 | 102 | A copy of the licenses for these projects can be found under the `backend/scanmem` folder. And can be found in the `bin` folder of the plugin once compiled. 103 | 104 | This project also uses a modified version of a `scanmem` Python bindings library. The source for this is located under `scanmem.py`. This library is also licensed under LGPL-3.0. -------------------------------------------------------------------------------- /backend/scanmem/show_message.c: -------------------------------------------------------------------------------- 1 | /* 2 | Message printing helper functions. 3 | 4 | Copyright (C) 2010 WANG Lu 5 | 6 | This file is part of libscanmem. 7 | 8 | This library is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU Lesser General Public License as published 10 | by the Free Software Foundation; either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | This library is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU Lesser General Public License for more details. 17 | 18 | You should have received a copy of the GNU Lesser General Public License 19 | along with this library. If not, see . 20 | */ 21 | 22 | #ifndef _GNU_SOURCE 23 | # define _GNU_SOURCE 24 | #endif 25 | 26 | #include "config.h" 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "common.h" 38 | #include "show_message.h" 39 | #include "scanmem.h" 40 | 41 | void show_info(const char *fmt, ...) 42 | { 43 | va_list args; 44 | va_start (args, fmt); 45 | fprintf(stderr, "info: "); 46 | vfprintf(stderr, fmt, args); 47 | va_end (args); 48 | } 49 | 50 | void show_error(const char *fmt, ...) 51 | { 52 | va_list args; 53 | va_start (args, fmt); 54 | fprintf(stderr, "error: "); 55 | vfprintf(stderr, fmt, args); 56 | va_end (args); 57 | } 58 | 59 | void show_warn(const char *fmt, ...) 60 | { 61 | va_list args; 62 | va_start (args, fmt); 63 | fprintf(stderr, "warn: "); 64 | vfprintf(stderr, fmt, args); 65 | va_end (args); 66 | } 67 | 68 | void show_user(const char *fmt, ...) 69 | { 70 | va_list args; 71 | va_start (args, fmt); 72 | if (!(sm_globals.options.backend)) 73 | { 74 | vfprintf(stderr, fmt, args); 75 | } 76 | va_end (args); 77 | } 78 | 79 | void show_debug(const char *fmt, ...) 80 | { 81 | va_list args; 82 | va_start (args, fmt); 83 | if (sm_globals.options.debug) 84 | { 85 | fprintf(stderr, "debug: "); 86 | vfprintf(stderr, fmt, args); 87 | } 88 | va_end (args); 89 | } 90 | 91 | FILE *get_pager(FILE *fallback_output) 92 | { 93 | const char *pager; 94 | pid_t pgpid; 95 | int pgret; 96 | int pgpipe[2]; 97 | bool pgcmdfail = false; 98 | FILE *retfd = NULL; 99 | char *const emptyvec[1] = { NULL }; 100 | 101 | assert(fallback_output != NULL && fileno(fallback_output) != -1); 102 | 103 | if (sm_globals.options.backend || !isatty(fileno(fallback_output))) 104 | return fallback_output; 105 | 106 | if ((pager = util_getenv("PAGER")) == NULL || *pager == '\0') { 107 | show_warn("get_pager(): couldn't get $PAGER, falling back to `more`\n"); 108 | pager = "more"; 109 | } 110 | 111 | if (pipe2(pgpipe, O_NONBLOCK) == -1) { 112 | show_error("get_pager(): pipe2() error `%s`. falling back to normal output\n", strerror(errno)); 113 | return fallback_output; 114 | } 115 | /* 116 | * we write here to ensure we will always 117 | * have something to read() into pgcmdfail. 118 | */ 119 | write(pgpipe[1], "", 1); 120 | 121 | /* XXX: is $PATH modified prior? */ 122 | retry: 123 | switch ((pgpid = fork())) { 124 | case -1: 125 | show_warn("get_pager(): fork() failed. falling back to normal output\n"); 126 | return fallback_output; 127 | case 0: 128 | execvp(pager, emptyvec); 129 | /* 130 | * if we got here, it means execvp() failed. 131 | * errno contains the error, so pass it back 132 | * up to parent. we use a pipe here to let 133 | * the parent know that we are indeed returning 134 | * the return value of the failed execvp(). 135 | */ 136 | char nullbuf; 137 | /* read() to empty pipe */ 138 | read(pgpipe[0], &nullbuf, 1); 139 | write(pgpipe[1], "1", 2); 140 | exit(errno); 141 | /* NOTREACHED */ 142 | default: 143 | if (waitpid(pgpid, &pgret, 0) == -1) { 144 | show_debug("pager: waitpid() error `%s`\n", strerror(errno)); 145 | show_warn("pager: waitpid() error. falling back to normal output\n"); 146 | return fallback_output; 147 | } else { 148 | pgret = WEXITSTATUS(pgret); 149 | if (read(pgpipe[0], &pgcmdfail, 1) == -1) { 150 | show_error("pager: pipe read() error `%s`. falling back to normal output\n", strerror(errno)); 151 | return fallback_output; 152 | } 153 | if (pgcmdfail) { 154 | show_debug("pager: execvp(pg=%s) ret -> %d (%s)\n", pager, pgret, strerror(pgret)); 155 | if (!strcmp(pager, "more")) { 156 | show_warn("pager: sh failed to execute more. falling back to normal output\n"); 157 | return fallback_output; 158 | } else { 159 | show_warn("pager: sh failed to execute `%s`. trying `more`...\n", pager); 160 | pager = "more"; 161 | goto retry; 162 | } 163 | } 164 | } 165 | } 166 | 167 | if ((retfd = popen(pager, "w")) == NULL) { 168 | show_warn("pager: couldn't popen() pager, falling back to normal output\n"); 169 | return fallback_output; 170 | } 171 | 172 | /* 173 | * we need to ignore SIGPIPE in case the pager exits wihout draining the 174 | * pipe (less seems to do this if you exit a large listing without 175 | * scrolling down) 176 | */ 177 | signal(SIGPIPE, SIG_IGN); 178 | 179 | assert(retfd != NULL); 180 | 181 | return retfd; 182 | } 183 | -------------------------------------------------------------------------------- /backend/scanmem/build_for_android.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ "x$1" = "x--help" ] || [ "x$1" = "xhelp" ] || [ "x$1" = "x-h" ]; then 4 | echo " 5 | ## Before building: 6 | # Run autogen.sh if \"./configure\" does not exist. 7 | # From scanmem source directory 8 | ./autogen.sh 9 | 10 | ## For libreadline support: 11 | # HOST is the compiler architecture for your toolchain and Android 12 | # device. (e.g. arm-linux-androideabi) 13 | # NDK_STANDALONE_TOOLCHAIN is the directory of your NDK standalone 14 | # toolchain. 15 | 16 | # Install libreadline to your NDK sysroot. 17 | # From libreadline source directory, execute: 18 | export PATH=\"\$NDK_STANDALONE_TOOLCHAIN/bin:\$PATH\" 19 | bash_cv_wcwidth_broken=false ./configure --host=\"\$HOST\" \\ 20 | --disable-shared --enable-static \\ 21 | --prefix=\"\$NDK_STANDALONE_TOOLCHAIN/sysroot/usr\" 22 | make 23 | make install 24 | 25 | # Install ncurses to your NDK sysgen. 26 | # From ncurses source directory, execute: 27 | export PATH=\"\$NDK_STANDALONE_TOOLCHAIN/bin:\$PATH\" 28 | ac_cv_header_locale_h=no ./configure --host=\"\$HOST\" \\ 29 | --disable-shared --enable-static \\ 30 | --prefix=\"\$NDK_STANDALONE_TOOLCHAIN/sysroot/usr\" 31 | make 32 | make install 33 | 34 | ## Building for Android 5.0 and above requires exporting PIE flags, such as: 35 | export CFLAGS="-fPIE" LDFLAGS="-pie" 36 | 37 | ## To build with standalone toolchain: 38 | export NDK_STANDALONE_TOOLCHAIN=\"/your/toolchain/path\" 39 | export HOST=\"your-androideabi\" # Default arm-linux-androideabi 40 | ./build_for_android.sh 41 | 42 | ## Advanced features and Environment variables that may be set... 43 | NDK_STANDALONE_TOOLCHAIN - A standalone toolchain is required to build full 44 | capabilities. 45 | HOST - Compiler architecture that will be used for 46 | cross-compiling, default is arm-linux-androideabi 47 | SCANMEM_HOME - Path which has scanmem sources, and will be used 48 | to build scanmem. Default current directory 49 | LIBREADLINE_DIR - Path which has libreadline sources to build 50 | automatically. Default is to download sources 51 | NCURSES_DIR - Path which has ncurses sources to build 52 | automatically. Default is to download sources 53 | " 54 | exit 0 55 | fi 56 | 57 | # Resolve ndk toolchain or other 58 | if [ "x${NDK_STANDALONE_TOOLCHAIN}" = "x" ]; then 59 | echo "NDK_STANDALONE_TOOLCHAIN was not found. 60 | Please enter the toolchain path:" 61 | read NDK_STANDALONE_TOOLCHAIN 62 | # Nothing entered 63 | if [ "x${NDK_STANDALONE_TOOLCHAIN}" = "x" ]; then 64 | echo "Error: Please set \$NDK_STANDALONE_TOOLCHAIN env variable." 1>&2 65 | exit 1 66 | fi 67 | fi 68 | export SYSROOT="${NDK_STANDALONE_TOOLCHAIN}/sysroot" 69 | export PATH="${NDK_STANDALONE_TOOLCHAIN}/bin:${PATH}" 70 | 71 | # Host architecture 72 | if [ "x${HOST}" = "x" ]; then 73 | HOST=arm-linux-androideabi 74 | echo "Env variable \$HOST, host architecture, is not specified. 75 | Defaulting to ${HOST}" 76 | fi 77 | 78 | # Build and return directory 79 | if [ "x${SCANMEM_HOME}" = "x" ]; then 80 | export SCANMEM_HOME="$(pwd)" 81 | else 82 | cd "${SCANMEM_HOME}" 83 | fi 84 | 85 | # Processor count for make instructions 86 | procnum="$(getconf _NPROCESSORS_ONLN)" 87 | if [ "x${procnum}" = "x" ] || [ $procnum -eq 0 ]; then 88 | procnum=1 89 | fi 90 | 91 | # Do not fail for source downloads, workarounds may be found for broken links 92 | if [ ! -f "${SYSROOT}/usr/lib/libreadline.a" ]; then 93 | # Build libreadline for android 94 | if [ "x${LIBREADLINE_DIR}" = "x" ]; then 95 | echo "LIBREADLINE_DIR was not found. Please enter the path where 96 | libreadline source is located, or press enter to try a source download:" 97 | read LIBREADLINE_DIR 98 | if [ "x${LIBREADLINE_DIR}" = "x" ]; then 99 | echo "Downloading libreadline..." 100 | if [ ! -f readline-6.3.tar.gz ]; then 101 | wget -c ftp://ftp.gnu.org/gnu/readline/readline-6.3.tar.gz 102 | fi 103 | tar xvf readline-6.3.tar.gz 104 | export LIBREADLINE_DIR="$(pwd)/readline-6.3" 105 | fi 106 | fi 107 | cd "${LIBREADLINE_DIR}" 108 | bash_cv_wcwidth_broken=false ./configure --host="${HOST}" \ 109 | --disable-shared --enable-static --prefix="${SYSROOT}/usr" 110 | make -j ${procnum} 111 | make install 112 | cd "${SCANMEM_HOME}" 113 | # To make sure headers can be found 114 | if [ ! -f readline ]; then 115 | ln -s "${LIBREADLINE_DIR}" readline 116 | fi 117 | fi 118 | 119 | # ncurses, same logic as libreadline 120 | if [ ! -f "${SYSROOT}/usr/lib/libncurses.a" ]; then 121 | # Build libncurses for android (needed by libreadline) 122 | if [ "x${NCURSES_DIR}" = "x" ]; then 123 | echo "NCURSES_DIR was not found. Please enter the path where 124 | ncurses source is located, or press enter to try a source download:" 125 | read NCURSES_DIR 126 | if [ "x${NCURSES_DIR}" = "x" ]; then 127 | echo "Downloading ncurses..." 128 | if [ ! -f ncurses-6.0.tar.gz ]; then 129 | wget -c http://invisible-mirror.net/archives/ncurses/ncurses-6.0.tar.gz 130 | fi 131 | tar xvf ncurses-6.0.tar.gz 132 | export NCURSES_DIR="$(pwd)/ncurses-6.0" 133 | fi 134 | fi 135 | cd "${NCURSES_DIR}" 136 | ac_cv_header_locale_h=no ./configure --host="${HOST}" \ 137 | --disable-shared --enable-static --prefix="${SYSROOT}/usr" 138 | make -j ${procnum} 139 | make install 140 | cd "${SCANMEM_HOME}" 141 | # To make sure headers can be found 142 | if [ ! -f ncurses ]; then 143 | ln -s "${NCURSES_DIR}" ncurses 144 | fi 145 | fi 146 | 147 | # Build scanmem for android 148 | if [ "$(uname -s)" = "Darwin" ]; then 149 | PATH=/usr/local/opt/gettext/bin:${PATH} # brew install gettext 150 | fi 151 | LIBS="-lncurses -lm" ./configure --host="${HOST}" --prefix="${SYSROOT}/usr" \ 152 | --enable-static --disable-shared 153 | [ "$?" != "0" ] && exit 1 154 | make -j ${procnum} && make install 155 | -------------------------------------------------------------------------------- /backend/scanmem/menu.c: -------------------------------------------------------------------------------- 1 | /* 2 | Prompt and command completion. 3 | 4 | Copyright (C) 2006,2007,2009 Tavis Ormandy 5 | Copyright (C) 2010,2011 Lu Wang 6 | Copyright (C) 2018 Sebastian Parschauer 7 | 8 | This file is part of scanmem. 9 | 10 | This program is free software: you can redistribute it and/or modify 11 | it under the terms of the GNU General Public License as published by 12 | the Free Software Foundation, either version 3 of the License, or 13 | (at your option) any later version. 14 | 15 | This program is distributed in the hope that it will be useful, 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | GNU General Public License for more details. 19 | 20 | You should have received a copy of the GNU General Public License 21 | along with this program. If not, see . 22 | */ 23 | 24 | #ifndef _GNU_SOURCE 25 | # define _GNU_SOURCE 26 | #endif 27 | 28 | #include "config.h" 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #if HAVE_LIBREADLINE 38 | #include 39 | #include 40 | #else 41 | #include "readline.h" 42 | #endif 43 | 44 | #include "menu.h" 45 | #include "list.h" 46 | #include "getline.h" 47 | #include "scanmem.h" 48 | #include "commands.h" 49 | #include "show_message.h" 50 | 51 | /* sub-command generator for readline completion */ 52 | static char *subcommandgenerator(char *start, int state, list_t *list, 53 | unsigned *index) 54 | { 55 | unsigned i; 56 | element_t *np; 57 | size_t len; 58 | char *spos = strchr(start, ' '); 59 | 60 | len = strlen(start); 61 | if (spos) 62 | len = spos - start; 63 | 64 | /* reset generator if state == 0, otherwise continue from last time */ 65 | *index = (state && !spos) ? *index : 0; 66 | 67 | np = list->head; 68 | 69 | /* skip to the last node checked */ 70 | for (i = 0; np && i < *index; i++) 71 | np = np->next; 72 | 73 | /* traverse the completion list, checking for matches */ 74 | while (np) { 75 | completion_t *compl = np->data; 76 | 77 | np = np->next; 78 | 79 | /* record progress */ 80 | (*index)++; 81 | 82 | if (compl == NULL || compl->word == NULL) 83 | continue; 84 | 85 | /* check if we have a match */ 86 | if (strncmp(start, compl->word, len) == 0) { 87 | if (!spos) 88 | return strdup(compl->word); 89 | if (!compl->list) 90 | return NULL; 91 | while (*spos == ' ') 92 | spos++; 93 | return subcommandgenerator(spos, state, compl->list, &compl->index); 94 | } 95 | } 96 | 97 | return NULL; 98 | } 99 | 100 | static char *find_command(const char *start, int state, unsigned *index, 101 | bool is_subcmd) 102 | { 103 | unsigned i; 104 | size_t len; 105 | element_t *np; 106 | globals_t *vars = &sm_globals; 107 | char *spos = strchr(start, ' '); 108 | 109 | /* reset generator if state == 0, otherwise continue from last time */ 110 | *index = (state && !spos) ? *index : 0; 111 | 112 | np = vars->commands ? vars->commands->head : NULL; 113 | 114 | len = strlen(start); 115 | if (spos) 116 | len = spos - start; 117 | 118 | /* skip to the last node checked */ 119 | for (i = 0; np && i < *index; i++) 120 | np = np->next; 121 | 122 | /* traverse the commands list, checking for matches */ 123 | while (np) { 124 | command_t *command = np->data; 125 | 126 | np = np->next; 127 | 128 | /* record progress */ 129 | (*index)++; 130 | 131 | /* if shortdoc is NULL, this is not supposed to be user visible */ 132 | if (command == NULL || command->command == NULL 133 | || command->shortdoc == NULL) 134 | continue; 135 | 136 | /* check if we have a match */ 137 | if (strncmp(start, command->command, len) == 0) { 138 | if (!spos) 139 | return strdup(command->command); 140 | if (!command->completions || is_subcmd) 141 | return NULL; 142 | while (*spos == ' ') 143 | spos++; 144 | if (strncmp(command->command, "help", sizeof("help") - 1) == 0) 145 | return find_command(spos, state, &command->complidx, true); 146 | return subcommandgenerator(spos, state, command->completions, 147 | &command->complidx); 148 | } 149 | } 150 | 151 | return NULL; 152 | } 153 | 154 | /* command generator for readline completion */ 155 | static char *commandgenerator(const char *text, int state) 156 | { 157 | char *start = rl_line_buffer; 158 | static unsigned index = 0; 159 | 160 | while (*start == ' ') 161 | start++; 162 | return find_command(start, state, &index, false); 163 | } 164 | 165 | /* custom completor program for readline */ 166 | static char **commandcompletion(const char *text, int start, int end) 167 | { 168 | (void) end; 169 | 170 | /* never use default completer (filenames), even if I dont generate any matches */ 171 | rl_attempted_completion_over = 1; 172 | 173 | /* complete everything */ 174 | return rl_completion_matches(text, commandgenerator); 175 | } 176 | 177 | 178 | /* 179 | * getcommand() reads in a command using readline and places a pointer to 180 | * the read string into *line, which must be free'd by caller. 181 | * returns true on success, or false on error. 182 | */ 183 | 184 | bool getcommand(globals_t *vars, char **line) 185 | { 186 | char prompt[64]; 187 | bool success = true; 188 | 189 | assert(vars != NULL); 190 | 191 | if (vars->matches) { 192 | snprintf(prompt, sizeof(prompt), "%ld> ", vars->num_matches); 193 | } else { 194 | snprintf(prompt, sizeof(prompt), "> "); 195 | } 196 | 197 | rl_readline_name = "scanmem"; 198 | rl_attempted_completion_function = commandcompletion; 199 | 200 | while (true) { 201 | 202 | success = ((*line = readline(prompt)) != NULL); 203 | if (!success) { 204 | /* EOF */ 205 | if ((*line = strdup("__eof")) == NULL) { 206 | show_error("sorry, there was a memory allocation error.\n"); 207 | return false; 208 | } 209 | return true; /* exit immediately to not commit `__eof` to history */ 210 | } 211 | 212 | if (strlen(*line)) { 213 | break; 214 | } 215 | 216 | free(*line); 217 | } 218 | 219 | /* record this line to readline history */ 220 | add_history(*line); 221 | return true; 222 | } 223 | -------------------------------------------------------------------------------- /backend/scanmem/targetmem.c: -------------------------------------------------------------------------------- 1 | /* 2 | The target memory information array (storage of matches). 3 | 4 | Copyright (C) 2009 Eli Dupree 5 | Copyright (C) 2010 WANG Lu 6 | 7 | This file is part of libscanmem. 8 | 9 | This library is free software: you can redistribute it and/or modify 10 | it under the terms of the GNU Lesser General Public License as published 11 | by the Free Software Foundation; either version 3 of the License, or 12 | (at your option) any later version. 13 | 14 | This library is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | GNU Lesser General Public License for more details. 18 | 19 | You should have received a copy of the GNU Lesser General Public License 20 | along with this library. If not, see . 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "targetmem.h" 31 | #include "value.h" 32 | 33 | 34 | matches_and_old_values_array * 35 | allocate_array (matches_and_old_values_array *array, size_t max_bytes) 36 | { 37 | /* make enough space for the array header and a null first swath */ 38 | size_t bytes_to_allocate = 39 | sizeof(matches_and_old_values_array) + 40 | sizeof(matches_and_old_values_swath); 41 | 42 | if (!(array = realloc(array, bytes_to_allocate))) 43 | return NULL; 44 | 45 | array->bytes_allocated = bytes_to_allocate; 46 | array->max_needed_bytes = max_bytes; 47 | 48 | return array; 49 | } 50 | 51 | matches_and_old_values_array * 52 | null_terminate (matches_and_old_values_array *array, 53 | matches_and_old_values_swath *swath) 54 | { 55 | size_t bytes_needed; 56 | 57 | if (swath->number_of_bytes == 0) { 58 | assert(swath->first_byte_in_child == NULL); 59 | 60 | } else { 61 | swath = local_address_beyond_last_element(swath ); 62 | array = allocate_enough_to_reach(array, ((void *)swath) + 63 | sizeof(matches_and_old_values_swath), 64 | &swath); 65 | swath->first_byte_in_child = NULL; 66 | swath->number_of_bytes = 0; 67 | } 68 | 69 | bytes_needed = ((void *)swath + sizeof(matches_and_old_values_swath) - 70 | (void *)array); 71 | 72 | if (bytes_needed < array->bytes_allocated) { 73 | /* reduce array to its final size */ 74 | if (!(array = realloc(array, bytes_needed))) 75 | return NULL; 76 | 77 | array->bytes_allocated = bytes_needed; 78 | } 79 | 80 | return array; 81 | } 82 | 83 | void data_to_printable_string (char *buf, int buf_length, 84 | matches_and_old_values_swath *swath, 85 | size_t index, int string_length) 86 | { 87 | long swath_length = swath->number_of_bytes - index; 88 | /* TODO: what if length is too large ? */ 89 | long max_length = (swath_length >= string_length) ? string_length : swath_length; 90 | int i; 91 | 92 | for (i = 0; i < max_length; ++i) { 93 | uint8_t byte = swath->data[index+i].old_value; 94 | buf[i] = isprint(byte) ? byte : '.'; 95 | } 96 | buf[i] = 0; /* null-terminate */ 97 | } 98 | 99 | void data_to_bytearray_text (char *buf, int buf_length, 100 | matches_and_old_values_swath *swath, 101 | size_t index, int bytearray_length) 102 | { 103 | int i; 104 | int bytes_used = 0; 105 | long swath_length = swath->number_of_bytes - index; 106 | 107 | /* TODO: what if length is too large ? */ 108 | long max_length = (swath_length >= bytearray_length) ? 109 | bytearray_length : swath_length; 110 | 111 | for (i = 0; i < max_length; ++i) { 112 | uint8_t byte = swath->data[index+i].old_value; 113 | 114 | /* TODO: check error here */ 115 | snprintf(buf+bytes_used, buf_length-bytes_used, 116 | (iswaths; 130 | 131 | while (reading_swath_index->first_byte_in_child) { 132 | /* only actual matches are considered */ 133 | if (reading_swath_index->data[reading_iterator].match_info != flags_empty) { 134 | 135 | if (i == n) 136 | return (match_location){reading_swath_index, reading_iterator}; 137 | 138 | ++i; 139 | } 140 | 141 | /* go on to the next one... */ 142 | ++reading_iterator; 143 | if (reading_iterator >= reading_swath_index->number_of_bytes) { 144 | reading_swath_index = 145 | local_address_beyond_last_element(reading_swath_index); 146 | 147 | reading_iterator = 0; 148 | } 149 | } 150 | 151 | /* I guess this is not a valid match-id */ 152 | return (match_location){ NULL, 0 }; 153 | } 154 | 155 | /* deletes matches in [start, end) and resizes the matches array */ 156 | matches_and_old_values_array * 157 | delete_in_address_range (matches_and_old_values_array *array, 158 | unsigned long *num_matches, 159 | void *start_address, void *end_address) 160 | { 161 | assert(array); 162 | 163 | size_t reading_iterator = 0; 164 | matches_and_old_values_swath *reading_swath_index = array->swaths; 165 | 166 | matches_and_old_values_swath reading_swath = *reading_swath_index; 167 | 168 | matches_and_old_values_swath *writing_swath_index = array->swaths; 169 | 170 | writing_swath_index->first_byte_in_child = NULL; 171 | writing_swath_index->number_of_bytes = 0; 172 | 173 | *num_matches = 0; 174 | 175 | while (reading_swath.first_byte_in_child) { 176 | void *address = reading_swath.first_byte_in_child + reading_iterator; 177 | 178 | if (address < start_address || address >= end_address) { 179 | old_value_and_match_info old_byte; 180 | 181 | old_byte = reading_swath_index->data[reading_iterator]; 182 | 183 | /* Still a candidate. Write data. 184 | (We can get away with overwriting in the same array because 185 | it is guaranteed to take up the same number of bytes or fewer, 186 | and because we copied out the reading swath metadata already.) 187 | (We can get away with assuming that the pointers will stay 188 | valid, because as we never add more data to the array than 189 | there was before, it will not reallocate.) */ 190 | writing_swath_index = add_element(&array, 191 | writing_swath_index, address, 192 | old_byte.old_value, old_byte.match_info); 193 | 194 | /* actual matches are recorded */ 195 | if (old_byte.match_info != flags_empty) 196 | ++(*num_matches); 197 | } 198 | 199 | /* go on to the next one... */ 200 | ++reading_iterator; 201 | if (reading_iterator >= reading_swath.number_of_bytes) { 202 | 203 | reading_swath_index = (matches_and_old_values_swath *) 204 | (&reading_swath_index->data[reading_swath.number_of_bytes]); 205 | 206 | reading_swath = *reading_swath_index; 207 | 208 | reading_iterator = 0; 209 | } 210 | } 211 | 212 | return null_terminate(array, writing_swath_index); 213 | } 214 | -------------------------------------------------------------------------------- /backend/scanmem/lgpl-3.0.txt: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /backend/scanmem/gui/misc.py: -------------------------------------------------------------------------------- 1 | """ 2 | Misc functions for Game Conqueror 3 | 4 | Copyright (C) 2010,2011,2013 Wang Lu 5 | Copyright (C) 2013 Mattias 6 | Copyright (C) 2016 Andrea Stacchiotti 7 | 8 | This program is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program. If not, see . 20 | """ 21 | 22 | import sys 23 | 24 | from gi.repository import Gtk 25 | 26 | PY3K = sys.version_info >= (3, 0) 27 | 28 | # check command syntax, data range etc. 29 | # return a valid scanmem command 30 | # raise if something is invalid 31 | def check_scan_command (data_type, cmd, is_first_scan): 32 | if cmd == '': 33 | raise ValueError(_('No value provided')) 34 | if data_type == 'string': 35 | return '" ' + cmd 36 | 37 | cmd = cmd.strip() 38 | # hack for snapshot/update (TODO: make it possible with string) 39 | if cmd == '?': 40 | if is_first_scan: 41 | return 'snapshot' 42 | else: 43 | return 'update' 44 | 45 | if data_type == 'bytearray': 46 | bytes = cmd.split(' ') 47 | for byte in bytes: 48 | if byte.strip() == '': 49 | continue 50 | if len(byte) != 2: 51 | raise ValueError(_('Bad value: %s') % (byte, )) 52 | if byte == '??': 53 | continue 54 | try: 55 | _tmp = int(byte,16) 56 | except: 57 | raise ValueError(_('Bad value: %s') % (byte, )) 58 | return cmd 59 | else: # for numbers 60 | is_operator_cmd = cmd in {'=', '!=', '>', '<', '+', '-'} 61 | if not is_first_scan and is_operator_cmd: 62 | return cmd 63 | 64 | if is_first_scan and (is_operator_cmd or cmd[:2] in {'+ ', '- '}): 65 | raise ValueError(_('Command \"%s\" is not valid for the first scan') % (cmd[:2],)) 66 | 67 | # evaluating the command 68 | range_nums = cmd.split("..") 69 | if len(range_nums) == 2: 70 | # range detected 71 | num_1 = eval_operand(range_nums[0]) 72 | num_2 = eval_operand(range_nums[1]) 73 | cmd = str(num_1) + ".." + str(num_2) 74 | check_int(data_type, num_1) 75 | check_int(data_type, num_2) 76 | else: 77 | # regular command processing 78 | if cmd[:2] in {'+ ', '- ', '> ', '< '}: 79 | num = cmd[2:] 80 | cmd = cmd[:2] 81 | elif cmd[:3] == '!= ': 82 | num = cmd[3:] 83 | cmd = cmd[:3] 84 | else: 85 | num = cmd 86 | cmd = '' 87 | num = eval_operand(num) 88 | cmd += str(num) 89 | check_int(data_type, num) 90 | 91 | # finally 92 | return cmd 93 | 94 | # evaluate the expression 95 | def eval_operand(s): 96 | try: 97 | v = eval(s) 98 | py2_long = not PY3K and isinstance(v, long) 99 | if isinstance(v, int) or isinstance(v, float) or py2_long: 100 | return v 101 | except: 102 | pass 103 | 104 | raise ValueError(_('Bad value: %s') % (s,)) 105 | 106 | # check if a number is a valid integer 107 | # raise an exception if not 108 | def check_int (data_type, num): 109 | if data_type.startswith('int'): 110 | py2_long = not PY3K and isinstance(num, long) 111 | if not (isinstance(num, int) or py2_long): 112 | raise ValueError(_('%s is not an integer') % (num,)) 113 | if data_type == 'int': 114 | width = 64 115 | else: 116 | width = int(data_type[len('int'):]) 117 | if num > ((1< val2: return 1 156 | if val1 == val2: return 0 157 | return -1 158 | 159 | # format number in base16 (callback for TreeView) 160 | def format16(col, cell, model, iter, hex_col): 161 | cell.set_property("text", "%x" % model.get_value(iter, hex_col)) 162 | 163 | # append a column to `treeview`, with given `title` 164 | # keyword parameters 165 | # renderer_class -- default: Gtk.CellRendererText 166 | # attributes -- if not None, will be applied to renderer 167 | # properties -- if not None, will be applied to renderer 168 | # signals -- if not None, will be connected to renderer 169 | # the latter two should be a list of tuples, i.e. ((name1, value1), (name2, value2)) 170 | def treeview_append_column(treeview, title, sort_id=None, resizable=True, hex_col=None, **kwargs): 171 | renderer_class = kwargs.get('renderer_class', Gtk.CellRendererText) 172 | attributes = kwargs.get('attributes') 173 | properties = kwargs.get('properties') 174 | signals = kwargs.get('signals') 175 | 176 | column = Gtk.TreeViewColumn(title) 177 | treeview.append_column(column) 178 | if sort_id is not None: 179 | column.set_sort_column_id(sort_id) 180 | column.set_resizable(resizable) 181 | renderer = renderer_class() 182 | column.pack_start(renderer, True) 183 | if hex_col is not None: 184 | column.set_cell_data_func(renderer, format16, hex_col) 185 | if attributes: 186 | for k,v in attributes: 187 | column.add_attribute(renderer, k, v) 188 | if properties: 189 | for k,v in properties: 190 | renderer.set_property(k,v) 191 | if signals: 192 | for k,v in signals: 193 | renderer.connect(k,v) 194 | 195 | # data is optional data to callback 196 | def menu_append_item(menu, name, callback, data=None): 197 | item = Gtk.MenuItem(label=name) 198 | menu.append(item) 199 | item.connect('activate', callback, data) 200 | 201 | # Interface for bytes<>string conversion for py2/3 202 | # Usage is the same you'd do in py3, call `decode` on external raw data 203 | # and `encode` to work with the memory representation 204 | def decode(raw_bytes, errors='strict'): 205 | if PY3K: 206 | return raw_bytes.decode(errors=errors) 207 | else: 208 | return str(raw_bytes) 209 | 210 | def encode(unicode_string, errors='strict'): 211 | if PY3K: 212 | return unicode_string.encode(errors=errors) 213 | else: 214 | return unicode_string 215 | 216 | # Convert codepoints to integers byte by byte 217 | def str2bytes(string): 218 | if PY3K: 219 | return bytes(string) 220 | else: 221 | return map(ord, string) 222 | -------------------------------------------------------------------------------- /backend/scanmem/value.h: -------------------------------------------------------------------------------- 1 | /* 2 | Simple routines for working with the value_t data structure. 3 | 4 | Copyright (C) 2006,2007,2009 Tavis Ormandy 5 | Copyright (C) 2009 Eli Dupree 6 | Copyright (C) 2009,2010 WANG Lu 7 | Copyright (C) 2015 Sebastian Parschauer 8 | Copyright (C) 2017 Andrea Stacchiotti 9 | 10 | This file is part of libscanmem. 11 | 12 | This library is free software: you can redistribute it and/or modify 13 | it under the terms of the GNU Lesser General Public License as published 14 | by the Free Software Foundation; either version 3 of the License, or 15 | (at your option) any later version. 16 | 17 | This library is distributed in the hope that it will be useful, 18 | but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | GNU Lesser General Public License for more details. 21 | 22 | You should have received a copy of the GNU Lesser General Public License 23 | along with this library. If not, see . 24 | */ 25 | 26 | 27 | #ifndef VALUE_H 28 | #define VALUE_H 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | /* some routines for working with value_t structures */ 37 | 38 | /* match_flags: they MUST be implemented as an `uint16_t`, the `__packed__` ensures so. 39 | * They are reinterpreted as a normal integer when scanning for VLT, which is 40 | * valid for both endians, as the flags are ordered from smaller to bigger. 41 | * NAMING: Primitive, single-bit flags are called `flag_*`, while aggregates, 42 | * defined for convenience, are called `flags_*`*/ 43 | typedef enum __attribute__((__packed__)) { 44 | flags_empty = 0, 45 | 46 | flag_u8b = 1 << 0, /* could be an unsigned 8-bit variable (e.g. unsigned char) */ 47 | flag_s8b = 1 << 1, /* could be a signed 8-bit variable (e.g. signed char) */ 48 | flag_u16b = 1 << 2, /* could be an unsigned 16-bit variable (e.g. unsigned short) */ 49 | flag_s16b = 1 << 3, /* could be a signed 16-bit variable (e.g. short) */ 50 | flag_u32b = 1 << 4, /* could be an unsigned 32-bit variable (e.g. unsigned int) */ 51 | flag_s32b = 1 << 5, /* could be a signed 32-bit variable (e.g. int) */ 52 | flag_u64b = 1 << 6, /* could be an unsigned 64-bit variable (e.g. unsigned long long) */ 53 | flag_s64b = 1 << 7, /* could be a signed 64-bit variable (e.g. long long) */ 54 | 55 | flag_f32b = 1 << 8, /* could be a 32-bit floating point variable (i.e. float) */ 56 | flag_f64b = 1 << 9, /* could be a 64-bit floating point variable (i.e. double) */ 57 | 58 | flags_i8b = flag_u8b | flag_s8b, 59 | flags_i16b = flag_u16b | flag_s16b, 60 | flags_i32b = flag_u32b | flag_s32b, 61 | flags_i64b = flag_u64b | flag_s64b, 62 | 63 | flags_integer = flags_i8b | flags_i16b | flags_i32b | flags_i64b, 64 | flags_float = flag_f32b | flag_f64b, 65 | flags_all = flags_integer | flags_float, 66 | 67 | flags_8b = flags_i8b, 68 | flags_16b = flags_i16b, 69 | flags_32b = flags_i32b | flag_f32b, 70 | flags_64b = flags_i64b | flag_f64b, 71 | 72 | flags_max = 0xffffU /* ensures we're using an uint16_t */ 73 | } match_flags; 74 | 75 | /* this struct describes matched values */ 76 | typedef struct { 77 | union { 78 | int8_t int8_value; 79 | uint8_t uint8_value; 80 | int16_t int16_value; 81 | uint16_t uint16_value; 82 | int32_t int32_value; 83 | uint32_t uint32_value; 84 | int64_t int64_value; 85 | uint64_t uint64_value; 86 | float float32_value; 87 | double float64_value; 88 | uint8_t bytes[sizeof(int64_t)]; 89 | char chars[sizeof(int64_t)]; 90 | }; 91 | 92 | match_flags flags; 93 | } value_t; 94 | 95 | /* This union describes 8 bytes retrieved from target memory. 96 | * Pointers to this union are the only ones that are allowed to be unaligned: 97 | * to avoid performance degradation/crashes on arches that don't support unaligned access 98 | * (e.g. ARM) we access unaligned memory only through the attributes of this packed union. 99 | * As described in http://www.alfonsobeato.net/arm/how-to-access-safely-unaligned-data/ , 100 | * a packed structure forces the compiler to write general access methods to its members 101 | * that don't depend on alignment. 102 | * So NEVER EVER dereference a mem64_t*, but use its accessors to obtain the needed type. 103 | */ 104 | typedef union __attribute__((packed)) { 105 | int8_t int8_value; 106 | uint8_t uint8_value; 107 | int16_t int16_value; 108 | uint16_t uint16_value; 109 | int32_t int32_value; 110 | uint32_t uint32_value; 111 | int64_t int64_value; 112 | uint64_t uint64_value; 113 | float float32_value; 114 | double float64_value; 115 | uint8_t bytes[sizeof(int64_t)]; 116 | char chars[sizeof(int64_t)]; 117 | } mem64_t; 118 | 119 | /* bytearray wildcards: they must be uint8_t. They are ANDed with the incoming 120 | * memory before the comparison, so that '??' wildcards always return true 121 | * It's possible to extend them to fully granular wildcard-ing, if needed */ 122 | typedef enum __attribute__ ((__packed__)) { 123 | FIXED = 0xffu, 124 | WILDCARD = 0x00u, 125 | } wildcard_t; 126 | 127 | /* this struct describes values provided by users */ 128 | typedef struct { 129 | int8_t int8_value; 130 | uint8_t uint8_value; 131 | int16_t int16_value; 132 | uint16_t uint16_value; 133 | int32_t int32_value; 134 | uint32_t uint32_value; 135 | int64_t int64_value; 136 | uint64_t uint64_value; 137 | float float32_value; 138 | double float64_value; 139 | 140 | const uint8_t *bytearray_value; 141 | const wildcard_t *wildcard_value; 142 | 143 | const char *string_value; 144 | 145 | match_flags flags; 146 | } uservalue_t; 147 | 148 | /* used when outputting values to user */ 149 | /* only works for numbers */ 150 | void valtostr(const value_t *val, char *str, size_t n); 151 | /* parse bytearray, it will allocate the arrays itself, then needs to be free'd by `free_uservalue()` */ 152 | bool parse_uservalue_bytearray(char *const *argv, unsigned argc, uservalue_t *val); 153 | bool parse_uservalue_number(const char *nptr, uservalue_t * val); /* parse int or float */ 154 | bool parse_uservalue_int(const char *nptr, uservalue_t * val); 155 | bool parse_uservalue_float(const char *nptr, uservalue_t * val); 156 | void free_uservalue(uservalue_t *uval); 157 | void valcpy(value_t * dst, const value_t * src); 158 | void uservalue2value(value_t * dst, const uservalue_t * src); /* dst.flags must be set beforehand */ 159 | 160 | #define get_s8b(val) ((val)->int8_value) 161 | #define get_u8b(val) ((val)->uint8_value) 162 | #define get_s16b(val) ((val)->int16_value) 163 | #define get_u16b(val) ((val)->uint16_value) 164 | #define get_s32b(val) ((val)->int32_value) 165 | #define get_u32b(val) ((val)->uint32_value) 166 | #define get_s64b(val) ((val)->int64_value) 167 | #define get_u64b(val) ((val)->uint64_value) 168 | #define get_f32b(val) ((val)->float32_value) 169 | #define get_f64b(val) ((val)->float64_value) 170 | 171 | #define set_s8b(val, v) (((val)->int8_value) = v) 172 | #define set_u8b(val, v) (((val)->uint8_value) = v) 173 | #define set_s16b(val, v) (((val)->int16_value) = v) 174 | #define set_u16b(val, v) (((val)->uint16_value) = v) 175 | #define set_s32b(val, v) (((val)->int32_value) = v) 176 | #define set_u32b(val, v) (((val)->uint32_value) = v) 177 | #define set_s64b(val, v) (((val)->int64_value) = v) 178 | #define set_u64b(val, v) (((val)->uint64_value) = v) 179 | #define set_f32b(val, v) (((val)->float32_value) = v) 180 | #define set_f64b(val, v) (((val)->float64_value) = v) 181 | 182 | static inline void zero_value(value_t *val) 183 | { 184 | /* zero components separately - 185 | 10 bytes memset() is too slow */ 186 | val->int64_value = 0; /* zero the whole union */ 187 | val->flags = flags_empty; 188 | } 189 | 190 | static inline void zero_uservalue(uservalue_t *val) 191 | { 192 | memset(val, 0, sizeof(*val)); 193 | } 194 | 195 | #endif /* VALUE_H */ 196 | -------------------------------------------------------------------------------- /backend/scanmem/ChangeLog: -------------------------------------------------------------------------------- 1 | 2016-04-28 Sebastian Parschauer 2 | * See https://github.com/scanmem/scanmem/releases for new releases 3 | 4 | 2012-03-26 WANG Lu 5 | * Fix for '!=' as 'not equal to' 6 | * Typo 7 | * Option 'dump_with_ascii' added (Thanks to sec) 8 | 9 | 2011-12-16 WANG Lu 10 | * Support wildcards when writing bytearrays 11 | [Thanks to Incarus6] 12 | * Delete an item upon DELETE key is pressed 13 | * Copy address & cheatlist reorderable 14 | 15 | 2011-08-16 WANG Lu 16 | * Fix freeze after switching the target process 17 | 18 | 2011-08-15 WANG Lu 19 | * Improve the process selection dialog 20 | * Make tooltip for the 'Value' label more obvious 21 | 22 | 2011-08-02 WANG Lu 23 | * Fix memory browser 24 | 25 | 2011-07-08 WANG Lu 26 | * Fix manpage 27 | * Fix process list displaying (Issue 26) 28 | 29 | 2011-03-14 WANG Lu 30 | * Ignore empty input at cheatlist 31 | * Process list dialog is resizable 32 | 33 | 2011-01-13 WANG Lu 34 | * Run as root 35 | * Skip when readlink on /proc/pid/exe fails 36 | * Added a script for building packages for PPA 37 | 38 | 2011-01-12 WANG Lu 39 | * Using pager when showing help messages 40 | 41 | 2011-01-11 WANG Lu 42 | * Check version of scanmem right after GameConqueror starts 43 | 44 | 2010-09-19 WANG Lu 45 | * Fixed memory leak 46 | * Set monospace font to the lists 47 | * Fixed value parsing for strings 48 | 49 | 2010-05-22 Wang Lu 50 | * Fixed `set' command 51 | 52 | 2010-05-16 Wang Lu 53 | * Fixed build on FreeBSD 54 | * Bug fixed: freezing when scanning multiple variables 55 | 56 | 2010-04-28 Wang Lu 57 | * Bug fixed: dump_to_file 58 | [Thanks to Bryan Cain] 59 | * Manually add a cheat entry 60 | * Update values in the lists 61 | 62 | 2010-01-19 WANG Lu 63 | * Bug fixed: read /proc//mem on 32bit machines 64 | * Bug fixed: scan for double variables on 32bit machines 65 | * a little improvement on peekbuf 66 | * Better communication, replace readline with printf when running as 67 | backend 68 | * `scan for address` supported 69 | 70 | 2010-01-16 WANG Lu 71 | * Memory Editor 72 | * Fix copyright strings (again) 73 | * new 'dump' command 74 | 75 | 2010-01-12 WANG Lu 76 | * Bugs fixed 77 | * Better DEFAULT_LONGDOC 78 | * Better GUI 79 | * Better communication between GUI & backend 80 | * Progress bar in GUI 81 | * Several other UI improvement 82 | 83 | 2010-01-10 WANG Lu 84 | * STRING supported 85 | * Fixed a bug relacted to match_flag, now match_flags seems to be 86 | `dangerous` since I made it a union now. 87 | * Fixed a memory bug in target_memory_info_array.c 88 | 89 | 2010-01-09 WANG Lu 90 | * BYTEARRAY supported, oh yeah 91 | 92 | 2010-01-07 WANG Lu 93 | * Added uservalue_t and clean up some value_t, this make it more flexible, 94 | and make it possible to support more data types and even user-specific 95 | scan routine 96 | * in scan routines old_value and user_value are both provided 97 | * clean up MATCHES_AND_VALUES stuff, since currently we don't use MATCHES 98 | or VALUES 99 | * removed snapshot and use MATCHANY instead, such that this can be used to 100 | filter out data types 101 | * now user can provide float numbers 102 | * INCREASEDBY and DECREASEDBY support 103 | 104 | 2010-01-06 WANG Lu 105 | * Fixed detection of reverse change, but it's now working partially, see 106 | comments in `help option` 107 | * MATCHANY can be used to filter out types now 108 | * GREATERTHAN & LESSTHAN supported 109 | 110 | 2010-01-03 WANG Lu 111 | * better GUI 112 | * support scanning for a speicific type of data 113 | * autotools-ized gui 114 | * added an option to determine which regions to be searched 115 | 116 | 2009-12-20 WANG Lu 117 | * basic float/double support 118 | * fixed a peek buffer bug, that the buffer is not shifted correctly 119 | * change format of some commands for the front-end 120 | * added a new 'write' command 121 | * fixed a data overwritten issue (Issue 1) 122 | 123 | xxxx-xx-xx Tavis Ormandy 124 | * fixed infinite loop if a command was just leading whitespace. 125 | * add message to configure script about `libreadline-dev` package. 126 | * correct some typos reported by debian. 127 | 128 | 2007-06-07 Tavis Ormandy 129 | * make license clearer, added gpl notice to all source files, and added a show command. 130 | - eg, show copying, show warranty, etc. 131 | * handle unspecified value in set properly, eg set /4 132 | * autotooled the build process. 133 | * fixed bug where unaligned variables might be missed at end of region. 134 | * begin testsuite using dejagnu, to help prevent any regressions in future. 135 | * use /proc/pid/mem if available. 136 | 137 | 2007-04-08 Tavis Ormandy 138 | * corrected lots of lint errors. 139 | * include copy of GPL in COPYING file. 140 | * use more EXPECT(). 141 | * fixed two memory leaks (very small). 142 | * much more scalable commandline parsing, simple switch/case was getting too unwieldy. 143 | * deprecated cont command, and re-wrote set to make it much more powerful. 144 | - use set x/y instead of cont, cont will print a warning if you try to use it. 145 | * preliminary support for float type (WARNING: experimental) 146 | * implemented simple custom completer for readline, so tab completion now works for commands. 147 | - arguments will work with completion in next version. 148 | * massive improvements to online help, each command can register its own documentation. 149 | - example : `help set` will now print detailed usage information. 150 | * added quick shell escape support (shell command). 151 | * added interrupt support to commands that continue until stopped. 152 | * changed the format of list output, which now has a set of flags that represent 153 | the possible formats that this variable could be, eg 'CSi' means could be a char 154 | or a short, but cant be an int. 155 | * new command `watch` that monitors how a variable changes. 156 | * updated manual 157 | 158 | 2007-03-04 Tavis Ormandy 159 | * buffered overlapping peeks to improve scan performance. 160 | * automatically support chars, shorts and ints. 161 | - removed width command 162 | * pid command can change target. 163 | * = command, to accompany < and > to indicate variable has not changed. 164 | * snapshot command to enhance <, > and =. WARNING: experimental, very inefficient. 165 | - snapshot will use a more efficient format next version, right now it 166 | should not be used on large programs, or will eat all your free memory. 167 | 168 | 2007-01-11 Tavis Ormandy 169 | * cleaned up some lintian errors 170 | * used __builtin_expect() to try to improve performance in some areas 171 | * minor UI tweaks, give region counts during scan. 172 | * start using readline(), should implement completion at some point. 173 | * fixed memory leak. 174 | 175 | 2006-11-16 Tavis Ormandy 176 | * Made buffer management more intelligent. 177 | * Add commands to list and delete regions (lregions, dregion). 178 | * Add commands to indicate variable has increased or decreased (<, >). 179 | * Improved usability of set, cont, list, etc. 180 | * Add progress meter, large programs can take a long time for initial scan. 181 | * Testing support for variable width targets, signedness not currenlty handled. 182 | * delete command to eliminate matches. 183 | * list command now shows where a variable is located. 184 | * General code cleanup. 185 | * Eliminate useless command line options, use interactive versions instead. 186 | * Improved documentation. 187 | 188 | 2006-11-14 Tavis Ormandy 189 | * Applied patch from Brian Lewis 190 | * Install signal handler to detach from target on sigint/sighup/sigquit. 191 | * Improved Makefile 192 | * Added initial man page 193 | 194 | 2006-11-13 Tavis Ormandy 195 | * Initial version 196 | -------------------------------------------------------------------------------- /backend/scanmem/commands.c: -------------------------------------------------------------------------------- 1 | /* 2 | Registration and general execution of commands. 3 | 4 | Copyright (C) 2006,2007,2009 Tavis Ormandy 5 | Copyright (C) 2009 Eli Dupree 6 | Copyright (C) 2009,2010 WANG Lu 7 | Copyright (C) 2018 Sebastian Parschauer 8 | 9 | This file is part of libscanmem. 10 | 11 | This library is free software: you can redistribute it and/or modify 12 | it under the terms of the GNU Lesser General Public License as published 13 | by the Free Software Foundation; either version 3 of the License, or 14 | (at your option) any later version. 15 | 16 | This library is distributed in the hope that it will be useful, 17 | but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | GNU Lesser General Public License for more details. 20 | 21 | You should have received a copy of the GNU Lesser General Public License 22 | along with this library. If not, see . 23 | */ 24 | 25 | #ifndef _GNU_SOURCE 26 | # define _GNU_SOURCE 27 | #endif 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include "commands.h" 39 | #include "common.h" 40 | #include "show_message.h" 41 | 42 | static void free_completions(list_t *list) 43 | { 44 | element_t *np = list->head; 45 | completion_t *compl = NULL; 46 | 47 | while (np) { 48 | compl = np->data; 49 | np = np->next; 50 | if (!compl) 51 | continue; 52 | if (compl->list) { 53 | free_completions(compl->list); 54 | l_destroy(compl->list); 55 | compl->list = NULL; 56 | } 57 | if (compl->word) { 58 | free(compl->word); 59 | compl->word = NULL; 60 | } 61 | } 62 | } 63 | 64 | void sm_free_all_completions(list_t *commands) 65 | { 66 | element_t *np = commands->head; 67 | command_t *command = NULL; 68 | 69 | while (np) { 70 | command = np->data; 71 | np = np->next; 72 | if (command && command->completions) { 73 | free_completions(command->completions); 74 | l_destroy(command->completions); 75 | } 76 | } 77 | } 78 | 79 | static bool add_completion(list_t *wlist, char **start, size_t wlen) 80 | { 81 | char *word = NULL; 82 | completion_t *compl = NULL; 83 | 84 | word = malloc(wlen + 1); 85 | if (!word) 86 | goto err; 87 | compl = calloc(1, sizeof(completion_t)); 88 | if (!compl) 89 | goto err_free; 90 | memcpy(word, *start, wlen); 91 | word[wlen] = '\0'; 92 | *start += wlen + 1; 93 | compl->word = word; 94 | if (l_append(wlist, NULL, compl) == -1) 95 | goto err_free; 96 | 97 | return true; 98 | err_free: 99 | if (compl) 100 | free(compl); 101 | if (word) 102 | free(word); 103 | err: 104 | return false; 105 | } 106 | 107 | static list_t *init_subcmdlist(command_t *command, const char *complstr) 108 | { 109 | char *cpos, *bopos = NULL, *bcpos = NULL; 110 | char *start = (char *)complstr; 111 | bool in_sublist = false; 112 | list_t *wlist = NULL, *list; 113 | completion_t *compl = NULL; 114 | 115 | wlist = l_init(); 116 | if (!wlist) 117 | return NULL; 118 | list = wlist; 119 | 120 | bopos = strchr(start, '{'); 121 | while ((cpos = strchr(start, ','))) { 122 | if (in_sublist) { 123 | bcpos = strchr(start, '}'); 124 | if (bcpos && cpos > bcpos) { 125 | if (cpos != bcpos + 1) 126 | goto err; 127 | cpos = bcpos; 128 | in_sublist = false; 129 | bopos = strchr(start, '{'); 130 | } 131 | } else if (bopos && cpos > bopos) { 132 | cpos = bopos; 133 | in_sublist = true; 134 | bopos = NULL; 135 | } 136 | if (!add_completion(list, &start, cpos - start)) 137 | goto err; 138 | if (list == wlist && in_sublist && list->head->data) { 139 | compl = list->head->data; 140 | compl->list = l_init(); 141 | list = compl->list; 142 | } else if (list != wlist && !in_sublist) { 143 | list = wlist; 144 | if (bcpos && cpos && cpos == bcpos) 145 | start++; 146 | bcpos = NULL; 147 | } 148 | } 149 | if (in_sublist) { 150 | bcpos = strchr(start, '}'); 151 | if (!bcpos) 152 | goto err; 153 | if (!add_completion(list, &start, strlen(start) - 1)) 154 | goto err; 155 | } else { 156 | if (!add_completion(wlist, &start, strlen(start))) 157 | goto err; 158 | } 159 | 160 | command->completions = wlist; 161 | return wlist; 162 | 163 | err: 164 | free_completions(wlist); 165 | l_destroy(wlist); 166 | return NULL; 167 | } 168 | 169 | /* 170 | * sm_registercommand - add the command and a pointer to its handler to the commands list. 171 | * 172 | * So that free(data) works when destroying the list, I just concatenate the string 173 | * with the command structure. I could have used a static vector of commands, but this 174 | * way I can add aliases and macros at runtime (planned in future). 175 | * 176 | */ 177 | 178 | bool sm_registercommand(const char *command, handler_ptr handler, list_t *commands, 179 | char *shortdoc, char *longdoc, const char *complstr) 180 | { 181 | command_t *data; 182 | 183 | assert(commands != NULL); 184 | 185 | if (command != NULL) { 186 | if ((data = malloc(sizeof(command_t) + strlen(command) + 1)) == NULL) { 187 | show_error("sorry, there was a memory allocation problem.\n"); 188 | return false; 189 | } 190 | data->command = (char *) data + sizeof(*data); 191 | 192 | /* command points to the extra space allocated after data */ 193 | strcpy(data->command, command); 194 | } else { 195 | if ((data = malloc(sizeof(command_t))) == NULL) { 196 | show_error("sorry, there was a memory allocation problem.\n"); 197 | return false; 198 | } 199 | data->command = NULL; 200 | } 201 | 202 | data->handler = handler; 203 | data->shortdoc = shortdoc; 204 | data->longdoc = longdoc; 205 | data->completions = (complstr) ? init_subcmdlist(data, complstr) : NULL; 206 | data->complidx = 0; 207 | 208 | /* add new command to list */ 209 | if (l_append(commands, NULL, data) == -1) { 210 | free(data); 211 | return false; 212 | } 213 | 214 | return true; 215 | } 216 | 217 | bool sm_execcommand(globals_t *vars, const char *commandline) 218 | { 219 | unsigned argc; 220 | char *str = NULL, *tok = NULL; 221 | char **argv = NULL; 222 | command_t *err = NULL; 223 | bool ret = false; 224 | list_t *commands = vars->commands; 225 | element_t *np = NULL; 226 | 227 | assert(commandline != NULL); 228 | assert(commands != NULL); 229 | 230 | vars->current_cmdline = commandline; 231 | 232 | np = commands->head; 233 | 234 | str = tok = strdupa(commandline); 235 | 236 | /* tokenize command line into an argument vector */ 237 | for (argc = 0; tok; argc++, str = NULL) { 238 | 239 | /* make enough size for another pointer (+1 for NULL at end) */ 240 | if ((argv = realloc(argv, (argc + 1) * sizeof(char *))) == NULL) { 241 | show_error("sorry there was a memory allocation error.\n"); 242 | return false; 243 | } 244 | 245 | /* insert next token */ 246 | argv[argc] = tok = strtok(str, " \t"); 247 | } 248 | 249 | assert(argc >= 1); 250 | assert(argv != NULL); 251 | 252 | /* just leading whitespace? */ 253 | if (argv[0] == NULL) { 254 | free(argv); 255 | 256 | /* legal I guess, just don't do anything */ 257 | return true; 258 | } 259 | 260 | /* search commands list for appropriate handler */ 261 | while (np) { 262 | command_t *command = np->data; 263 | 264 | /* check if this command matches */ 265 | 266 | if (command->command == NULL) { 267 | /* the default handler has a NULL command */ 268 | err = command; 269 | } else if (strcasecmp(argv[0], command->command) == 0) { 270 | 271 | /* match found, execute handler */ 272 | ret = command->handler(vars, argv, argc - 1); 273 | 274 | free(argv); 275 | return ret; 276 | } 277 | 278 | np = np->next; 279 | } 280 | 281 | /* no match, if there was a default handler found, run it now */ 282 | if (err != NULL) { 283 | ret = err->handler(vars, argv, argc - 1); 284 | } 285 | 286 | free(argv); 287 | 288 | return ret; 289 | } 290 | -------------------------------------------------------------------------------- /backend/scanmem/scanmem.c: -------------------------------------------------------------------------------- 1 | /* 2 | Provide interfaces for front-ends. 3 | 4 | Copyright (C) 2006,2007,2009 Tavis Ormandy 5 | Copyright (C) 2009 Eli Dupree 6 | Copyright (C) 2009-2013 WANG Lu 7 | Copyright (C) 2016 Sebastian Parschauer 8 | 9 | This file is part of libscanmem. 10 | 11 | This library is free software: you can redistribute it and/or modify 12 | it under the terms of the GNU Lesser General Public License as published 13 | by the Free Software Foundation; either version 3 of the License, or 14 | (at your option) any later version. 15 | 16 | This library is distributed in the hope that it will be useful, 17 | but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | GNU Lesser General Public License for more details. 20 | 21 | You should have received a copy of the GNU Lesser General Public License 22 | along with this library. If not, see . 23 | */ 24 | 25 | #ifndef _GNU_SOURCE 26 | #define _GNU_SOURCE 27 | #endif 28 | 29 | #include "config.h" 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include "scanmem.h" 39 | #include "commands.h" 40 | #include "handlers.h" 41 | #include "show_message.h" 42 | 43 | 44 | void sm_printversion(FILE *outfd) 45 | { 46 | fprintf(outfd, "libscanmem version %s\n", PACKAGE_VERSION); 47 | } 48 | 49 | /* global settings */ 50 | globals_t sm_globals = { 51 | 0, /* exit flag */ 52 | 0, /* pid target */ 53 | NULL, /* matches */ 54 | 0, /* match count */ 55 | 0, /* scan progress */ 56 | false, /* stop flag */ 57 | NULL, /* regions */ 58 | NULL, /* commands */ 59 | NULL, /* current_cmdline */ 60 | sm_printversion, /* printversion() pointer */ 61 | /* options */ 62 | { 63 | 1, /* alignment */ 64 | 0, /* debug */ 65 | 0, /* backend */ 66 | ANYINTEGER, /* scan_data_type */ 67 | REGION_HEAP_STACK_EXECUTABLE_BSS, /* region_detail_level */ 68 | 1, /* dump_with_ascii */ 69 | 0, /* reverse_endianness */ 70 | 0, /* no_ptrace */ 71 | } 72 | }; 73 | 74 | /* signal handler - use async-signal safe functions ONLY! */ 75 | static void sighandler(int n) 76 | { 77 | const char err_msg[] = "error: \nKilled by signal "; 78 | const char msg_end[] = ".\n"; 79 | char num_str[4] = {0}; 80 | ssize_t num_size; 81 | ssize_t wbytes; 82 | 83 | wbytes = write(STDERR_FILENO, err_msg, sizeof(err_msg) - 1); 84 | if (wbytes != sizeof(err_msg) - 1) 85 | goto out; 86 | /* manual int to str conversion */ 87 | if (n < 10) { 88 | num_str[0] = (char) (0x30 + n); 89 | num_size = 1; 90 | } else if (n >= 100) { 91 | goto out; 92 | } else { 93 | num_str[0] = (char) (0x30 + n / 10); 94 | num_str[1] = (char) (0x30 + n % 10); 95 | num_size = 2; 96 | } 97 | wbytes = write(STDERR_FILENO, num_str, num_size); 98 | if (wbytes != num_size) 99 | goto out; 100 | wbytes = write(STDERR_FILENO, msg_end, sizeof(msg_end) - 1); 101 | if (wbytes != sizeof(msg_end) - 1) 102 | goto out; 103 | out: 104 | _exit(EXIT_FAILURE); /* also detaches from tracee */ 105 | } 106 | 107 | 108 | bool sm_init(void) 109 | { 110 | globals_t *vars = &sm_globals; 111 | 112 | /* before attaching to target, install signal handler to detach on error */ 113 | if (vars->options.debug == 0) /* in debug mode, let it crash and see the core dump */ 114 | { 115 | (void) signal(SIGHUP, sighandler); 116 | (void) signal(SIGINT, sighandler); 117 | (void) signal(SIGSEGV, sighandler); 118 | (void) signal(SIGABRT, sighandler); 119 | (void) signal(SIGILL, sighandler); 120 | (void) signal(SIGFPE, sighandler); 121 | (void) signal(SIGTERM, sighandler); 122 | } 123 | 124 | /* linked list of commands and function pointers to their handlers */ 125 | if ((vars->commands = l_init()) == NULL) { 126 | show_error("sorry, there was a memory allocation error.\n"); 127 | return false; 128 | } 129 | 130 | /* NULL shortdoc means don't display this command in `help` listing */ 131 | sm_registercommand("set", handler__set, vars->commands, SET_SHRTDOC, 132 | SET_LONGDOC, NULL); 133 | sm_registercommand("list", handler__list, vars->commands, LIST_SHRTDOC, 134 | LIST_LONGDOC, NULL); 135 | sm_registercommand("delete", handler__delete, vars->commands, DELETE_SHRTDOC, 136 | DELETE_LONGDOC, NULL); 137 | sm_registercommand("reset", handler__reset, vars->commands, RESET_SHRTDOC, 138 | RESET_LONGDOC, NULL); 139 | sm_registercommand("pid", handler__pid, vars->commands, PID_SHRTDOC, 140 | PID_LONGDOC, NULL); 141 | sm_registercommand("snapshot", handler__snapshot, vars->commands, 142 | SNAPSHOT_SHRTDOC, SNAPSHOT_LONGDOC, NULL); 143 | sm_registercommand("dregion", handler__dregion, vars->commands, 144 | DREGION_SHRTDOC, DREGION_LONGDOC, NULL); 145 | sm_registercommand("dregions", handler__dregion, vars->commands, 146 | NULL, DREGION_LONGDOC, NULL); 147 | sm_registercommand("lregions", handler__lregions, vars->commands, 148 | LREGIONS_SHRTDOC, LREGIONS_LONGDOC, NULL); 149 | sm_registercommand("version", handler__version, vars->commands, 150 | VERSION_SHRTDOC, VERSION_LONGDOC, NULL); 151 | sm_registercommand("=", handler__operators, vars->commands, NOTCHANGED_SHRTDOC, 152 | NOTCHANGED_LONGDOC, NULL); 153 | sm_registercommand("!=", handler__operators, vars->commands, CHANGED_SHRTDOC, 154 | CHANGED_LONGDOC, NULL); 155 | sm_registercommand("<", handler__operators, vars->commands, LESSTHAN_SHRTDOC, 156 | LESSTHAN_LONGDOC, NULL); 157 | sm_registercommand(">", handler__operators, vars->commands, GREATERTHAN_SHRTDOC, 158 | GREATERTHAN_LONGDOC, NULL); 159 | sm_registercommand("+", handler__operators, vars->commands, INCREASED_SHRTDOC, 160 | INCREASED_LONGDOC, NULL); 161 | sm_registercommand("-", handler__operators, vars->commands, DECREASED_SHRTDOC, 162 | DECREASED_LONGDOC, NULL); 163 | sm_registercommand("\"", handler__string, vars->commands, STRING_SHRTDOC, 164 | STRING_LONGDOC, NULL); 165 | sm_registercommand("update", handler__update, vars->commands, UPDATE_SHRTDOC, 166 | UPDATE_LONGDOC, NULL); 167 | sm_registercommand("exit", handler__exit, vars->commands, EXIT_SHRTDOC, 168 | EXIT_LONGDOC, NULL); 169 | sm_registercommand("quit", handler__exit, vars->commands, NULL, 170 | EXIT_LONGDOC, NULL); 171 | sm_registercommand("q", handler__exit, vars->commands, NULL, 172 | EXIT_LONGDOC, NULL); 173 | sm_registercommand("help", handler__help, vars->commands, HELP_SHRTDOC, 174 | HELP_LONGDOC, HELP_COMPLETE); 175 | sm_registercommand("shell", handler__shell, vars->commands, SHELL_SHRTDOC, 176 | SHELL_LONGDOC, NULL); 177 | sm_registercommand("!", handler__shell, vars->commands, NULL, SHELL_LONGDOC, 178 | NULL); 179 | sm_registercommand("watch", handler__watch, vars->commands, WATCH_SHRTDOC, 180 | WATCH_LONGDOC, NULL); 181 | sm_registercommand("show", handler__show, vars->commands, SHOW_SHRTDOC, 182 | SHOW_LONGDOC, SHOW_COMPLETE); 183 | sm_registercommand("dump", handler__dump, vars->commands, DUMP_SHRTDOC, 184 | DUMP_LONGDOC, NULL); 185 | sm_registercommand("write", handler__write, vars->commands, WRITE_SHRTDOC, 186 | WRITE_LONGDOC, WRITE_COMPLETE); 187 | sm_registercommand("option", handler__option, vars->commands, OPTION_SHRTDOC, 188 | OPTION_LONGDOC, OPTION_COMPLETE); 189 | 190 | /* commands beginning with __ have special meaning */ 191 | sm_registercommand("__eof", handler__eof, vars->commands, NULL, NULL, NULL); 192 | 193 | /* special value NULL means no other matches */ 194 | sm_registercommand(NULL, handler__default, vars->commands, DEFAULT_SHRTDOC, 195 | DEFAULT_LONGDOC, NULL); 196 | 197 | return true; 198 | } 199 | 200 | void sm_cleanup(void) 201 | { 202 | /* free any allocated memory used */ 203 | l_destroy(sm_globals.regions); 204 | if (sm_globals.commands) 205 | sm_free_all_completions(sm_globals.commands); 206 | l_destroy(sm_globals.commands); 207 | 208 | /* free matches array */ 209 | if (sm_globals.matches) 210 | free(sm_globals.matches); 211 | 212 | /* attempt to detach just in case */ 213 | sm_detach(sm_globals.target); 214 | } 215 | 216 | /* for front-ends */ 217 | void sm_set_backend(void) 218 | { 219 | sm_globals.options.backend = 1; 220 | } 221 | 222 | void sm_backend_exec_cmd(const char *commandline) 223 | { 224 | sm_execcommand(&sm_globals, commandline); 225 | fflush(stdout); 226 | fflush(stderr); 227 | } 228 | 229 | unsigned long sm_get_num_matches(void) 230 | { 231 | return sm_globals.num_matches; 232 | } 233 | 234 | const char *sm_get_version(void) 235 | { 236 | return PACKAGE_VERSION; 237 | } 238 | 239 | double sm_get_scan_progress(void) 240 | { 241 | return sm_globals.scan_progress; 242 | } 243 | 244 | void sm_set_stop_flag(bool stop_flag) 245 | { 246 | sm_globals.stop_flag = stop_flag; 247 | } 248 | -------------------------------------------------------------------------------- /backend/scanmem/value.c: -------------------------------------------------------------------------------- 1 | /* 2 | Simple routines for working with the value_t data structure. 3 | 4 | Copyright (C) 2006,2007,2009 Tavis Ormandy 5 | Copyright (C) 2009 Eli Dupree 6 | Copyright (C) 2009,2010 WANG Lu 7 | Copyright (C) 2015 Sebastian Parschauer 8 | 9 | This file is part of libscanmem. 10 | 11 | This library is free software: you can redistribute it and/or modify 12 | it under the terms of the GNU Lesser General Public License as published 13 | by the Free Software Foundation; either version 3 of the License, or 14 | (at your option) any later version. 15 | 16 | This library is distributed in the hope that it will be useful, 17 | but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | GNU Lesser General Public License for more details. 20 | 21 | You should have received a copy of the GNU Lesser General Public License 22 | along with this library. If not, see . 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include /* for fixed-width formatters */ 35 | 36 | #include "value.h" 37 | #include "show_message.h" 38 | 39 | void valtostr(const value_t *val, char *str, size_t n) 40 | { 41 | char buf[128]; 42 | int np = 0; 43 | 44 | #define FLAG_MACRO(bytes, string) \ 45 | (val->flags & flag_u##bytes##b && val->flags & flag_s##bytes##b) ? (string " ") : \ 46 | (val->flags & flag_u##bytes##b) ? (string "u ") : \ 47 | (val->flags & flag_s##bytes##b) ? (string "s ") : "" 48 | 49 | /* set the flags */ 50 | np = snprintf(buf, sizeof(buf), "[%s%s%s%s%s%s]", 51 | FLAG_MACRO(64, "I64"), 52 | FLAG_MACRO(32, "I32"), 53 | FLAG_MACRO(16, "I16"), 54 | FLAG_MACRO(8, "I8"), 55 | (val->flags & flag_f64b) ? "F64 " : "", 56 | (val->flags & flag_f32b) ? "F32 " : ""); 57 | /* handle having no type at all */ 58 | if (np <= 2) { 59 | show_debug("BUG: No type\n"); 60 | goto err; 61 | } 62 | 63 | if (val->flags & flag_u64b) np = snprintf(str, n, "%" PRIu64 ", %s", get_u64b(val), buf); 64 | else if (val->flags & flag_s64b) np = snprintf(str, n, "%" PRId64 ", %s", get_s64b(val), buf); 65 | else if (val->flags & flag_u32b) np = snprintf(str, n, "%" PRIu32 ", %s", get_u32b(val), buf); 66 | else if (val->flags & flag_s32b) np = snprintf(str, n, "%" PRId32 ", %s", get_s32b(val), buf); 67 | else if (val->flags & flag_u16b) np = snprintf(str, n, "%" PRIu16 ", %s", get_u16b(val), buf); 68 | else if (val->flags & flag_s16b) np = snprintf(str, n, "%" PRId16 ", %s", get_s16b(val), buf); 69 | else if (val->flags & flag_u8b) np = snprintf(str, n, "%" PRIu8 ", %s", get_u8b(val), buf); 70 | else if (val->flags & flag_s8b) np = snprintf(str, n, "%" PRId8 ", %s", get_s8b(val), buf); 71 | else if (val->flags & flag_f64b) np = snprintf(str, n, "%lg, %s", get_f64b(val), buf); 72 | else if (val->flags & flag_f32b) np = snprintf(str, n, "%g, %s", get_f32b(val), buf); 73 | else { 74 | show_debug("BUG: No formatting found\n"); 75 | goto err; 76 | } 77 | if (np <= 0 || (size_t)np >= (n - 1)) 78 | goto err; 79 | 80 | return; 81 | err: 82 | /* always print a value and a type to not crash front-ends */ 83 | strncpy(str, "unknown, [unknown]", n); 84 | } 85 | 86 | void valcpy(value_t * dst, const value_t * src) 87 | { 88 | memcpy(dst, src, sizeof(value_t)); 89 | return; 90 | } 91 | 92 | /* dst.flags must be set beforehand. Prefer setting floats to ints */ 93 | void uservalue2value(value_t *dst, const uservalue_t *src) 94 | { 95 | /* Zero whole value union, in case high bytes won't be set */ 96 | dst->uint64_value = 0; 97 | 98 | if (dst->flags & flag_f64b) set_f64b(dst, get_f64b(src)); 99 | else if (dst->flags & flag_u64b) set_u64b(dst, get_u64b(src)); 100 | else if (dst->flags & flag_s64b) set_s64b(dst, get_s64b(src)); 101 | 102 | else if (dst->flags & flag_f32b) set_f32b(dst, get_f32b(src)); 103 | else if (dst->flags & flag_u32b) set_u32b(dst, get_u32b(src)); 104 | else if (dst->flags & flag_s32b) set_s32b(dst, get_s32b(src)); 105 | 106 | else if (dst->flags & flag_u16b) set_u16b(dst, get_u16b(src)); 107 | else if (dst->flags & flag_s16b) set_s16b(dst, get_s16b(src)); 108 | 109 | else if (dst->flags & flag_u8b) set_u8b (dst, get_u8b(src)); 110 | else if (dst->flags & flag_s8b) set_s8b (dst, get_s8b(src)); 111 | 112 | else assert(false); 113 | } 114 | 115 | /* parse bytearray, it will allocate the arrays itself, then needs to be free'd by `free_uservalue()` */ 116 | bool parse_uservalue_bytearray(char *const *argv, unsigned argc, uservalue_t *val) 117 | { 118 | unsigned int i,j; 119 | uint8_t *bytes_array = malloc(argc*sizeof(uint8_t)); 120 | wildcard_t *wildcards_array = malloc(argc*sizeof(wildcard_t)); 121 | 122 | if (bytes_array == NULL || wildcards_array == NULL) 123 | { 124 | show_error("memory allocation for bytearray failed.\n"); 125 | goto err; 126 | } 127 | 128 | const char *cur_str; 129 | char *endptr; 130 | 131 | for(i = 0; i < argc; ++i) 132 | { 133 | /* get current string */ 134 | cur_str = argv[i]; 135 | /* test its length */ 136 | for(j = 0; (j < 3) && (cur_str[j]); ++j) {} 137 | if (j != 2) /* length is not 2 */ 138 | goto err; 139 | 140 | if (strcmp(cur_str, "??") == 0) 141 | { 142 | wildcards_array[i] = WILDCARD; 143 | bytes_array[i] = 0x00; 144 | } 145 | else 146 | { 147 | /* parse as hex integer */ 148 | uint8_t cur_byte = (uint8_t)strtoul(cur_str, &endptr, 16); 149 | if (*endptr != '\0') 150 | goto err; 151 | 152 | wildcards_array[i] = FIXED; 153 | bytes_array[i] = cur_byte; 154 | } 155 | } 156 | 157 | /* everything is ok */ 158 | val->bytearray_value = bytes_array; 159 | val->wildcard_value = wildcards_array; 160 | val->flags = argc; 161 | return true; 162 | 163 | err: 164 | if (bytes_array) free(bytes_array); 165 | if (wildcards_array) free(wildcards_array); 166 | zero_uservalue(val); 167 | return false; 168 | } 169 | 170 | bool parse_uservalue_number(const char *nptr, uservalue_t * val) 171 | { 172 | if (parse_uservalue_int(nptr, val)) 173 | { 174 | val->flags |= flags_float; 175 | if (val->flags & flag_s64b) { 176 | val->float32_value = (float) val->int64_value; 177 | val->float64_value = (double) val->int64_value; 178 | } 179 | else { 180 | val->float32_value = (float) val->uint64_value; 181 | val->float64_value = (double) val->uint64_value; 182 | } 183 | return true; 184 | } 185 | else if(parse_uservalue_float(nptr, val)) 186 | { 187 | double num = val->float64_value; 188 | if (num >= 0 && num <= UINT8_MAX) { val->flags |= flag_u8b; set_u8b(val, (uint8_t)num); } 189 | if (num >= INT8_MIN && num <= INT8_MAX) { val->flags |= flag_s8b; set_s8b(val, (int8_t)num); } 190 | if (num >= 0 && num <= UINT16_MAX) { val->flags |= flag_u16b; set_u16b(val, (uint16_t)num); } 191 | if (num >= INT16_MIN && num <= INT16_MAX) { val->flags |= flag_s16b; set_s16b(val, (int16_t)num); } 192 | if (num >= 0 && num <= UINT32_MAX) { val->flags |= flag_u32b; set_u32b(val, (uint32_t)num); } 193 | if (num >= INT32_MIN && num <= INT32_MAX) { val->flags |= flag_s32b; set_s32b(val, (int32_t)num); } 194 | if (num >= 0 && num <= UINT64_MAX) { val->flags |= flag_u64b; set_u64b(val, (uint64_t)num); } 195 | if (num >= INT64_MIN && num <= INT64_MAX) { val->flags |= flag_s64b; set_s64b(val, (int64_t)num); } 196 | return true; 197 | } 198 | 199 | return false; 200 | } 201 | 202 | bool parse_uservalue_int(const char *nptr, uservalue_t * val) 203 | { 204 | int64_t snum; 205 | bool valid_sint; 206 | uint64_t unum; 207 | bool valid_uint; 208 | char *endptr; 209 | 210 | assert(nptr != NULL); 211 | assert(val != NULL); 212 | 213 | zero_uservalue(val); 214 | 215 | /* skip past any whitespace */ 216 | while (isspace(*nptr)) 217 | ++nptr; 218 | 219 | /* parse it as signed int */ 220 | errno = 0; 221 | snum = strtoll(nptr, &endptr, 0); 222 | valid_sint = (errno == 0) && (*endptr == '\0'); 223 | 224 | /* parse it as unsigned int */ 225 | errno = 0; 226 | unum = strtoull(nptr, &endptr, 0); 227 | valid_uint = (*nptr != '-') && (errno == 0) && (*endptr == '\0'); 228 | 229 | if (!valid_sint && !valid_uint) 230 | return false; 231 | 232 | /* determine correct flags */ 233 | if (valid_uint && unum <= UINT8_MAX) { val->flags |= flag_u8b; set_u8b(val, (uint8_t)unum); } 234 | if (valid_sint && snum >= INT8_MIN && snum <= INT8_MAX) { val->flags |= flag_s8b; set_s8b(val, (int8_t)snum); } 235 | if (valid_uint && unum <= UINT16_MAX) { val->flags |= flag_u16b; set_u16b(val, (uint16_t)unum); } 236 | if (valid_sint && snum >= INT16_MIN && snum <= INT16_MAX) { val->flags |= flag_s16b; set_s16b(val, (int16_t)snum); } 237 | if (valid_uint && unum <= UINT32_MAX) { val->flags |= flag_u32b; set_u32b(val, (uint32_t)unum); } 238 | if (valid_sint && snum >= INT32_MIN && snum <= INT32_MAX) { val->flags |= flag_s32b; set_s32b(val, (int32_t)snum); } 239 | if (valid_uint && unum <= UINT64_MAX) { val->flags |= flag_u64b; set_u64b(val, (uint64_t)unum); } 240 | if (valid_sint && snum >= INT64_MIN && snum <= INT64_MAX) { val->flags |= flag_s64b; set_s64b(val, (int64_t)snum); } 241 | 242 | return true; 243 | } 244 | 245 | bool parse_uservalue_float(const char *nptr, uservalue_t * val) 246 | { 247 | double num; 248 | char *endptr; 249 | assert(nptr); 250 | assert(val); 251 | 252 | zero_uservalue(val); 253 | while (isspace(*nptr)) 254 | ++nptr; 255 | 256 | errno = 0; 257 | num = strtod(nptr, &endptr); 258 | if ((errno != 0) || (*endptr != '\0')) 259 | return false; 260 | 261 | /* I'm not sure how to distinguish between float and double, but I guess it's not necessary here */ 262 | val->flags |= flags_float; 263 | val->float32_value = (float) num; 264 | val->float64_value = num; 265 | return true; 266 | } 267 | 268 | void free_uservalue(uservalue_t *uval) 269 | { 270 | /* bytearray arrays are dynamically allocated and have to be freed, strings are not */ 271 | if (uval->bytearray_value) 272 | free((void*)uval->bytearray_value); 273 | if (uval->wildcard_value) 274 | free((void*)uval->wildcard_value); 275 | } 276 | -------------------------------------------------------------------------------- /backend/scanmem/maps.c: -------------------------------------------------------------------------------- 1 | /* 2 | Reading the data from /proc/pid/maps into a regions list. 3 | 4 | Copyright (C) 2006,2007,2009 Tavis Ormandy 5 | Copyright (C) 2009 Eli Dupree 6 | Copyright (C) 2009,2010 WANG Lu 7 | Copyright (C) 2014-2016 Sebastian Parschauer 8 | 9 | This file is part of libscanmem. 10 | 11 | This library is free software: you can redistribute it and/or modify 12 | it under the terms of the GNU Lesser General Public License as published 13 | by the Free Software Foundation; either version 3 of the License, or 14 | (at your option) any later version. 15 | 16 | This library is distributed in the hope that it will be useful, 17 | but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | GNU Lesser General Public License for more details. 20 | 21 | You should have received a copy of the GNU Lesser General Public License 22 | along with this library. If not, see . 23 | */ 24 | 25 | #ifndef _GNU_SOURCE 26 | # define _GNU_SOURCE 27 | #endif 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "list.h" 38 | #include "maps.h" 39 | #include "getline.h" 40 | #include "show_message.h" 41 | 42 | const char *region_type_names[] = REGION_TYPE_NAMES; 43 | 44 | bool sm_readmaps(pid_t target, list_t *regions, region_scan_level_t region_scan_level) 45 | { 46 | FILE *maps; 47 | char name[128], *line = NULL; 48 | char exelink[128]; 49 | size_t len = 0; 50 | unsigned int code_regions = 0, exe_regions = 0; 51 | unsigned long prev_end = 0, load_addr = 0, exe_load = 0; 52 | bool is_exe = false; 53 | 54 | #define MAX_LINKBUF_SIZE 256 55 | char linkbuf[MAX_LINKBUF_SIZE], *exename = linkbuf; 56 | int linkbuf_size; 57 | char binname[MAX_LINKBUF_SIZE]; 58 | 59 | /* check if target is valid */ 60 | if (target == 0) 61 | return false; 62 | 63 | /* construct the maps filename */ 64 | snprintf(name, sizeof(name), "/proc/%u/maps", target); 65 | 66 | /* attempt to open the maps file */ 67 | if ((maps = fopen(name, "r")) == NULL) { 68 | show_error("failed to open maps file %s.\n", name); 69 | return false; 70 | } 71 | 72 | show_info("maps file located at %s opened.\n", name); 73 | 74 | /* get executable name */ 75 | snprintf(exelink, sizeof(exelink), "/proc/%u/exe", target); 76 | linkbuf_size = readlink(exelink, exename, MAX_LINKBUF_SIZE - 1); 77 | if (linkbuf_size > 0) 78 | { 79 | exename[linkbuf_size] = 0; 80 | } else { 81 | /* readlink may fail for special processes, just treat as empty in 82 | order not to miss those regions */ 83 | exename[0] = 0; 84 | } 85 | 86 | /* read every line of the maps file */ 87 | while (getline(&line, &len, maps) != -1) { 88 | unsigned long start, end; 89 | region_t *map = NULL; 90 | char read, write, exec, cow; 91 | int offset, dev_major, dev_minor, inode; 92 | region_type_t type = REGION_TYPE_MISC; 93 | 94 | /* slight overallocation */ 95 | char filename[len]; 96 | 97 | /* initialise to zero */ 98 | memset(filename, '\0', len); 99 | 100 | /* parse each line */ 101 | if (sscanf(line, "%lx-%lx %c%c%c%c %x %x:%x %u %[^\n]", &start, &end, &read, 102 | &write, &exec, &cow, &offset, &dev_major, &dev_minor, &inode, filename) >= 6) { 103 | /* 104 | * get the load address for regions of the same ELF file 105 | * 106 | * When the ELF loader loads an executable or a library into 107 | * memory, there is one region per ELF segment created: 108 | * .text (r-x), .rodata (r--), .data (rw-) and .bss (rw-). The 109 | * 'x' permission of .text is used to detect the load address 110 | * (region start) and the end of the ELF file in memory. All 111 | * these regions have the same filename. The only exception 112 | * is the .bss region. Its filename is empty and it is 113 | * consecutive with the .data region. But the regions .bss and 114 | * .rodata may not be present with some ELF files. This is why 115 | * we can't rely on other regions to be consecutive in memory. 116 | * There should never be more than these four regions. 117 | * The data regions use their variables relative to the load 118 | * address. So determining it makes sense as we can get the 119 | * variable address used within the ELF file with it. 120 | * But for the executable there is the special case that there 121 | * is a gap between .text and .rodata. Other regions might be 122 | * loaded via mmap() to it. So we have to count the number of 123 | * regions belonging to the exe separately to handle that. 124 | * References: 125 | * http://en.wikipedia.org/wiki/Executable_and_Linkable_Format 126 | * http://wiki.osdev.org/ELF 127 | * http://lwn.net/Articles/531148/ 128 | */ 129 | 130 | /* detect further regions of the same ELF file and its end */ 131 | if (code_regions > 0) { 132 | if (exec == 'x' || (strncmp(filename, binname, 133 | MAX_LINKBUF_SIZE) != 0 && (filename[0] != '\0' || 134 | start != prev_end)) || code_regions >= 4) { 135 | code_regions = 0; 136 | is_exe = false; 137 | /* exe with .text and without .data is impossible */ 138 | if (exe_regions > 1) 139 | exe_regions = 0; 140 | } else { 141 | code_regions++; 142 | if (is_exe) 143 | exe_regions++; 144 | } 145 | } 146 | if (code_regions == 0) { 147 | /* detect the first region belonging to an ELF file */ 148 | if (exec == 'x' && filename[0] != '\0') { 149 | code_regions++; 150 | if (strncmp(filename, exename, MAX_LINKBUF_SIZE) == 0) { 151 | exe_regions = 1; 152 | exe_load = start; 153 | is_exe = true; 154 | } 155 | strncpy(binname, filename, MAX_LINKBUF_SIZE); 156 | binname[MAX_LINKBUF_SIZE - 1] = '\0'; /* just to be sure */ 157 | /* detect the second region of the exe after skipping regions */ 158 | } else if (exe_regions == 1 && filename[0] != '\0' && 159 | strncmp(filename, exename, MAX_LINKBUF_SIZE) == 0) { 160 | code_regions = ++exe_regions; 161 | load_addr = exe_load; 162 | is_exe = true; 163 | strncpy(binname, filename, MAX_LINKBUF_SIZE); 164 | binname[MAX_LINKBUF_SIZE - 1] = '\0'; /* just to be sure */ 165 | } 166 | if (exe_regions < 2) 167 | load_addr = start; 168 | } 169 | prev_end = end; 170 | 171 | /* must have permissions to read and be non-zero size */ 172 | if ((read == 'r') && ((end - start) > 0)) { 173 | bool useful = false; 174 | 175 | /* determine region type */ 176 | if (is_exe) 177 | type = REGION_TYPE_EXE; 178 | else if (code_regions > 0) 179 | type = REGION_TYPE_CODE; 180 | else if (!strcmp(filename, "[heap]")) 181 | type = REGION_TYPE_HEAP; 182 | else if (!strcmp(filename, "[stack]")) 183 | type = REGION_TYPE_STACK; 184 | 185 | if (region_scan_level != REGION_ALL && write != 'w') { 186 | /* Only REGION_ALL scans non-writable memory regions */ 187 | continue; 188 | } 189 | 190 | /* determine if this region is useful */ 191 | switch (region_scan_level) 192 | { 193 | case REGION_ALL: 194 | useful = true; 195 | break; 196 | case REGION_ALL_RW: 197 | useful = true; 198 | break; 199 | case REGION_HEAP_STACK_EXECUTABLE_BSS: 200 | if (filename[0] == '\0') 201 | { 202 | useful = true; 203 | break; 204 | } 205 | /* fall through */ 206 | case REGION_HEAP_STACK_EXECUTABLE: 207 | if (type == REGION_TYPE_HEAP || type == REGION_TYPE_STACK) 208 | { 209 | useful = true; 210 | break; 211 | } 212 | /* test if the region is mapped to the executable */ 213 | if (type == REGION_TYPE_EXE || 214 | strncmp(filename, exename, MAX_LINKBUF_SIZE) == 0) 215 | useful = true; 216 | break; 217 | } 218 | 219 | if (!useful) 220 | continue; 221 | 222 | /* allocate a new region structure */ 223 | if ((map = calloc(1, sizeof(region_t) + strlen(filename))) == NULL) { 224 | show_error("failed to allocate memory for region.\n"); 225 | goto error; 226 | } 227 | 228 | /* initialize this region */ 229 | map->flags.read = true; 230 | map->flags.write = (write == 'w'); 231 | map->start = (void *) start; 232 | map->size = (unsigned long) (end - start); 233 | map->type = type; 234 | map->load_addr = load_addr; 235 | 236 | /* setup other permissions */ 237 | map->flags.exec = (exec == 'x'); 238 | map->flags.shared = (cow == 's'); 239 | map->flags.private = (cow == 'p'); 240 | 241 | /* save pathname */ 242 | if (strlen(filename) != 0) { 243 | /* the pathname is concatenated with the structure */ 244 | strcpy(map->filename, filename); 245 | } 246 | 247 | /* add a unique identifier */ 248 | map->id = regions->size; 249 | 250 | /* okay, add this guy to our list */ 251 | if (l_append(regions, regions->tail, map) == -1) { 252 | show_error("failed to save region.\n"); 253 | goto error; 254 | } 255 | } 256 | } 257 | } 258 | 259 | show_info("%lu suitable regions found.\n", regions->size); 260 | 261 | /* release memory allocated */ 262 | free(line); 263 | fclose(maps); 264 | 265 | return true; 266 | 267 | error: 268 | free(line); 269 | fclose(maps); 270 | 271 | return false; 272 | } 273 | -------------------------------------------------------------------------------- /backend/scanmem/targetmem.h: -------------------------------------------------------------------------------- 1 | /* 2 | The target memory information array (storage of matches). 3 | 4 | Copyright (C) 2009 Eli Dupree 5 | Copyright (C) 2010 WANG Lu 6 | Copyright (C) 2015 Sebastian Parschauer 7 | 8 | This file is part of libscanmem. 9 | 10 | This library is free software: you can redistribute it and/or modify 11 | it under the terms of the GNU Lesser General Public License as published 12 | by the Free Software Foundation; either version 3 of the License, or 13 | (at your option) any later version. 14 | 15 | This library is distributed in the hope that it will be useful, 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | GNU Lesser General Public License for more details. 19 | 20 | You should have received a copy of the GNU Lesser General Public License 21 | along with this library. If not, see . 22 | */ 23 | 24 | #ifndef TARGETMEM_H 25 | #define TARGETMEM_H 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "value.h" 34 | #include "show_message.h" 35 | 36 | /* Public structs */ 37 | 38 | /* Single match struct */ 39 | typedef struct { 40 | uint8_t old_value; 41 | match_flags match_info; 42 | } old_value_and_match_info; 43 | 44 | /* Array that contains a consecutive (in memory) sequence of matches (= swath). 45 | - the first_byte_in_child pointer refers to locations in the child, 46 | it cannot be followed except using ptrace() 47 | - the number_of_bytes refers to the number of bytes in the child 48 | process's memory that are covered, not the number of bytes the struct 49 | takes up. It's the length of data. */ 50 | typedef struct __attribute__((packed,aligned(sizeof(old_value_and_match_info)))) { 51 | void *first_byte_in_child; 52 | size_t number_of_bytes; 53 | old_value_and_match_info data[0]; 54 | } matches_and_old_values_swath; 55 | 56 | /* Master matches array, smartly resized, contains swaths. 57 | Both `bytes` values refer to real struct bytes this time. */ 58 | typedef struct { 59 | size_t bytes_allocated; 60 | size_t max_needed_bytes; 61 | matches_and_old_values_swath swaths[0]; 62 | } matches_and_old_values_array; 63 | 64 | /* Location of a match in a matches_and_old_values_array */ 65 | typedef struct { 66 | matches_and_old_values_swath *swath; 67 | size_t index; 68 | } match_location; 69 | 70 | 71 | /* Public functions */ 72 | 73 | matches_and_old_values_array *allocate_array (matches_and_old_values_array *array, 74 | size_t max_bytes); 75 | 76 | matches_and_old_values_array *null_terminate (matches_and_old_values_array *array, 77 | matches_and_old_values_swath *swath); 78 | 79 | /* for printable text representation */ 80 | void data_to_printable_string (char *buf, int buf_length, 81 | matches_and_old_values_swath *swath, 82 | size_t index, int string_length); 83 | 84 | /* for bytearray representation */ 85 | void data_to_bytearray_text (char *buf, int buf_length, 86 | matches_and_old_values_swath *swath, 87 | size_t index, int bytearray_length); 88 | 89 | match_location nth_match (matches_and_old_values_array *matches, size_t n); 90 | 91 | /* deletes matches in [start, end) and resizes the matches array */ 92 | matches_and_old_values_array * 93 | delete_in_address_range (matches_and_old_values_array *array, 94 | unsigned long *num_matches, 95 | void *start_address, void *end_address); 96 | 97 | /* The following functions are called in the hot scanning path and were moved 98 | to this header from the .c file so that they could be inlined */ 99 | 100 | static inline size_t 101 | index_of_last_element (matches_and_old_values_swath *swath) 102 | { 103 | return swath->number_of_bytes - 1; 104 | } 105 | 106 | static inline void * 107 | remote_address_of_nth_element (matches_and_old_values_swath *swath, size_t n) 108 | { 109 | return swath->first_byte_in_child + n; 110 | } 111 | 112 | static inline void * 113 | remote_address_of_last_element (matches_and_old_values_swath *swath) 114 | { 115 | return (remote_address_of_nth_element(swath, index_of_last_element(swath))); 116 | } 117 | 118 | static inline void * 119 | local_address_beyond_nth_element (matches_and_old_values_swath *swath, size_t n) 120 | { 121 | return &(swath->data[n + 1]); 122 | } 123 | 124 | static inline void * 125 | local_address_beyond_last_element (matches_and_old_values_swath *swath) 126 | { 127 | return (local_address_beyond_nth_element(swath, index_of_last_element(swath))); 128 | } 129 | 130 | static inline matches_and_old_values_array * 131 | allocate_enough_to_reach (matches_and_old_values_array *array, 132 | void *last_byte_to_reach_plus_one, 133 | matches_and_old_values_swath **swath_pointer_to_correct) 134 | { 135 | size_t bytes_needed = last_byte_to_reach_plus_one - (void *)array; 136 | 137 | if (bytes_needed <= array->bytes_allocated) { 138 | return array; 139 | 140 | } else { 141 | matches_and_old_values_array *original_location = array; 142 | 143 | /* allocate twice as much each time, 144 | so we don't have to do it too often */ 145 | size_t bytes_to_allocate = array->bytes_allocated; 146 | while (bytes_to_allocate < bytes_needed) 147 | bytes_to_allocate *= 2; 148 | 149 | show_debug("to_allocate %ld, max %ld\n", bytes_to_allocate, 150 | array->max_needed_bytes); 151 | 152 | /* sometimes we know an absolute max that we will need */ 153 | if (array->max_needed_bytes < bytes_to_allocate) { 154 | assert(array->max_needed_bytes >= bytes_needed); 155 | bytes_to_allocate = array->max_needed_bytes; 156 | } 157 | 158 | if (!(array = realloc(array, bytes_to_allocate))) 159 | return NULL; 160 | 161 | array->bytes_allocated = bytes_to_allocate; 162 | 163 | /* Put the swath pointer back where it should be, if needed. 164 | We cast everything to void pointers in this line to make 165 | sure the math works out. */ 166 | if (swath_pointer_to_correct) { 167 | (*swath_pointer_to_correct) = (matches_and_old_values_swath *) 168 | (((void *)(*swath_pointer_to_correct)) + 169 | ((void *)array - (void *)original_location)); 170 | } 171 | 172 | return array; 173 | } 174 | } 175 | 176 | /* returns a pointer to the swath to which the element was added - 177 | i.e. the last swath in the array after the operation */ 178 | static inline matches_and_old_values_swath * 179 | add_element (matches_and_old_values_array **array, 180 | matches_and_old_values_swath *swath, 181 | void *remote_address, 182 | uint8_t new_byte, 183 | match_flags new_flags) 184 | { 185 | if (swath->number_of_bytes == 0) { 186 | assert(swath->first_byte_in_child == NULL); 187 | 188 | /* we have to overwrite this as a new swath */ 189 | *array = allocate_enough_to_reach(*array, (void *)swath + 190 | sizeof(matches_and_old_values_swath) + 191 | sizeof(old_value_and_match_info), &swath); 192 | 193 | swath->first_byte_in_child = remote_address; 194 | 195 | } else { 196 | size_t local_index_excess = 197 | remote_address - remote_address_of_last_element(swath); 198 | 199 | size_t local_address_excess = 200 | local_index_excess * sizeof(old_value_and_match_info); 201 | 202 | size_t needed_size_for_a_new_swath = 203 | sizeof(matches_and_old_values_swath) + 204 | sizeof(old_value_and_match_info); 205 | 206 | if (local_address_excess >= needed_size_for_a_new_swath) { 207 | /* It is more memory-efficient to start a new swath. 208 | * The equal case is decided for a new swath, so that 209 | * later we don't needlessly iterate through a bunch 210 | * of empty values */ 211 | *array = allocate_enough_to_reach(*array, 212 | local_address_beyond_last_element(swath) + 213 | needed_size_for_a_new_swath, &swath); 214 | 215 | swath = local_address_beyond_last_element(swath); 216 | swath->first_byte_in_child = remote_address; 217 | swath->number_of_bytes = 0; 218 | 219 | } else { 220 | /* It is more memory-efficient to write over the intervening 221 | space with null values */ 222 | *array = allocate_enough_to_reach(*array, 223 | local_address_beyond_last_element(swath) + 224 | local_address_excess, &swath); 225 | 226 | switch (local_index_excess) { 227 | case 1: 228 | /* do nothing, the new value is right after the old */ 229 | break; 230 | case 2: 231 | memset(local_address_beyond_last_element(swath), 0, 232 | sizeof(old_value_and_match_info)); 233 | break; 234 | default: 235 | /* slow due to unknown size to be zeroed */ 236 | memset(local_address_beyond_last_element(swath), 0, 237 | local_address_excess - sizeof(old_value_and_match_info)); 238 | break; 239 | } 240 | swath->number_of_bytes += local_index_excess - 1; 241 | } 242 | } 243 | 244 | /* add me */ 245 | old_value_and_match_info *dataptr = local_address_beyond_last_element(swath); 246 | dataptr->old_value = new_byte; 247 | dataptr->match_info = new_flags; 248 | ++swath->number_of_bytes; 249 | 250 | return swath; 251 | } 252 | 253 | /* only at most sizeof(int64_t) bytes will be read, 254 | if more bytes are needed (e.g. bytearray), 255 | read them separately (for performance) */ 256 | static inline value_t 257 | data_to_val_aux (const matches_and_old_values_swath *swath, 258 | size_t index, size_t swath_length) 259 | { 260 | unsigned int i; 261 | value_t val; 262 | size_t max_bytes = swath_length - index; 263 | 264 | /* Init all possible flags in a single go. 265 | * Also init length to the maximum possible value */ 266 | val.flags = 0xffffu; 267 | 268 | /* NOTE: This does the right thing for VLT because the flags are in 269 | * the same order as the number representation (for both endians), so 270 | * that the zeroing of a flag does not change useful bits of `length`. */ 271 | if (max_bytes > 8) max_bytes = 8; 272 | if (max_bytes < 8) val.flags &= ~flags_64b; 273 | if (max_bytes < 4) val.flags &= ~flags_32b; 274 | if (max_bytes < 2) val.flags &= ~flags_16b; 275 | if (max_bytes < 1) val.flags = flags_empty; 276 | 277 | for (i = 0; i < max_bytes; ++i) { 278 | /* Both uint8_t, no explicit casting needed */ 279 | val.bytes[i] = swath->data[index + i].old_value; 280 | } 281 | 282 | /* Truncate to the old flags, which are stored with the first matched byte */ 283 | val.flags &= swath->data[index].match_info; 284 | 285 | return val; 286 | } 287 | 288 | static inline value_t 289 | data_to_val (const matches_and_old_values_swath *swath, size_t index) 290 | { 291 | return data_to_val_aux(swath, index, swath->number_of_bytes); 292 | } 293 | 294 | #endif /* TARGETMEM_H */ 295 | --------------------------------------------------------------------------------