|
15 | 15 | using std::list; |
16 | 16 | using std::string, std::string_view; |
17 | 17 | using std::unordered_map, std::vector; |
18 | | - |
19 | | -typedef unsigned long long hash_t; |
20 | | -hash_t BKDR(const char *_ch, size_t sz) { |
21 | | - unsigned char *ch = (unsigned char *)_ch; |
22 | | - hash_t rv = 0; |
23 | | - for (int i = 0; i < sz; ++i) { |
24 | | - rv = rv * 127 - 36 + ch[i]; |
| 18 | +struct aphash { |
| 19 | + size_t operator()(const string &x) const { |
| 20 | + uint64_t rval = 0; |
| 21 | + for (size_t i = 0; x[i]; ++i) { |
| 22 | + if (i & 1) { |
| 23 | + rval ^= (~((rval << 11) ^ x[i] ^ (rval >> 5))); |
| 24 | + } else { |
| 25 | + rval ^= (~((rval << 7) ^ x[i] ^ (rval >> 3))); |
| 26 | + } |
| 27 | + } |
| 28 | + return rval; |
25 | 29 | } |
26 | | - return rv; |
27 | | -} |
28 | | -struct hashval { |
29 | | - string key; |
30 | | - hash_t hash; |
31 | | - string data; |
32 | | - hashval() { hash = 0; } |
33 | | - hashval(string &&a, hash_t b, string &&c) |
34 | | - : key(std::forward<string>(a)), hash(b), data(std::forward<string>(c)) {} |
35 | 30 | }; |
36 | | -struct RoDB_R { |
37 | | - std::ifstream fp; |
38 | | - unsigned int cnt_bucket; |
39 | | - unsigned int bucket[4096]; // offset |
40 | | - unsigned int data_off; |
41 | | - RoDB_R(const char *path) { |
42 | | - fp.open(path, std::ios::binary); |
43 | | -#define ppch(x) ((char *)&x) |
44 | | - fp.read(ppch(cnt_bucket), 4); |
45 | | - fp.read(ppch(data_off), 4); |
46 | | - fp.read(ppch(bucket[0]), 4 * (size_t)cnt_bucket); |
| 31 | +struct SymDBBase { |
| 32 | + // constexpr static int SEGMENT_MAX = 8; |
| 33 | + constexpr static int SEGMENT_HASH_ORDERED = 0; |
| 34 | + constexpr static int SEGMENT_RVA_INT = 1; |
| 35 | + constexpr static int SEGMENT_STRINGS_IDX = 2; |
| 36 | + constexpr static int SEGMENT_STRINGS = 3; |
| 37 | + constexpr static int SEGMENT_COUNT = 4; |
| 38 | +}; |
| 39 | +#include <vector> |
| 40 | +typedef int s32; |
| 41 | +typedef int64_t s64; |
| 42 | +typedef uint64_t u64; |
| 43 | +typedef uint32_t u32; |
| 44 | +typedef uint16_t u16; |
| 45 | +typedef uint8_t u8; |
| 46 | +template <int BKDR_MUL = 131, int BKDR_ADD = 0> |
| 47 | +constexpr u64 BKDRHash(const char *x, int len) { |
| 48 | + u64 rval = 0; |
| 49 | + for (size_t i = 0; i < len; ++i) { |
| 50 | + rval *= BKDR_MUL; |
| 51 | + rval += x[i]; |
| 52 | + rval += BKDR_ADD; |
47 | 53 | } |
48 | | - void read(void *buf, unsigned int off, size_t sz) { |
49 | | - fp.seekg(std::streampos(off)); |
50 | | - fp.read((char *)buf, sz); |
| 54 | + return rval; |
| 55 | +} |
| 56 | +#include <algorithm> |
| 57 | +#include <fstream> |
| 58 | + |
| 59 | +struct SymDBReader : SymDBBase { |
| 60 | + int SEGOFF[SEGMENT_COUNT], SIZSEG[SEGMENT_COUNT]; |
| 61 | + std::ifstream ifs; |
| 62 | + u64 *hashes; |
| 63 | + u64 *hashes_end; |
| 64 | + template <typename T> |
| 65 | + T _pfread(int off = -1) { |
| 66 | + if (off != -1) |
| 67 | + ifs.seekg({off}); |
| 68 | + T rv; |
| 69 | + ifs.read((char *)&rv, sizeof(T)); |
| 70 | + return rv; |
51 | 71 | } |
52 | | - void getstr(string &buf, unsigned int off) { |
53 | | - if (off != 0) { |
54 | | - fp.seekg(std::streampos(off)); |
| 72 | + SymDBReader(const char *fn) { |
| 73 | + ifs.open(fn, std::ios::binary); |
| 74 | + for (int &off : SEGOFF) { |
| 75 | + off = _pfread<int>(); |
| 76 | + } |
| 77 | + for (int &siz : SIZSEG) { |
| 78 | + siz = _pfread<int>(); |
| 79 | + } |
| 80 | + if (SEGOFF[0] != 8 * SEGMENT_COUNT) { |
| 81 | + printf("SymDB format error detected!!\n"); |
55 | 82 | } |
56 | | - char ch; |
57 | | - while ((ch = fp.get()) != 0) |
58 | | - buf.append(1, ch); |
| 83 | + int seg_begin = SEGOFF[SEGMENT_HASH_ORDERED]; |
| 84 | + int seg_siz = SIZSEG[SEGMENT_HASH_ORDERED]; |
| 85 | + char *buf = (char *)malloc(seg_siz); |
| 86 | + ifs.seekg({seg_begin}); |
| 87 | + ifs.read(buf, seg_siz); |
| 88 | + hashes = (u64 *)buf; |
| 89 | + hashes_end = hashes + (seg_siz / 8); |
59 | 90 | } |
60 | | - string val2key(unsigned int rva) { |
61 | | - fp.seekg(std::streampos(data_off)); |
62 | | - string name; |
63 | | - name.reserve(8192); |
64 | | - while (!fp.eof()) { |
65 | | - int ch = fp.get(); |
66 | | - if (ch == 0) { |
67 | | - unsigned int dst; |
68 | | - fp.read((char *)&dst, 4); |
69 | | - if (dst == rva) { |
70 | | - return name; |
71 | | - } else { |
72 | | - name.clear(); |
| 91 | + ~SymDBReader() { free(hashes); } |
| 92 | + int lookupInternal(u64 hash, int length, u64 hash2, const char *symname) { |
| 93 | + // step 1:lookup internal index |
| 94 | + u64 *lbound = std::lower_bound(hashes, hashes_end, hash); |
| 95 | + if (lbound == hashes_end || *lbound != hash) |
| 96 | + return -1; |
| 97 | + int internalIdx = int(std::distance(hashes, lbound)); |
| 98 | + int matchCount = 0; |
| 99 | + for (u64 *start = lbound; start < hashes_end; ++start) { |
| 100 | + if (*start == hash) |
| 101 | + ++matchCount; |
| 102 | + else |
| 103 | + break; |
| 104 | + } |
| 105 | + // printf("internalIdx [%d,%d)\n", internalIdx, internalIdx+matchCount); |
| 106 | + // step 2:load string offsets |
| 107 | + ifs.seekg({SEGOFF[SEGMENT_STRINGS_IDX] + internalIdx * 4}); |
| 108 | + int string_off[256]; |
| 109 | + for (int i = 0; i <= matchCount; ++i) { |
| 110 | + // string_off[i] = _pfread<int>(SEGOFF[SEGMENT_STRINGS_IDX] + (internalIdx + i) * 4); |
| 111 | + string_off[i] = _pfread<int>(); |
| 112 | + } |
| 113 | + int idx_hits[128]; |
| 114 | + int siz_idx_hits = 0; |
| 115 | + for (int i = 0; i < matchCount; ++i) { |
| 116 | + int strlength = string_off[i + 1] - string_off[i]; |
| 117 | + if (length == strlength) { |
| 118 | + idx_hits[siz_idx_hits++] = internalIdx + i; |
| 119 | + } |
| 120 | + } |
| 121 | + if (siz_idx_hits == 0) { |
| 122 | + return -1; |
| 123 | + } |
| 124 | + int retval = -1; |
| 125 | + for (int i = 0; i < siz_idx_hits; ++i) { |
| 126 | + int nowidx = idx_hits[i]; |
| 127 | + int strptr = string_off[i] + SEGOFF[SEGMENT_STRINGS]; |
| 128 | + ifs.seekg({strptr}); |
| 129 | + if (symname != nullptr) { |
| 130 | + static constexpr auto CMPSTR = [](std::ifstream &ifs, const char *symname) -> bool { |
| 131 | + for (char const *compare = symname; *compare; ++compare) { |
| 132 | + if (*compare != ifs.get()) |
| 133 | + return false; |
| 134 | + } |
| 135 | + return true; |
| 136 | + }; |
| 137 | + if (CMPSTR(ifs, symname)) { |
| 138 | + return _pfread<int>(SEGOFF[SEGMENT_RVA_INT] + nowidx * 4); |
73 | 139 | } |
74 | 140 | } else { |
75 | | - name.push_back(ch); |
| 141 | + static constexpr auto HASHSTR = [](std::ifstream &ifs, int len) -> u64 { |
| 142 | + u64 rv = 0; |
| 143 | + while (len-- > 0) { |
| 144 | + rv = 131 * rv + char(ifs.get()); |
| 145 | + } |
| 146 | + return rv; |
| 147 | + }; |
| 148 | + if (HASHSTR(ifs, length) == hash2) { |
| 149 | + if (retval != -1) { |
| 150 | + printf("hash coll detected!\n"); |
| 151 | + exit(1); |
| 152 | + } |
| 153 | + retval = _pfread<int>(SEGOFF[SEGMENT_RVA_INT] + nowidx * 4); |
| 154 | + } |
76 | 155 | } |
77 | 156 | } |
78 | | - return "(nil)"; |
| 157 | + return retval; |
79 | 158 | } |
80 | | - bool _cmp(string_view key) { |
81 | | - for (uint32_t i = 0; i < key.size(); ++i) { |
82 | | - int ch = fp.get(); |
83 | | - if (ch != key[i]) { |
84 | | - return false; |
85 | | - } |
86 | | - } |
87 | | - return fp.get() == 0; |
| 159 | + int getsym(const char *name) { |
| 160 | + int len = int(strlen(name)); |
| 161 | + return lookupInternal(do_hash(name, len), len, 0, name); |
88 | 162 | } |
89 | | - unsigned int get(string_view key) { // return file offset |
90 | | - auto hash = BKDR(key.data(), key.size()); |
91 | | - auto bkoff = bucket[hash % cnt_bucket]; |
92 | | - fp.seekg(std::streampos(bkoff)); |
93 | | - vector<unsigned int> tolookup; |
94 | | - tolookup.reserve(512); |
95 | | - while (1) { |
96 | | - hash_t hs; |
97 | | - unsigned int off; |
98 | | - fp.read(ppch(hs), 8); |
99 | | - fp.read(ppch(off), 4); |
100 | | - if (off == 0xffffffff) |
101 | | - break; |
102 | | - if (hs == hash) |
103 | | - tolookup.push_back(off + data_off); |
104 | | - /* |
105 | | - if (hs == hash) { |
106 | | - off += data_off; |
107 | | - auto pos = fp.tellg(); |
108 | | - string key_now; |
109 | | - getstr(key_now, off); |
110 | | - if (key == key_now) { |
111 | | - return off + (unsigned int)key.size() + 1; |
112 | | - } |
113 | | - fp.seekg(pos); |
114 | | - }*/ |
| 163 | + std::string rva2name(int rva) { |
| 164 | + int siz_rva = SIZSEG[SEGMENT_RVA_INT]; |
| 165 | + char *buf = new char[siz_rva]; |
| 166 | + ifs.seekg({SEGOFF[SEGMENT_RVA_INT]}); |
| 167 | + ifs.read(buf, siz_rva); |
| 168 | + int *need = (int *)buf; |
| 169 | + int *end = need + siz_rva / 4; |
| 170 | + int *found = std::find(need, end, rva); |
| 171 | + if (found == end) { |
| 172 | + return "(nil)"; |
115 | 173 | } |
116 | | - for (auto off : tolookup) { |
117 | | - fp.seekg({off}); |
118 | | - if (_cmp(key)) { |
119 | | - return off + (uint32_t)key.size() + 1; |
120 | | - } |
| 174 | + int internalId = int(std::distance(need, found)); |
| 175 | + int soff = _pfread<int>(SEGOFF[SEGMENT_STRINGS_IDX] + internalId * 4); |
| 176 | + int slen = _pfread<int>(SEGOFF[SEGMENT_STRINGS_IDX] + internalId * 4 + 4) - soff; |
| 177 | + ifs.seekg({int(SEGOFF[SEGMENT_STRINGS] + soff)}); |
| 178 | + std::string rv; |
| 179 | + rv.resize(slen, 0); |
| 180 | + ifs.read(rv.data(), slen); |
| 181 | + delete[] buf; |
| 182 | + return rv; |
| 183 | + } |
| 184 | + void dumpall(unordered_map<string, int, aphash>* hashMap) { |
| 185 | + int siz_rva = SIZSEG[SEGMENT_RVA_INT]; |
| 186 | + char *buf = new char[siz_rva]; |
| 187 | + ifs.seekg({SEGOFF[SEGMENT_RVA_INT]}); |
| 188 | + ifs.read(buf, siz_rva); |
| 189 | + int *need = (int *)buf; |
| 190 | + int *end = need + siz_rva / 4; |
| 191 | + for (int a = 0; need + a != end; a++) { |
| 192 | + int *rva = need + a; |
| 193 | + int internalId = int(std::distance(need, rva)); |
| 194 | + int soff = _pfread<int>(SEGOFF[SEGMENT_STRINGS_IDX] + internalId * 4); |
| 195 | + int slen = _pfread<int>(SEGOFF[SEGMENT_STRINGS_IDX] + internalId * 4 + 4) - soff; |
| 196 | + ifs.seekg({int(SEGOFF[SEGMENT_STRINGS] + soff)}); |
| 197 | + char *rv = new char[slen]; |
| 198 | + ifs.read((char *)rv, slen); |
| 199 | + hashMap->insert({rv, *rva}); |
| 200 | + //hashMap[rv] = *rva; |
| 201 | + // printf("[%08d] %s\n", *rva, rv.c_str()); |
| 202 | + delete[] rv; |
121 | 203 | } |
122 | | - return 0; |
| 204 | + |
| 205 | + delete[] buf; |
123 | 206 | } |
124 | 207 | }; |
125 | | -static RoDB_R *pdb; |
| 208 | +int fnstat = 0; |
| 209 | +static SymDBReader *SymDB; |
126 | 210 | static uintptr_t BaseAdr; |
127 | | -string ptr2name(void *ptr) { |
128 | | - unsigned int va = uint32_t(((uintptr_t)ptr - BaseAdr)); |
129 | | - return pdb->val2key(va); |
| 211 | +unordered_map<string, int, aphash> *FuncMap; |
| 212 | +void InitFastDlsym() { |
| 213 | + printf("[Info] Loading Symbols\n"); |
| 214 | + unordered_map<string, int, aphash> *realFuncMap = new unordered_map<string, int, aphash>; |
| 215 | + SymDB->dumpall(realFuncMap); |
| 216 | + fnstat = 1; |
| 217 | + SymDB = nullptr; |
| 218 | + SymDB = new SymDBReader("bedrock_server.symdb2"); |
| 219 | + FuncMap = realFuncMap; |
| 220 | + printf("[Info] FastDlsymInited <%zd>\n", realFuncMap->size()); |
130 | 221 | } |
131 | 222 | void *dlsym_real(const char *x) { |
132 | | - if (pdb == nullptr) { |
133 | | - if (!std::filesystem::exists("bedrock_server.symdb")) { |
134 | | - printf("SymDB not found\ntry to run RoDB.exe\n"); |
| 223 | + if (SymDB == nullptr) { |
| 224 | + if (!std::filesystem::exists("bedrock_server.symdb2")) { |
| 225 | + printf("SymDB not found\ntry to run SymDB2.exe\n"); |
135 | 226 | std::this_thread::sleep_for(std::chrono::seconds(10)); |
136 | 227 | exit(1); |
137 | 228 | } |
138 | | - pdb = new RoDB_R("bedrock_server.symdb"); |
| 229 | + SymDB = new SymDBReader("bedrock_server.symdb2"); |
139 | 230 | BaseAdr = (uintptr_t)GetModuleHandle(NULL); |
140 | 231 | static_assert(sizeof(GetModuleHandle(NULL)) == 8); |
141 | 232 | } |
142 | | - auto rv = pdb->get(x); |
143 | | - if (!rv) |
144 | | - return nullptr; |
145 | | - unsigned int rva; |
146 | | - pdb->read(&rva, rv, 4); |
147 | | - return (void *)(BaseAdr + rva); |
| 233 | + if (fnstat == 0) |
| 234 | + InitFastDlsym(); |
| 235 | + for (;fnstat == 0;) { |
| 236 | + std::this_thread::sleep_for(std::chrono::seconds(1)); |
| 237 | + } |
| 238 | + if (fnstat == 1) { |
| 239 | + auto iter = FuncMap->find(x); |
| 240 | + if (iter != FuncMap->end()) { |
| 241 | + //std::cout<<x<<" : "<<iter->second<<" == "<<rv<<std::endl; |
| 242 | + return (void *)(BaseAdr + iter->second); |
| 243 | + } |
| 244 | + } |
| 245 | + auto rv = SymDB->getsym(x); |
| 246 | + if (!rv) |
| 247 | + return nullptr; |
| 248 | + return (void *)(BaseAdr + rv); |
148 | 249 | } |
149 | 250 | inline static void HookFunction__begin() { |
150 | 251 | DetourTransactionBegin(); |
|
0 commit comments