Browse code

libclamav: initial support for Mach-O executables (part of bb#1592)

Tomasz Kojm authored on 2009/07/08 22:01:17
Showing 2 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,398 @@
0
+/*
1
+ *  Copyright (C) 2009 Sourcefire, Inc.
2
+ *
3
+ *  Authors: Tomasz Kojm <tkojm@clamav.net>
4
+ *
5
+ *  This program is free software; you can redistribute it and/or modify
6
+ *  it under the terms of the GNU General Public License version 2 as
7
+ *  published by the Free Software Foundation.
8
+ *
9
+ *  This program is distributed in the hope that it will be useful,
10
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
+ *  GNU General Public License for more details.
13
+ *
14
+ *  You should have received a copy of the GNU General Public License
15
+ *  along with this program; if not, write to the Free Software
16
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17
+ *  MA 02110-1301, USA.
18
+ */
19
+
20
+/* TODO:
21
+ *  - handle LC_THREAD
22
+ *  - integrate with the matcher
23
+ */
24
+
25
+#include <stdio.h>
26
+#include <string.h>
27
+#include <unistd.h>
28
+#include <sys/types.h>
29
+#include <sys/stat.h>
30
+#include <fcntl.h>
31
+
32
+#include "clamav.h"
33
+#include "cltypes.h"
34
+#include "others.h"
35
+#include "macho.h"
36
+#include "execs.h"
37
+
38
+#define EC32(v, conv)	(conv ? cbswap32(v) : v)
39
+#define EC64(v, conv)	(conv ? cbswap64(v) : v)
40
+
41
+struct macho_hdr
42
+{
43
+    uint32_t magic;
44
+    uint32_t cpu_type;
45
+    uint32_t cpu_subtype;
46
+    uint32_t filetype;
47
+    uint32_t ncmds;
48
+    uint32_t sizeofcmds;
49
+    uint32_t flags;
50
+};
51
+
52
+struct macho_load_cmd
53
+{
54
+    uint32_t cmd;
55
+    uint32_t cmdsize;
56
+};
57
+
58
+struct macho_segment_cmd
59
+{
60
+    char segname[16];
61
+    uint32_t vmaddr;
62
+    uint32_t vmsize;
63
+    uint32_t fileoff;
64
+    uint32_t filesize;
65
+    uint32_t maxprot;
66
+    uint32_t initprot;
67
+    uint32_t nsects;
68
+    uint32_t flags;
69
+};
70
+
71
+struct macho_segment_cmd64
72
+{
73
+    char segname[16];
74
+    uint64_t vmaddr;
75
+    uint64_t vmsize;
76
+    uint64_t fileoff;
77
+    uint64_t filesize;
78
+    uint32_t maxprot;
79
+    uint32_t initprot;
80
+    uint32_t nsects;
81
+    uint32_t flags;
82
+};
83
+
84
+struct macho_section
85
+{
86
+    char sectname[16];
87
+    char segname[16];
88
+    uint32_t addr;
89
+    uint32_t size;
90
+    uint32_t offset;
91
+    uint32_t align;
92
+    uint32_t reloff;
93
+    uint32_t nreloc;
94
+    uint32_t flags;
95
+    uint32_t res1;
96
+    uint32_t res2;
97
+};
98
+
99
+struct macho_section64
100
+{
101
+    char sectname[16];
102
+    char segname[16];
103
+    uint64_t addr;
104
+    uint64_t size;
105
+    uint32_t offset;
106
+    uint32_t align;
107
+    uint32_t reloff;
108
+    uint32_t nreloc;
109
+    uint32_t flags;
110
+    uint32_t res1;
111
+    uint32_t res2;
112
+};
113
+
114
+int cli_scanmacho(int fd, cli_ctx *ctx)
115
+{
116
+	struct macho_hdr hdr;
117
+	struct macho_load_cmd load_cmd;
118
+	struct macho_segment_cmd segment_cmd;
119
+	struct macho_segment_cmd64 segment_cmd64;
120
+	struct macho_section section;
121
+	struct macho_section64 section64;
122
+	unsigned int i, j, sect = 0, conv, m64, nsects, vaddr, vsize, offset;
123
+	char name[16];
124
+
125
+    if(read(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) {
126
+	cli_dbgmsg("cli_scanmacho: Can't read header\n");
127
+	return CL_EFORMAT;
128
+    }
129
+
130
+    if(hdr.magic == 0xfeedface) {
131
+	conv = 0;
132
+	m64 = 0;
133
+    } else if(hdr.magic == 0xcefaedfe) {
134
+	conv = 1;
135
+	m64 = 0;
136
+    } else if(hdr.magic == 0xfeedfacf) {
137
+	conv = 0;
138
+	m64 = 1;
139
+    } else if(hdr.magic == 0xcffaedfe) {
140
+	conv = 1;
141
+	m64 = 1;
142
+    } else {
143
+	cli_dbgmsg("cli_scanmacho: Incorrect magic\n");
144
+	return CL_EFORMAT;
145
+    }
146
+
147
+    switch(EC32(hdr.cpu_type, conv)) {
148
+	case 7:
149
+	    cli_dbgmsg("MACHO: CPU Type: Intel 32-bit\n");
150
+	    break;
151
+	case 7 | 0x1000000:
152
+	    cli_dbgmsg("MACHO: CPU Type: Intel 64-bit\n");
153
+	    break;
154
+	case 12:
155
+	    cli_dbgmsg("MACHO: CPU Type: ARM\n");
156
+	    break;
157
+	case 14:
158
+	    cli_dbgmsg("MACHO: CPU Type: SPARC\n");
159
+	    break;
160
+	case 18:
161
+	    cli_dbgmsg("MACHO: CPU Type: POWERPC 32-bit\n");
162
+	    break;
163
+	case 18 | 0x1000000:
164
+	    cli_dbgmsg("MACHO: CPU Type: POWERPC 64-bit\n");
165
+	    break;
166
+	default:
167
+	    cli_dbgmsg("MACHO: CPU Type: ** UNKNOWN ** (%u)\n", EC32(hdr.cpu_type, conv));
168
+	    break;
169
+    }
170
+
171
+    switch(EC32(hdr.filetype, conv)) {
172
+	case 0x1: /* MH_OBJECT */
173
+	    cli_dbgmsg("MACHO: Filetype: Relocatable object file\n");
174
+	    break;
175
+	case 0x2: /* MH_EXECUTE */
176
+	    cli_dbgmsg("MACHO: Filetype: Executable\n");
177
+	    break;
178
+	case 0x3: /* MH_FVMLIB */
179
+	    cli_dbgmsg("MACHO: Filetype: Fixed VM shared library file\n");
180
+	    break;
181
+	case 0x4: /* MH_CORE */
182
+	    cli_dbgmsg("MACHO: Filetype: Core file\n");
183
+	    break;
184
+	case 0x5: /* MH_PRELOAD */
185
+	    cli_dbgmsg("MACHO: Filetype: Preloaded executable file\n");
186
+	    break;
187
+	case 0x6: /* MH_DYLIB */
188
+	    cli_dbgmsg("MACHO: Filetype: Dynamically bound shared library\n");
189
+	    break;
190
+	case 0x7: /* MH_DYLINKER */
191
+	    cli_dbgmsg("MACHO: Filetype: Dynamic link editor\n");
192
+	    break;
193
+	case 0x8: /* MH_BUNDLE */
194
+	    cli_dbgmsg("MACHO: Filetype: Dynamically bound bundle file\n");
195
+	    break;
196
+	case 0x9: /* MH_DYLIB_STUB */
197
+	    cli_dbgmsg("MACHO: Filetype: Shared library stub for static\n");
198
+	    break;
199
+	default:
200
+	    cli_dbgmsg("MACHO: Filetype: ** UNKNOWN ** (0x%x)\n", EC32(hdr.filetype, conv));
201
+    }
202
+
203
+    cli_dbgmsg("MACHO: Number of load commands: %u\n", EC32(hdr.ncmds, conv));
204
+    cli_dbgmsg("MACHO: Size of load commands: %u\n", EC32(hdr.sizeofcmds, conv));
205
+
206
+    if((m64 && EC32(load_cmd.cmdsize, conv) % 8) || (!m64 && EC32(load_cmd.cmdsize, conv) % 4)) {
207
+	cli_dbgmsg("cli_scanmacho: Invalid command size (%u)\n", EC32(load_cmd.cmdsize, conv));
208
+        if(DETECT_BROKEN) {
209
+	    if(ctx->virname)
210
+		*ctx->virname = "Broken.Executable";
211
+	    return cli_checkfp(fd, ctx) ? CL_CLEAN : CL_VIRUS;
212
+        }
213
+	return CL_EFORMAT;
214
+    }
215
+
216
+    if(m64)
217
+	lseek(fd, 4, SEEK_CUR);
218
+
219
+    for(i = 0; i < EC32(hdr.ncmds, conv); i++) {
220
+	if(read(fd, &load_cmd, sizeof(load_cmd)) != sizeof(load_cmd)) {
221
+	    cli_dbgmsg("cli_scanmacho: Can't read load command\n");
222
+	    if(DETECT_BROKEN) {
223
+		if(ctx->virname)
224
+		    *ctx->virname = "Broken.Executable";
225
+		return cli_checkfp(fd, ctx) ? CL_CLEAN : CL_VIRUS;
226
+	    }
227
+	    return CL_EFORMAT;
228
+	}
229
+
230
+	if((m64 && EC32(load_cmd.cmd, conv) == 0x19) || (!m64 && EC32(load_cmd.cmd, conv) == 0x01)) { /* LC_SEGMENT */
231
+	    if(m64) {
232
+		if(read(fd, &segment_cmd64, sizeof(segment_cmd64)) != sizeof(segment_cmd64)) {
233
+		    cli_dbgmsg("cli_scanmacho: Can't read segment command\n");
234
+		    if(DETECT_BROKEN) {
235
+			if(ctx->virname)
236
+			    *ctx->virname = "Broken.Executable";
237
+			return cli_checkfp(fd, ctx) ? CL_CLEAN : CL_VIRUS;
238
+		    }
239
+		    return CL_EFORMAT;
240
+		}
241
+		nsects = EC32(segment_cmd64.nsects, conv);
242
+		strncpy(name, segment_cmd64.segname, 16);
243
+	    } else {
244
+		if(read(fd, &segment_cmd, sizeof(segment_cmd)) != sizeof(segment_cmd)) {
245
+		    cli_dbgmsg("cli_scanmacho: Can't read segment command\n");
246
+		    if(DETECT_BROKEN) {
247
+			if(ctx->virname)
248
+			    *ctx->virname = "Broken.Executable";
249
+			return cli_checkfp(fd, ctx) ? CL_CLEAN : CL_VIRUS;
250
+		    }
251
+		    return CL_EFORMAT;
252
+		}
253
+		nsects = EC32(segment_cmd.nsects, conv);
254
+		strncpy(name, segment_cmd.segname, 16);
255
+	    }
256
+	    name[15] = 0;
257
+	    cli_dbgmsg("MACHO: Segment name: %s\n", name);
258
+	    cli_dbgmsg("MACHO: Number of sections: %u\n", nsects);
259
+	    for(j = 0; j < nsects; j++) {
260
+		if(m64) {
261
+		    if(read(fd, &section64, sizeof(section64)) != sizeof(section64)) {
262
+			cli_dbgmsg("cli_scanmacho: Can't read section\n");
263
+			if(DETECT_BROKEN) {
264
+			    if(ctx->virname)
265
+				*ctx->virname = "Broken.Executable";
266
+			    return cli_checkfp(fd, ctx) ? CL_CLEAN : CL_VIRUS;
267
+			}
268
+			return CL_EFORMAT;
269
+		    }
270
+		    vaddr = EC64(section64.addr, conv);
271
+		    vsize = EC64(section64.size, conv);
272
+		    offset = EC32(section64.offset, conv);
273
+		    strncpy(name, section64.sectname, 16);
274
+		} else {
275
+		    if(read(fd, &section, sizeof(section)) != sizeof(section)) {
276
+			cli_dbgmsg("cli_scanmacho: Can't read section\n");
277
+			if(DETECT_BROKEN) {
278
+			    if(ctx->virname)
279
+				*ctx->virname = "Broken.Executable";
280
+			    return cli_checkfp(fd, ctx) ? CL_CLEAN : CL_VIRUS;
281
+			}
282
+			return CL_EFORMAT;
283
+		    }
284
+		    vaddr = EC32(section.addr, conv);
285
+		    vsize = EC32(section.size, conv);
286
+		    offset = EC32(section.offset, conv);
287
+		    strncpy(name, section.sectname, 16);
288
+		}
289
+		name[15] = 0;
290
+		cli_dbgmsg("MACHO: --- Section %u ---\n", sect);
291
+		cli_dbgmsg("MACHO: Name: %s\n", name);
292
+		cli_dbgmsg("MACHO: Virtual address: 0x%x\n", vaddr);
293
+		cli_dbgmsg("MACHO: Virtual size: %u\n", vsize);
294
+		if(offset)
295
+		    cli_dbgmsg("MACHO: File offset: %u\n", offset);
296
+		sect++;
297
+	    }
298
+	    cli_dbgmsg("MACHO: ------------------\n");
299
+	} else {
300
+	    if(EC32(load_cmd.cmdsize, conv) > sizeof(load_cmd))
301
+		lseek(fd, EC32(load_cmd.cmdsize, conv) - sizeof(load_cmd), SEEK_CUR);
302
+	}
303
+    }
304
+
305
+    return CL_SUCCESS;
306
+}
307
+
308
+int cli_machoheader(int fd, struct cli_exe_info *elfinfo)
309
+{
310
+	struct macho_hdr hdr;
311
+	struct macho_load_cmd load_cmd;
312
+	struct macho_segment_cmd segment_cmd;
313
+	struct macho_segment_cmd64 segment_cmd64;
314
+	struct macho_section section;
315
+	struct macho_section64 section64;
316
+	unsigned int i, j, sect = 0, conv, m64, nsects, vaddr, vsize, offset;
317
+
318
+    cli_dbgmsg("in cli_machoheader()\n");
319
+
320
+    if(read(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) {
321
+	cli_dbgmsg("cli_scanmacho: Can't read header\n");
322
+	return -1;
323
+    }
324
+
325
+    if(hdr.magic == 0xfeedface) {
326
+	conv = 0;
327
+	m64 = 0;
328
+    } else if(hdr.magic == 0xcefaedfe) {
329
+	conv = 1;
330
+	m64 = 0;
331
+    } else if(hdr.magic == 0xfeedfacf) {
332
+	conv = 0;
333
+	m64 = 1;
334
+    } else if(hdr.magic == 0xcffaedfe) {
335
+	conv = 1;
336
+	m64 = 1;
337
+    } else {
338
+	cli_dbgmsg("cli_scanmacho: Incorrect magic\n");
339
+	return CL_EFORMAT;
340
+    }
341
+
342
+    if((m64 && EC32(load_cmd.cmdsize, conv) % 8) || (!m64 && EC32(load_cmd.cmdsize, conv) % 4)) {
343
+	cli_dbgmsg("cli_scanmacho: Invalid command size (%u)\n", EC32(load_cmd.cmdsize, conv));
344
+	return -1;
345
+    }
346
+
347
+    if(m64)
348
+	lseek(fd, 4, SEEK_CUR);
349
+
350
+    for(i = 0; i < EC32(hdr.ncmds, conv); i++) {
351
+	if(read(fd, &load_cmd, sizeof(load_cmd)) != sizeof(load_cmd)) {
352
+	    cli_dbgmsg("cli_scanmacho: Can't read load command\n");
353
+	    return -1;
354
+	}
355
+
356
+	if((m64 && EC32(load_cmd.cmd, conv) == 0x19) || (!m64 && EC32(load_cmd.cmd, conv) == 0x01)) { /* LC_SEGMENT */
357
+	    if(m64) {
358
+		if(read(fd, &segment_cmd64, sizeof(segment_cmd64)) != sizeof(segment_cmd64)) {
359
+		    cli_dbgmsg("cli_scanmacho: Can't read segment command\n");
360
+		    return -1;
361
+		}
362
+		nsects = EC32(segment_cmd64.nsects, conv);
363
+	    } else {
364
+		if(read(fd, &segment_cmd, sizeof(segment_cmd)) != sizeof(segment_cmd)) {
365
+		    cli_dbgmsg("cli_scanmacho: Can't read segment command\n");
366
+		    return -1;
367
+		}
368
+		nsects = EC32(segment_cmd.nsects, conv);
369
+	    }
370
+	    for(j = 0; j < nsects; j++) {
371
+		if(m64) {
372
+		    if(read(fd, &section64, sizeof(section64)) != sizeof(section64)) {
373
+			cli_dbgmsg("cli_scanmacho: Can't read section\n");
374
+			return -1;
375
+		    }
376
+		    vaddr = EC64(section64.addr, conv);
377
+		    vsize = EC64(section64.size, conv);
378
+		    offset = EC32(section64.offset, conv);
379
+		} else {
380
+		    if(read(fd, &section, sizeof(section)) != sizeof(section)) {
381
+			cli_dbgmsg("cli_scanmacho: Can't read section\n");
382
+			return -1;
383
+		    }
384
+		    vaddr = EC32(section.addr, conv);
385
+		    vsize = EC32(section.size, conv);
386
+		    offset = EC32(section.offset, conv);
387
+		}
388
+		sect++;
389
+	    }
390
+	} else {
391
+	    if(EC32(load_cmd.cmdsize, conv) > sizeof(load_cmd))
392
+		lseek(fd, EC32(load_cmd.cmdsize, conv) - sizeof(load_cmd), SEEK_CUR);
393
+	}
394
+    }
395
+
396
+    return 0;
397
+}
0 398
new file mode 100644
... ...
@@ -0,0 +1,30 @@
0
+/*
1
+ *  Copyright (C) 2009 Sourcefire, Inc.
2
+ *
3
+ *  Authors: Tomasz Kojm <tkojm@clamav.net>
4
+ *
5
+ *  This program is free software; you can redistribute it and/or modify
6
+ *  it under the terms of the GNU General Public License version 2 as
7
+ *  published by the Free Software Foundation.
8
+ *
9
+ *  This program is distributed in the hope that it will be useful,
10
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
+ *  GNU General Public License for more details.
13
+ *
14
+ *  You should have received a copy of the GNU General Public License
15
+ *  along with this program; if not, write to the Free Software
16
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17
+ *  MA 02110-1301, USA.
18
+ */
19
+
20
+#ifndef __MACHO_H
21
+#define __MACHO_H
22
+
23
+#include "others.h"
24
+#include "execs.h"
25
+
26
+int cli_scanmacho(int fd, cli_ctx *ctx);
27
+int cli_machoheader(int fd, struct cli_exe_info *elfinfo);
28
+
29
+#endif