Browse code

libclamav: added APM parsing for raw DMGs

Kevin Lin authored on 2014/02/07 08:59:53
Showing 2 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,316 @@
0
+/*
1
+ *  Copyright (C) 2014 Cisco Systems, Inc.
2
+ *
3
+ *  Authors: Kevin Lin <kevlin2@cisco.com>
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
+#if HAVE_CONFIG_H
21
+#include "clamav-config.h"
22
+#endif
23
+
24
+#include <stdio.h>
25
+#include <errno.h>
26
+#if HAVE_STRING_H
27
+#include <string.h>
28
+#endif
29
+#include <ctype.h>
30
+#include <fcntl.h>
31
+
32
+#include "cltypes.h"
33
+#include "others.h"
34
+#include "apm.h"
35
+#include "prtn_intxn.h"
36
+#include "scanners.h"
37
+
38
+#define DEBUG_APM_PARSE
39
+
40
+#ifdef DEBUG_APM_PARSE
41
+#  define apm_parsemsg(...) cli_dbgmsg( __VA_ARGS__)
42
+#else
43
+#  define apm_parsemsg(...) ;
44
+#endif
45
+
46
+static int apm_prtn_intxn(cli_ctx *ctx, struct apm_partition_info aptable, size_t sectorsize, int old_school);
47
+
48
+int cli_scanapm(cli_ctx *ctx)
49
+{
50
+    struct apm_driver_desc_map ddm;
51
+    struct apm_partition_info aptable, apentry;
52
+    int ret = 0, old_school = 0;
53
+    size_t sectorsize, maplen, partsize, sectorcheck;;
54
+    off_t pos = 0, partoff = 0;
55
+    unsigned i;
56
+    uint32_t max_prtns = 0;
57
+
58
+    if (!ctx || !ctx->fmap) {
59
+        cli_errmsg("cli_scanapm: Invalid context\n");
60
+        return CL_ENULLARG;
61
+    }
62
+
63
+    /* read driver description map at sector 0  */
64
+    if (fmap_readn(*ctx->fmap, &ddm, pos, sizeof(ddm)) != sizeof(ddm)) {
65
+        cli_dbgmsg("cli_scanapm: Invalid Apple driver description map\n");
66
+        return CL_EFORMAT;
67
+    }
68
+
69
+    /* convert driver description map big-endian to host */
70
+    ddm.signature = be16_to_host(ddm.signature);
71
+    ddm.blockSize = be16_to_host(ddm.blockSize);
72
+    ddm.blockCount = be32_to_host(ddm.blockCount);
73
+
74
+    /* check DDM signature */
75
+    if (ddm.signature != DDM_SIGNATURE) {
76
+        cli_dbgmsg("cli_scanapm: Apple driver description map signature mismatch\n");
77
+        return CL_EFORMAT;
78
+    }
79
+
80
+    /* sector size is determined by the ddm */
81
+    sectorsize = ddm.blockSize;
82
+
83
+    /* size of total file must be described by the ddm */
84
+    maplen = (*ctx->fmap)->real_len;
85
+    if ((ddm.blockSize * ddm.blockCount) != maplen) {
86
+        cli_dbgmsg("cli_scanapm: File described %u size does not match %u actual size\n",
87
+                   (ddm.blockSize * ddm.blockCount), maplen);
88
+        return CL_EFORMAT;
89
+    }
90
+
91
+    /* check for old-school partition map */
92
+    if (sectorsize == 2048) {
93
+        if (fmap_readn(*ctx->fmap, &aptable, APM_FALLBACK_SECTOR_SIZE, sizeof(aptable)) != sizeof(aptable)) {
94
+            cli_dbgmsg("cli_scanapm: Invalid Apple partition entry\n");
95
+            return CL_EFORMAT;
96
+        }
97
+
98
+        aptable.signature = be16_to_host(aptable.signature);
99
+        if (aptable.signature == APM_SIGNATURE) {
100
+            sectorsize = APM_FALLBACK_SECTOR_SIZE;
101
+            old_school = 1;
102
+        }
103
+    }
104
+
105
+    /* read partition table at sector 1 (or after the ddm if old-school) */
106
+    pos = APM_PTABLE_BLOCK * sectorsize;
107
+
108
+    if (fmap_readn(*ctx->fmap, &aptable, pos, sizeof(aptable)) != sizeof(aptable)) {
109
+        cli_dbgmsg("cli_scanapm: Invalid Apple partition table\n");
110
+        return CL_EFORMAT;
111
+    }
112
+
113
+    /* convert partition table big endian to host */
114
+    aptable.signature = be16_to_host(aptable.signature);
115
+    aptable.numPartitions = be32_to_host(aptable.numPartitions);
116
+    aptable.pBlockStart = be32_to_host(aptable.pBlockStart);
117
+    aptable.pBlockCount = be32_to_host(aptable.pBlockCount);
118
+
119
+    /* check the partition entry signature */
120
+    if (aptable.signature != APM_SIGNATURE) {
121
+        cli_dbgmsg("cli_scanapm: Apple partition table signature mismatch\n");
122
+        return CL_EFORMAT;
123
+    }
124
+
125
+    /* check if partition table partition */
126
+    if (strncmp((char*)aptable.type, "Apple_Partition_Map", 32) &&
127
+        strncmp((char*)aptable.type, "Apple_partition_map", 32) &&
128
+        strncmp((char*)aptable.type, "Apple_patition_map", 32)){
129
+        cli_dbgmsg("cli_scanapm: Initial Apple Partition Map partition is not detected\n");
130
+        return CL_EFORMAT;
131
+    }
132
+
133
+    /* check that the partition table fits in the space specified - HEURISTICS */
134
+    if (ctx->options & CL_SCAN_PARTITION_INTXN) {
135
+        ret = apm_prtn_intxn(ctx, aptable, sectorsize, old_school);
136
+        if ((ret != CL_CLEAN) &&
137
+            !((ctx->options & CL_SCAN_ALLMATCHES) && (ret == CL_VIRUS))) {
138
+
139
+            return ret;
140
+        }
141
+    }
142
+
143
+    /* print debugging info on partition tables */
144
+    cli_dbgmsg("APM Partition Table:\n");
145
+    cli_dbgmsg("Name: %s\n", (char*)aptable.name);
146
+    cli_dbgmsg("Type: %s\n", (char*)aptable.type);
147
+    cli_dbgmsg("Signature: %x\n", aptable.signature);
148
+    cli_dbgmsg("Partition Count: %u\n", aptable.numPartitions);
149
+    cli_dbgmsg("Blocks: [%u, +%u), ([%u, +%u))\n",
150
+               aptable.pBlockStart, aptable.pBlockCount,
151
+               (aptable.pBlockStart * sectorsize),
152
+               (aptable.pBlockCount * sectorsize));
153
+
154
+    /* check engine maxpartitions limit */
155
+    if (aptable.numPartitions < ctx->engine->maxpartitions) {
156
+        max_prtns = aptable.numPartitions;
157
+    }
158
+    else {
159
+        max_prtns = ctx->engine->maxpartitions;
160
+    }
161
+
162
+    /* partition table is a partition [at index 1], so skip it */
163
+    for (i = 2; i <= max_prtns; ++i) {
164
+        /* read partition table entry */
165
+        pos = i * sectorsize;
166
+        if (fmap_readn(*ctx->fmap, &apentry, pos, sizeof(apentry)) != sizeof(apentry)) {
167
+            cli_dbgmsg("cli_scanapm: Invalid Apple partition entry\n");
168
+            return CL_EFORMAT;
169
+        }
170
+
171
+        /* convert partition entry big endian to host */
172
+        apentry.signature = be16_to_host(apentry.signature);
173
+        apentry.reserved = be16_to_host(apentry.reserved);
174
+        apentry.numPartitions = be32_to_host(apentry.numPartitions);
175
+        apentry.pBlockStart = be32_to_host(apentry.pBlockStart);
176
+        apentry.pBlockCount = be32_to_host(apentry.pBlockCount);
177
+
178
+        /* check the partition entry signature */
179
+        if (aptable.signature != APM_SIGNATURE) {
180
+            cli_dbgmsg("cli_scanapm: Apple partition entry signature mismatch\n");
181
+            return CL_EFORMAT;
182
+        }
183
+
184
+        /* check if a out-of-order partition map */
185
+        if (!strncmp((char*)apentry.type, "Apple_Partition_Map", 32) ||
186
+            !strncmp((char*)apentry.type, "Apple_partition_map", 32) ||
187
+            !strncmp((char*)apentry.type, "Apple_patition_map", 32)) {
188
+
189
+            cli_dbgmsg("cli_scanapm: Out of order Apple Partition Map partition\n");
190
+            continue;
191
+        }
192
+
193
+        partoff = apentry.pBlockStart * sectorsize;
194
+        partsize = apentry.pBlockCount * sectorsize;
195
+        /* re-calculate if old_school and aligned [512 * 4 => 2048] */
196
+        if (old_school && ((i % 4) == 0)) {
197
+            if (!strncmp((char*)apentry.type, "Apple_Driver",       32) ||
198
+                !strncmp((char*)apentry.type, "Apple_Driver43",     32) ||
199
+                !strncmp((char*)apentry.type, "Apple_Driver43_CD",  32) ||
200
+                !strncmp((char*)apentry.type, "Apple_Driver_ATA",   32) ||
201
+                !strncmp((char*)apentry.type, "Apple_Driver_ATAPI", 32) ||
202
+                !strncmp((char*)apentry.type, "Apple_Patches",      32)) {
203
+
204
+                partsize = apentry.pBlockCount * 2048;;
205
+            }
206
+        }
207
+
208
+        /* check if invalid partition */
209
+        if ((partoff == 0) || (partoff+partsize > maplen)) {
210
+            cli_dbgmsg("cli_scanapm: Detected invalid Apple partition entry\n");
211
+            continue;
212
+        }
213
+
214
+        /* print debugging info on partition */
215
+        cli_dbgmsg("APM Partition Entry %u:\n", i);
216
+        cli_dbgmsg("Name: %s\n", (char*)apentry.name);
217
+        cli_dbgmsg("Type: %s\n", (char*)apentry.type);
218
+        cli_dbgmsg("Signature: %x\n", apentry.signature);
219
+        cli_dbgmsg("Partition Count: %u\n", apentry.numPartitions);
220
+        cli_dbgmsg("Blocks: [%u, +%u), ([%u, +%u))\n",
221
+                   apentry.pBlockStart, apentry.pBlockCount, partoff, partsize);
222
+
223
+        /* send the partition to cli_map_scan */
224
+        ret = cli_map_scan(*ctx->fmap, partoff, partsize, ctx, CL_TYPE_PART_ANY);
225
+        if ((ret != CL_CLEAN) &&
226
+            !((ctx->options & CL_SCAN_ALLMATCHES) && (ret == CL_VIRUS))) {
227
+                return ret;
228
+        }
229
+    } 
230
+
231
+    if (i < aptable.numPartitions) {
232
+        cli_dbgmsg("cli_scanapm: max partitions exceeded\n");
233
+    }
234
+
235
+    return ret;
236
+}
237
+
238
+static int apm_prtn_intxn(cli_ctx *ctx, struct apm_partition_info aptable, size_t sectorsize, int old_school)
239
+{
240
+    prtn_intxn_list_t prtncheck;
241
+    struct apm_partition_info apentry;
242
+    unsigned i, pitxn;
243
+    int ret = 0, tmp = 0;
244
+    off_t pos;
245
+    uint32_t max_prtns = 0;
246
+
247
+    prtn_intxn_list_init(&prtncheck);
248
+
249
+    /* check engine maxpartitions limit */
250
+    if (aptable.numPartitions < ctx->engine->maxpartitions) {
251
+        max_prtns = aptable.numPartitions;
252
+    }
253
+    else {
254
+        max_prtns = ctx->engine->maxpartitions;
255
+    }
256
+
257
+    for (i = 1; i <= max_prtns; ++i) {
258
+        /* read partition table entry */
259
+        pos = i * sectorsize;
260
+        if (fmap_readn(*ctx->fmap, &apentry, pos, sizeof(apentry)) != sizeof(apentry)) {
261
+            cli_dbgmsg("cli_scanapm: Invalid Apple partition entry\n");
262
+            prtn_intxn_list_free(&prtncheck);
263
+            return CL_EFORMAT;
264
+        }
265
+
266
+        /* convert necessary info big endian to host */
267
+        apentry.pBlockStart = be32_to_host(apentry.pBlockStart);
268
+        apentry.pBlockCount = be32_to_host(apentry.pBlockCount);
269
+        /* re-calculate if old_school and aligned [512 * 4 => 2048] */
270
+        if (old_school && ((i % 4) == 0)) {
271
+            if (!strncmp((char*)apentry.type, "Apple_Driver",       32) ||
272
+                !strncmp((char*)apentry.type, "Apple_Driver43",     32) ||
273
+                !strncmp((char*)apentry.type, "Apple_Driver43_CD",  32) ||
274
+                !strncmp((char*)apentry.type, "Apple_Driver_ATA",   32) ||
275
+                !strncmp((char*)apentry.type, "Apple_Driver_ATAPI", 32) ||
276
+                !strncmp((char*)apentry.type, "Apple_Patches",      32)) {
277
+
278
+                apentry.pBlockCount = apentry.pBlockCount * 4;;
279
+            }
280
+        }
281
+
282
+        tmp = prtn_intxn_list_check(&prtncheck, &pitxn, apentry.pBlockStart, apentry.pBlockCount);
283
+        if (tmp != CL_CLEAN) {
284
+            if ((ctx->options & CL_SCAN_ALLMATCHES) && (tmp == CL_VIRUS)) {
285
+                apm_parsemsg("Name: %s\n", (char*)aptable.name);
286
+                apm_parsemsg("Type: %s\n", (char*)aptable.type);
287
+
288
+                cli_dbgmsg("cli_scanapm: detected intersection with partitions "
289
+                           "[%u, %u]\n", pitxn, i);
290
+                cli_append_virus(ctx, "Heuristic.PartitionIntersection");
291
+                ret = tmp;
292
+                tmp = 0;
293
+            }
294
+            else if (tmp == CL_VIRUS) {
295
+                apm_parsemsg("Name: %s\n", (char*)aptable.name);
296
+                apm_parsemsg("Type: %s\n", (char*)aptable.type);
297
+
298
+                cli_dbgmsg("cli_scanapm: detected intersection with partitions "
299
+                           "[%u, %u]\n", pitxn, i);
300
+                cli_append_virus(ctx, "Heuristic.PartitionIntersection");
301
+                prtn_intxn_list_free(&prtncheck);
302
+                return CL_VIRUS;
303
+            }
304
+            else {
305
+                prtn_intxn_list_free(&prtncheck);
306
+                return tmp;
307
+            }
308
+        }
309
+
310
+        pos += sectorsize;
311
+    }
312
+
313
+    prtn_intxn_list_free(&prtncheck);
314
+    return ret;
315
+}
0 316
new file mode 100644
... ...
@@ -0,0 +1,117 @@
0
+/*
1
+ *  Copyright (C) 2014 Sourcefire, Inc.
2
+ *
3
+ *  Authors: Kevin Lin <klin@sourcefire.com>
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 __APM_H
21
+#define __APM_H
22
+
23
+#if HAVE_CONFIG_H
24
+#include "clamav-config.h"
25
+#endif
26
+
27
+#include "cltypes.h"
28
+#include "others.h"
29
+
30
+#define APM_FALLBACK_SECTOR_SIZE 512
31
+
32
+#define APM_PTABLE_BLOCK 1
33
+#define APM_STRUCT_SIZE 512
34
+
35
+#define DDM_SIGNATURE 0x4552 /* driver description signature ('ER') */
36
+#define APM_SIGNATURE 0x504D /* partition map signature ('PM') */
37
+
38
+/* partition flags */
39
+#define VALID          0x00000001
40
+#define ALLOCATED      0x00000002
41
+#define IN_USE         0x00000004
42
+#define BOOTABLE       0x00000008
43
+#define READABLE       0x00000010
44
+#define WRITEABLE      0x00000020
45
+#define POSINDEPENDENT 0x00000040
46
+/* end of partition flags */
47
+
48
+#ifndef HAVE_ATTRIB_PACKED
49
+#define __attribute__(x)
50
+#endif
51
+
52
+#ifdef HAVE_PRAGMA_PACK
53
+#pragma pack(1)
54
+#endif
55
+
56
+#ifdef HAVE_PRAGMA_PACK_HPPA
57
+#pragma pack 1
58
+#endif
59
+
60
+/* 8-byte driver description entry for ddmap, big endian */
61
+struct apm_driver_desc_entry {
62
+    uint32_t block  __attribute__ ((packed));
63
+    uint16_t size  __attribute__ ((packed));
64
+    uint16_t type  __attribute__ ((packed));
65
+}; //NOTE may need to be renamed
66
+
67
+/* 512(82)-byte driver descriptor map (Block0), big endian */
68
+struct apm_driver_desc_map {
69
+    uint16_t signature  __attribute__ ((packed));
70
+    uint16_t blockSize  __attribute__ ((packed));
71
+    uint32_t blockCount  __attribute__ ((packed));
72
+    uint16_t deviceType  __attribute__ ((packed));
73
+    uint16_t deviceID  __attribute__ ((packed));
74
+    uint32_t driverData  __attribute__ ((packed));
75
+    uint16_t driverCount  __attribute__ ((packed));
76
+    struct apm_driver_desc_entry driverTable[8];
77
+    /* zeroes fill remainder of sector (430 bytes in 512 sector size) */
78
+};
79
+
80
+/* 512(136)-byte partition info, big endian;
81
+ * both the partition table and the individual partitions use this 
82
+ * struct to describe their details
83
+ */
84
+struct apm_partition_info {
85
+    uint16_t signature  __attribute__ ((packed));
86
+    uint16_t reserved  __attribute__ ((packed));
87
+    uint32_t numPartitions  __attribute__ ((packed));
88
+    uint32_t pBlockStart  __attribute__ ((packed));
89
+    uint32_t pBlockCount  __attribute__ ((packed));
90
+    uint8_t name[32];
91
+    uint8_t type[32];
92
+    uint32_t lBlockStart  __attribute__ ((packed));
93
+    uint32_t lBlockCount  __attribute__ ((packed));
94
+    uint32_t flags  __attribute__ ((packed));
95
+    uint32_t bootBlockStart  __attribute__ ((packed));
96
+    uint32_t bootSize  __attribute__ ((packed));
97
+    uint32_t bootAddr  __attribute__ ((packed));
98
+    uint32_t bootAddr2  __attribute__ ((packed));
99
+    uint32_t bootEntry  __attribute__ ((packed));
100
+    uint32_t bootEntry2  __attribute__ ((packed));
101
+    uint32_t bootChecksum  __attribute__ ((packed));
102
+    uint8_t processor[16];
103
+    /* zeroes fill remainder of sector (376 bytes in 512 sector size) */
104
+};
105
+
106
+#ifdef HAVE_PRAGMA_PACK
107
+#pragma pack()
108
+#endif
109
+
110
+#ifdef HAVE_PRAGMA_PACK_HPPA
111
+#pragma pack
112
+#endif
113
+
114
+int cli_scanapm(cli_ctx *ctx);
115
+
116
+#endif