Some debugging of modules, eg, proc_PID.py
[signduterre.git] / proc_PID.py
blob636db6c899b1b5d001d9e44950e0db46503ad725
1 # proc_PID
2 #
3 # Signature-du-Terroir module to handle /proc/<pid>
5 # Help text
6 help_text = """
7 Start a command that will stall on reading STDIN, eg, grep, cat, dd, perl, python, ruby, bash.
8 Or enter the PID of an existing, running, task.
10 Use /proc/<pid>/{exe, maps} to collect information, eg, all loaded libraries, the file paths,
11 inode numbers, and mem addresses. Then use dbugfs to read out the inode numbers. The results can be
12 hashed with sha512sum (recommended) or returned raw.
14 System commands
15 reset() : Empty cache, next call to get_info() will run the command
16 get_info(command) : Run command and read all info, if command differs from current_command
17 get_info(PID) : Read all info from running process PID, if command differs from current pid
19 Values
20 current_command : Command (path) for which the current values are relevant
21 address_list : The start, end, and offset addresses for each file (path) loaded (/proc/<pid>/maps). Ie, each address_list[path] is a list of address lists.
22 map_list : Information for /proc/<pid>/maps - [path]={'permissions', 'device', 'inode'}
23 path_list : All files (paths) loaded for the current command (/proc/<pid>/maps).
24 mapsfile : The first file path in /proc/<pid>/maps, ie, the file with the command
25 file_system : Device, /dev/..., on which mapsfile is stored
26 exe : The path to which /proc/<pid>/exe links
28 Methods returning contents of /proc/<pid>/maps as text tables
29 paths (command, key_list) : A table of all paths with one or more of ['permissions', 'device', 'inode'] (inode is default)
31 The following methods return the SHA512 hash
32 Files are read using the inode numbers with debugfs (sudo only)
33 inodeSHA (command, file): The file contents based on the inode number in /proc/<pid>/maps, if 'file' is '', the mapsfile is used.
34 mapsSHA (command) : All files in /proc/<pid>/maps, based on inode number
36 For debugging purposes:
37 fileSHA (command) : Simple file read from disk using mapsfile
38 exeSHA (command) : File read using the exe link
40 The following methods return the WHOLE, binary file content
41 Files are read using the inode numbers with debugfs (sudo only)
42 inode (command, file): The file based on the inode number in /proc/<pid>/maps
43 maps (command) : All files in /proc/<pid>/maps, based on inode number
45 For debugging purposes:
46 file (command) : Simple file read from disk using mapsfile
47 exe (command) : File read using the exe link
49 Standalone use:
50 > python3 proc_PID.py [path1 ....]
51 Will run for each path:
52 fileSHA (path)
53 exeSHA (path)
54 inodeSHA (path, '')
55 mapsSHA (path)
56 """
58 import sys;
59 import os;
60 import subprocess;
61 import hashlib;
62 import re;
64 # Define mainprefix
65 if __name__ == "__main__":
66 mainprefix = b'';
68 # Basic support routines
69 def create_process (command):
70 devnull = open('/dev/null', 'w');
71 return subprocess.Popen(command.split(), stdin = subprocess.PIPE, stdout = devnull, stderr = subprocess.PIPE, shell=False, universal_newlines=False);
73 def kill_process (process):
74 process.stdin.close();
75 errormess = process.stderr.read();
76 if len(errormess) > 0: print(str(errormess), file=sys.stderr);
78 # Define values
79 file_system = None;
80 address_list= {};
81 map_list = {};
82 path_list = [];
83 mapsfile = None;
84 exe = None;
85 current_command = '';
87 # Determine the file system on which a file is stored (using df)
88 def get_filesystem(path):
89 command = 'df -h '+path+' |tail -1';
90 process = subprocess.Popen(command, stdin = None, stdout = subprocess.PIPE, stderr = sys.stderr, shell=True, universal_newlines=True);
91 df_output = process.stdout.read();
92 return df_output.split()[0];
94 def get_debugfs_command ():
95 return "/sbin/debugfs -f - "+file_system+" -R ";
97 def reset ():
98 global file_system;
99 global address_list;
100 global map_list;
101 global path_list;
102 global mapsfile;
103 global exe;
104 global current_command;
105 file_system = None;
106 address_list = {};
107 map_list = {};
108 path_list = [];
109 mapsfile = None;
110 exe = None;
111 current_command = '';
113 # Start command and read info from /proc/<pid>/
114 # Do nothing if the command is identical to previous one
115 def getinfo (command): # -> pid
116 global file_system;
117 global address_list;
118 global map_list;
119 global path_list;
120 global mapsfile;
121 global exe;
122 global current_command;
124 if command == current_command:
125 return -1;
127 reset();
128 current_command = command;
130 # Do not create a process if it already exists
131 kill_process = False;
132 if isinstance(command, int):
133 pid = command;
134 elif re.search(r'[^0-9]', command) == None:
135 pid = int(command);
136 else:
137 process = create_process(command);
138 pid = process.pid;
140 dir = '/proc/'+str(pid);
141 exe = os.readlink(dir+'/exe');
143 # Get all mapped memory and files
144 with open(dir+'/maps', 'r') as file:
145 for l in file:
146 record_list = l.split();
147 if len(record_list) == 6 and int(record_list[4]) > 0:
148 (addresses, permissions, offset, device, inode, path) = record_list;
149 if path not in path_list:
150 path_list.append(path);
151 map_list[path] = {'permissions':permissions, 'device':device, 'inode':inode};
152 address_list[path] = [];
153 (address1, address2) = addresses.split('-');
154 address_list[path].append({'start':address1, 'end':address2, 'offset':offset});
155 if kill_process: kill_process(process);
156 mapsfile = path_list[0];
157 file_system = get_filesystem(mapsfile);
158 return pid;
160 # Print out running proc stats
161 def paths (command, key_list = 'inode'): # 'key'/['key1', 'key2', ...] -> text-table
162 getinfo (command);
164 # Normalize keys
165 if isinstance(key_list, str):
166 key_list = [key_list];
167 # Construct a line for each path,
168 result_table = '';
169 for path in path_list:
170 line = path ;
171 for key in key_list:
172 line += '\t'+map_list[path][key];
173 line += '\n';
174 result_table += line;
175 return result_table;
177 # Methods that return the raw file contents (could be LARGE)
178 def file (command):
179 getinfo (command);
180 return_value = b'';
181 with open(mapsfile, 'rb') as file:
182 for l in file:
183 if type(l).__name__ == 'str':
184 l = bytes(l, encoding='utf8');
185 return_value += l;
186 return return_value;
188 def exe (command):
189 getinfo (command);
190 return_value = b'';
191 with open(exe, 'rb') as file:
192 for l in file:
193 if type(l).__name__ == 'str':
194 l = bytes(l, encoding='utf8');
195 return_value += l;
196 kill_process(process);
197 return return_value;
199 def inode (command, path):
200 getinfo(command);
202 # Get file contents on inode number
203 return_value = b'';
204 inode = map_list[path]['inode'];
205 cat = "'cat <"+str(inode)+">'";
206 debugfs = subprocess.Popen(get_debugfs_command()+cat, stdin = None, stdout = subprocess.PIPE, stderr = sys.stderr, shell=True, universal_newlines=False);
207 # read out the file contents
208 for l in debugfs.stdout:
209 if type(l).__name__ == 'str':
210 l = bytes(l, encoding='utf8');
211 return_value += l;
213 return return_value;
215 def maps (command):
216 getinfo(command);
218 # Get file contents on inode number
219 return_value = b'';
220 for file in path_list:
221 inode = map_list[file]['inode'];
222 cat = "'cat <"+str(inode)+">'";
223 debugfs = subprocess.Popen(get_debugfs_command()+cat, stdin = None, stdout = subprocess.PIPE, stderr = sys.stderr, shell=True, universal_newlines=False);
224 # read out the file contents
225 for l in debugfs.stdout:
226 if type(l).__name__ == 'str':
227 l = bytes(l, encoding='utf8');
228 return_value += l;
230 return return_value;
232 # Methods that return the SHA512sum
233 def fileSHA (command):
234 getinfo (command);
235 filehash = hashlib.sha512(mainprefix);
236 with open(mapsfile, 'rb') as file:
237 for l in file:
238 if type(l).__name__ == 'str':
239 l = bytes(l, encoding='utf8');
240 filehash.update(l);
241 return str(filehash.hexdigest());
243 def exeSHA (command):
244 getinfo(command);
245 exehash = hashlib.sha512(mainprefix);
246 with open(exe, 'rb') as file:
247 for l in file:
248 if type(l).__name__ == 'str':
249 l = bytes(l, encoding='utf8');
250 exehash.update(l);
251 return str(exehash.hexdigest());
253 def inodeSHA (command, path=''):
254 getinfo(command);
255 if len(path) == 0: path = mapsfile;
257 # Get file contents on inode number
258 mapshash = hashlib.sha512(mainprefix);
259 inode = map_list[path]['inode'];
260 cat = "'cat <"+str(inode)+">'";
261 debugfs = subprocess.Popen(get_debugfs_command()+cat, stdin = None, stdout = subprocess.PIPE, stderr = sys.stderr, shell=True, universal_newlines=False);
262 # read out the file contents
263 for l in debugfs.stdout:
264 if type(l).__name__ == 'str':
265 l = bytes(l, encoding='utf8');
266 mapshash.update(l);
268 return str(mapshash.hexdigest());
270 def mapsSHA (command):
271 getinfo(command);
273 # Get file contents on inode number
274 mapshash = hashlib.sha512(mainprefix);
275 for path in path_list:
276 inode = map_list[path]['inode'];
277 cat = "'cat <"+str(inode)+">'";
278 debugfs = subprocess.Popen(get_debugfs_command()+cat, stdin = None, stdout = subprocess.PIPE, stderr = sys.stderr, shell=True, universal_newlines=False);
279 # read out the file contents
280 for l in debugfs.stdout:
281 if type(l).__name__ == 'str':
282 l = bytes(l, encoding='utf8');
283 mapshash.update(l);
285 return str(mapshash.hexdigest());
287 if __name__ == "__main__":
288 if len(sys.argv) == 1:
289 print(help_text);
290 else:
291 for command in sys.argv[1:]:
292 result = fileSHA(command);
293 print(mapsfile+(len("/proc/<pid>/inode") - len(mapsfile))*' ', result);
294 result = exeSHA(command);
295 print("/proc/<pid>/exe"+(len("/proc/<pid>/inode") - len("/proc/<pid>/exe"))*' ', result);
296 if os.getuid() == 0:
297 result = inodeSHA(command);
298 print("/proc/<pid>/inode"+(len(mapsfile) - len("/proc/<pid>/inode"))*' ', result);
299 result = mapsSHA(command);
300 print("/proc/<pid>/maps"+(len("/proc/<pid>/inode") - len("/proc/<pid>/all"))*' ', result);
302 print("");
303 result = paths(command);
304 print(result);
305 print("");