KittyMemoryEx.cpp
1 #include "KittyMemoryEx.hpp" 2 #include "KittyIOFile.hpp" 3 #include <fstream> 4 5 6 namespace KittyMemoryEx 7 { 8 std::string getProcessName(pid_t pid) 9 { 10 if (pid <= 0) 11 return ""; 12 13 char filePath[256] = {0}; 14 snprintf(filePath, sizeof(filePath), "/proc/%d/cmdline", pid); 15 16 errno = 0; 17 FILE *fp = fopen(filePath, "r"); 18 if (!fp) 19 { 20 KITTY_LOGD("Couldn't open cmdline file %s, error=%s", filePath, strerror(errno)); 21 return ""; 22 } 23 24 char cmdline[128] = {0}; 25 fgets(cmdline, sizeof(cmdline), fp); 26 fclose(fp); 27 return cmdline; 28 } 29 30 std::vector<pid_t> getProcessIDs(const std::string &processName) 31 { 32 std::vector<pid_t> pids; 33 34 if (processName.empty()) 35 return pids; 36 37 errno = 0; 38 DIR *dir = opendir("/proc/"); 39 if (!dir) 40 { 41 KITTY_LOGD("Couldn't open /proc/, error=%s", strerror(errno)); 42 return pids; 43 } 44 45 dirent *entry = nullptr; 46 while ((entry = readdir(dir)) != nullptr) 47 { 48 int entry_pid = atoi(entry->d_name); 49 if (entry_pid > 0) 50 { 51 if (processName == getProcessName(entry_pid)) 52 { 53 pids.push_back(entry_pid); 54 } 55 } 56 } 57 closedir(dir); 58 return pids; 59 } 60 61 pid_t getProcessID(const std::string &processName) 62 { 63 if (processName.empty()) 64 return 0; 65 66 pid_t pid = 0; 67 68 errno = 0; 69 DIR *dir = opendir("/proc/"); 70 if (!dir) 71 { 72 KITTY_LOGD("Couldn't open /proc/, error=%s", strerror(errno)); 73 return pid; 74 } 75 76 dirent *entry = nullptr; 77 while ((entry = readdir(dir)) != nullptr) 78 { 79 if (entry->d_name[0] == '.') 80 continue; 81 82 int entry_pid = atoi(entry->d_name); 83 if (entry_pid > 0) 84 { 85 if (processName == getProcessName(entry_pid)) 86 { 87 pid = entry_pid; 88 break; 89 } 90 } 91 } 92 closedir(dir); 93 return pid; 94 } 95 96 std::vector<pid_t> getAllThreads(pid_t pid) 97 { 98 std::vector<pid_t> tids; 99 if (pid <= 0) 100 return tids; 101 102 char filePath[256] = {0}; 103 snprintf(filePath, sizeof(filePath), "/proc/%d/task", pid); 104 105 errno = 0; 106 DIR *dir = opendir(filePath); 107 if (!dir) 108 { 109 KITTY_LOGD("Couldn't open %s, error=%s", filePath, strerror(errno)); 110 return tids; 111 } 112 113 dirent *entry = nullptr; 114 while ((entry = readdir(dir)) != nullptr) 115 { 116 if (entry->d_name[0] == '.') 117 continue; 118 119 int entry_tid = atoi(entry->d_name); 120 if (entry_tid > 0) 121 { 122 tids.push_back(entry_tid); 123 } 124 } 125 closedir(dir); 126 return tids; 127 } 128 129 bool ProcStatus::parse(const std::string &path, ProcStatus *out) 130 { 131 if (out) 132 out->data.clear(); 133 134 std::ifstream file(path.c_str()); 135 if (!file.is_open()) 136 return false; 137 138 std::string line; 139 140 while (std::getline(file, line)) 141 { 142 std::size_t colon = line.find(':'); 143 if (colon == std::string::npos) 144 continue; 145 146 std::string key = line.substr(0, colon); 147 148 while (!key.empty() && std::isspace(static_cast<unsigned char>(key.back()))) 149 key.pop_back(); 150 151 const char *p = line.c_str() + colon + 1; 152 153 while (*p && std::isspace(static_cast<unsigned char>(*p))) 154 ++p; 155 156 std::string value(p); 157 158 while (!value.empty() && std::isspace(static_cast<unsigned char>(value.back()))) 159 value.pop_back(); 160 161 if (out) 162 out->data.emplace(std::move(key), std::string(p)); 163 } 164 165 return true; 166 } 167 168 std::vector<ProcMap> getAllMaps(pid_t pid) 169 { 170 std::vector<ProcMap> retMaps; 171 if (pid <= 0) 172 return retMaps; 173 174 char filePath[256] = {0}; 175 snprintf(filePath, sizeof(filePath), "/proc/%d/maps", pid); 176 177 errno = 0; 178 FILE *fp = fopen(filePath, "r"); 179 if (!fp) 180 { 181 KITTY_LOGD("Couldn't open maps file %s, error=%s", filePath, strerror(errno)); 182 return retMaps; 183 } 184 185 char line[512] = {0}; 186 while (fgets(line, sizeof(line), fp)) 187 { 188 ProcMap map; 189 map.pid = pid; 190 191 char perms[5] = {0}, dev[11] = {0}, pathname[256] = {0}; 192 // parse a line in maps file 193 // (format) startAddress-endAddress perms offset dev inode pathname 194 sscanf(line, 195 "%" SCNxPTR "-%" SCNxPTR " %4s %" SCNxPTR " %s %lu %s", 196 &map.startAddress, 197 &map.endAddress, 198 perms, 199 &map.offset, 200 dev, 201 &map.inode, 202 pathname); 203 204 map.length = map.endAddress - map.startAddress; 205 map.dev = dev; 206 map.pathname = pathname; 207 208 if (perms[0] == 'r') 209 { 210 map.protection |= PROT_READ; 211 map.readable = true; 212 } 213 if (perms[1] == 'w') 214 { 215 map.protection |= PROT_WRITE; 216 map.writeable = true; 217 } 218 if (perms[2] == 'x') 219 { 220 map.protection |= PROT_EXEC; 221 map.executable = true; 222 } 223 224 map.is_private = (perms[3] == 'p'); 225 map.is_shared = (perms[3] == 's'); 226 227 map.is_rx = (strncmp(perms, "r-x", 3) == 0); 228 map.is_rw = (strncmp(perms, "rw-", 3) == 0); 229 map.is_ro = (strncmp(perms, "r--", 3) == 0); 230 231 retMaps.push_back(map); 232 } 233 234 fclose(fp); 235 236 if (retMaps.empty()) 237 { 238 KITTY_LOGD("getAllMaps err couldn't find any map"); 239 } 240 return retMaps; 241 } 242 243 std::vector<ProcMap> getMaps(pid_t pid, 244 EProcMapFilter filter, 245 const std::string &name, 246 const std::vector<ProcMap> &maps) 247 { 248 std::vector<ProcMap> retMaps; 249 regex_t re{}; 250 bool isRegex = (filter == EProcMapFilter::Regex); 251 252 if (isRegex) 253 { 254 if (regcomp(&re, name.c_str(), REG_EXTENDED | REG_NOSUB) != 0) 255 return retMaps; 256 } 257 258 for (auto &it : (maps.empty() ? getAllMaps(pid) : maps)) 259 { 260 if (!it.isValid()) 261 continue; 262 263 bool match = false; 264 switch (filter) 265 { 266 case EProcMapFilter::Equal: 267 match = (it.pathname == name); 268 break; 269 case EProcMapFilter::StartWith: 270 match = KittyUtils::String::startsWith(it.pathname, name); 271 break; 272 case EProcMapFilter::EndWith: 273 match = KittyUtils::String::endsWith(it.pathname, name); 274 break; 275 case EProcMapFilter::Regex: 276 match = (regexec(&re, it.pathname.c_str(), 0, NULL, 0) == 0); 277 break; 278 case EProcMapFilter::Contains: 279 default: 280 match = KittyUtils::String::contains(it.pathname, name); 281 break; 282 } 283 284 if (match) 285 { 286 retMaps.push_back(it); 287 } 288 } 289 290 if (isRegex) 291 regfree(&re); 292 293 return retMaps; 294 } 295 296 std::vector<ProcMap> getMaps(EProcMapFilter filter, const std::string &name, const std::vector<ProcMap> &maps) 297 { 298 std::vector<ProcMap> retMaps; 299 regex_t re{}; 300 bool isRegex = (filter == EProcMapFilter::Regex); 301 302 if (isRegex) 303 { 304 if (regcomp(&re, name.c_str(), REG_EXTENDED | REG_NOSUB) != 0) 305 return retMaps; 306 } 307 308 for (const auto &it : maps) 309 { 310 if (!it.isValid()) 311 continue; 312 313 bool match = false; 314 switch (filter) 315 { 316 case EProcMapFilter::Equal: 317 match = (it.pathname == name); 318 break; 319 case EProcMapFilter::StartWith: 320 match = KittyUtils::String::startsWith(it.pathname, name); 321 break; 322 case EProcMapFilter::EndWith: 323 match = KittyUtils::String::endsWith(it.pathname, name); 324 break; 325 case EProcMapFilter::Regex: 326 match = (regexec(&re, it.pathname.c_str(), 0, NULL, 0) == 0); 327 break; 328 case EProcMapFilter::Contains: 329 default: 330 match = KittyUtils::String::contains(it.pathname, name); 331 break; 332 } 333 334 if (match) 335 { 336 retMaps.push_back(it); 337 } 338 } 339 340 if (isRegex) 341 regfree(&re); 342 343 return retMaps; 344 } 345 346 ProcMap getAddressMap(pid_t pid, uintptr_t address, const std::vector<ProcMap> &maps) 347 { 348 if (!address) 349 return {}; 350 351 address = KittyUtils::untagHeepPtr(address); 352 353 if (!maps.empty()) 354 { 355 auto it = std::lower_bound(maps.begin(), maps.end(), address, [](const ProcMap &m, uintptr_t val) { 356 return m.endAddress <= val; 357 }); 358 359 if (it != maps.end() && address >= it->startAddress && address < it->endAddress) 360 { 361 return *it; 362 } 363 } 364 else 365 { 366 auto pmaps = getAllMaps(pid); 367 auto it = std::lower_bound(pmaps.begin(), pmaps.end(), address, [](const ProcMap &m, uintptr_t val) { 368 return m.endAddress <= val; 369 }); 370 371 if (it != pmaps.end() && address >= it->startAddress && address < it->endAddress) 372 { 373 return *it; 374 } 375 } 376 377 return {}; 378 } 379 380 #ifdef __ANDROID__ 381 std::string getAppDirectory(const std::string &pkg) 382 { 383 std::string directory = "/data/app/", base_apk = "base.apk", ret; 384 KittyIOFile::listFilesCallback(directory, [&](const std::string &filePath) { 385 if (KittyUtils::Path::fileName(filePath) == base_apk) 386 { 387 const std::string fileDir = KittyUtils::Path::fileDirectory(filePath); 388 if (strstr(fileDir.c_str(), pkg.c_str())) 389 { 390 ret = fileDir; 391 return true; 392 } 393 } 394 return false; 395 }); 396 return ret; 397 } 398 #endif 399 400 } // namespace KittyMemoryEx