Debugging of the use of proc_PID
[signduterre.git] / proc_PID.py
blobc6a316fc30f6db319cbb848c6e6ab222635ce458
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. They use the prefix to start the hash
32 Files are read using the inode numbers with debugfs (sudo only)
33 inodeSHA (command, file, prefix): The file contents based on the inode number in /proc/<pid>/maps, if 'file' is '', the mapsfile is used.
34 mapsSHA (command, prefix) : All files in /proc/<pid>/maps, based on inode number
36 For debugging purposes:
37 fileSHA (command, prefix) : Simple file read from disk using mapsfile
38 exeSHA (command, prefix) : 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;
63 import time;
65 # Basic support routines
66 def create_process (command):
67 devnull = open('/dev/null', 'w');
68 return subprocess.Popen(command.split(), stdin = subprocess.PIPE, stdout = devnull, stderr = subprocess.PIPE, shell=False, universal_newlines=False);
70 def kill_process (process):
71 process.stdin.close();
72 errormess = process.stderr.read();
73 if len(errormess) > 0: print(str(errormess), file=sys.stderr);
75 # Define values
76 file_system = None;
77 address_list= {};
78 map_list = {};
79 path_list = [];
80 mapsfile = None;
81 exe = None;
82 current_command = '';
84 # Determine the file system on which a file is stored (using df)
85 def get_filesystem(path):
86 command = 'df -h '+path+' |tail -1';
87 process = subprocess.Popen(command, stdin = None, stdout = subprocess.PIPE, stderr = sys.stderr, shell=True, universal_newlines=True);
88 df_output = process.stdout.read();
89 return df_output.split()[0];
91 def get_debugfs_command ():
92 return "/sbin/debugfs -f - "+file_system+" -R ";
94 def reset ():
95 global file_system;
96 global address_list;
97 global map_list;
98 global path_list;
99 global mapsfile;
100 global exe;
101 global current_command;
102 file_system = None;
103 address_list = {};
104 map_list = {};
105 path_list = [];
106 mapsfile = None;
107 exe = None;
108 current_command = '';
110 # Start command and read info from /proc/<pid>/
111 # Do nothing if the command is identical to previous one
112 def getinfo (command): # -> pid
113 global file_system;
114 global address_list;
115 global map_list;
116 global path_list;
117 global mapsfile;
118 global exe;
119 global current_command;
121 if command == current_command:
122 return -1;
124 reset();
125 current_command = command;
127 # Do not create a process if it already exists
128 kill_process = False;
129 if isinstance(command, int):
130 pid = command;
131 elif re.search(r'[^0-9]', command) == None:
132 pid = int(command);
133 else:
134 process = create_process(command);
135 pid = process.pid;
136 # Wait a quarter of a second to allow the loading of the command to finish
137 time.sleep(0.25);
139 dir = '/proc/'+str(pid);
140 exe = os.readlink(dir+'/exe');
142 # Get all mapped memory and files
143 with open(dir+'/maps', 'r') as file:
144 for l in file:
145 record_list = l.split();
146 if len(record_list) == 6 and int(record_list[4]) > 0:
147 (addresses, permissions, offset, device, inode, path) = record_list;
148 if path not in path_list:
149 path_list.append(path);
150 map_list[path] = {'permissions':permissions, 'device':device, 'inode':inode};
151 address_list[path] = [];
152 (address1, address2) = addresses.split('-');
153 address_list[path].append({'start':address1, 'end':address2, 'offset':offset});
154 if kill_process: kill_process(process);
155 mapsfile = path_list[0];
156 file_system = get_filesystem(mapsfile);
157 return pid;
159 # Print out running proc stats
160 def paths (command, key_list = 'inode'): # 'key'/['key1', 'key2', ...] -> text-table
161 getinfo (command);
163 # Normalize keys
164 if isinstance(key_list, str):
165 key_list = [key_list];
166 # Construct a line for each path,
167 result_table = '';
168 for path in path_list:
169 line = path ;
170 for key in key_list:
171 line += '\t'+map_list[path][key];
172 line += '\n';
173 result_table += line;
174 return result_table;
176 # Methods that return the raw file contents (could be LARGE)
177 def file (command):
178 getinfo (command);
179 return_value = b'';
180 with open(mapsfile, 'rb') as file:
181 for l in file:
182 if type(l).__name__ == 'str':
183 l = bytes(l, encoding='utf8');
184 return_value += l;
185 return return_value;
187 def exe (command):
188 getinfo (command);
189 return_value = b'';
190 with open(exe, 'rb') as file:
191 for l in file:
192 if type(l).__name__ == 'str':
193 l = bytes(l, encoding='utf8');
194 return_value += l;
195 kill_process(process);
196 return return_value;
198 def inode (command, path):
199 getinfo(command);
201 # Get file contents on inode number
202 return_value = b'';
203 inode = map_list[path]['inode'];
204 cat = "'cat <"+str(inode)+">'";
205 debugfs = subprocess.Popen(get_debugfs_command()+cat, stdin = None, stdout = subprocess.PIPE, stderr = sys.stderr, shell=True, universal_newlines=False);
206 # read out the file contents
207 for l in debugfs.stdout:
208 if type(l).__name__ == 'str':
209 l = bytes(l, encoding='utf8');
210 return_value += l;
212 return return_value;
214 def maps (command):
215 getinfo(command);
217 # Get file contents on inode number
218 return_value = b'';
219 for file in path_list:
220 inode = map_list[file]['inode'];
221 cat = "'cat <"+str(inode)+">'";
222 debugfs = subprocess.Popen(get_debugfs_command()+cat, stdin = None, stdout = subprocess.PIPE, stderr = sys.stderr, shell=True, universal_newlines=False);
223 # read out the file contents
224 for l in debugfs.stdout:
225 if type(l).__name__ == 'str':
226 l = bytes(l, encoding='utf8');
227 return_value += l;
229 return return_value;
231 # Methods that return the SHA512sum
232 def fileSHA (command, prefix=b''):
233 getinfo (command);
234 filehash = hashlib.sha512(prefix);
235 with open(mapsfile, 'rb') as file:
236 for l in file:
237 if type(l).__name__ == 'str':
238 l = bytes(l, encoding='utf8');
239 filehash.update(l);
240 return str(filehash.hexdigest());
242 def exeSHA (command, prefix=b''):
243 getinfo(command);
244 exehash = hashlib.sha512(prefix);
245 with open(exe, 'rb') as file:
246 for l in file:
247 if type(l).__name__ == 'str':
248 l = bytes(l, encoding='utf8');
249 exehash.update(l);
250 return str(exehash.hexdigest());
252 def inodeSHA (command, path='', prefix=b''):
253 getinfo(command);
254 if len(path) == 0: path = mapsfile;
256 # Get file contents on inode number
257 mapshash = hashlib.sha512(prefix);
258 inode = map_list[path]['inode'];
259 cat = "'cat <"+str(inode)+">'";
260 debugfs = subprocess.Popen(get_debugfs_command()+cat, stdin = None, stdout = subprocess.PIPE, stderr = sys.stderr, shell=True, universal_newlines=False);
261 # read out the file contents
262 for l in debugfs.stdout:
263 if type(l).__name__ == 'str':
264 l = bytes(l, encoding='utf8');
265 mapshash.update(l);
267 return str(mapshash.hexdigest());
269 def mapsSHA (command, prefix=b''):
270 getinfo(command);
272 # Get file contents on inode number
273 mapshash = hashlib.sha512(prefix);
274 for path in path_list:
275 inode = map_list[path]['inode'];
276 cat = "'cat <"+str(inode)+">'";
277 debugfs = subprocess.Popen(get_debugfs_command()+cat, stdin = None, stdout = subprocess.PIPE, stderr = sys.stderr, shell=True, universal_newlines=False);
278 # read out the file contents
279 for l in debugfs.stdout:
280 if type(l).__name__ == 'str':
281 l = bytes(l, encoding='utf8');
282 mapshash.update(l);
284 return str(mapshash.hexdigest());
286 if __name__ == "__main__":
287 if len(sys.argv) == 1:
288 print(help_text);
289 else:
290 for command in sys.argv[1:]:
291 result = fileSHA(command);
292 print(mapsfile+(len("/proc/<pid>/inode") - len(mapsfile))*' ', result);
293 result = exeSHA(command);
294 print("/proc/<pid>/exe"+(len("/proc/<pid>/inode") - len("/proc/<pid>/exe"))*' ', result);
295 if os.getuid() == 0:
296 result = inodeSHA(command);
297 print("/proc/<pid>/inode"+(len(mapsfile) - len("/proc/<pid>/inode"))*' ', result);
298 result = mapsSHA(command);
299 print("/proc/<pid>/maps"+(len("/proc/<pid>/inode") - len("/proc/<pid>/all"))*' ', result);
301 print("");
302 result = paths(command);
303 print(result);
304 print("");