static bool lc_dump_dyld_export_trie(char *trie, char *p, char *buf, uint32_t bufidx, uint32_t bufsize, lambchop_logger *logger) { uint64_t infolen = parse_uleb128(&p); uint8_t children; int i; if (infolen) { char *q = p + infolen; uint64_t flags = parse_uleb128(&p); uint64_t address; uint64_t kind = flags & EXPORT_SYMBOL_FLAGS_KIND_MASK; flags &= ~EXPORT_SYMBOL_FLAGS_KIND_MASK; buf[bufidx] = '\0'; if (kind == EXPORT_SYMBOL_FLAGS_KIND_REGULAR) { address = parse_uleb128(&p); lambchop_info(logger, "export_info: infolen = %llu, symbol = %s, flags = 0x%llx, kind=EXPORT_SYMBOL_FLAGS_KIND_REGULAR, address = %llx\n", infolen, buf, flags, address); } else { lambchop_err(logger, "unsupported export info: infolen = %llu, symbol = %s, flags = 0x%llx, kind=0x%llx\n", infolen, buf, flags, kind); return false; } if (p != q) { lambchop_err(logger, "failed to parse export trie: infolen = %llu, flags = 0x%llx, kind = 0x%llx\n", infolen, flags, kind); return false; } } children = (uint8_t)(*p); p++; for (i = 0; i < children; i++) { int len = strlen(p); uint64_t offset; // TODO check bufsize memcpy(buf + bufidx, p, len); p += len + 1; offset = parse_uleb128(&p); if (!lc_dump_dyld_export_trie(trie, trie + offset, buf, bufidx + len, bufsize, logger)) { return false; } } return true; }
static int64_t parse_sleb128(char **pp) { char *s, *e; uint64_t uleb; int n; s = *pp; uleb = parse_uleb128(pp); e = *pp; n = e - s; return (int64_t)(uleb - pow(128, n)); }
/* Parses the ARM attributes section's 'eabi' subsection */ int parse_eabi_attr_aeabi_subsection(int fd, off_t* pos, size_t sh_size, char wchar_size) { unsigned long int attr, value; int ret; char buf[1024]; if (sh_size < 1) { printf("Error: aeabi subsection too small.\n"); return 1; } unsigned char tag; if (read(fd, &tag, sizeof(tag)) != sizeof(tag)) { perror("reading aeabi subsection tag\n"); return 1; } ++(*pos); // if tag = section or tag = symbol, skip over section/symbol identifiers if (tag == 2 || tag == 3) { unsigned long int id; do { ret = parse_uleb128(fd, &id, pos, sh_size); if (ret != 0) return ret; } while (id != 0); } while (*pos < sh_size) { ret = parse_uleb128(fd, &attr, pos, sh_size); if (ret != 0) return ret; switch (attr) { case 4: // Tag_CPU_raw_name case 5: // Tag_CPU_name case 67: // Tag_conformance case 32: // Tag_compatibility ret = parse_ntbs(fd, buf, sizeof(buf), pos, sh_size); if (ret != 0) return ret; break; case 18: // Tag_ABI_PCS_wchar_t ret = parse_uleb128(fd, &value, pos, sh_size); if (ret != 0) return ret; printf("Tag_ABI_PCS_wchar_t = %ld", value); if (wchar_size >= 0) { if (value > 0x7f) { // This utility does not support resizing structures printf("Error: Unable to patch Tag_ABI_PCS_wchar_t: old value is too big.\n"); } if (lseek(fd, -1, SEEK_CUR) == (off_t)-1) { perror("seeking to patch"); return 1; } if (write(fd, &wchar_size, sizeof(wchar_size)) != sizeof(wchar_size)) { perror("patching"); return 1; } printf(", patched to %d\n", wchar_size); } else { printf("\n"); } break; default: // skip over tag -- for >32, we follow ARM's convention if (attr > 32) { if ((attr % 2) == 0) // even ret = parse_uleb128(fd, &value, pos, sh_size); else // odd ret = parse_ntbs(fd, NULL, 0, pos, sh_size); } else { // all NTBS tags are in the switch above; the rest are ULEB128 ret = parse_uleb128(fd, &value, pos, sh_size); } if (ret != 0) return ret; break; } } return 0; }
osrstats *parse_osr(const unsigned char *s, int len){ osrstats *ret = malloc(sizeof(osrstats)); unsigned char *p = (unsigned char *)s; const void *end = s+len; #define INC(x) do { \ p+=x; \ if((const void*)p >= end) \ goto fail; \ } while(0); // mode ret->mode = *p; INC(1); // don't care INC(4); // osu file MD5 if(*p != 0x0B) goto fail; INC(1); if(*p != 0x20) goto fail; INC(1); memcpy(&ret->md5_map, p, sizeof(MD5)); INC(0x20); // user name { if(*p != 0x0B) goto fail; INC(1); // FIXME: username len > 128 uint8_t len = *p; if(len >= 128) goto fail; INC(1); memcpy(&ret->username, p, len); ret->username[len] = 0; INC(len); } // replay MD5 if(*p != 0x0B) goto fail; INC(1); if(*p != 0x20) goto fail; INC(1); memcpy(&ret->md5_osr, p, sizeof(MD5)); INC(0x20); // Stats ret->n_300 = *(uint16_t *)p; INC(2); ret->n_100 = *(uint16_t *)p; INC(2); ret->n_50 = *(uint16_t *)p; INC(2); ret->n_geki = *(uint16_t *)p; INC(2); ret->n_katu = *(uint16_t *)p; INC(2); ret->n_miss = *(uint16_t *)p; INC(2); ret->score = *(uint32_t *)p; INC(4); ret->max_combo = *(uint16_t *)p; INC(2); ret->perfect = *(bool *)p; INC(1); ret->mods = *(uint32_t *)p; INC(4); // HP Graph { if(*p != 0x0B) goto fail; INC(1); uint32_t len; int n = parse_uleb128(p, &len); INC(n); // TODO INC(len); } // Datetime uint64_t rawtime = *(uint64_t *)p - 621355968000000000; ret->achieve_time.tv_sec = rawtime / 10000000; ret->achieve_time.tv_usec = rawtime % 10000000; INC(8) return ret; #undef INC fail: free(ret); return NULL; }
static bool lc_dump_dyld_bind_info(uint32_t offset, uint32_t size, char *img, lambchop_logger *logger) { char *bind_info = img + offset; char *p = bind_info; if (!size) { return true; } while (p < (bind_info + size)) { uint8_t opcode = *p & BIND_OPCODE_MASK; uint8_t immediate = *p & BIND_IMMEDIATE_MASK; const char *type; int64_t sleb; uint64_t uleb, uleb2; p++; switch(opcode) { case BIND_OPCODE_DONE: return true; case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: lambchop_info(logger, "bind_info: op=BIND_OPCODE_SET_DYLIB_ORDINAL_IMM ordinal=%d\n", immediate); break; case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: uleb = parse_uleb128(&p); lambchop_info(logger, "bind_info: op=BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB ordinal=%d\n", uleb); break; case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: lambchop_info(logger, "bind_info: op=BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM flags=0x%x, symbol = %s\n", immediate, p); p += strlen(p) + 1; break; case BIND_OPCODE_SET_TYPE_IMM: type = bind_type(immediate); if (!type) { lambchop_err(logger, "unsupported bind type 0x%x\n", immediate); return false; } lambchop_info(logger, "bind_info: op=BIND_OPCODE_SET_TYPE_IMM type=%s\n", type); break; case BIND_OPCODE_SET_ADDEND_SLEB: sleb = parse_sleb128(&p); lambchop_info(logger, "bind_info: op=BIND_OPCODE_SET_ADDEND_SLEB addend=%lld\n", sleb); break; case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: uleb = parse_uleb128(&p); lambchop_info(logger, "bind_info: op=BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB segment=%d offset=0x%x\n", immediate, uleb); break; case BIND_OPCODE_ADD_ADDR_ULEB: uleb = parse_uleb128(&p); lambchop_info(logger, "bind_info: op=BIND_OPCODE_ADD_ADDR_ULEB offset=0x%x\n", uleb); break; case BIND_OPCODE_DO_BIND: lambchop_info(logger, "bind_info: op=BIND_OPCODE_DO_BIND\n"); break; case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: uleb = parse_uleb128(&p); lambchop_info(logger, "bind_info: op=BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB offset=0x%x\n", uleb); break; case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: lambchop_info(logger, "bind_info: op=BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED offset=0x%x\n", immediate); break; case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: uleb = parse_uleb128(&p); uleb2 = parse_uleb128(&p); lambchop_info(logger, "bind_info: op=BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB, times=%d, offset=0x%x\n", uleb, uleb2); break; default: lambchop_err(logger, "unsupported bind info opcode 0x%x\n", opcode); return false; } } lambchop_err(logger, "BIND_OPCODE_DONE not found\n"); return false; }
static bool lc_dump_dyld_rebase_info(struct dyld_info_command *command, char *img, lambchop_logger *logger) { char *rebase_info = img + command->rebase_off; char *p = rebase_info; int i; if (!command->rebase_size) { return true; } while (p < (rebase_info + command->rebase_size)) { uint8_t opcode = *p & REBASE_OPCODE_MASK; uint8_t immediate = *p & REBASE_IMMEDIATE_MASK; uint64_t uleb, uleb2; const char *type; p++; switch(opcode) { case REBASE_OPCODE_DONE: return true; case REBASE_OPCODE_SET_TYPE_IMM: /*REBASE_TYPE_POINTER: ポインタの中身を書き換える*/ type = rebase_type(immediate); if (!type) { lambchop_err(logger, "unsupported rebase type 0x%x\n", immediate); return false; } lambchop_info(logger, "rebase_info: op=REBASE_OPCODE_SET_TYPE_IMM type=%s\n", type); break; case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: /* * どのセグメントのどのオフセットを書き換えるかを指定する。 * この命令ではセグメントのインデックスを immediate によって与える。 * セグメントのインデックスは0から数える。 * セグメントのインデックスが2だった場合は3つ目のセグメント。 */ uleb = parse_uleb128(&p); lambchop_info(logger, "rebase_info: op=REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB, segment=%u, offset=0x%x\n", immediate, uleb); break; case REBASE_OPCODE_ADD_ADDR_ULEB: uleb = parse_uleb128(&p); lambchop_info(logger, "rebase_info: op=REBASE_OPCODE_ADD_ADDR_ULEB, offset = 0x%x\n", uleb); break; case REBASE_OPCODE_ADD_ADDR_IMM_SCALED: lambchop_info(logger, "rebase_info: op=REBASE_OPCODE_ADD_ADDR_IMM_SCALED, immediate = 0x%x\n", immediate); break; case REBASE_OPCODE_DO_REBASE_IMM_TIMES: /* * 指定した回数 segment の offset の rebase を繰り返す。 * 書き換えるべき値は毎回ポインタのサイズ分だけずれる(REBASE_TYPE_POINTER の場合)。 * segment や offset には REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB などで指定された値を用いる。 * この命令では繰り返しの回数は immediate によって与えられる。 */ lambchop_info(logger, "rebase_info: op=REBASE_OPCODE_DO_REBASE_IMM_TIMES, times=%d\n", immediate); break; case REBASE_OPCODE_DO_REBASE_ULEB_TIMES: uleb = parse_uleb128(&p); lambchop_info(logger, "rebase_info: op=REBASE_OPCODE_DO_REBASE_ULEB_TIMES, times=%d\n", uleb); break; case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: /* * rebase を実行した後に、uleb でのアドレス加算を行う。 */ uleb = parse_uleb128(&p); lambchop_info(logger, "rebase_info: op=REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB, immediate=0x%x, offset=0x%x\n", immediate, uleb); break; case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB: /** * rebase を最初のuleb128回繰り返す。繰り返しの度に次のuleb128回オフセットをずらす。 */ uleb = parse_uleb128(&p); uleb2 = parse_uleb128(&p); lambchop_info(logger, "rebase_info: op=REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB, times=%d, offset=0x%x\n", uleb, uleb2); break; default: lambchop_err(logger, "unsupported rebase info opcode 0x%x\n", opcode); return false; } } lambchop_err(logger, "REBASE_OPCODE_DONE not found\n"); return false; }