Files
yara/tests/test-macho.c
Victor M. Alvarez 4369ca6a1b Fix endianness issues in macho module (#2041)
This is backward compatible change that affects the `magic` field. After this change the value in the `magic` field looks exactly as it looks in the file regardless of the endianness of the current platform, if the file starts with `CA FE BA BE` the value in magic is `0xCAFEBABE`, not `0xBEBAFECA` as it used to be in little-endian architectures.
2024-02-12 15:23:09 +01:00

307 lines
9.7 KiB
C

#include <stdlib.h>
#include <unistd.h>
#include <yara.h>
#include "blob.h"
#include "util.h"
int main(int argc, char** argv)
{
int result = 0;
YR_DEBUG_INITIALIZE();
YR_DEBUG_FPRINTF(1, stderr, "+ %s() { // in %s\n", __FUNCTION__, argv[0]);
init_top_srcdir();
yr_initialize();
// Tests for executable files
assert_true_rule_blob(
"import \"macho\" rule test { condition: \
macho.cputype == macho.CPU_TYPE_X86 }",
MACHO_X86_FILE);
assert_true_rule_blob(
"import \"macho\" rule test { condition: \
macho.filetype == macho.MH_EXECUTE }",
MACHO_X86_FILE);
assert_true_rule_blob(
"import \"macho\" rule test { condition: \
macho.flags & macho.MH_PIE }",
MACHO_X86_FILE);
// Segments
assert_true_rule_blob(
"import \"macho\" rule test { condition: \
macho.number_of_segments == 4 }",
MACHO_X86_FILE);
assert_true_rule_blob(
"import \"macho\" rule test { condition: \
macho.segments[0].segname == \"__PAGEZERO\" and \
macho.segments[1].segname == \"__TEXT\" and \
macho.segments[2].segname == \"__DATA\" and \
macho.segments[3].segname == \"__LINKEDIT\" }",
MACHO_X86_FILE);
assert_true_rule_blob(
"import \"macho\" rule test { condition: \
macho.segments[0].vmaddr == 0 and \
macho.segments[0].vmsize == 0x1000 and \
macho.segments[2].nsects == 2 and \
macho.segments[3].fsize == 0x118 }",
MACHO_X86_FILE);
assert_true_rule_file(
"import \"macho\" rule test { condition: \
macho.number_of_segments == 1 }",
"tests/data/tiny-macho");
// Sections
assert_true_rule_blob(
"import \"macho\" rule test { condition: \
macho.segments[1].sections[0].addr == 0x1e90 and \
macho.segments[1].sections[0].size == 0xa6 and \
macho.segments[1].sections[0].offset == 0x0e90 }",
MACHO_X86_FILE);
assert_true_rule_blob(
"import \"macho\" rule test { condition: \
macho.segments[1].sections[0].sectname == \"__text\" and \
macho.segments[1].sections[0].segname == \"__TEXT\" }",
MACHO_X86_FILE);
assert_true_rule_blob(
"import \"macho\" rule test { condition: \
macho.segments[1].sections[1].sectname == \"__symbol_stub\" and \
macho.segments[1].sections[2].sectname == \"__stub_helper\" and \
macho.segments[1].sections[3].sectname == \"__cstring\" and \
macho.segments[1].sections[4].sectname == \"__unwind_info\" and \
macho.segments[2].sections[0].sectname == \"__nl_symbol_ptr\" and \
macho.segments[2].sections[1].sectname == \"__la_symbol_ptr\" }",
MACHO_X86_FILE);
// Entry point (LC_MAIN)
assert_true_rule_blob(
"import \"macho\" rule test { condition: \
macho.entry_point == 0xe90 }",
MACHO_X86_FILE);
// Tests for object files
assert_true_rule_blob(
"import \"macho\" rule test { condition: \
macho.cputype == macho.CPU_TYPE_X86 }",
MACHO_X86_OBJECT_FILE);
assert_true_rule_blob(
"import \"macho\" rule test { condition: \
macho.filetype == macho.MH_OBJECT }",
MACHO_X86_OBJECT_FILE);
// Segments and sections
assert_true_rule_blob(
"import \"macho\" rule test { condition: \
macho.number_of_segments == 1 and macho.segments[0].segname == \"\" and \
macho.segments[0].sections[0].sectname == \"__text\" and \
macho.segments[0].sections[0].segname == \"__TEXT\" }",
MACHO_X86_OBJECT_FILE);
// Tests for big-endian byte order
assert_true_rule_blob(
"import \"macho\" rule test { condition: \
macho.cputype == macho.CPU_TYPE_POWERPC }",
MACHO_PPC_FILE);
assert_true_rule_blob(
"import \"macho\" rule test { condition: \
macho.filetype == macho.MH_EXECUTE }",
MACHO_PPC_FILE);
// Segments
assert_true_rule_blob(
"import \"macho\" rule test { condition: \
macho.number_of_segments == 4 and \
macho.segments[0].segname == \"__PAGEZERO\" and \
macho.segments[2].segname == \"__DATA\" and \
macho.segments[2].nsects == 6 and \
macho.segments[0].vmaddr == 0 and \
macho.segments[0].vmsize == 0x1000 and \
macho.segments[3].fileoff == 65536 and \
macho.segments[3].fsize == 46032 }",
MACHO_PPC_FILE);
// Entry point (LC_UNIXTHREAD)
assert_true_rule_blob(
"import \"macho\" rule test { condition: \
macho.entry_point == 0xeb8 }",
MACHO_PPC_FILE);
// Tests for 64-bit and shared library files
assert_true_rule_blob(
"import \"macho\" rule test { condition: \
macho.flags == 0x0000100085 }",
MACHO_X86_64_DYLIB_FILE);
assert_true_rule_blob(
"import \"macho\" rule test { condition: \
macho.reserved == 0x00000001 }",
MACHO_X86_64_DYLIB_FILE);
assert_true_rule_blob(
"import \"macho\" rule test { condition: \
macho.cputype == macho.CPU_TYPE_X86_64 }",
MACHO_X86_64_DYLIB_FILE);
assert_true_rule_blob(
"import \"macho\" rule test { condition: \
macho.filetype == macho.MH_DYLIB }",
MACHO_X86_64_DYLIB_FILE);
assert_true_rule_blob(
"import \"macho\" rule test { condition: \
macho.flags & macho.MH_DYLDLINK and \
macho.flags & macho.MH_NOUNDEFS and \
macho.flags & macho.MH_NO_REEXPORTED_DYLIBS and \
macho.flags & macho.MH_TWOLEVEL }",
MACHO_X86_64_DYLIB_FILE);
// Segments and sections
assert_true_rule_blob(
"import \"macho\" rule test { condition: \
macho.number_of_segments == 2 }",
MACHO_X86_64_DYLIB_FILE);
assert_true_rule_blob(
"import \"macho\" rule test { condition: \
macho.segments[0].segname == \"__TEXT\" and \
macho.segments[1].segname == \"__LINKEDIT\" }",
MACHO_X86_64_DYLIB_FILE);
assert_true_rule_blob(
"import \"macho\" rule test { condition: \
macho.segments[1].vmaddr == 0x0000000000001000 and \
macho.segments[1].vmsize == 0x0000000000001000 and \
macho.segments[1].nsects == 0 and \
macho.segments[1].fsize == 128 }",
MACHO_X86_64_DYLIB_FILE);
assert_true_rule_blob(
"import \"macho\" rule test { condition: \
macho.segments[0].sections[0].sectname == \"__text\" and \
macho.segments[0].sections[0].segname == \"__TEXT\" }",
MACHO_X86_64_DYLIB_FILE);
assert_true_rule_blob(
"import \"macho\" rule test { condition: \
macho.segments[0].sections[1].addr == 0x0000000000000f98 and \
macho.segments[0].sections[1].size == 0x0000000000000048 and \
macho.segments[0].sections[1].offset == 3992 }",
MACHO_X86_64_DYLIB_FILE);
// Mach-O Universal Binaries tests
assert_true_rule_file(
"import \"macho\" rule test { condition: \
macho.fat_magic == macho.FAT_MAGIC and macho.nfat_arch == 2 }",
"tests/data/tiny-universal");
assert_true_rule_file(
"import \"macho\" rule test { condition: \
macho.fat_arch[0].cputype == macho.CPU_TYPE_I386 and \
macho.fat_arch[0].cpusubtype == macho.CPU_SUBTYPE_I386_ALL and \
macho.fat_arch[0].offset == 4096 and \
macho.fat_arch[1].cputype == macho.CPU_TYPE_X86_64 and \
macho.fat_arch[1].cpusubtype == macho.CPU_SUBTYPE_X86_64_ALL | \
macho.CPU_SUBTYPE_LIB64 and macho.fat_arch[1].align == 12 }",
"tests/data/tiny-universal");
assert_true_rule_file(
"import \"macho\" rule test { condition: \
macho.file[0].cputype == macho.fat_arch[0].cputype and \
macho.file[1].cputype == macho.fat_arch[1].cputype }",
"tests/data/tiny-universal");
assert_true_rule_file(
"import \"macho\" rule test { condition: \
macho.file[0].magic == 0xcefaedfe and \
macho.file[1].magic == 0xcffaedfe }",
"tests/data/tiny-universal");
// Entry points for files (LC_MAIN)
assert_true_rule_file(
"import \"macho\" rule test { \
strings: $1 = { 55 89 e5 56 83 ec 34 } \
condition: $1 at macho.file[0].entry_point + macho.fat_arch[0].offset }",
"tests/data/tiny-universal");
assert_true_rule_file(
"import \"macho\" rule test { \
strings: $1 = { 55 48 89 e5 48 83 ec 20 } \
condition: $1 at macho.file[1].entry_point + macho.fat_arch[1].offset }",
"tests/data/tiny-universal");
// Helper functions
assert_true_rule_file(
"import \"macho\" rule test { condition: \
macho.file[macho.file_index_for_arch(macho.CPU_TYPE_I386)].entry_point == \
macho.file[0].entry_point }",
"tests/data/tiny-universal");
assert_true_rule_file(
"import \"macho\" rule test { condition: \
macho.file[macho.file_index_for_arch(macho.CPU_TYPE_X86_64)].entry_point == \
macho.file[1].entry_point }",
"tests/data/tiny-universal");
assert_true_rule_file(
"import \"macho\" rule test { condition: \
macho.file[macho.file_index_for_arch(macho.CPU_TYPE_I386, \
macho.CPU_SUBTYPE_I386_ALL)].entry_point == \
macho.file[0].entry_point }",
"tests/data/tiny-universal");
assert_true_rule_file(
"import \"macho\" rule test { condition: \
macho.file[macho.file_index_for_arch(macho.CPU_TYPE_X86_64, \
macho.CPU_SUBTYPE_X86_64_ALL | \
macho.CPU_SUBTYPE_LIB64)].entry_point == \
macho.file[1].entry_point }",
"tests/data/tiny-universal");
// Entry point for specific architecture
assert_true_rule_file(
"import \"macho\" rule test { \
strings: $1 = { 55 89 e5 56 83 ec 34 } \
condition: $1 at macho.entry_point_for_arch(macho.CPU_TYPE_I386, \
macho.CPU_SUBTYPE_I386_ALL) }",
"tests/data/tiny-universal");
assert_true_rule_file(
"import \"macho\" rule test { \
strings: $1 = { 55 48 89 e5 48 83 ec 20 } \
condition: $1 at macho.entry_point_for_arch(macho.CPU_TYPE_X86_64) }",
"tests/data/tiny-universal");
yr_finalize();
YR_DEBUG_FPRINTF(
1, stderr, "} = %d // %s() in %s\n", result, __FUNCTION__, argv[0]);
return result;
}