// SPDX-FileCopyrightText: 2021 Florian Märkl // SPDX-FileCopyrightText: 2019 pancake // SPDX-License-Identifier: LGPL-3.0-only #include #include #include "../unit/minunit.h" bool test_rz_bin(void) { RzBin *bin = rz_bin_new(); const char *default_algos[] = { "md5", "sha1", "sha256", "crc32", "entropy" }; bin->default_hashes = rz_list_new_from_array((const void **)default_algos, RZ_ARRAY_SIZE(default_algos)); RzIO *io = rz_io_new(); rz_io_bind(io, &bin->iob); RzBinOptions opt = { 0 }; rz_bin_options_init(&opt, 0, 0, 0, false); RzBinFile *bf = rz_bin_open(bin, "bins/elf/ioli/crackme0x00", &opt); mu_assert_notnull(bf, "crackme0x00 binary could not be opened"); mu_assert_notnull(bf->o, "bin object"); RzBinObject *obj = rz_bin_cur_object(bin); RzPVector *sections = rz_bin_object_get_sections(obj); mu_assert_eq(rz_pvector_len(sections), 29, "rz_bin_object_get_sections"); rz_pvector_free(sections); RzPVector *segments = rz_bin_object_get_segments(obj); mu_assert_eq(rz_pvector_len(segments), 10, "rz_bin_object_get_segments"); rz_pvector_free(segments); const RzPVector *entries = rz_bin_object_get_entries(obj); mu_assert_eq(rz_pvector_len(entries), 1, "rz_bin_object_get_entries"); RzBinAddr *entry = (RzBinAddr *)rz_pvector_at(entries, 0); mu_assert_eq(entry->vaddr, 0x8048360, "entry virtual address"); mu_assert_eq(entry->paddr, 0x360, "entry file offset"); const RzPVector *imports = rz_bin_object_get_imports(obj); mu_assert_eq(rz_pvector_len(imports), 5, "rz_bin_object_get_imports"); const char *import_names[] = { "__libc_start_main", "printf", "scanf", "strcmp", "__gmon_start__" }; bool has_import_names[sizeof(import_names)] = { 0 }; RzBinImport *import; void **it; void **vec_it; rz_pvector_foreach (imports, vec_it) { import = *vec_it; for (int i = 0; i < RZ_ARRAY_SIZE(import_names); ++i) { if (!strcmp(import->name, import_names[i])) { has_import_names[i] = true; break; } } } for (int i = 0; i < RZ_ARRAY_SIZE(import_names); ++i) { mu_assert_true(has_import_names[i], "Import name was not found"); } const RzPVector *strings = rz_bin_object_get_strings(obj); mu_assert_eq(rz_pvector_len(strings), 13, "rz_bin_object_get_strings"); const char *exp_strings[] = { // .dynstr is mapped (SHF_ALLOC) so its strings are now reported "__gmon_start__", "libc.so.6", "printf", "strcmp", "scanf", "_IO_stdin_used", "__libc_start_main", "GLIBC_2.0", "IOLI Crackme Level 0x00\n", "Password: ", // "%s", // This is not automatically recognized because too short "250382", "Invalid Password!\n", "Password OK :)\n", }; RzBinString *s; int i = 0; rz_pvector_foreach (strings, it) { s = *it; mu_assert_streq(s->string, exp_strings[i], "String not found"); mu_assert_true(rz_bin_object_get_string_at(obj, s->vaddr, true) != NULL, "is_string (virt) should be true"); mu_assert_false(rz_bin_object_get_string_at(obj, s->vaddr, false) != NULL, "is_string (phys) should be false"); mu_assert_true(rz_bin_object_get_string_at(obj, s->paddr, false) != NULL, "is_string (virt) should be false"); mu_assert_false(rz_bin_object_get_string_at(obj, s->paddr, true) != NULL, "is_string (phys) should be true"); i++; } RzPVector *hashes = rz_bin_file_compute_hashes(bin, bf, UT64_MAX); mu_assert_eq(rz_pvector_len(hashes), 5, "rz_bin_file_get_hashes"); const char *hash_names[] = { "md5", "sha1", "sha256", "crc32", "entropy" }; const char *hash_hexes[] = { "99327411dd72a11d7198b54298648adf", "f2bf1c7758c7b1e22bdea1d7681882783b658705", "3aed9a3821134a2ab1d69cb455e5e9d80bb651a1c97af04cdba4f3bb0adaa37b", "cf8cb28a", "3.484857" }; RzBinFileHash *hash; i = 0; void **v_it; rz_pvector_foreach (hashes, v_it) { hash = *v_it; mu_assert_streq(hash->type, hash_names[i], "hash name is wrong"); mu_assert_streq(hash->hex, hash_hexes[i], "hash digest is wrong"); i++; } rz_pvector_free(hashes); rz_bin_free(bin); rz_io_free(io); mu_end; } static RzBinReloc *add_reloc(RzPVector *l, ut64 paddr, ut64 vaddr, ut64 target_vaddr) { RzBinReloc *reloc = RZ_NEW0(RzBinReloc); reloc->type = RZ_BIN_RELOC_8; reloc->paddr = paddr; reloc->vaddr = vaddr; reloc->target_vaddr = target_vaddr; rz_pvector_push(l, reloc); return reloc; } bool test_rz_bin_reloc_storage(void) { RzPVector *l = rz_pvector_new(NULL); RzBinReloc *r0 = add_reloc(l, 0x108, 0x1000, 0x2004); mu_assert_notnull(r0, "reloc"); RzBinReloc *r1 = add_reloc(l, 0x2002, 0x1003, 0x2008); mu_assert_notnull(r1, "reloc"); RzBinReloc *rz = add_reloc(l, 0x1001, 0x1004, UT64_MAX); mu_assert_notnull(rz, "reloc"); RzBinReloc *r3 = add_reloc(l, 0x1003, 0x1006, 0x200c); mu_assert_notnull(r3, "reloc"); RzBinRelocStorage *relocs = rz_bin_reloc_storage_new(l); RzBinReloc *r = rz_bin_reloc_storage_get_reloc_in(relocs, 0xfff, 1); mu_assert_null(r, "reloc in"); r = rz_bin_reloc_storage_get_reloc_in(relocs, 0xfff, 2); mu_assert_ptreq(r, r0, "reloc in"); r = rz_bin_reloc_storage_get_reloc_in(relocs, 0x1000, 1); mu_assert_ptreq(r, r0, "reloc in"); r = rz_bin_reloc_storage_get_reloc_in(relocs, 0x1002, 1); mu_assert_null(r, "reloc in"); r = rz_bin_reloc_storage_get_reloc_in(relocs, 0x1002, 10); mu_assert_ptreq(r, r1, "reloc in"); r = rz_bin_reloc_storage_get_reloc_in(relocs, 0x1003, 10); mu_assert_ptreq(r, r1, "reloc in"); r = rz_bin_reloc_storage_get_reloc_in(relocs, 0x1006, 8); mu_assert_ptreq(r, r3, "reloc in"); r = rz_bin_reloc_storage_get_reloc_in(relocs, 0x1007, 8); mu_assert_null(r, "reloc in"); r = rz_bin_reloc_storage_get_reloc_in(relocs, 0x2004, 8); mu_assert_null(r, "reloc in"); r = rz_bin_reloc_storage_get_reloc_in(relocs, 0x108, 8); mu_assert_null(r, "reloc in"); r = rz_bin_reloc_storage_get_reloc_to(relocs, 0x1000); mu_assert_null(r, "reloc to"); r = rz_bin_reloc_storage_get_reloc_to(relocs, 0x108); mu_assert_null(r, "reloc to"); r = rz_bin_reloc_storage_get_reloc_to(relocs, 0x2003); mu_assert_null(r, "reloc to"); r = rz_bin_reloc_storage_get_reloc_to(relocs, 0x2004); mu_assert_ptreq(r, r0, "reloc to"); r = rz_bin_reloc_storage_get_reloc_to(relocs, 0x2005); mu_assert_null(r, "reloc to"); r = rz_bin_reloc_storage_get_reloc_to(relocs, 0x2007); mu_assert_null(r, "reloc to"); r = rz_bin_reloc_storage_get_reloc_to(relocs, 0x2008); mu_assert_ptreq(r, r1, "reloc to"); r = rz_bin_reloc_storage_get_reloc_to(relocs, 0x2009); mu_assert_null(r, "reloc to"); r = rz_bin_reloc_storage_get_reloc_to(relocs, 0x200c); mu_assert_ptreq(r, r3, "reloc to"); r = rz_bin_reloc_storage_get_reloc_to(relocs, 0x200d); mu_assert_null(r, "reloc to"); rz_bin_reloc_storage_free(relocs); mu_end; } typedef struct { RzList /**/ *expect; /// things whose delete events are expected now bool failed_unexpected; } DelTracker; static void event_file_del_cb(RzEvent *ev, int type, void *user, void *data) { DelTracker *tracker = user; if (type != RZ_EVENT_BIN_FILE_DEL) { tracker->failed_unexpected = true; return; } RzEventBinFileDel *bev = data; RzListIter *it = rz_list_find_val(tracker->expect, bev->bf); if (!it) { tracker->failed_unexpected = true; return; } rz_list_delete(tracker->expect, it); } bool test_rz_bin_file_delete(void) { RzIO *io = rz_io_new(); RzBin *bin = rz_bin_new(); rz_io_bind(io, &bin->iob); DelTracker tracker = { .expect = rz_list_new(), .failed_unexpected = false }; rz_event_hook(bin->event, RZ_EVENT_BIN_FILE_DEL, event_file_del_cb, &tracker); RzBinOptions opt = { 0 }; RzBinFile *f0 = rz_bin_open(bin, "hex://42424242424242", &opt); mu_assert_notnull(f0, "open file"); RzBinFile *f1 = rz_bin_open(bin, "malloc://1024", &opt); mu_assert_notnull(f1, "open file"); mu_assert_ptrneq(f1, f0, "unique files"); mu_assert_eq(rz_list_length(bin->binfiles), 2, "files count"); mu_assert_true(rz_list_contains(bin->binfiles, f0), "f0 in list"); mu_assert_true(rz_list_contains(bin->binfiles, f1), "f1 in list"); mu_assert_false(tracker.failed_unexpected, "unexpected del event"); rz_list_push(tracker.expect, f0); rz_bin_file_delete(bin, f0); mu_assert_false(tracker.failed_unexpected, "unexpected del event"); mu_assert_true(rz_list_empty(tracker.expect), "missing del event"); mu_assert_eq(rz_list_length(bin->binfiles), 1, "files count"); mu_assert_true(rz_list_contains(bin->binfiles, f1), "f1 in list"); rz_bin_free(bin); rz_io_free(io); // free should not send del events for remaining files mu_assert_true(rz_list_empty(tracker.expect), "missing del event"); mu_assert_false(tracker.failed_unexpected, "unexpected del event"); rz_list_free(tracker.expect); mu_end; } bool test_rz_bin_file_delete_all(void) { RzIO *io = rz_io_new(); RzBin *bin = rz_bin_new(); rz_io_bind(io, &bin->iob); DelTracker tracker = { .expect = rz_list_new(), .failed_unexpected = false }; rz_event_hook(bin->event, RZ_EVENT_BIN_FILE_DEL, event_file_del_cb, &tracker); RzBinOptions opt = { 0 }; RzBinFile *f0 = rz_bin_open(bin, "hex://42424242424242", &opt); mu_assert_notnull(f0, "open file"); RzBinFile *f1 = rz_bin_open(bin, "malloc://1024", &opt); mu_assert_notnull(f1, "open file"); mu_assert_ptrneq(f1, f0, "unique files"); mu_assert_eq(rz_list_length(bin->binfiles), 2, "files count"); mu_assert_true(rz_list_contains(bin->binfiles, f0), "f0 in list"); mu_assert_true(rz_list_contains(bin->binfiles, f1), "f1 in list"); mu_assert_false(tracker.failed_unexpected, "unexpected del event"); rz_list_push(tracker.expect, f0); rz_list_push(tracker.expect, f1); rz_bin_file_delete_all(bin); mu_assert_false(tracker.failed_unexpected, "unexpected del event"); mu_assert_true(rz_list_empty(tracker.expect), "missing del event"); mu_assert_eq(rz_list_length(bin->binfiles), 0, "files count"); rz_bin_free(bin); rz_io_free(io); mu_assert_true(rz_list_empty(tracker.expect), "missing del event"); mu_assert_false(tracker.failed_unexpected, "unexpected del event"); rz_list_free(tracker.expect); mu_end; } bool test_rz_bin_sections_mapping(void) { RzBin *bin = rz_bin_new(); RzIO *io = rz_io_new(); rz_io_bind(io, &bin->iob); RzBinOptions opt = { 0 }; rz_bin_options_init(&opt, 0, 0, 0, false); RzBinFile *bf = rz_bin_open(bin, "bins/elf/ioli/crackme0x00", &opt); mu_assert_notnull(bf, "crackme0x00 binary could not be opened"); mu_assert_notnull(bf->o, "bin object"); mu_assert_streq(bf->file, "bins/elf/ioli/crackme0x00", "filename should be right"); RzBinObject *o = bf->o; RzVector *maps = rz_bin_object_sections_mapping_list(o); mu_assert_eq(rz_vector_len(maps), 10, "there should be 10 maps, because 10 are the segments"); RzBinSectionMap *map0 = rz_vector_index_ptr(maps, 0); mu_assert_streq(map0->segment->name, "PHDR", "first map is for PHDR"); mu_assert_eq(rz_pvector_len(&map0->sections), 0, "no sections in PHDR"); RzBinSectionMap *map1 = rz_vector_index_ptr(maps, 1); mu_assert_streq(map1->segment->name, "INTERP", "second map is for INTERP"); mu_assert_eq(rz_pvector_len(&map1->sections), 1, "just .interp in INTERP"); RzBinSection *sec1_0 = rz_pvector_at(&map1->sections, 0); mu_assert_streq(sec1_0->name, ".interp", "section is .interp"); RzBinSectionMap *map7 = rz_vector_index_ptr(maps, 7); mu_assert_streq(map7->segment->name, "GNU_RELRO", "seventh map is for GNURELRO"); mu_assert_eq(rz_pvector_len(&map7->sections), 5, "5 elements in GNURELRO"); RzBinSection *sec7_0 = rz_pvector_at(&map7->sections, 0); mu_assert_streq(sec7_0->name, ".ctors", "section is .ctors"); RzBinSection *sec7_1 = rz_pvector_at(&map7->sections, 1); mu_assert_streq(sec7_1->name, ".dtors", "section is .dtors"); rz_vector_free(maps); rz_bin_free(bin); rz_io_free(io); mu_end; } bool test_rz_bin_p2v2p(void) { RzBin *bin = rz_bin_new(); RzIO *io = rz_io_new(); rz_io_bind(io, &bin->iob); RzBinOptions opt = { 0 }; rz_bin_options_init(&opt, 0, 0, 0, false); RzBinFile *bf = rz_bin_open(bin, "bins/elf/ls", &opt); mu_assert_notnull(bf, "ls binary could not be opened"); RzBinObject *o = bf->o; mu_assert_eq(rz_bin_object_p2v(o, 0x0001c0f8), 0x0001c0f8, "xstrtoumax string p2v"); mu_assert_eq(rz_bin_object_v2p(o, 0x0001c0f8), 0x0001c0f8, "xstrtoumax string v2p"); mu_assert_eq(rz_bin_object_p2v(o, 0x00021260), 0x00022260, "obstack_alloc_failed_handler symbol p2v"); mu_assert_eq(rz_bin_object_v2p(o, 0x00022260), 0x00021260, "obstack_alloc_failed_handler symbol v2p"); mu_assert_eq(rz_bin_object_v2p(o, 0xcafebabe), UT64_MAX, "non existing vaddr"); mu_assert_eq(rz_bin_object_p2v(o, 0xcafebabe), UT64_MAX, "non existing paddr"); RzVector *v = rz_bin_object_p2v_all(o, 0xcafebabe); mu_assert_eq(rz_vector_len(v), 0, "non existing paddr should have 0 elements in vector"); rz_vector_free(v); v = rz_bin_object_p2v_all(o, 0x21260); mu_assert_eq(rz_vector_len(v), 1, "p2v_all of obstack_alloc_failed_handler paddr should have 1 element"); mu_assert_eq(*(ut64 *)rz_vector_head(v), 0x22260, "obstack_alloc_failed_handler symbol p2v_all"); rz_vector_free(v); rz_bin_free(bin); rz_io_free(io); mu_end; } bool all_tests() { mu_run_test(test_rz_bin); mu_run_test(test_rz_bin_reloc_storage); mu_run_test(test_rz_bin_file_delete); mu_run_test(test_rz_bin_file_delete_all); mu_run_test(test_rz_bin_sections_mapping); mu_run_test(test_rz_bin_p2v2p); return tests_passed != tests_run; } mu_main(all_tests)