KittyMemoryEx 1.0.0
Dedicated library for runtime code patching, memory scanning, tracing, injection and some more useful memory utilities. works for both Android and Linux
Loading...
Searching...
No Matches
KittyUtils.hpp
1#pragma once
2
3#include <sys/mman.h>
4#include <sys/types.h>
5#include <sys/stat.h>
6
7#include <regex.h>
8#include <stdio.h>
9#include <fcntl.h>
10#include <unistd.h>
11#include <dirent.h>
12
13#include <errno.h>
14#include <inttypes.h>
15
16#include <cerrno>
17#include <cstring>
18#include <cstdint>
19#include <cstdarg>
20#include <cctype>
21
22#include <string>
23#include <sstream>
24#include <iomanip>
25#include <memory>
26#include <algorithm>
27#include <vector>
28#include <utility>
29#include <map>
30#include <random>
31#include <functional>
32#include <mutex>
33#include <type_traits>
34
35#ifdef __ANDROID__
36#include <sys/system_properties.h>
37#endif
38
42inline size_t KTGetPageSize()
43{
44 static long pageSize = 0;
45
46 if (pageSize <= 0)
47 pageSize = sysconf(_SC_PAGESIZE);
48
49 return pageSize > 0 ? static_cast<size_t>(pageSize) : 4096;
50}
51
52#define KT_PAGE_SIZE (KTGetPageSize())
53#define KT_PAGE_START(x) (uintptr_t(x) & ~(KT_PAGE_SIZE - 1))
54#define KT_PAGE_END(x) (KT_PAGE_START(uintptr_t(x) + KT_PAGE_SIZE - 1))
55#define KT_PAGE_OFFSET(x) (uintptr_t(x) - KT_PAGE_START(x))
56#define KT_PAGE_REMAIN(x) (size_t(KT_PAGE_SIZE - KT_PAGE_OFFSET(x)))
57
58#define KT_ALIGN_UP(ptr, align) (((uintptr_t)(ptr) + (align) - 1) & ~((align) - 1))
59#define KT_ALIGN_DOWN(ptr, align) (((uintptr_t)(ptr)) & ~((uintptr_t)(align) - 1))
60
61#define KT_IS_ALIGNED_OF(ptr, align) ((((uintptr_t)(ptr)) & ((uintptr_t)(align) - 1)) == 0)
62
63#define KT_IO_CHUNK_SIZE ((size_t)(1024 * 1024))
64
65#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
66typedef off_t kt_off64_t;
67typedef struct stat kt_stat64_t;
68#define kt_lseek64 ::lseek
69#define kt_pread64 ::pread
70#define kt_pwrite64 ::pwrite
71#define kt_stat64 ::stat
72#else
73typedef off64_t kt_off64_t;
74typedef struct stat64 kt_stat64_t;
75#define kt_lseek64 ::lseek64
76#define kt_pread64 ::pread64
77#define kt_pwrite64 ::pwrite64
78#define kt_stat64 ::stat64
79#endif
80
81#define KT_EINTR_RETRY(exp) \
82 ({ \
83 __typeof__(exp) _rc; \
84 do \
85 { \
86 _rc = (exp); \
87 } while (_rc == -1 && errno == EINTR); \
88 _rc; \
89 })
90
91#if defined(__ANDROID__) && defined(kUSE_LOGCAT)
92
93#include <android/log.h>
94#define KITTY_LOG_TAG "KittyMemoryEx"
95
96#ifdef kITTYMEMORY_DEBUG
97#define KITTY_LOGD(fmt, ...) ((void)__android_log_print(ANDROID_LOG_DEBUG, KITTY_LOG_TAG, fmt, ##__VA_ARGS__))
98#else
99#define KITTY_LOGD(fmt, ...) \
100 do \
101 { \
102 } while (0)
103#endif
104
105#define KITTY_LOGI(fmt, ...) ((void)__android_log_print(ANDROID_LOG_INFO, KITTY_LOG_TAG, fmt, ##__VA_ARGS__))
106#define KITTY_LOGE(fmt, ...) ((void)__android_log_print(ANDROID_LOG_ERROR, KITTY_LOG_TAG, fmt, ##__VA_ARGS__))
107#define KITTY_LOGW(fmt, ...) ((void)__android_log_print(ANDROID_LOG_WARN, KITTY_LOG_TAG, fmt, ##__VA_ARGS__))
108
109#else
110
111#ifdef kITTYMEMORY_DEBUG
112#define KITTY_LOGD(fmt, ...) \
113 do \
114 { \
115 printf("D: " fmt "\n", ##__VA_ARGS__); \
116 } while (0)
117#else
118#define KITTY_LOGD(fmt, ...) \
119 do \
120 { \
121 } while (0)
122#endif
123
124#define KITTY_LOGI(fmt, ...) \
125 do \
126 { \
127 printf("I: " fmt "\n", ##__VA_ARGS__); \
128 } while (0)
129
130#define KITTY_LOGE(fmt, ...) \
131 do \
132 { \
133 fflush(stdout); \
134 fflush(stderr); \
135 fprintf(stderr, "E: " fmt "\n", ##__VA_ARGS__); \
136 fflush(stderr); \
137 } while (0)
138
139#define KITTY_LOGW(fmt, ...) \
140 do \
141 { \
142 printf("W: " fmt "\n", ##__VA_ARGS__); \
143 } while (0)
144
145#endif
146
147#include <elf.h>
148#ifdef __LP64__
149#define KT_ELFCLASS_BITS 64
150#define KT_ELF_EICLASS 2
151#define KT_ElfW(x) Elf64_##x
152#define KT_ELFW(x) ELF64_##x
153#else
154#define KT_ELFCLASS_BITS 32
155#define KT_ELF_EICLASS 1
156#define KT_ElfW(x) Elf32_##x
157#define KT_ELFW(x) ELF32_##x
158#endif
159#define KT_ELF_ST_BIND(val) (((unsigned char)(val)) >> 4)
160#define KT_ELF_ST_TYPE(val) ((val) & 0xf)
161#define KT_ELF_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf))
162#define KT_ELF_ST_VISIBILITY(o) ((o) & 0x03)
163
167namespace KittyUtils
168{
169
173 class KTScopedFD
174 {
175 public:
176 explicit KTScopedFD(int fd) : _fd(fd)
177 {
178 }
179
180 ~KTScopedFD()
181 {
182 if (_fd >= 0)
183 KT_EINTR_RETRY(close(_fd));
184 }
185
186 int get() const
187 {
188 return _fd;
189 }
190
191 KTScopedFD(const KTScopedFD &) = delete;
192 KTScopedFD &operator=(const KTScopedFD &) = delete;
193
194 private:
195 int _fd;
196 };
197
201 class KTScopedMMap
202 {
203 public:
204 KTScopedMMap(void *ptr, size_t size) : _ptr(ptr), _size(size)
205 {
206 }
207
208 ~KTScopedMMap()
209 {
210 if (_ptr && _ptr != MAP_FAILED)
211 munmap(_ptr, _size);
212 }
213
214 uint8_t *data() const
215 {
216 return static_cast<uint8_t *>(_ptr);
217 }
218
219 size_t size() const
220 {
221 return _size;
222 }
223
224 bool valid() const
225 {
226 return _ptr && _ptr != MAP_FAILED;
227 }
228
229 private:
230 void *_ptr;
231 size_t _size;
232 };
233
234#ifdef __ANDROID__
238 namespace Android
239 {
240 inline int getUserId()
241 {
242 uid_t uid = getuid(); // Linux UID
243 return uid / 100000; // approximate Android user ID
244 }
245
257 template <typename T>
258 inline T getSystemProperty(const std::string &key, T defaultValue)
259 {
260 static_assert(
261 std::is_same_v<T, std::string> || std::is_same_v<T, bool> || std::is_integral_v<T> ||
262 std::is_floating_point_v<T>,
263 "getSystemProperty: unsupported type. Supported types: string, bool, integral, floating-point.");
264
265 char value[PROP_VALUE_MAX] = {0};
266 int len = __system_property_get(key.c_str(), value);
267 if (len <= 0)
268 return defaultValue;
269
270 if constexpr (std::is_same_v<T, std::string>)
271 {
272 return std::string(value, len);
273 }
274 else if constexpr (std::is_same_v<T, bool>)
275 {
276 for (int i = 0; i < len; ++i)
277 value[i] = std::tolower(value[i]);
278
279 if (std::strcmp(value, "1") == 0 || std::strcmp(value, "true") == 0 || std::strcmp(value, "y") == 0 ||
280 std::strcmp(value, "yes") == 0)
281 return true;
282
283 if (std::strcmp(value, "0") == 0 || std::strcmp(value, "false") == 0 || std::strcmp(value, "n") == 0 ||
284 std::strcmp(value, "no") == 0)
285 return false;
286
287 return defaultValue;
288 }
289 else if constexpr (std::is_integral_v<T>)
290 {
291 char *end = nullptr;
292 long long result = std::strtoll(value, &end, 0);
293 if (end == value)
294 return defaultValue;
295 return static_cast<T>(result);
296 }
297 else if constexpr (std::is_floating_point_v<T>)
298 {
299 char *end = nullptr;
300 double result = std::strtod(value, &end);
301 if (end == value)
302 return defaultValue;
303 return static_cast<T>(result);
304 }
305
306 // unsupported type
307 return defaultValue;
308 }
309
313 int getVersion();
314
318 int getSDK();
319
323 bool is64BitSupported();
324
335 inline std::string getExternalStorage()
336 {
337 const char *storage = std::getenv("EXTERNAL_STORAGE");
338 return (storage && storage[0] != '\0') ? storage : "/sdcard";
339 }
340
353 std::string getAppInternalDataDir(const std::string &packageName);
354
367 std::string getAppInternalFilesDir(const std::string &packageName);
368
383 std::string getAppInternalCacheDir(const std::string &packageName);
384
394 inline std::string getAppExternalDataDir(const std::string &packageName)
395 {
396 return getExternalStorage() + "/Android/data/" + packageName;
397 }
398
411 inline std::string getAppExternalFilesDir(const std::string &packageName)
412 {
413 return getAppExternalDataDir(packageName) + "/files";
414 }
415
428 inline std::string getAppExternalCacheDir(const std::string &packageName)
429 {
430 return getAppExternalDataDir(packageName) + "/cache";
431 }
432
442 inline std::string getAppExternalMediaDir(const std::string &packageName)
443 {
444 return getExternalStorage() + "/Android/media/" + packageName;
445 }
446
456 inline std::string getAppObbDir(const std::string &packageName)
457 {
458 return getExternalStorage() + "/Android/obb/" + packageName;
459 }
460 } // namespace Android
461#endif
462
474 inline uintptr_t untagPointer(uintptr_t ptr)
475 {
476#if defined(__LP64__) && defined(__ANDROID__)
477 return ptr & ((static_cast<uintptr_t>(1) << 56) - 1);
478#else
479 return ptr;
480#endif
481 }
482
489 template <typename T>
490 inline T *untagPointer(T *ptr)
491 {
492 return reinterpret_cast<T *>(untagPointer(reinterpret_cast<uintptr_t>(ptr)));
493 }
494
501 template <typename T>
502 inline const T *untagPointer(const T *ptr)
503 {
504 return reinterpret_cast<const T *>(untagPointer(reinterpret_cast<uintptr_t>(ptr)));
505 }
506
510 namespace Path
511 {
519 std::string fileName(const std::string &filePath);
520
528 std::string fileDirectory(const std::string &filePath);
529
537 std::string fileExtension(const std::string &filePath);
538 } // namespace Path
539
543 namespace String
544 {
548 inline bool charEqualsIgnoreCase(char a, char b)
549 {
550 return std::tolower(static_cast<unsigned char>(a)) == std::tolower(static_cast<unsigned char>(b));
551 }
552
562 bool startsWith(const std::string &str, const std::string &prefix, bool sensitive = true);
563
573 bool startsWith(const std::string &str, const std::vector<std::string> &prefixes, bool sensitive = true);
574
584 bool contains(const std::string &str, const std::string &substring, bool sensitive = true);
585
595 bool contains(const std::string &str, const std::vector<std::string> &substrings, bool sensitive = true);
596
606 bool endsWith(const std::string &str, const std::string &suffix, bool sensitive = true);
607
617 bool endsWith(const std::string &str, const std::vector<std::string> &suffixes, bool sensitive = true);
618
624 void trim(std::string &str);
625
631 void removeAllWhitespace(std::string &str);
632
642 bool isValidHex(const std::string &hex);
643
650 bool validateHex(std::string &hex);
651
659 std::string fmt(const char *fmt, ...);
660 } // namespace String
661
670 template <typename T>
671 T randInt(T min, T max)
672 {
673 using param_type = typename std::uniform_int_distribution<T>::param_type;
674
675 static std::mutex mtx;
676 std::lock_guard<std::mutex> lock(mtx);
677
678 static std::mt19937 gen{std::random_device{}()};
679
680 std::uniform_int_distribution<T> dist;
681 return dist(gen, param_type{min, max});
682 }
683
690 std::vector<uint8_t> randomBytes(std::size_t length);
691
698 std::string randomString(size_t length);
699
703 namespace Data
704 {
714 bool fromHex(std::string in, void *data);
715
724 std::string toHex(const void *data, const size_t dataLength);
725
734 template <typename T>
735 std::string toHex(const T &data)
736 {
737 return toHex(&data, sizeof(T));
738 }
739
757 template <size_t rowSize = 8, bool showASCII = true>
758 std::string hexDump(const void *address, size_t len)
759 {
760 if (!address || len == 0 || rowSize == 0)
761 return "";
762
763 const unsigned char *data = static_cast<const unsigned char *>(address);
764
765 std::stringstream ss;
766 ss << std::hex << std::uppercase << std::setfill('0');
767
768 size_t i, j;
769
770 for (i = 0; i < len; i += rowSize)
771 {
772 // offset
773 ss << std::setw(8) << i << ": ";
774
775 // row bytes
776 for (j = 0; (j < rowSize) && ((i + j) < len); j++)
777 ss << std::setw(2) << static_cast<unsigned int>(data[i + j]) << " ";
778
779 // fill row empty space
780 for (; j < rowSize; j++)
781 ss << " ";
782
783 // ASCII
784 if (showASCII)
785 {
786 ss << " ";
787
788 for (j = 0; (j < rowSize) && ((i + j) < len); j++)
789 {
790 if (std::isprint(data[i + j]))
791 ss << data[i + j];
792 else
793 ss << '.';
794 }
795 }
796
797 ss << std::endl;
798 }
799
800 return ss.str();
801 }
802 } // namespace Data
803
807 namespace Zip
808 {
813 {
814 uint64_t offset = 0;
815 uint64_t size = 0;
816 uint64_t entries = 0;
817 bool zip64 = false;
818 };
819
824 {
825 std::string fileName;
826 uint64_t compressedSize = 0;
827 uint64_t uncompressedSize = 0;
828 uint16_t compressionMethod = 0;
829 uint32_t crc32 = 0;
830 uint16_t modTime = 0;
831 uint16_t modDate = 0;
832 uint64_t dataOffset = 0;
833 };
834
839 {
840 void *mappingBase = nullptr;
841 size_t mappingSize = 0;
842 uint8_t *data = nullptr;
843 uint64_t size = 0;
844 };
845
855 bool findCentralDirectory(const uint8_t *data, uint64_t fileSize, CentralDirectoryInfo *info);
856
864 std::vector<ZipEntryInfo> listEntriesInZip(const std::string &zipPath);
865
875 bool findEntryInfoByDataOffset(const std::string &zipPath, uint64_t dataOffset, ZipEntryInfo *out);
876
886 bool mmapEntryByDataOffset(const std::string &zipPath, uint64_t dataOffset, ZipEntryMMap *out);
887 } // namespace Zip
888
889} // namespace KittyUtils
Provides utility functions for data.
Definition KittyUtils.hpp:704
bool fromHex(std::string in, void *data)
Converts a hexadecimal string to a binary data buffer.
std::string hexDump(const void *address, size_t len)
Hex dumps the memory block at the specified address.
Definition KittyUtils.hpp:758
std::string toHex(const void *data, const size_t dataLength)
Converts binary data to a hexadecimal string.
Provides utility functions for paths.
Definition KittyUtils.hpp:511
std::string fileExtension(const std::string &filePath)
Extracts the file extension from a given file path.
std::string fileDirectory(const std::string &filePath)
Extracts the directory from a given file path.
std::string fileName(const std::string &filePath)
Extracts the file name from a given file path.
Provides utility functions for strings.
Definition KittyUtils.hpp:544
bool charEqualsIgnoreCase(char a, char b)
Helper to compare two characters case-insensitively.
Definition KittyUtils.hpp:548
void trim(std::string &str)
Trims whitespace from the beginning and end of a string.
bool startsWith(const std::string &str, const std::string &prefix, bool sensitive=true)
Checks if a string starts with a given prefix.
bool contains(const std::string &str, const std::string &substring, bool sensitive=true)
Checks if a string contains a given substring.
bool validateHex(std::string &hex)
Validates a hexadecimal string.
std::string fmt(const char *fmt,...)
Formats a string using a printf-style format.
void removeAllWhitespace(std::string &str)
Removes all whitespace characters from a string, not just leading/trailing.
bool endsWith(const std::string &str, const std::string &suffix, bool sensitive=true)
Checks if a string ends with a given suffix.
bool isValidHex(const std::string &hex)
Checks if the provided string is a valid hexadecimal representation.
Provides utility functions for handling ZIP files.
Definition KittyUtils.hpp:808
std::vector< ZipEntryInfo > listEntriesInZip(const std::string &zipPath)
Lists all entries in a ZIP file.
bool findEntryInfoByDataOffset(const std::string &zipPath, uint64_t dataOffset, ZipEntryInfo *out)
Finds the ZipEntryInfo for an entry by its data offset.
bool findCentralDirectory(const uint8_t *data, uint64_t fileSize, CentralDirectoryInfo *info)
Finds the central directory of a ZIP file.
bool mmapEntryByDataOffset(const std::string &zipPath, uint64_t dataOffset, ZipEntryMMap *out)
Maps an entry in a ZIP file by its data offset.
Provides general utility functions.
Definition KittyUtils.hpp:168
std::string randomString(size_t length)
Generates a random string of a specified length.
std::vector< uint8_t > randomBytes(std::size_t length)
Generates a random bytes of a specified length.
uintptr_t untagPointer(uintptr_t ptr)
Removes AArch64 top-byte pointer tags from an address.
Definition KittyUtils.hpp:474
T randInt(T min, T max)
Generates a random number of type T within a specified range.
Definition KittyUtils.hpp:671
Structure to hold ZIP Central Directory info.
Definition KittyUtils.hpp:813
Structure to hold ZIP entry info.
Definition KittyUtils.hpp:824
Structure to hold memory mapped ZIP entry info.
Definition KittyUtils.hpp:839