Browse code

Mach-O bytecode unpackers

Jonas Zaddach authored on 2019/04/18 03:30:21
Showing 6 changed files
... ...
@@ -2986,6 +2986,9 @@ void cli_bytecode_describe(const struct cli_bc *bc)
2986 2986
         case BC_ELF_UNPACKER:
2987 2987
             puts("ELF unpacker hook");
2988 2988
             break;
2989
+        case BC_MACHO_UNPACKER:
2990
+            puts("Mach-O unpacker hook");
2991
+            break;
2989 2992
         default:
2990 2993
             printf("Unknown (type %u)", bc->kind);
2991 2994
             break;
... ...
@@ -3033,6 +3036,12 @@ void cli_bytecode_describe(const struct cli_bc *bc)
3033 3033
             else
3034 3034
                 puts("all ELF files! (unpacked)");
3035 3035
             break;
3036
+        case BC_MACHO_UNPACKER:
3037
+            if (bc->lsig)
3038
+                puts("Mach-O files matching logical signature (unpacked)");
3039
+            else
3040
+                puts("all Mach-O files! (unpacked)");
3041
+            break;
3036 3042
         default:
3037 3043
             puts("N/A (unknown type)\n");
3038 3044
             break;
... ...
@@ -72,6 +72,8 @@ enum BytecodeKind {
72 72
     BC_PRECLASS,
73 73
     /** specifies an ELF unpacker, executed on ELF files on a logical trigger */
74 74
     BC_ELF_UNPACKER,
75
+    /** specifies an Mach-O unpacker, executed on Mach-O files on a logical trigger */
76
+    BC_MACHO_UNPACKER,
75 77
     _BC_LAST_HOOK
76 78
 };
77 79
 
... ...
@@ -120,6 +120,7 @@ enum perfev {
120 120
     PERFT_KTIME,
121 121
     PERFT_UTIME,
122 122
     PERFT_ELF,
123
+    PERFT_MACHO,
123 124
     PERFT_LAST
124 125
 };
125 126
 
... ...
@@ -35,6 +35,14 @@
35 35
 #include "execs.h"
36 36
 #include "scanners.h"
37 37
 
38
+#define CLI_TMPUNLK()               \
39
+    if (!ctx->engine->keeptmp) {    \
40
+        if (cli_unlink(tempfile)) { \
41
+            free(tempfile);         \
42
+            return CL_EUNLINK;      \
43
+        }                           \
44
+    }
45
+
38 46
 #define EC32(v, conv) (conv ? cbswap32(v) : v)
39 47
 #define EC64(v, conv) (conv ? cbswap64(v) : v)
40 48
 
... ...
@@ -556,3 +564,55 @@ int cli_scanmacho_unibin(cli_ctx *ctx)
556 556
 
557 557
     return ret; /* result from the last binary */
558 558
 }
559
+
560
+int cli_unpackmacho(cli_ctx *ctx)
561
+{
562
+    char *tempfile;
563
+    int ndesc;
564
+    struct cli_bc_ctx *bc_ctx;
565
+    int ret;
566
+    fmap_t *map = *ctx->fmap;
567
+
568
+    /* Bytecode BC_MACHO_UNPACKER hook */
569
+    bc_ctx = cli_bytecode_context_alloc();
570
+    if (!bc_ctx) {
571
+        cli_errmsg("cli_scanelf: can't allocate memory for bc_ctx\n");
572
+        return CL_EMEM;
573
+    }
574
+
575
+    cli_bytecode_context_setctx(bc_ctx, ctx);
576
+
577
+    ret = cli_bytecode_runhook(ctx, ctx->engine, bc_ctx, BC_MACHO_UNPACKER, map);
578
+    switch (ret) {
579
+        case CL_VIRUS:
580
+            cli_bytecode_context_destroy(bc_ctx);
581
+            return CL_VIRUS;
582
+        case CL_SUCCESS:
583
+            ndesc = cli_bytecode_context_getresult_file(bc_ctx, &tempfile);
584
+            cli_bytecode_context_destroy(bc_ctx);
585
+            if (ndesc != -1 && tempfile) {
586
+                if (ctx->engine->keeptmp) 
587
+                    cli_dbgmsg("cli_scanmacho: Unpacked and rebuilt executable saved in %s\n", tempfile);
588
+                else
589
+                    cli_dbgmsg("cli_scanmacho: Unpacked and rebuilt executable\n");
590
+                lseek(ndesc, 0, SEEK_SET);
591
+                cli_dbgmsg("***** Scanning rebuilt Mach-O file *****\n");
592
+                if (cli_magic_scandesc(ndesc, tempfile, ctx) == CL_VIRUS) {
593
+                    close(ndesc);
594
+                    CLI_TMPUNLK();
595
+                    free(tempfile);
596
+                    return CL_VIRUS;
597
+                } 
598
+                close(ndesc);
599
+                CLI_TMPUNLK();
600
+                free(tempfile);
601
+                return CL_SUCCESS;
602
+            }
603
+            break;
604
+        default:
605
+            cli_bytecode_context_destroy(bc_ctx);
606
+    }
607
+
608
+    return CL_CLEAN;
609
+}
610
+
... ...
@@ -29,5 +29,6 @@
29 29
 int cli_scanmacho(cli_ctx *ctx, struct cli_exe_info *fileinfo);
30 30
 int cli_machoheader(fmap_t *map, struct cli_exe_info *fileinfo);
31 31
 int cli_scanmacho_unibin(cli_ctx *ctx);
32
+int cli_unpackmacho(cli_ctx *ctx);
32 33
 
33 34
 #endif
... ...
@@ -3525,6 +3525,12 @@ static int magic_scandesc(cli_ctx *ctx, cli_file_t type)
3525 3525
             ret = cli_unpackelf(ctx);
3526 3526
             perf_nested_stop(ctx, PERFT_ELF, PERFT_SCAN);
3527 3527
             break;
3528
+        case CL_TYPE_MACHO:
3529
+        case CL_TYPE_MACHO_UNIBIN:
3530
+            perf_nested_start(ctx, PERFT_MACHO, PERFT_SCAN);
3531
+            ret = cli_unpackmacho(ctx);
3532
+            perf_nested_stop(ctx, PERFT_MACHO, PERFT_SCAN);
3533
+            break;
3528 3534
         case CL_TYPE_BINARY_DATA:
3529 3535
             ret = cli_fmap_scandesc(ctx, CL_TYPE_OTHER, 0, NULL, AC_SCAN_VIR, NULL, NULL);
3530 3536
             break;