|
2 | 2 | use colored::Colorize;
|
3 | 3 | use serde::{Deserialize, Serialize};
|
4 | 4 | use std::fmt;
|
| 5 | +#[cfg(all(feature = "maps", target_os = "linux"))] |
| 6 | +use std::fs; |
| 7 | +#[cfg(all(feature = "maps", target_os = "linux"))] |
| 8 | +use std::io::Error; |
| 9 | +#[cfg(all(feature = "maps", target_os = "linux"))] |
| 10 | +use std::io::ErrorKind; |
| 11 | +#[cfg(all(feature = "maps", target_os = "linux"))] |
| 12 | +use std::path::PathBuf; |
| 13 | +use std::usize; |
5 | 14 |
|
6 | 15 | #[cfg(feature = "elf")]
|
7 | 16 | use checksec::elf;
|
@@ -135,15 +144,170 @@ impl Binaries {
|
135 | 144 | }
|
136 | 145 | }
|
137 | 146 |
|
| 147 | +#[cfg(all(feature = "maps", target_os = "linux"))] |
| 148 | +#[derive(Deserialize, Serialize)] |
| 149 | +pub struct Region { |
| 150 | + pub start: usize, |
| 151 | + pub end: usize, |
| 152 | +} |
| 153 | +#[cfg(all(feature = "maps", target_os = "linux"))] |
| 154 | +impl Region { |
| 155 | + pub fn new(start: usize, end: usize) -> Self { |
| 156 | + Self { start, end } |
| 157 | + } |
| 158 | +} |
| 159 | + |
| 160 | +#[cfg(all(feature = "maps", target_os = "linux"))] |
| 161 | +#[derive(Deserialize, Serialize)] |
| 162 | +pub struct MapFlags { |
| 163 | + pub r: bool, |
| 164 | + pub w: bool, |
| 165 | + pub x: bool, |
| 166 | +} |
| 167 | +#[cfg(all(feature = "maps", target_os = "linux"))] |
| 168 | +impl MapFlags { |
| 169 | + pub fn new(flagstr: &str) -> Self { |
| 170 | + let r = flagstr.get(0..1) == Some("r"); |
| 171 | + let w = flagstr.get(1..2) == Some("w"); |
| 172 | + let x = flagstr.get(2..3) == Some("x"); |
| 173 | + Self { r, w, x } |
| 174 | + } |
| 175 | +} |
| 176 | +#[cfg(all(not(feature = "color"), feature = "maps", target_os = "linux"))] |
| 177 | +impl fmt::Display for MapFlags { |
| 178 | + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 179 | + write!( |
| 180 | + f, |
| 181 | + "{}{}{}", |
| 182 | + if self.r { "r" } else { "-" }, |
| 183 | + if self.w { "w" } else { "-" }, |
| 184 | + if self.x { "x" } else { "-" } |
| 185 | + ) |
| 186 | + } |
| 187 | +} |
| 188 | +#[cfg(all(feature = "color", feature = "maps", target_os = "linux"))] |
| 189 | +impl fmt::Display for MapFlags { |
| 190 | + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 191 | + if self.r & self.w & self.x { |
| 192 | + return write!(f, "{}", "rwx".red()); |
| 193 | + } |
| 194 | + write!( |
| 195 | + f, |
| 196 | + "{}{}{}", |
| 197 | + if self.r { "r" } else { "-" }, |
| 198 | + if self.w { "w" } else { "-" }, |
| 199 | + if self.x { "x" } else { "-" } |
| 200 | + ) |
| 201 | + } |
| 202 | +} |
| 203 | + |
| 204 | +#[cfg(all(feature = "maps", target_os = "linux"))] |
| 205 | +#[derive(Deserialize, Serialize)] |
| 206 | +pub struct MapEntry { |
| 207 | + pub region: Region, |
| 208 | + pub flags: MapFlags, |
| 209 | + pub pathname: Option<PathBuf>, |
| 210 | +} |
| 211 | +#[cfg(all(not(feature = "color"), feature = "maps", target_os = "linux"))] |
| 212 | +impl fmt::Display for MapEntry { |
| 213 | + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 214 | + write!( |
| 215 | + f, |
| 216 | + "0x{:x}->0x{:x} {} {}", |
| 217 | + self.region.start, |
| 218 | + self.region.end, |
| 219 | + self.flags, |
| 220 | + match &self.pathname { |
| 221 | + Some(pathname) => pathname.display().to_string(), |
| 222 | + None => "".to_string(), |
| 223 | + } |
| 224 | + ) |
| 225 | + } |
| 226 | +} |
| 227 | +#[cfg(all(feature = "color", feature = "maps", target_os = "linux"))] |
| 228 | +impl fmt::Display for MapEntry { |
| 229 | + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 230 | + if self.flags.r & self.flags.w & self.flags.x { |
| 231 | + write!( |
| 232 | + f, |
| 233 | + "{} {}", |
| 234 | + format!( |
| 235 | + "0x{:x}->0x{:x} {}", |
| 236 | + self.region.start, self.region.end, self.flags |
| 237 | + ) |
| 238 | + .red(), |
| 239 | + match &self.pathname { |
| 240 | + Some(pathname) => pathname.display().to_string().red(), |
| 241 | + None => "".to_string().red(), |
| 242 | + } |
| 243 | + ) |
| 244 | + } else { |
| 245 | + write!( |
| 246 | + f, |
| 247 | + "0x{:x}->0x{:x} {} {}", |
| 248 | + self.region.start, |
| 249 | + self.region.end, |
| 250 | + self.flags, |
| 251 | + match &self.pathname { |
| 252 | + Some(pathname) => pathname.display().to_string(), |
| 253 | + None => "".to_string(), |
| 254 | + } |
| 255 | + ) |
| 256 | + } |
| 257 | + } |
| 258 | +} |
| 259 | + |
138 | 260 | #[derive(Deserialize, Serialize)]
|
139 | 261 | pub struct Process {
|
140 | 262 | pub pid: usize,
|
141 | 263 | pub binary: Vec<Binary>,
|
| 264 | + #[cfg(all(feature = "maps", target_os = "linux"))] |
| 265 | + pub maps: Option<Vec<MapEntry>>, |
142 | 266 | }
|
143 | 267 | impl Process {
|
| 268 | + #[cfg(any(not(feature = "maps"), not(target_os = "linux")))] |
144 | 269 | pub fn new(pid: usize, binary: Vec<Binary>) -> Self {
|
145 | 270 | Self { pid, binary }
|
146 | 271 | }
|
| 272 | + #[cfg(all(feature = "maps", target_os = "linux"))] |
| 273 | + pub fn new(pid: usize, binary: Vec<Binary>) -> Self { |
| 274 | + match Process::parse_maps(pid) { |
| 275 | + Ok(maps) => Self { pid, binary, maps: Some(maps) }, |
| 276 | + Err(_) => Self { pid, binary, maps: None }, |
| 277 | + } |
| 278 | + } |
| 279 | + #[cfg(all(feature = "maps", target_os = "linux"))] |
| 280 | + pub fn parse_maps(pid: usize) -> Result<Vec<MapEntry>, Error> { |
| 281 | + let mut maps: Vec<MapEntry> = Vec::new(); |
| 282 | + if let Ok(maps_str) = fs::read_to_string(format!("/proc/{}/maps", pid)) |
| 283 | + { |
| 284 | + for line in maps_str.lines() { |
| 285 | + let mut split_line = line.split_whitespace(); |
| 286 | + let (start_str, end_str) = split_line |
| 287 | + .next() |
| 288 | + .ok_or(ErrorKind::InvalidData)? |
| 289 | + .split_once('-') |
| 290 | + .ok_or(ErrorKind::InvalidData)?; |
| 291 | + let region = Region::new( |
| 292 | + usize::from_str_radix(start_str, 16).unwrap_or(0), |
| 293 | + usize::from_str_radix(end_str, 16).unwrap_or(0), |
| 294 | + ); |
| 295 | + let flags = MapFlags::new( |
| 296 | + split_line.next().ok_or(ErrorKind::InvalidData)?, |
| 297 | + ); |
| 298 | + split_line.next(); // skip offset |
| 299 | + split_line.next(); // skip dev |
| 300 | + split_line.next(); // skip inode |
| 301 | + let pathname = |
| 302 | + Some(split_line.collect::<Vec<&str>>().join(" ")) |
| 303 | + .filter(|x| !x.is_empty()) |
| 304 | + .map(PathBuf::from); |
| 305 | + maps.push(MapEntry { region, flags, pathname }); |
| 306 | + } |
| 307 | + return Ok(maps); |
| 308 | + } |
| 309 | + Err(Error::last_os_error()) |
| 310 | + } |
147 | 311 | }
|
148 | 312 |
|
149 | 313 | #[derive(Deserialize, Serialize)]
|
|
0 commit comments