├── .gitignore ├── README.md └── dex.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DexStringDeobfuscator 2 | simple dex string deobfuscator 3 | -------------------------------------------------------------------------------- /dex.cpp: -------------------------------------------------------------------------------- 1 | // dex.cpp : This file contains the 'main' function. Program execution begins and ends there. 2 | // 3 | 4 | #define _CRT_SECURE_NO_WARNINGS 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | static uint8_t buffer[8 * 1024 * 1024]; 12 | 13 | static constexpr size_t kSha1DigestSize = 20; 14 | 15 | // Raw header_item. 16 | struct Header { 17 | uint8_t magic_[8] = {}; 18 | uint32_t checksum_ = 0; // See also location_checksum_ 19 | uint8_t signature_[kSha1DigestSize] = {}; 20 | uint32_t file_size_ = 0; // size of entire file 21 | uint32_t header_size_ = 0; // offset to start of next section 22 | uint32_t endian_tag_ = 0; 23 | uint32_t link_size_ = 0; // unused 24 | uint32_t link_off_ = 0; // unused 25 | uint32_t map_off_ = 0; // unused 26 | uint32_t string_ids_size_ = 0; // number of StringIds 27 | uint32_t string_ids_off_ = 0; // file offset of StringIds array 28 | uint32_t type_ids_size_ = 0; // number of TypeIds, we don't support more than 65535 29 | uint32_t type_ids_off_ = 0; // file offset of TypeIds array 30 | uint32_t proto_ids_size_ = 0; // number of ProtoIds, we don't support more than 65535 31 | uint32_t proto_ids_off_ = 0; // file offset of ProtoIds array 32 | uint32_t field_ids_size_ = 0; // number of FieldIds 33 | uint32_t field_ids_off_ = 0; // file offset of FieldIds array 34 | uint32_t method_ids_size_ = 0; // number of MethodIds 35 | uint32_t method_ids_off_ = 0; // file offset of MethodIds array 36 | uint32_t class_defs_size_ = 0; // number of ClassDefs 37 | uint32_t class_defs_off_ = 0; // file offset of ClassDef array 38 | uint32_t data_size_ = 0; // size of data section 39 | uint32_t data_off_ = 0; // file offset of data section 40 | }; 41 | 42 | // Raw string_id_item. 43 | struct StringId { 44 | uint32_t string_data_off_; // offset in bytes from the base address 45 | }; 46 | 47 | // Raw method_id_item. 48 | struct MethodId { 49 | uint16_t class_idx_; // index into type_ids_ array for defining class 50 | uint16_t proto_idx_; // index into proto_ids_ array for method prototype 51 | uint32_t name_idx_; // index into string_ids_ array for method name 52 | }; 53 | 54 | // Raw field_id_item. 55 | struct FieldId { 56 | uint16_t class_idx_; // index into type_ids_ array for defining class 57 | uint16_t type_idx_; // index into type_ids_ array for field type 58 | uint32_t name_idx_; // index into string_ids_ array for field name 59 | }; 60 | 61 | // Raw type_id_item. 62 | struct TypeId { 63 | uint32_t descriptor_idx_; // index into string_ids 64 | }; 65 | 66 | // Reads an unsigned LEB128 value, updating the given pointer to point 67 | // just past the end of the read value. This function tolerates 68 | // non-zero high-order bits in the fifth encoded byte. 69 | static inline uint32_t DecodeUnsignedLeb128(const uint8_t** data) { 70 | const uint8_t* ptr = *data; 71 | int result = *(ptr++); 72 | if (result > 0x7f) { 73 | int cur = *(ptr++); 74 | result = (result & 0x7f) | ((cur & 0x7f) << 7); 75 | if (cur > 0x7f) { 76 | cur = *(ptr++); 77 | result |= (cur & 0x7f) << 14; 78 | if (cur > 0x7f) { 79 | cur = *(ptr++); 80 | result |= (cur & 0x7f) << 21; 81 | if (cur > 0x7f) { 82 | // Note: We don't check to see if cur is out of range here, 83 | // meaning we tolerate garbage in the four high-order bits. 84 | cur = *(ptr++); 85 | result |= cur << 28; 86 | } 87 | } 88 | } 89 | } 90 | *data = ptr; 91 | return static_cast(result); 92 | } 93 | 94 | static inline uint8_t *EncodeUnsignedLeb128(uint8_t *dest, uint32_t value) { 95 | uint8_t out = value & 0x7f; 96 | value >>= 7; 97 | while (value != 0) { 98 | *dest++ = out | 0x80; 99 | out = value & 0x7f; 100 | value >>= 7; 101 | } 102 | *dest++ = out; 103 | return dest; 104 | } 105 | 106 | static inline uint32_t GetUTFCharsSize(const uint8_t *const dd, const uint32_t ll) { 107 | uint32_t s = 0, l = 0; 108 | while (dd[s] != '\0') { 109 | if (dd[s] <= 0b01111111u) { 110 | ++s; 111 | ++l; 112 | } else if (dd[s] <= 0b11011111u) { 113 | s += 2; 114 | ++l; 115 | } else if (dd[s] <= 0b11101111u) { 116 | s += 3; 117 | ++l; 118 | } else { 119 | *reinterpret_cast(NULL) = NULL; 120 | } 121 | } 122 | /* 123 | 0000 0000-0000 007F:0xxxxxxx 124 | 0000 0080-0000 07FF:110xxxxx 10xxxxxx 125 | 0000 0800-0000 FFFF:1110xxxx 10xxxxxx 10xxxxxx 126 | 0001 0000-001F FFFF:11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 127 | 0020 0000-03FF FFFF:111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 128 | 0400 0000-7FFF FFFF:1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 129 | */ 130 | if (ll != l) { 131 | *reinterpret_cast(NULL) = NULL; 132 | } 133 | return s; 134 | } 135 | 136 | static inline std::string RandomUniqueLegalName(uint8_t *const dd, const uint32_t l, std::unordered_set &set) { 137 | static const char cs[] = "QT9qrUV7GHIJKLMN8EFOPhijkW56lmnwxyzABXYZ012potuvRSbsefgadc34CD"; 138 | static std::random_device r; 139 | static std::default_random_engine e(r()); 140 | static std::uniform_int_distribution<> u(0, _countof(cs) - 2); 141 | 142 | static uint8_t d[sizeof(buffer) / 1024]; 143 | 144 | bool b; 145 | uint32_t y; 146 | 147 | __retry: 148 | b = false; 149 | y = 0; 150 | memcpy(d, dd, l); 151 | for (; y < l; ++y) { 152 | if ((d[y] >= '0' && d[y] <= '9') || (d[y] >= 'A' && d[y] <= 'Z') || (d[y] >= 'a' && d[y] <= 'z')) { 153 | continue; 154 | } 155 | switch (d[y]) { 156 | case '.': 157 | case '_': 158 | case '*': 159 | case '(': 160 | case ')': 161 | case '>': 162 | case '<': 163 | case ';': 164 | case '/': 165 | case '$': 166 | continue; 167 | } 168 | 169 | b = true; 170 | 171 | d[y] = cs[u(e)]; 172 | if (d[y] == '\0') { 173 | *reinterpret_cast(NULL) = NULL; 174 | } 175 | 176 | if (y == 0) { 177 | while (d[0] >= '0' && d[0] <= '9') { 178 | d[0] = cs[u(e)]; 179 | } 180 | } else { 181 | switch (d[y - 1]) { 182 | case '/': 183 | case '.': 184 | case '$': 185 | while (d[y] >= '0' && d[y] <= '9') { 186 | d[y] = cs[u(e)]; 187 | } 188 | } 189 | } 190 | } 191 | 192 | { 193 | std::string s(reinterpret_cast(d), l); 194 | if (!b) { 195 | set.insert(s); 196 | return s; 197 | } 198 | if (set.find(s) == set.end()) { 199 | set.insert(s); 200 | return s; 201 | } 202 | } 203 | 204 | goto __retry; 205 | } 206 | 207 | static inline void ReplaceUnicodeChars(const StringId *p, std::unordered_set &set, 208 | std::unordered_map &map) { 209 | const uint8_t *d = reinterpret_cast(buffer + p->string_data_off_); 210 | uint8_t *const dd = const_cast(d); 211 | const uint32_t s = GetUTFCharsSize(d, DecodeUnsignedLeb128(&d)); 212 | if (s > 0) { 213 | std::string ds(reinterpret_cast(d), s); 214 | std::unordered_map::iterator &&k = map.find(ds); 215 | std::string r; 216 | if (k != map.end()) { 217 | r = k->second; 218 | } else { 219 | r = RandomUniqueLegalName(const_cast(d), s, set); 220 | map.insert(std::pair {ds, r}); 221 | } 222 | EncodeUnsignedLeb128(dd, static_cast(r.length())); 223 | memcpy(const_cast(d), r.data(), r.length()); 224 | } 225 | } 226 | 227 | int main() 228 | { 229 | FILE *f = fopen("C:\\Users\\Administrator\\AppData\\Local\\Temp\\pull\\classes.dex", "rb"); 230 | fseek(f, 0, SEEK_END); 231 | const long size = ftell(f); 232 | fseek(f, 0, SEEK_SET); 233 | fread_s(buffer, sizeof(buffer), 1, size, f); 234 | fclose(f); 235 | 236 | const Header *const h = reinterpret_cast
(buffer); 237 | std::unordered_set set(h->string_ids_size_); 238 | std::unordered_map map(h->string_ids_size_); 239 | for (uint32_t i = 0; i < h->method_ids_size_; ++i) { 240 | const MethodId *m = reinterpret_cast(buffer + h->method_ids_off_) + i; 241 | const StringId *p = reinterpret_cast(buffer + h->string_ids_off_) + m->name_idx_; 242 | ReplaceUnicodeChars(p, set, map); 243 | } 244 | for (uint32_t i = 0; i < h->field_ids_size_; ++i) { 245 | const FieldId *m = reinterpret_cast(buffer + h->field_ids_off_) + i; 246 | const StringId *p = reinterpret_cast(buffer + h->string_ids_off_) + m->name_idx_; 247 | ReplaceUnicodeChars(p, set, map); 248 | } 249 | for (uint32_t i = 0; i < h->type_ids_size_; ++i) { 250 | const TypeId *m = reinterpret_cast(buffer + h->type_ids_off_) + i; 251 | const StringId *p = reinterpret_cast(buffer + h->string_ids_off_) + m->descriptor_idx_; 252 | ReplaceUnicodeChars(p, set, map); 253 | } 254 | // Annotations 255 | for (uint32_t i = 0; i < h->string_ids_size_; ++i) { 256 | const StringId *p = reinterpret_cast(buffer + h->string_ids_off_) + i; 257 | const uint8_t *d = reinterpret_cast(buffer + p->string_data_off_); 258 | uint8_t *const dd = const_cast(d); 259 | const uint32_t s = GetUTFCharsSize(d, DecodeUnsignedLeb128(&d)); 260 | if (d[0] == 'L' && d[s - 1] == '<') { 261 | const_cast(d)[s - 1] = ';'; 262 | 263 | std::string ds(reinterpret_cast(d), s); 264 | std::unordered_map::iterator &&k = map.find(ds); 265 | if (k != map.end()) { 266 | std::string &r = k->second; 267 | const uint32_t rl = static_cast(r.length()); 268 | EncodeUnsignedLeb128(dd, rl); 269 | memcpy(const_cast(d), r.data(), rl); 270 | const_cast(d)[rl - 1] = '<'; 271 | } else { 272 | *reinterpret_cast(NULL) = NULL; 273 | } 274 | } 275 | } 276 | 277 | FILE *o = fopen("C:\\Users\\Administrator\\AppData\\Local\\Temp\\pull\\classes_out.dex", "wb"); 278 | fwrite(buffer, 1, h->file_size_, o); 279 | fclose(o); 280 | 281 | std::cout << "Done!\n"; 282 | } 283 | --------------------------------------------------------------------------------