Dismember is a command-line toolkit for Linux that can be used to explore processes and (especially) their memory. Essentially for playing with `/proc`.
4
+
5
+
One core feature is the ability to scan the memory of all processes for common secrets, or for custom regular expressions.
2
6
3
-
Dismember is a command-line tool for Linux used to grep for patterns across the entire memory used by a process (or processes).
7
+
![A gif showing dismember finding credentials from the memory of a browser](demo.gif)
4
8
5
-
![A gif showing dismember finding a password from a Slack message](demo.gif)
9
+
Using the `grep` command, it can match a regular expression across all memory for all (accessible) processes. This could be used to find sensitive data in memory, identify a process by something included in its memory, or to interrogate a processes' memory for interesting information.
10
+
11
+
There are many built-in patterns included via the `scan` command, which effectively works as a secret scanner against the memory on your machine.
6
12
7
13
Dismember can be used to search memory of all processes it has access to, so running it as root is the most effective method.
8
14
9
15
Commands are also included to list processes, explore process status and related information, draw process trees, and more...
| `files` | Show a list of files being accessed by a process |
22
+
| `find` | Find a PID given a process name. If multiple processes match, the first one is returned. |
23
+
| `grep` | Search process memory for a given string or regex |
24
+
| `info` | Show information about a process |
25
+
| `kernel` | Show information about the kernel |
26
+
| `kill` | Kill a process using SIGKILL |
27
+
| `list` | List all processes currently available on the system |
28
+
| `resume` | Resume a suspended process using SIGCONT |
29
+
| `scan` | Search process memory for a set of predefined secret patterns |
30
+
| `suspend` | Suspend a process using SIGSTOP (use 'dismember resume' to leave suspension) |
31
+
| `tree` | Show a tree diagram of a process and all children (defaults to PID 1). |
32
+
11
33
## Installation
12
34
13
35
Grab a binary from the [latest release](https://github.com/liamg/dismember/releases/latest) and add it to your path.
14
36
15
-
## Examples
37
+
## UsageExamples
16
38
17
39
### Search for a pattern in a process by PID
18
40
```bash
skipped 13 lines
32
54
dismember grep 'gh[pousr]_[0-9a-zA-Z]{36}'
33
55
```
34
56
35
-
### Render a complete process tree
57
+
### Search for secrets in memory across all processes
36
58
```bash
37
-
# defaults to pid=1 to show all (accessible) processes
38
-
dismember tree
59
+
# search all accessible memory for common secrets
60
+
dismember scan
39
61
```
40
62
63
+
## FAQ
64
+
65
+
> Isn't this information all just sitting in `/proc`?
66
+
Pretty much. Dismember just reads and presents it for the most part. If you can get away with `grep whatever /proc/[pid]/blah` then go for it! I built this as an educational experience because I couldn't sleep one night and stayed up late reading the `proc` man-pages (I live an extremely rock 'n' roll lifestyle). It's not a replacement for existing tools, but perhaps it can complement them.
67
+
68
+
> Do you know how horrific some of these commands seem when read out of context?
69
+
Yes, I realised after running `dismember kill --children 291458` and grimacing as I looked back at the command. Oops.
grepCmd.Flags().IntVarP(&flagPID, "pid", "p", 0, "PID of the process whose memory should be grepped. Omitting this option will grep the memory of all available processes on the system.")
30
-
grepCmd.Flags().StringVarP(&flagProcessName, "pname", "n", "", "Grep memory of all processes whose name contains this string.")
32
+
grepCmd.Flags().StringVarP(&flagProcessName, "process-name", "n", "", "Grep memory of all processes whose name contains this string.")
31
33
grepCmd.Flags().IntVarP(&flagDumpRadius, "dump-radius", "r", 2, "The number of lines of memory to dump both above and below each match.")
32
34
grepCmd.Flags().BoolVarP(&flagIncludeSelf, "self", "s", false, "Include results that are matched against the current process, or an ancestor of that process.")
33
-
grepCmd.Flags().BoolVarP(&flagFast, "fast", "f", false, "Skip mapped files in order to run faster.")
35
+
grepCmd.Flags().BoolVarP(&flagFast, "fast", "f", false, "Skip memory-mapped files in order to run faster.")
34
36
rootCmd.AddCommand(grepCmd)
35
37
}
36
38
skipped 20 lines
57
59
_ = stdErr
58
60
stdOut := cmd.OutOrStdout()
59
61
62
+
pattern := secrets.Pattern{
63
+
Regex: regex,
64
+
}
65
+
60
66
var total int
61
67
for _, process := range processes {
62
68
if flagProcessName != "" {
skipped 5 lines
68
74
continue
69
75
}
70
76
}
71
-
if !flagIncludeSelf && process.IsInHierarchyOf(proc.Self()) {
77
+
if !flagIncludeSelf && process.IsAncestor(proc.Self()) {
Short: "Search process memory for a set of predefined secret patterns",
17
+
Long: ``,
18
+
RunE: scanHandler,
19
+
Args: cobra.ExactArgs(0),
20
+
}
21
+
22
+
scanCmd.Flags().IntVarP(&flagPID, "pid", "p", 0, "PID of the process whose memory should be grepped. Omitting this option will grep the memory of all available processes on the system.")
23
+
scanCmd.Flags().StringVarP(&flagProcessName, "process-name", "n", "", "Grep memory of all processes whose name contains this string.")
24
+
scanCmd.Flags().IntVarP(&flagDumpRadius, "dump-radius", "r", 2, "The number of lines of memory to dump both above and below each match.")
25
+
scanCmd.Flags().BoolVarP(&flagIncludeSelf, "self", "s", false, "Include results that are matched against the current process, or an ancestor of that process.")
26
+
scanCmd.Flags().BoolVarP(&flagFast, "fast", "f", false, "Skip memory-mapped files in order to run faster.")
// String returns a string representation of the state.
25
+
func (s State) String() string {
26
+
switch s {
27
+
case StateRunning:
28
+
return "R (running)"
29
+
case StateSleeping:
30
+
return "S (sleeping)"
31
+
case StateDiskSleep:
32
+
return "D (disk sleep)"
33
+
case StateStopped:
34
+
return "T (stopped)"
35
+
case StateTracingStop:
36
+
return "t (tracing stop)"
37
+
case StateZombie:
38
+
return "Z (zombie)"
39
+
case StateDead:
40
+
return "X (dead)"
41
+
default:
42
+
return "? (unknown)"
43
+
}
44
+
}
45
+
46
+
// Status summarised data from /proc/[pid]/stat*
47
+
// fields with proc annotation are sourced from /proc/[pid]/status instead of /proc/[pid]/stat
10
48
type Status struct {
11
-
Name string `proc:"Name"`
12
-
Parent Process `proc:"PPid"`
49
+
Name string `proc:"Name"` // Name of the command run by this process. Strings longer than TASK_COMM_LEN (16) characters (including the terminating null byte) are silently truncated.
50
+
State State // Constant derived from StateDescription
51
+
Parent Process // Parent process (0 if none)
52
+
ProcessGroup int // The process group ID
53
+
Session int // Session ID
54
+
TTY Device // The controlling terminal of the process. (The minor device number is contained in the combination of bits 31 to 20 and 7 to 0; the major device number is in bits 15 to 8.)
55
+
ForegroundTerminalProcessGroup int // The ID of the foreground process group of the controlling terminal of the process.
56
+
KernelFlags uint // The kernel flags word of the process. For bit meanings, see the PF_* defines in the Linux kernel source file include/linux/sched.h. Details depend on the kernel version.