├── src ├── Build.bat ├── BI.bat ├── MBI.bat ├── jni │ ├── Application.mk │ ├── main.cpp │ ├── Android.mk │ ├── ElfUtils.h │ ├── ElfUtils.cpp │ ├── LinuxProcess.h │ └── LinuxProcess.cpp └── Update.bat ├── .gitignore ├── README.md └── LICENSE /src/Build.bat: -------------------------------------------------------------------------------- 1 | ndk-build clean & ndk-build -j4 -B -------------------------------------------------------------------------------- /src/BI.bat: -------------------------------------------------------------------------------- 1 | Build.bat & Update.bat & pause & cls 2 | -------------------------------------------------------------------------------- /src/MBI.bat: -------------------------------------------------------------------------------- 1 | ndk-build & Update.bat & pause & cls 2 | -------------------------------------------------------------------------------- /src/jni/Application.mk: -------------------------------------------------------------------------------- 1 | APP_ABI := armeabi-v7a arm64-v8a 2 | APP_PLATFORM := android-21 3 | APP_STL := c++_static -------------------------------------------------------------------------------- /src/jni/main.cpp: -------------------------------------------------------------------------------- 1 | #include "LinuxProcess.h" 2 | 3 | int main() 4 | { 5 | LinuxProcess p("com.yourtargetprocess.name"); 6 | 7 | return 0; 8 | } 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | src/obj/local/arm64-v8a/objs/mem/main.o 3 | src/obj/local/arm64-v8a/objs/mem/main.o.d 4 | *.o 5 | *.d 6 | src/obj/local/armeabi-v7a/mem 7 | src/obj/local/arm64-v8a/mem 8 | src/libs/armeabi-v7a/mem 9 | src/libs/arm64-v8a/mem 10 | .vscode/settings.json 11 | -------------------------------------------------------------------------------- /src/jni/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | include $(CLEAR_VARS) 3 | LOCAL_ARM_MODE := arm 4 | LOCAL_LDLIBS := -llog 5 | LOCAL_ARM_NEON := false 6 | LOCAL_MODULE := mem 7 | LOCAL_SRC_FILES := main.cpp LinuxProcess.cpp ElfUtils.cpp 8 | LOCAL_CFLAGS := -fexceptions 9 | LOCAL_CPPFLAGS := 10 | include $(BUILD_EXECUTABLE) -------------------------------------------------------------------------------- /src/Update.bat: -------------------------------------------------------------------------------- 1 | set moduleName=mem 2 | 3 | HD-Adb -s emulator-5554 shell "su -c 'rm data/local/tmp/%moduleName%'" 4 | HD-Adb -s emulator-5554 push libs/armeabi-v7a/%moduleName% data/local/tmp/%moduleName% 5 | HD-Adb -s emulator-5554 shell "su -c 'chmod 7777 data/local/tmp/%moduleName%'" 6 | HD-Adb -s emulator-5554 shell "su -c 'chown 0 data/local/tmp/%moduleName%'" 7 | HD-Adb -s emulator-5554 shell "su -c 'chgrp 0 data/local/tmp/%moduleName%'" -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Android-External-Root-Memory-Library 2 | 3 | ## Library Features 4 | 5 | - LoadToMemoryAndHook (will a passed function(should be simple function or shellcode)) 6 | - Hook 7 | - DisablePtrace (Deprecated) 8 | - GetFuncSizeArm 9 | - FindCodeCave 10 | - EnumSegments 11 | - FindExternalSymbol (Extract it from ELF Format) 12 | - ReadMemory (Also Aux Wrapper for Easy Type Reads) 13 | - WriteMemory (Also Aux Wrapper for Easy Type Writes) 14 | - GetFullModulePath 15 | - GetLocalModBaseAddr 16 | - GetModBaseAddr 17 | - FindDMAddy (Utility for multilevel Pointers) 18 | - FindPid 19 | 20 | ## How To Build 21 | 22 | - Clone this repo 23 | - Explore src in command line. 24 | - Run ndk-build 25 | 26 | ## Demo 27 | https://www.youtube.com/watch?v=O4B3t2-67jc&t=7s 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Mst1k 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/jni/ElfUtils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | /** 8 | * @brief Holds the ELF File Mapping 9 | */ 10 | union ElfPack { 11 | /** 12 | * @brief Pointer to ELF Header 13 | */ 14 | Elf32_Ehdr* header; 15 | 16 | /** 17 | * @brief Pointer to ELF64 Header 18 | */ 19 | Elf64_Ehdr* header64; 20 | 21 | /** 22 | * @brief ELF File mapping base address 23 | */ 24 | uintptr_t base; 25 | 26 | /** 27 | * @brief Pointer to the ELF Mapping 28 | */ 29 | void* baseV; 30 | 31 | /** 32 | * @brief Lazy int version of the Mapping Pointer, to easily do checks 33 | */ 34 | int res; 35 | }; 36 | 37 | /** 38 | * @brief Initializes the ELF Library, Notify Callback, Cleanup, Frees the ELF library 39 | * @returns true if all the operations was sucessfully, false otherwise 40 | */ 41 | bool ElfOpen(const std::string& fullModulePath, std::function callback); 42 | 43 | /** 44 | * @brief Check if ELF file is 64 bits. 45 | * @returns true if ELF File is 64 bits, false otherwise 46 | */ 47 | bool ElfPeekIs64(const std::string& fullModulePath, bool& outResult); 48 | 49 | /** 50 | * @brief Get a ELF Section by its given Index. 51 | * @param sectionIdx: the given section index 52 | * @returns a pointer to a section header if valid, nullptr otherwise 53 | */ 54 | Elf32_Shdr* ElfSectionByIndex(ElfPack libMap, unsigned int sectionIdx); 55 | 56 | /** 57 | * @brief Traverses all sections within the ELF File 58 | * @param callback: will be reported, all the given sections 59 | */ 60 | void ElfForEachSection(ElfPack libMap, std::function callback); 61 | 62 | /** 63 | * @brief Lookup an ELF Section by its given type 64 | * @param sectionType: ELF Section Type 65 | * @returns A pointer to the section if it exists; nullptr otherwise. 66 | */ 67 | Elf32_Shdr* ElfLookupSectionByType(ElfPack libMap, uint32_t sectionType); 68 | 69 | /** 70 | * @brief Retrieve the ELF Section Headers Name Blob (shstr) Entry. 71 | * @returns A pointer to the char blob entry if exist; nullptr otherwise 72 | */ 73 | const char* ElfGetSectionHeadersStringBlob(ElfPack libMap); 74 | 75 | /** 76 | * @brief Retrieve ELF Section name 77 | * @param sectionHdr: Pointer to ELF Section Header 78 | * @returns A Pointer to the section name if exist; nullptr otherwise. 79 | */ 80 | const char* ElfGetSectionName(ElfPack libMap, Elf32_Shdr* sectionHdr); 81 | 82 | /** 83 | * @brief Lookup a ELF Header by its name 84 | * @param sectionName: Name of the section (ex: ".rodata", ".text" ...) 85 | * @returns A Pointer to the ELF Section if found; nullptr otherwise. 86 | */ 87 | Elf32_Shdr* ElfLookupSectionByName(ElfPack libMap, const std::string& sectionName); 88 | 89 | /** 90 | * @brief Retrieve any available Symbol Table ELF Section. 91 | * @returns A pointer to the Symbol Table ELF Section if exist; nullptr otherwise; 92 | * @note This function first searches for an SHT_SYMTAB type, and if none is found, 93 | * it searches for an SHT_DYNSYM type. 94 | */ 95 | Elf32_Shdr* ElfGetSymbolSection(ElfPack libMap); 96 | 97 | /** 98 | * @brief Traverse the symbol table. 99 | * @param callback: A callback, for each symbol found, this callback will be invocated with the actual symbol & its name. 100 | * @returns true if a symbol table to traverse was found, nullptr otherwise. 101 | */ 102 | bool ElfForEachSymbol(ElfPack libMap, std::function callback); 103 | 104 | /** 105 | * @brief Lookup a symbol by its name. 106 | * @param symbolName: The Name of the symbol to look for. 107 | * @param outSymbolOff: (optional) A Pointer to variable where resulting relative displacement of the symbol will be saved if found. 108 | * @returns true if the symbol was found, false otherwise. 109 | * @note Symbol lookup may fail for various reasons, such as the absence of a symbol table or the symbol not being present in the symbol table. 110 | */ 111 | bool ElfLookupSymbol(ElfPack libMap, const std::string& symbolName, uint64_t* outSymbolOff = nullptr); -------------------------------------------------------------------------------- /src/jni/ElfUtils.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "ElfUtils.h" 4 | #include 5 | #include 6 | 7 | bool ElfOpen(const std::string& fullModulePath, std::function callback) 8 | { 9 | int libFd = open(fullModulePath.c_str(), O_RDONLY); 10 | 11 | if(libFd < 0) 12 | return false; 13 | 14 | struct stat libStats; 15 | 16 | if(fstat(libFd, &libStats) != 0) 17 | { 18 | close(libFd); 19 | return false; 20 | } 21 | 22 | ElfPack libMap; 23 | 24 | libMap.baseV = mmap(NULL, libStats.st_size, PROT_READ, MAP_SHARED, libFd, 0 ); 25 | 26 | if(libMap.res == -1) 27 | { 28 | close(libFd); 29 | return false; 30 | } 31 | 32 | callback(libMap); 33 | 34 | munmap(libMap.baseV, libStats.st_size); 35 | 36 | close(libFd); 37 | return true; 38 | } 39 | 40 | bool ElfPeekIs64(const std::string& fullModulePath, bool& outResult) 41 | { 42 | outResult = false; 43 | 44 | bool elfOpened = ElfOpen(fullModulePath, [&](ElfPack elfPack){ 45 | outResult = elfPack.header->e_ident[EI_CLASS] == ELFCLASS64; 46 | }); 47 | 48 | return elfOpened; 49 | } 50 | 51 | Elf32_Shdr* ElfSectionByIndex(ElfPack libMap, unsigned int sectionIdx) 52 | { 53 | if((sectionIdx < libMap.header->e_shnum) == false) 54 | return nullptr; 55 | 56 | Elf32_Shdr* libElfSections = (Elf32_Shdr*)(libMap.base + libMap.header->e_shoff); 57 | 58 | return libElfSections + sectionIdx; 59 | } 60 | 61 | void ElfForEachSection(ElfPack libMap, std::function callback) 62 | { 63 | Elf32_Shdr* libElfSections = (Elf32_Shdr*)(libMap.base + libMap.header->e_shoff); 64 | 65 | for(int i = 0; i < libMap.header->e_shnum; i++) 66 | { 67 | if(callback(libElfSections + i) == false) 68 | break; 69 | } 70 | } 71 | 72 | Elf32_Shdr* ElfLookupSectionByType(ElfPack libMap, uint32_t sectionType) 73 | { 74 | Elf32_Shdr* secHeader = nullptr; 75 | 76 | ElfForEachSection(libMap, [&](Elf32_Shdr* currSection){ 77 | if(currSection->sh_type != sectionType) 78 | return true; 79 | 80 | secHeader = currSection; 81 | 82 | return false; 83 | }); 84 | 85 | return secHeader; 86 | } 87 | 88 | const char* ElfGetSectionHeadersStringBlob(ElfPack libMap) 89 | { 90 | if(libMap.header->e_shstrndx == SHN_UNDEF) 91 | return nullptr; 92 | 93 | Elf32_Shdr* shStrSec = ElfSectionByIndex(libMap, libMap.header->e_shstrndx); 94 | 95 | if(shStrSec == nullptr || shStrSec->sh_offset < 1) 96 | return nullptr; 97 | 98 | return (const char*)(libMap.base + shStrSec->sh_offset); 99 | } 100 | 101 | const char* ElfGetSectionName(ElfPack libMap, Elf32_Shdr* sectionHdr) 102 | { 103 | const char* shStrBlob = ElfGetSectionHeadersStringBlob(libMap); 104 | 105 | if(shStrBlob == nullptr) 106 | return nullptr; 107 | 108 | return shStrBlob + sectionHdr->sh_name; 109 | } 110 | 111 | Elf32_Shdr* ElfLookupSectionByName(ElfPack libMap, const std::string& sectionName) 112 | { 113 | Elf32_Shdr* secHeader = nullptr; 114 | 115 | ElfForEachSection(libMap, [&](Elf32_Shdr* currSection){ 116 | const char* currSectionName = ElfGetSectionName(libMap, currSection); 117 | 118 | if(currSectionName == nullptr) 119 | return true; 120 | 121 | if(strcmp(currSectionName, sectionName.c_str())) 122 | return true; 123 | 124 | secHeader = currSection; 125 | 126 | return false; 127 | }); 128 | 129 | return secHeader; 130 | } 131 | 132 | Elf32_Shdr* ElfGetSymbolSection(ElfPack libMap) 133 | { 134 | Elf32_Shdr* result = nullptr; 135 | 136 | result = ElfLookupSectionByType(libMap, SHT_SYMTAB); 137 | 138 | if(result) 139 | return result; 140 | 141 | result = ElfLookupSectionByType(libMap, SHT_DYNSYM); 142 | 143 | if(result) 144 | return result; 145 | 146 | return result; 147 | } 148 | 149 | bool ElfForEachSymbol(ElfPack libMap, std::function callback) 150 | { 151 | Elf32_Shdr* symTable = ElfGetSymbolSection(libMap); 152 | 153 | if(symTable == nullptr) 154 | return false; 155 | 156 | Elf32_Shdr* strTable = ElfSectionByIndex(libMap, symTable->sh_link); 157 | 158 | if(strTable == nullptr) 159 | return false; 160 | 161 | const char* elfStrBlob = (const char*)(libMap.base + strTable->sh_offset); 162 | 163 | int nSyms = symTable->sh_size / sizeof(Elf32_Sym); 164 | Elf32_Sym* symEntry = (Elf32_Sym*)(libMap.base + symTable->sh_offset); 165 | Elf32_Sym* symEnd = symEntry + nSyms; 166 | 167 | for(Elf32_Sym* sym = symEntry; sym < symEnd; sym++) 168 | { 169 | if((ELF32_ST_BIND(sym->st_info) & (STT_FUNC | STB_GLOBAL)) == 0) 170 | continue; 171 | 172 | if(callback(sym, elfStrBlob + sym->st_name) == false) 173 | break; 174 | } 175 | 176 | return true; 177 | } 178 | 179 | bool ElfLookupSymbol(ElfPack libMap, const std::string& symbolName, uint64_t* outSymbolOff) 180 | { 181 | Elf32_Sym* result = nullptr; 182 | if(ElfForEachSymbol(libMap, [&](Elf32_Sym* currSym, const char* currSymName){ 183 | if(strcmp(currSymName, symbolName.c_str())) 184 | return true; 185 | 186 | result = currSym; 187 | 188 | return false; 189 | }) == false) 190 | return false; 191 | 192 | if(outSymbolOff && result) 193 | *outSymbolOff = result->st_value; 194 | 195 | return result != nullptr; 196 | } -------------------------------------------------------------------------------- /src/jni/LinuxProcess.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | /** 9 | * @brief Describes a memory segment (ex. a row from the proc/self/maps) 10 | */ 11 | struct SegmentInfo{ 12 | /** 13 | * @brief full segment name, if is present 14 | * 15 | */ 16 | std::string name; 17 | /** 18 | * @brief segment size range protection 19 | * 20 | */ 21 | std::string prot; 22 | /** 23 | * @brief entry of the the segment in memory 24 | * 25 | */ 26 | uintptr_t start; 27 | /** 28 | * @brief end of the segment in memory 29 | * 30 | */ 31 | uintptr_t end; 32 | /** 33 | * @brief segment size in memory 34 | * 35 | */ 36 | uintptr_t size; 37 | }; 38 | 39 | /** 40 | * @brief Describes a linux process 41 | */ 42 | class LinuxProcess 43 | { 44 | private: 45 | /** 46 | * @brief targets process identifier(PID) 47 | * 48 | */ 49 | int pid; 50 | 51 | /** 52 | * @brief target process memory file descriptor 53 | * 54 | */ 55 | int memfd; 56 | 57 | /** 58 | * @brief target process maps file descriptor 59 | * 60 | */ 61 | int mapsfd; 62 | 63 | 64 | public: 65 | /** 66 | * @brief Construct a new Process Manager object 67 | * 68 | * @param procName the name of the target process to attach. 69 | */ 70 | LinuxProcess(const char* procName); 71 | 72 | /** 73 | * @brief will find a proc id, enumerating all the /proc folders(pids). 74 | * 75 | * @param procName its the name of the process that we want to get the pid 76 | * @return int 77 | */ 78 | static int FindPid(const char* procName); 79 | 80 | /** 81 | * @brief read a T item from target address 82 | * 83 | * @tparam T target object type in target memory 84 | * @param addr it the target address 85 | * @return T target object return type from this function 86 | */ 87 | template 88 | T ReadMemoryWrapper(uintptr_t addr); 89 | 90 | /** 91 | * @brief solve multilevel pointers 92 | * 93 | * @param base base address to start the multilevel pointer calculation 94 | * @param offsets array of offset to index the multilevel pointer 95 | * @return uintptr_t return the address pointed in the multilevel pointer provided 96 | */ 97 | uintptr_t FindDMAddy(uintptr_t base, std::vector offsets); 98 | 99 | /** 100 | * @brief write a T item to given addr in target memory 101 | * 102 | * @tparam T target object type to be writed in target process memory 103 | * @param addr target address in process target memory 104 | * @param newValue target value to be write in target process memory 105 | */ 106 | template 107 | void WriteMemoryWrapper(uintptr_t addr, T newValue); 108 | 109 | /** 110 | * @brief Get the Module Base Address of a module loaded in target process memory 111 | * 112 | * @param modName its the name of the module in target memory 113 | * @return uintptr_t the base address of the target module in the target memory 114 | */ 115 | uintptr_t GetModBaseAddr(const char* modName); 116 | 117 | /** 118 | * @brief Get the Module Base Address of a module loaded in local process memory 119 | * 120 | * @param modName its the name of the module in local memory 121 | * @return uintptr_t the base address of the target module in the local memory 122 | */ 123 | static uintptr_t GetLocalModBaseAddr(const char* modName); 124 | 125 | /** 126 | * @brief will get the full module path in disk from a loaded module in target memory 127 | * 128 | * @param modName name of the target module in target memory 129 | * @param result contains the full path of the target module 130 | * @return true result contain the full path 131 | * @return false an error ocurred while getting the full path, may wrong target library in target memory name 132 | */ 133 | bool GetFullModulePath(const char* modName, std::string & result); 134 | 135 | /** 136 | * @brief will copy data from local memory to target process memory 137 | * 138 | * @param source here is the source bytes in local memory 139 | * @param destination here is the destination point in target memory 140 | * @param size this is the count in bytes to be write 141 | */ 142 | bool WriteMemory(const void* source, uintptr_t destination, int size); 143 | 144 | /** 145 | * @brief will copy data from target memory to local process memory 146 | * 147 | * @param source here is the source bytes in target memory 148 | * @param destination here is the destination point in local memory 149 | * @param size this is the count in bytes to be write 150 | * @return true sucesfully copied 151 | * @return false error while copying 152 | */ 153 | bool ReadMemory(uintptr_t source, void* destination, int size); 154 | 155 | /** 156 | * @brief will find for a symbol in target memory 157 | * 158 | * @param modName the name of the target module 159 | * @param symbolName the name of the target symbol 160 | * @param outResult output of the absolute symbol address 161 | * @return bool whether it found or not an actual symbol 162 | */ 163 | bool FindExternalSymbol(const char* modName, const char* symbolName, uint64_t* outResult = nullptr); 164 | 165 | /** 166 | * @brief it will enumerate/parse all segments from the maps file 167 | * 168 | * @param segments this is a reference to a vector of segmentInfo struct 169 | * @param protection this is the target protection, could be PROT_READ, PROT_WRITE, PROT_EXEC, Same as mmap 170 | * @return true sucessfully enumerate all the target proteccion segments 171 | * @return false an error ocurred while enumerating 172 | */ 173 | bool EnumSegments(std::vector & segments, int protection = PROT_READ | PROT_WRITE | PROT_EXEC); 174 | 175 | /** 176 | * @brief will find a piece of memory "empty" in the target process memory 177 | * 178 | * @param size the target size of the "empty" memory 179 | * @param prot the target memory proteccion, could be PROT_READ, PROT_WRITE, PROT_EXEC, Same as mmap 180 | * @return uintptr_t if success the returned address is not null 181 | */ 182 | uintptr_t FindCodeCave(uintptr_t size, uintptr_t protection = PROT_READ | PROT_WRITE | PROT_EXEC); 183 | 184 | /** 185 | * @deprecated 186 | * @brief it will find and disable the ptrace function in the target process 187 | * 188 | */ 189 | //void DisablePtrace(); 190 | 191 | 192 | /** 193 | * @brief it will detour the execution flow in determinated target memory point 194 | * 195 | * @param src where the detour will be aplied in the target memory 196 | * @param dst where the detour will go after detour in the target memory 197 | * @param size if arm, dont pass argument, the default size is 8, but in x86 need the size 198 | * @return true sucess 199 | * @return false non sucess 200 | */ 201 | bool Hook(uintptr_t src, uintptr_t dst, uintptr_t targetLen = 8); 202 | 203 | /** 204 | * @brief will load a function located on local process memory in the target process memory and will make a detour from target process memory point to it 205 | * 206 | * @param targetSrc detour entry 207 | * @param targetDst destination function in local process memory 208 | * @param targetLen lenght of bytes to be overwrited 209 | * @return true sucess 210 | * @return false fail 211 | */ 212 | bool LoadToMemoryAndHook(uintptr_t targetSrc, void* targetDst, uintptr_t targetLen = 8); 213 | }; 214 | 215 | template 216 | T LinuxProcess::ReadMemoryWrapper(uintptr_t addr) 217 | { 218 | T result; 219 | 220 | ReadMemory(addr, (void*)&result, sizeof(T)); 221 | 222 | return result; 223 | } 224 | 225 | template 226 | void LinuxProcess::WriteMemoryWrapper(uintptr_t addr, T newValue) 227 | { 228 | WriteMemory((const void*)&newValue, addr, sizeof(T)); 229 | } 230 | 231 | 232 | -------------------------------------------------------------------------------- /src/jni/LinuxProcess.cpp: -------------------------------------------------------------------------------- 1 | #include "LinuxProcess.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "ElfUtils.h" 11 | 12 | const unsigned char popmask[] = "\xBD\xE8"; 13 | const unsigned char bxlrmask[] = "\x1E\xFF\x2F\xE1"; 14 | const unsigned char parammask[] = "\xCC\xCC\xCC\xCC"; 15 | 16 | const char* pattern_scan(const char* pattern, const char* mask, const char* data, size_t data_len) { 17 | size_t mask_len = strlen(mask) - 1; // Subtract 1 to exclude the null terminator 18 | 19 | for (size_t i = 0; i <= data_len - mask_len; i++) { 20 | int match = 1; 21 | for (size_t j = 0; j < mask_len; j++) { 22 | if (mask[j] == '?') { 23 | continue; 24 | } 25 | if (data[i + j] != pattern[j]) { 26 | match = 0; 27 | break; 28 | } 29 | } 30 | if (match) { 31 | return &data[i]; // Return the address where the pattern is found 32 | } 33 | } 34 | 35 | return nullptr; // Pattern not found 36 | } 37 | 38 | #define GetBit(buff, pos) (((buff) & (0x1 << (pos)))) ? true : false 39 | 40 | /** 41 | * @brief definig DEBUG will cause, all the function logging its call. 42 | * 43 | */ 44 | #define DEBUG 45 | 46 | #define ZeroMemory(dst, size) memset((void*)(dst), 0x0, (size)) 47 | 48 | int LinuxProcess::FindPid(const char* procName){ 49 | #ifdef DEBUG 50 | printf("FindPid(%s)",procName); 51 | #endif 52 | int pid = -1; 53 | struct dirent* pDirent; 54 | DIR* dir; 55 | 56 | if(!procName) 57 | return -1; 58 | 59 | dir = opendir("/proc/"); 60 | 61 | if(!dir) 62 | return -1; 63 | 64 | for(int currPid = 0;(pDirent = readdir(dir)) != NULL;) 65 | { 66 | if((currPid = atoi(pDirent->d_name)) == 0) 67 | continue; 68 | 69 | char currCmdLinePath[128]; 70 | sprintf(currCmdLinePath, "/proc/%d/cmdline", currPid); 71 | 72 | int currcmdLineFd = open((const char*)currCmdLinePath, O_RDONLY); 73 | if(currcmdLineFd == -1) 74 | continue; 75 | 76 | char currProcName[128]; 77 | 78 | memset(currProcName, 0, sizeof(currProcName)); 79 | 80 | if(!read(currcmdLineFd, currProcName , sizeof(currProcName))) 81 | continue; 82 | 83 | if(!strcmp(currProcName, procName)) 84 | { 85 | pid = currPid; 86 | break; 87 | } 88 | } 89 | 90 | #ifdef DEBUG 91 | printf(" = %d\n",pid); 92 | #endif 93 | closedir(dir); 94 | return pid; 95 | } 96 | 97 | LinuxProcess::LinuxProcess(const char* procName) 98 | { 99 | #ifdef DEBUG 100 | printf("LinuxProcess(%s)\n",procName); 101 | #endif 102 | 103 | char ProcessMemoryPath[128]; 104 | char ProcessMapsPath[128]; 105 | 106 | if((pid = FindPid(procName)) == -1) 107 | throw("LinuxProcess : Could not found the process"); 108 | 109 | sprintf(ProcessMemoryPath, "/proc/%d/mem", pid); 110 | memfd = open(ProcessMemoryPath, O_RDWR); 111 | 112 | if(memfd < -1) 113 | throw("LinuxProcess : Could not open the process memory"); 114 | 115 | sprintf(ProcessMapsPath, "/proc/%d/maps", pid); 116 | mapsfd = open(ProcessMapsPath, O_RDONLY); 117 | 118 | if(mapsfd < -1) 119 | throw("LinuxProcess : Could not open the process maps"); 120 | } 121 | 122 | bool ForEachLine(int fd, std::function callback) 123 | { 124 | lseek64(fd, 0, SEEK_SET); 125 | 126 | FILE* currFile = fdopen(fd, "r"); 127 | 128 | if(!currFile) 129 | return false; 130 | 131 | rewind(currFile); 132 | 133 | char currLine[1024]; 134 | 135 | while(fgets(currLine, sizeof(currLine), currFile) != NULL) 136 | { 137 | if(callback(std::string(currLine)) == false) 138 | break; 139 | } 140 | 141 | lseek64(fd, 0, SEEK_SET); 142 | 143 | return true; 144 | } 145 | 146 | void ParseMapLineSegment(const char* lineStartSegment, SegmentInfo& buff) 147 | { 148 | uintptr_t unk1; 149 | char tempName[256] {}; 150 | char tempProt[256] {}; 151 | 152 | sscanf(lineStartSegment, "%08X-%08X %s %08X %02X:%02X %d %s\n", &buff.start, &buff.end, tempProt, &unk1, &unk1, &unk1, &unk1, tempName); 153 | 154 | buff.name = std::string(tempName); 155 | buff.prot = std::string(tempProt); 156 | buff.size = buff.end - buff.start; 157 | 158 | return; 159 | } 160 | 161 | bool GetLineSegmentFromName(int fd, const char* modName, SegmentInfo & result) 162 | { 163 | std::string modMapsLine = ""; 164 | 165 | if(ForEachLine(fd, [&](const std::string& currLine){ 166 | if(strstr(currLine.c_str(), modName) == nullptr) 167 | return true; 168 | 169 | modMapsLine = currLine; 170 | 171 | return false; 172 | }) == false) 173 | return false; 174 | 175 | if(modMapsLine.empty()) 176 | return false; 177 | 178 | ParseMapLineSegment(modMapsLine.c_str(), result); 179 | 180 | return true; 181 | } 182 | 183 | uintptr_t LinuxProcess::GetModBaseAddr(const char* modName) 184 | { 185 | #ifdef DEBUG 186 | printf("LinuxProcess::GetModBaseAddr(%s)\n",modName); 187 | #endif 188 | 189 | SegmentInfo tSegment; 190 | 191 | if(!GetLineSegmentFromName(mapsfd, modName, tSegment)) 192 | return 0; 193 | 194 | return tSegment.start; 195 | } 196 | 197 | uintptr_t LinuxProcess::FindDMAddy(uintptr_t base, std::vector offsets) 198 | { 199 | uintptr_t result = base; 200 | 201 | for(int i = 0; i < offsets.size(); i++) 202 | { 203 | result = ReadMemoryWrapper(result); 204 | result += offsets[i]; 205 | } 206 | 207 | return result; 208 | } 209 | 210 | uintptr_t LinuxProcess::GetLocalModBaseAddr(const char* modName) 211 | { 212 | #ifdef DEBUG 213 | printf("LinuxProcess::GetLocalModBaseAdd(%s)\n",modName); 214 | #endif 215 | 216 | int localMapsfd = open("/proc/self/maps", O_RDONLY); 217 | 218 | if(localMapsfd < 0) 219 | return 0; 220 | 221 | SegmentInfo tSegment; 222 | ZeroMemory(&tSegment, sizeof(tSegment)); 223 | 224 | if(GetLineSegmentFromName(localMapsfd, modName, tSegment) == false) 225 | { 226 | close(localMapsfd); 227 | return 0; 228 | } 229 | 230 | close(localMapsfd); 231 | 232 | return tSegment.start; 233 | } 234 | 235 | bool LinuxProcess::GetFullModulePath(const char* modName, std::string & result) 236 | { 237 | #ifdef DEBUG 238 | printf("LinuxProcess::GetFullModulePath(%s)\n",modName); 239 | #endif 240 | 241 | SegmentInfo tSegment; 242 | 243 | if(!GetLineSegmentFromName(mapsfd, modName, tSegment)) 244 | return 0; 245 | 246 | result = tSegment.name; 247 | 248 | return true; 249 | } 250 | 251 | bool LinuxProcess::FindExternalSymbol(const char* modName, const char* symbolName, uint64_t* outResult) 252 | { 253 | #ifdef DEBUG 254 | printf("LinuxProcess::FindExternalSymbol(%s, %s)\n",modName, symbolName); 255 | #endif 256 | 257 | std::string fullModPath = ""; 258 | 259 | if(!GetFullModulePath(modName, fullModPath)) 260 | return false; 261 | 262 | bool symbolFound = false; 263 | 264 | bool libElfEnumRes = ElfOpen(fullModPath, [&](ElfPack libMap){ 265 | symbolFound = ElfLookupSymbol(libMap, symbolName, outResult); 266 | }); 267 | 268 | if(symbolFound && outResult) 269 | (*outResult) += GetModBaseAddr(modName); 270 | 271 | return symbolFound; 272 | } 273 | 274 | bool LinuxProcess::WriteMemory(const void* source, uintptr_t destination, int size) 275 | { 276 | if(lseek64(memfd, destination, SEEK_SET) < 0) 277 | return false; 278 | 279 | write(memfd, source, size); 280 | 281 | return true; 282 | } 283 | 284 | bool LinuxProcess::ReadMemory(uintptr_t source, void* destination, int size) 285 | { 286 | if(lseek64(memfd, source, SEEK_SET) < 0) 287 | return false; 288 | 289 | read(memfd, destination, size); 290 | 291 | return true; 292 | } 293 | 294 | bool LinuxProcess::EnumSegments(std::vector & segments, int protection) 295 | { 296 | #ifdef DEBUG 297 | printf("LinuxProcess::EnumSegments(&)\n"); 298 | #endif 299 | 300 | char protectionBuff[] = "---p"; 301 | 302 | if(protection & PROT_READ) 303 | protectionBuff[0] = 'r'; 304 | 305 | if(protection & PROT_WRITE) 306 | protectionBuff[1] = 'w'; 307 | 308 | if(protection & PROT_EXEC) 309 | protectionBuff[2] = 'x'; 310 | 311 | if(ForEachLine(mapsfd, [&](const std::string currLine){ 312 | if(strstr(currLine.c_str(), protectionBuff) == nullptr) 313 | return true; 314 | 315 | segments.push_back({}); 316 | 317 | SegmentInfo& segmentInfo = segments[segments.size() - 1]; 318 | 319 | ParseMapLineSegment(currLine.c_str(), segmentInfo); 320 | 321 | return true; 322 | }) == false) 323 | return false; 324 | 325 | return true; 326 | } 327 | 328 | // void LinuxProcess::DisablePtrace() 329 | // { 330 | // #ifdef DEBUG 331 | // printf("LinuxProcess::DisablePtrace()\n"); 332 | // #endif 333 | // uintptr_t ptraceAddr = FindExternalSymbol("libc.so", "ptrace"); 334 | 335 | // while(!ptraceAddr){ 336 | // printf("LinuxProcess : ptrace not found\n"); 337 | // ptraceAddr = FindExternalSymbol("libc.so", "ptrace"); 338 | // }; 339 | 340 | // unsigned char buff[] = "\x00\x00\xA0\xE3\x1E\xFF\x2F\xE1"; // mov r0, 0 341 | // // bx lr 342 | // WriteMemory(buff, ptraceAddr, sizeof(buff)); 343 | // } 344 | 345 | uintptr_t LinuxProcess::FindCodeCave(uintptr_t size, uintptr_t prot) 346 | { 347 | #ifdef DEBUG 348 | printf("LinuxProcess::FindCodeCave(%08X, %d)\n", size, prot); 349 | #endif 350 | 351 | int exp = 1; 352 | void* tmpBuff = malloc(exp * exp); 353 | 354 | if(tmpBuff == nullptr) 355 | return 0; 356 | 357 | std::vector segments; 358 | 359 | if(!EnumSegments(segments, prot)) 360 | { 361 | free(tmpBuff); 362 | return 0; 363 | } 364 | 365 | size = ((size / 4) + 1) * 4; // Aligning the size 366 | 367 | std::vector pattern; 368 | std::string mask = ""; 369 | 370 | for(int i = 0; i < size; i++) 371 | { 372 | pattern.push_back(0x0); 373 | mask.push_back('x'); 374 | } 375 | 376 | uintptr_t result = 0; 377 | 378 | for(int i = 0; i < segments.size(); i++) 379 | { 380 | while(exp * exp < segments[i].size) 381 | { 382 | free(tmpBuff); 383 | exp++; tmpBuff = malloc(exp * exp); 384 | 385 | if(tmpBuff == nullptr) 386 | return 0; 387 | } 388 | // At this point we have enought memory to store the entire current segment 389 | 390 | if(ReadMemory(segments[i].start, tmpBuff, segments[i].size) == false) 391 | continue; 392 | 393 | // We sucessfully Readed the current segment 394 | const char* codeCave = pattern_scan((const char*) pattern.data(), mask.c_str(), (const char*)tmpBuff, segments[i].size ); 395 | 396 | if(codeCave == nullptr) 397 | continue; 398 | 399 | // At this point, we have found a codecaves 400 | // Lets calculate its position in the remote area 401 | 402 | size_t offset = (uintptr_t)codeCave - (uintptr_t)tmpBuff; 403 | 404 | result = segments[i].start + offset; 405 | break; 406 | } 407 | 408 | free(tmpBuff); 409 | 410 | return ((result / 4) + 1) * 4; 411 | } 412 | 413 | bool LinuxProcess::Hook(uintptr_t src, uintptr_t dst, uintptr_t size) 414 | { 415 | #ifdef DEBUG 416 | printf("LinuxProcess::Hook(%08X, %08X, %08X)\n", src, dst, size); 417 | #endif 418 | unsigned char* detourPtr; 419 | uintptr_t tSize = size; 420 | #ifdef __arm__ 421 | if(tSize < 8) 422 | return false; 423 | 424 | unsigned char detour[] = {0x04, 0xF0, 0x1F, 0xE5, 0x0, 0x0, 0x0, 0x0}; 425 | *(uintptr_t*)(detour + 4) = dst; 426 | detourPtr = ( unsigned char*)detour; 427 | #elif defined(__i386__) 428 | if(tSize < 5) 429 | return false; 430 | 431 | unsigned char detour[] = {0xE9, 0x0, 0x0, 0x0, 0x0}; 432 | uintptr_t relativeAddr = src - dst - 5; 433 | *(uintptr_t*)(detour + 1) = relativeAddr; 434 | #endif 435 | 436 | WriteMemory(detourPtr, src, tSize); 437 | 438 | return true; 439 | } 440 | 441 | bool RelatedReturn(void* _chunk) 442 | { 443 | bool found = false; 444 | if (!memcmp(popmask, (unsigned char*)_chunk + 2, 2)) 445 | { 446 | int8_t chunk = *(int8_t*)((unsigned char*)_chunk + 1); 447 | if(GetBit(chunk, 7)) 448 | found = true; 449 | } 450 | 451 | if (!memcmp(bxlrmask, _chunk, 4)) 452 | found = true; 453 | 454 | return found; 455 | } 456 | 457 | uintptr_t GetFuncSizeArm(void* Func) 458 | { 459 | if (!Func) 460 | return 0; 461 | 462 | for (unsigned char* i = (unsigned char*)Func; ; i += 4) 463 | if (RelatedReturn(i)) 464 | { 465 | uintptr_t size = (((uintptr_t)i - (uintptr_t)Func)) + 4; 466 | size += 4; 467 | 468 | return size; 469 | } 470 | 471 | return 0; 472 | } 473 | 474 | bool LinuxProcess::LoadToMemoryAndHook(uintptr_t targetSrc, void* targetDst, uintptr_t targetLen) 475 | { 476 | #ifdef DEBUG 477 | printf("LinuxProcess::LoadToMemoryAndHook(%08X, %08X, %08X)\n", targetSrc, (uintptr_t)targetDst, targetLen); 478 | #endif 479 | uintptr_t DstAddrinTargetMemory; 480 | uintptr_t localDstSize = 1024; 481 | 482 | #ifdef __arm__ 483 | if(targetLen < 8) 484 | return false; 485 | 486 | localDstSize = GetFuncSizeArm(targetDst); 487 | if(!localDstSize) 488 | return false; 489 | 490 | DstAddrinTargetMemory = FindCodeCave(localDstSize, PROT_READ); 491 | if(!DstAddrinTargetMemory) 492 | return false; 493 | 494 | #elif defined(__i386__) 495 | if(targetLen < 5) 496 | return false; 497 | 498 | 499 | #endif 500 | 501 | WriteMemory((unsigned char*)targetDst, DstAddrinTargetMemory, localDstSize); 502 | return Hook(targetSrc, DstAddrinTargetMemory); 503 | 504 | } --------------------------------------------------------------------------------