scanmem(1)
๐ scanmem โ General Commands Manual ๐
okay so bestie, sit down, hydrate, because this one's a whole journey and I'm gonna walk you through it โจ
๐ NAME ๐
scanmem โ locate and modify variables in an executing process. that's the whole job description fr fr ๐
โจ SYNOPSIS โจ
scanmem [options] [target-program-pid]
๐ธ DESCRIPTION ๐ธ
scanmem is an interactive debugging utility, and what it does is lowkey iconic: it isolates the address of a variable inside a running process by successively scanning that process's address space looking for values that match. ๐ฆ
so here's the tea โ you tell scanmem how the value of the variable changes over time, and it figures out the actual location (or locations!) of that variable by successively eliminating the non-matches. process of elimination but make it โจforensicsโจ.
it decides where to look by searching for mappings with read/write permission, and those are called regions. you, the user, can eliminate regions you think are probably unrelated to your target variable (say, something living in a shared library that has nothing to do with your variable) โ and that speeds up the scan, which ngl can be quite slow at first in big programs. ๐ญ
once a variable has been found, scanmem can monitor it, or change it to a value you specify โ either once, or continually over a period of time. ๐
and the vibe? scanmem works similarly to the "pokefinders" that were once commonly used to cheat at video games. that function is a great demo of how to use scanmem, and it's used in the documentation. ๐ฎ
๐ USAGE ๐
you invoke scanmem with the process id of the program you wanna debug as an argument. once it's up, scanmem accepts interactive commands (all described below, but also โ just type help at the > prompt and you get access to scanmem's online documentation ๐ซถ).
the target-program-pid can be specified in decimal, hexadecimal, or octal using the standard C language notation:
- leading
0xโ hexadecimal - leading
0โ octal - anything else โ assumed to be decimal
here's the options list, no skips, no cuts ๐:
-p,--pid=pidโ set thetarget-program-pid.-c,--command=cmd1[;cmd2][;...]โ run given commands (separated by;) before starting the interactive shell.-v,--versionโ print version and exit.-h,--helpโ print a short description of command line options then exit.-d,--debugโ run in debug mode, more information will be outputted.-e,--errexitโ exit on initial commands error, ignored during interactive mode.
๐ COMMANDS ๐
while you're in interactive mode, scanmem prints a decimal number followed by >. that number is the current count of possible candidates for the target variable that are known. and if that number isn't there? that means no possible variables have been eliminated yet. ๐
the default scan data type is "int". you can change it with the option command. ๐
nโ wherenis any number in decimal, octal or hexadecimal. this command tellsscanmemthat the current value of the target variable is exactlyn.scanmemwill begin a search of the entire address space, or of the existing known matches (if any), eliminating any variable that does not have this value.n..mโ like thencommand, butscanmemsearches for a range of numbers betweennandminclusive instead.>,<,+,-,=,!=โ these next ones are extremely useful for locating a variable whose exact value we can't see, but whose changes over time we can see โ think a health bar. ๐ these commands usually can't be used for the first scan, but there are some exceptions:> n,< n,= nand!= n.> [n]โ ifnis given, match values that are greater thann. otherwise match all values that have increased.< [n]โ ifnis given, match values that are less thann. otherwise match all values that have decreased.+ [n]โ ifnis given, match values that have been increased byn. otherwise match all values that have increased (same as>).- [n]โ ifnis given, match values that have been decreased byn. otherwise match all values that have decreased (same as<).= [n]โ ifnis given, match values that are equal ton(same asn). otherwise match all values that have not changed.!= [n]โ ifnis given, match values that are different fromn. otherwise match all values that have changed.
snapshotโ match any value. this is useful when an initial value or range is not known, for subsequent scans with>,<,+,-,=, and!=. ๐ธ" textโ search for the provided text in memory if the scan data type is set to"string".updateโ scans the current process, getting the current values of all matches. these values can be viewed withlist, and they're also the old values thatscanmemcompares to when you use>,<, or=. this command is equivalent to a search command that all current results match. ๐list [max_to_print]โ list up tomax_to_print(default:10k) possible candidates currently known, including their address, region id, match offset, region type, last known value and possible value types. the value in the first column is the match id, and it can be used in conjunction with thedeletecommand to eliminate matches.
the match offset is determined by subtracting the load address of the associated ELF file or region from the address. it can be used to bypass Address Space Layout Randomization (ASLR). ๐ง
delete match-id_setโ delete matches in thematch-id_set. the match-ids can be found from the output of thelistcommand. set notation:[!][..a](,b..c | d, ...)[e..].
to delete all known matches, see the reset command. to delete all the matches associated with a particular library, see the dregion command, which also removes any associated matches.
please note that match-ids may be recalculated after matches are removed or added. โ ๏ธ
watch match-idโ monitor the value ofmatch-id, and print its value as it changes. every change is printed along with a timestamp, and you can interrupt this command with^Cto stop monitoring. ๐set [match-id_set=]value[/delay] [...]โ set the valuevalueinto the match numbers specified inmatch-id_set, or if justvalueis specified, all known matches.valuecan be specified in standard C language notation. all known matches, along with their match-id's, can be displayed using thelistcommand. multiplematch-id_sets can be specified, terminated with an=sign. set notation:[!][..a](,b..c | d, ...)[e..].
to set a value continually, suffix the command with / followed by the number of seconds to wait between sets. you can interrupt the set command with ^C to return to the scanmem prompt. this can be used to sustain the value of a variable which decreases over time โ for example, a timer that is decremented every second can be set to 100 every 10 seconds to prevent some property from ever changing. โฑ๏ธ
this command is used to change the value of the variable(s) once found by elimination. please note, some applications will store values in multiple locations.
write value_type address valueโ manually set the value of the variable at the specifiedaddress. names ofvalue_typeare subject to change in different versions ofscanmem, see more info using thehelp writecommand.dump address length [filename]โ dump the memory region starting fromaddressof lengthlengthin a human-readable format.
if filename is given, data will be saved into the file, otherwise data will be displayed on stdout. ๐
pid [new-pid]โ print out the process id of the current target program, or change the target tonew-pid, which will reset existing regions and matches.resetโ forget all known regions and matches and start again. clean slate bestie ๐งผlregionsโ list all the known regions. this can be used in combination with thedregioncommand to eliminate regions that the user believes are not related to the variable in question, thus reducing the address space required to search in. the value in the first column is theregion-id, which must be passed to thedregioncommand. besides the start address, the size and path (if applicable) are also printed. this can be used to eliminate regions located in shared libraries that are unlikely to be relevant to the variable required.
for experts: also the region type and the load address are displayed. the types are "exe" (executable), "code" (library), "heap", "stack" or "misc" (everything else). the load address is the memory location where an ELF file (exe/lib) has been loaded to. this helps to convert between the addresses in memory and in the associated ELF file. if the region does not belong to an ELF file, then it is the same as the start address. ๐ค
dregion region-id_setโ delete the regions inregion-id_set, along with any matches from the match list. set notation:[!][..a](,b..c | d, ...)[e..]. the region-id's can be found in the output of thelregionscommand.option name valueโ change options at runtime. e.g. the scan data type can be changed. seehelp optionfor all possible names/values.shell shell-commandโ executeshell-commandusing/bin/sh, then return. ๐show infoโ display information relating toinfoโ seehelp showfor details.versionโ print the version ofscanmemin use.helpโ print a short summary of available commands.exitโ detach from the target program and exit immediately. โ๏ธ
โจ EXAMPLES โจ
okay so bestie, we're cheating at nethack, on systems where nethack is not installed sgid. ๐
ATTENTION: scanmem usually requires root privileges. See KNOWN ISSUES for details. ๐
$ sudo scanmem `pgrep nethack`
info: maps file located at /proc/14658/maps opened.
info: 9 suitable regions found.
Please enter current value, or "help" for other commands.
>
I enter how much gold I currently have (58 pieces) and let scanmem find the potential candidates.
> 58
01/09 searching 0x79f000 - 0x7b0000..........ok
02/09 searching 0x7b0000 - 0x7cc000..........ok
03/09 searching 0x24d2000 - 0x24f3000..........ok
04/09 searching 0x7fcc04baa000 - 0x7fcc04bae000..........ok
05/09 searching 0x7fcc04de1000 - 0x7fcc04de2000..........ok
06/09 searching 0x7fcc051f7000 - 0x7fcc051fb000..........ok
07/09 searching 0x7fcc05227000 - 0x7fcc0522a000..........ok
08/09 searching 0x7fcc0522c000 - 0x7fcc0522d000..........ok
09/09 searching 0x7ffc8c113000 - 0x7ffc8c134000..........ok
info: we currently have 16 matches.
16> list
[ 0] 7b09e0, 1 + 3b09e0, exe, 58, [I64 I32 I16 I8 ]
[ 1] 7b907a, 1 + 3b907a, exe, 58, [I8 ]
[ 2] 24d4b6c, 2 + 2b6c, heap, 58, [I16 I8 ]
[ 3] 24d567e, 2 + 367e, heap, 58, [I16 I8 ]
[ 4] 24d5740, 2 + 3740, heap, 58, [I8 ]
[ 5] 7fcc05229951, 6 + 2951, misc, 58, [I8 ]
[ 6] 7ffc8c12ee28, 8 + 1be28, stack, 58, [I16 I8 ]
[ 7] 7ffc8c132381, 8 + 1f381, stack, 58, [I8 ]
[ 8] 7ffc8c132389, 8 + 1f389, stack, 58, [I8 ]
[ 9] 7ffc8c132391, 8 + 1f391, stack, 58, [I8 ]
[10] 7ffc8c132399, 8 + 1f399, stack, 58, [I8 ]
[11] 7ffc8c1323a1, 8 + 1f3a1, stack, 58, [I8 ]
[12] 7ffc8c1323a9, 8 + 1f3a9, stack, 58, [I8 ]
[13] 7ffc8c1331a3, 8 + 201a3, stack, 58, [I8 ]
[14] 7ffc8c13325f, 8 + 2025f, stack, 58, [I8 ]
[15] 7ffc8c133264, 8 + 20264, stack, 58, [I8 ]
16>
16 potential matches were found. This is also displayed in the prompt. Many of them are quite unrelated, as they are part of the stack, belong to libraries or miscellaneous memory-mapped files. Even the heap is unlikely for a very old command line game. We could make scanmem eliminate these manually using the delete command, however just waiting until the amount of gold changes and telling scanmem the new value should be enough. I find some more gold, and tell scanmem the new value, 83.
16> 83
..........info: we currently have 1 matches.
info: match identified, use "set" to modify value.
info: enter "help" for other commands.
1> list
[ 0] 7b09e0, 1 + 3b09e0, exe, 83, [I64 I32 I16 I8 ]
Only one of the 16 original candidates now has the value 83, so this must be where the amount of gold is stored. I'll try setting it to 10,000 pieces.
1> set 10000
info: setting *0x7b09e0 to 0x2710...
1>
The resulting nethack status:
Dlvl:1 $:10000 HP:15(15) Pw:2(2) AC:7 Exp:1
Conclusion: We've found and modified the gold value as I32 in static memory of the executable at virtual memory address 0x7b09e0. This address belongs to the region with id 1.
Now it is important to know if this is a position-independent executable (PIE). We list the regions for this and check the load address of the executable.
1> lregions
[ 0] 79f000, 69632 bytes, exe, 400000, rw-, /usr/lib/nethack/nethack.tty
[ 1] 7b0000, 114688 bytes, exe, 400000, rw-, unassociated
[ 2] 24d2000, 135168 bytes, heap, 24d2000, rw-, [heap]
[ 3] 7fcc04baa000, 16384 bytes, misc, 7fcc04baa000, rw-, unassociated
[ 4] 7fcc04de1000, 4096 bytes, misc, 7fcc04de1000, rw-, unassociated
[ 5] 7fcc051f7000, 16384 bytes, misc, 7fcc051f7000, rw-, unassociated
[ 6] 7fcc05227000, 12288 bytes, misc, 7fcc05227000, rw-, unassociated
[ 7] 7fcc0522c000, 4096 bytes, misc, 7fcc0522c000, rw-, unassociated
[ 8] 7ffc8c113000, 135168 bytes, stack, 7ffc8c113000, rw-, [stack]
We are on x86_64 and 0x400000 is the static load address for executables there. This means that this is not a PIE and the gold is always stored at 0x7b09e0. This makes it easy to use a game trainer like GameConqueror which refills the gold value periodically.
With a PIE we have to use the match offset (0x3b09e0 here) instead and an advanced game trainer with PIE support has to determine and add the current load address to it to get the current memory address of the gold value of the current game run.
๐ NOTES ๐
okay so scanmem has been tested on multiple large programs, including the 3d shoot-em-up quake3 linux. it's also been tested on ARM platforms and comes with Android support since version 0.16. slay. ๐
and obviously, scanmem can crash your program if used incorrectly. not a vibe. ๐
some programs store values in multiple locations, which is why set will change all known matches. periodt.
Address Space Layout Randomization (ASLR) together with position-independent executables (PIE), position-independent code (PIC) or dynamic memory on the heap causes variables to be loaded to different memory addresses at every game start. Advanced game trainers like ugtrain are required to periodically refill variables in such memory regions.
๐จ KNOWN ISSUES ๐จ
so scanmem usually requires root privileges for ptrace(2) because security modules control ptrace() capabilities. on x86 and x86_64 there is usually the Yama security module providing the file /proc/sys/kernel/yama/ptrace_scope. it's available since Linux 3.4. if this file contains "1", then only parents may ptrace() their children without root privileges. this means that scanmem would have to run the game. that's not possible as it would require major design changes. so we run scanmem as root. ๐
the first scan can be very slow on large programs, but this is not a problem for subsequent scans as huge portions of the address space are usually eliminated. this could be improved in future, perhaps by assuming all integers are aligned by default. suggestions welcome. ๐ซถ
the snapshot command uses memory inefficiently, and should probably not be used on large programs.
๐ HOMEPAGE ๐
https://github.com/scanmem/scanmem
๐ AUTHORS ๐
Tavis Ormandy <taviso(a)sdf.lonestar.org> http://taviso.decsystem.org/
Eli Dupree <elidupree(a)charter.net>
WANG Lu <coolwanglu(a)gmail.com>
Sebastian Parschauer <s.parschauer(a)gmx.de>
Andrea Stacchiotti <andreastacchiotti(a)gmail.com>
All bug reports, suggestions or feedback welcome. ๐ซถ
๐ SEE ALSO ๐
gameconqueror(1) ptrace(2) proc(5) nethack(6) pidof(8)
this iconic page is from scanmem-0.17, dated 2017-10-11, and it's scanmem(1) bestie ๐โจ
๐ฆ where this page comes from
- arch rolling: scanmem 0.17-11 ยท GPL3
- debian bookworm: scanmem 0.17-5 ยท GPL-3.0-or-later
- debian trixie: scanmem 0.17-5+b1 ยท GPL-3.0-or-later
- fedora 43: scanmem 0.17-22.fc43.1 ยท GPL-3.0-or-later AND LGPL-3.0-or-later
- fedora 44: scanmem 0.17-23.fc44.1 ยท GPL-3.0-or-later AND LGPL-3.0-or-later
- nixos 26.05: scanmem 0.17
- nixpkgs unstable: scanmem 0.17
- opensuse-leap 16.0: scanmem 0.17-bp160.1.12 ยท GPL-3.0-only
- opensuse-leap 16.0: scanmem 0.17-bp160.1.12 ยท GPL-3.0-only
- opensuse-leap 16.0: scanmem 0.17-bp160.1.12 ยท GPL-3.0-only
- opensuse-leap 16.0: scanmem 0.17-bp160.1.12 ยท GPL-3.0-only
- ubuntu jammy: scanmem 0.17-3 ยท GPL-3.0-or-later
- ubuntu noble: scanmem 0.17-5build2 ยท GPL-3.0-or-later
- ubuntu questing: scanmem 0.17-5build2
- ubuntu resolute: scanmem 0.17-6 ยท GPL-3.0-or-later
- ubuntu stonking: scanmem 0.17-6 ยท GPL-3.0-or-later
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms