Browse code

parse program headers and properly calculate file offset of entry point

git-svn: trunk@2448

Tomasz Kojm authored on 2006/10/29 05:46:32
Showing 3 changed files
... ...
@@ -1,3 +1,8 @@
1
+Sat Oct 28 22:44:46 CEST 2006 (tk)
2
+----------------------------------
3
+  * libclamav/elf.c: parse program headers and properly calculate file offset
4
+		     of entry point
5
+
1 6
 Sat Oct 28 16:56:51 BST 2006 (njh)
2 7
 ----------------------------------
3 8
   * clamav-milter:	Fix file descriptor leak when more than one email is
... ...
@@ -1,5 +1,5 @@
1 1
 /*
2
- *  Copyright (C) 2005 Tomasz Kojm <tkojm@clamav.net>
2
+ *  Copyright (C) 2005 - 2006 Tomasz Kojm <tkojm@clamav.net>
3 3
  *
4 4
  *  This program is free software; you can redistribute it and/or modify
5 5
  *  it under the terms of the GNU General Public License as published by
... ...
@@ -53,14 +53,36 @@ static inline uint32_t EC32(uint32_t v, uint8_t c)
53 53
 	return ((v >> 24) | ((v & 0x00FF0000) >> 8) | ((v & 0x0000FF00) << 8) | (v << 24));
54 54
 }
55 55
 
56
+static uint32_t cli_rawaddr(uint32_t vaddr, struct elf_program_hdr32 *ph, uint16_t phnum, uint8_t conv, uint8_t *err)
57
+{
58
+	uint16_t i, found = 0;
59
+
60
+
61
+    for(i = 0; i < phnum; i++) {
62
+	if(EC32(ph[i].p_vaddr, conv) <= vaddr && EC32(ph[i].p_vaddr, conv) + EC32(ph[i].p_memsz, conv) > vaddr) {
63
+	    found = 1;
64
+	    break;
65
+	}
66
+    }
67
+
68
+    if(!found) {
69
+	*err = 1;
70
+	return 0;
71
+    }
72
+
73
+    *err = 0;
74
+    return vaddr - EC32(ph[i].p_vaddr, conv) + EC32(ph[i].p_offset, conv);
75
+}
76
+
56 77
 int cli_scanelf(int desc, cli_ctx *ctx)
57 78
 {
58 79
 	struct elf_file_hdr32 file_hdr;
59 80
 	struct elf_section_hdr32 *section_hdr;
60
-	uint16_t shnum, shentsize;
61
-	uint32_t entry, shoff, image;
62
-	uint8_t conv = 0;
63
-	int i;
81
+	struct elf_program_hdr32 *program_hdr;
82
+	uint16_t shnum, phnum, shentsize, phentsize;
83
+	uint32_t entry, fentry, shoff, phoff, i;
84
+	uint8_t conv = 0, err;
85
+
64 86
 
65 87
     cli_dbgmsg("in cli_scanelf\n");
66 88
 
... ...
@@ -81,7 +103,6 @@ int cli_scanelf(int desc, cli_ctx *ctx)
81 81
     }
82 82
 
83 83
     if(file_hdr.e_ident[5] == 1) {
84
-	image =  0x8048000;
85 84
 #if WORDS_BIGENDIAN == 0
86 85
 	cli_dbgmsg("ELF: File is little-endian - conversion not required\n");
87 86
 #else
... ...
@@ -89,7 +110,6 @@ int cli_scanelf(int desc, cli_ctx *ctx)
89 89
 	conv = 1;
90 90
 #endif
91 91
     } else {
92
-	image =  0x10000;
93 92
 #if WORDS_BIGENDIAN == 0
94 93
 	cli_dbgmsg("ELF: File is big-endian - data conversion enabled\n");
95 94
 	conv = 1;
... ...
@@ -164,8 +184,92 @@ int cli_scanelf(int desc, cli_ctx *ctx)
164 164
     }
165 165
 
166 166
     entry = EC32(file_hdr.e_entry, conv);
167
-    cli_dbgmsg("ELF: Entry point address: 0x%.8x\n", entry);
168
-    cli_dbgmsg("ELF: Entry point offset: 0x%.8x (%d)\n", entry - image, entry - image);
167
+
168
+    /* Program headers */
169
+
170
+    phnum = EC16(file_hdr.e_phnum, conv);
171
+    cli_dbgmsg("ELF: Number of program headers: %d\n", phnum);
172
+    if(phnum > 128) {
173
+	cli_dbgmsg("ELF: Suspicious number of program headers\n");
174
+        if(DETECT_BROKEN) {
175
+	    if(ctx->virname)
176
+		*ctx->virname = "Broken.Executable";
177
+            return CL_VIRUS;
178
+        }
179
+	return CL_EFORMAT;
180
+    }
181
+
182
+    if(phnum && entry) {
183
+
184
+	phentsize = EC16(file_hdr.e_phentsize, conv);
185
+	if(phentsize != sizeof(struct elf_program_hdr32)) {
186
+	    cli_dbgmsg("ELF: phentsize != sizeof(struct elf_program_hdr32)\n");
187
+	    if(DETECT_BROKEN) {
188
+		if(ctx->virname)
189
+		    *ctx->virname = "Broken.Executable";
190
+		return CL_VIRUS;
191
+	    }
192
+	    return CL_EFORMAT;
193
+	}
194
+
195
+	phoff = EC32(file_hdr.e_phoff, conv);
196
+	cli_dbgmsg("ELF: Program header table offset: %d\n", phoff);
197
+	if((uint32_t) lseek(desc, phoff, SEEK_SET) != phoff) {
198
+	    if(DETECT_BROKEN) {
199
+		if(ctx->virname)
200
+		    *ctx->virname = "Broken.Executable";
201
+		return CL_VIRUS;
202
+	    }
203
+	    return CL_CLEAN;
204
+	}
205
+
206
+	program_hdr = (struct elf_program_hdr32 *) cli_calloc(phnum, phentsize);
207
+	if(!program_hdr) {
208
+	    cli_errmsg("ELF: Can't allocate memory for program headers\n");
209
+	    return CL_EMEM;
210
+	}
211
+
212
+	cli_dbgmsg("------------------------------------\n");
213
+
214
+	for(i = 0; i < phnum; i++) {
215
+
216
+	    if(read(desc, &program_hdr[i], sizeof(struct elf_program_hdr32)) != sizeof(struct elf_program_hdr32)) {
217
+		cli_dbgmsg("ELF: Can't read segment #%d\n", i);
218
+		cli_dbgmsg("ELF: Possibly broken ELF file\n");
219
+		free(program_hdr);
220
+		if(DETECT_BROKEN) {
221
+		    if(ctx->virname)
222
+			*ctx->virname = "Broken.Executable";
223
+		    return CL_VIRUS;
224
+		}
225
+		return CL_CLEAN;
226
+	    }
227
+
228
+	    cli_dbgmsg("ELF: Segment #%d\n", i);
229
+	    cli_dbgmsg("ELF: Segment type: 0x%x\n", EC32(program_hdr[i].p_type, conv));
230
+	    cli_dbgmsg("ELF: Segment offset: 0x%x\n", EC32(program_hdr[i].p_offset, conv));
231
+	    cli_dbgmsg("ELF: Segment virtual address: 0x%x\n", EC32(program_hdr[i].p_vaddr, conv));
232
+	    cli_dbgmsg("ELF: Segment real size: 0x%x\n", EC32(program_hdr[i].p_filesz, conv));
233
+	    cli_dbgmsg("ELF: Segment virtual size: 0x%x\n", EC32(program_hdr[i].p_memsz, conv));
234
+	    cli_dbgmsg("------------------------------------\n");
235
+	}
236
+
237
+	fentry = cli_rawaddr(entry, program_hdr, phnum, conv, &err);
238
+	free(program_hdr);
239
+	if(err) {
240
+	    cli_dbgmsg("ELF: Can't calculate file offset of entry point\n");
241
+	    if(DETECT_BROKEN) {
242
+		if(ctx->virname)
243
+		    *ctx->virname = "Broken.Executable";
244
+		return CL_VIRUS;
245
+	    }
246
+	    return CL_EFORMAT;
247
+	}
248
+	cli_dbgmsg("ELF: Entry point address: 0x%.8x\n", entry);
249
+	cli_dbgmsg("ELF: Entry point offset: 0x%.8x (%d)\n", fentry, fentry);
250
+    }
251
+
252
+    /* Sections */
169 253
 
170 254
     shnum = EC16(file_hdr.e_shnum, conv);
171 255
     cli_dbgmsg("ELF: Number of sections: %d\n", shnum);
... ...
@@ -304,10 +408,11 @@ int cli_elfheader(int desc, struct cli_exe_info *elfinfo)
304 304
 {
305 305
 	struct elf_file_hdr32 file_hdr;
306 306
 	struct elf_section_hdr32 *section_hdr;
307
-	uint16_t shnum, shentsize;
308
-	uint32_t entry, shoff, image;
309
-	uint8_t conv = 0;
310
-	int i;
307
+	struct elf_program_hdr32 *program_hdr;
308
+	uint16_t shnum, phnum, shentsize, phentsize, i;
309
+	uint32_t entry, fentry = 0, shoff, phoff;
310
+	uint8_t conv = 0, err;
311
+
311 312
 
312 313
     cli_dbgmsg("in cli_elfheader\n");
313 314
 
... ...
@@ -328,19 +433,58 @@ int cli_elfheader(int desc, struct cli_exe_info *elfinfo)
328 328
     }
329 329
 
330 330
     if(file_hdr.e_ident[5] == 1) {
331
-	image =  0x8048000;
332 331
 #if WORDS_BIGENDIAN == 1
333 332
 	conv = 1;
334 333
 #endif
335 334
     } else {
336
-	image =  0x10000;
337 335
 #if WORDS_BIGENDIAN == 0
338 336
 	conv = 1;
339 337
 #endif
340 338
     }
341 339
 
340
+    phnum = EC16(file_hdr.e_phnum, conv);
341
+    if(phnum > 128) {
342
+	cli_dbgmsg("ELF: Suspicious number of program headers\n");
343
+	return -1;
344
+    }
342 345
     entry = EC32(file_hdr.e_entry, conv);
343 346
 
347
+    if(phnum && entry) {
348
+	phentsize = EC16(file_hdr.e_phentsize, conv);
349
+	if(phentsize != sizeof(struct elf_program_hdr32)) {
350
+	    cli_dbgmsg("ELF: phentsize != sizeof(struct elf_program_hdr32)\n");
351
+	    return -1;
352
+	}
353
+
354
+	phoff = EC32(file_hdr.e_phoff, conv);
355
+	if((uint32_t) lseek(desc, phoff, SEEK_SET) != phoff) {
356
+	    return -1;
357
+	}
358
+
359
+	program_hdr = (struct elf_program_hdr32 *) cli_calloc(phnum, phentsize);
360
+	if(!program_hdr) {
361
+	    cli_errmsg("ELF: Can't allocate memory for program headers\n");
362
+	    return -1;
363
+	}
364
+
365
+	for(i = 0; i < phnum; i++) {
366
+	    if(read(desc, &program_hdr[i], sizeof(struct elf_program_hdr32)) != sizeof(struct elf_program_hdr32)) {
367
+		cli_dbgmsg("ELF: Can't read segment #%d\n", i);
368
+		free(program_hdr);
369
+		return -1;
370
+	    }
371
+	}
372
+
373
+	fentry = cli_rawaddr(entry, program_hdr, phnum, conv, &err);
374
+	free(program_hdr);
375
+	if(err) {
376
+	    cli_dbgmsg("ELF: Can't calculate file offset of entry point\n");
377
+	    return -1;
378
+	}
379
+    }
380
+
381
+    elfinfo->ep = fentry;
382
+
344 383
     shnum = EC16(file_hdr.e_shnum, conv);
345 384
     if(shnum > 256) {
346 385
 	cli_dbgmsg("ELF: Suspicious number of sections\n");
... ...
@@ -1,5 +1,5 @@
1 1
 /*
2
- *  Copyright (C) 2005 Tomasz Kojm <tkojm@clamav.net>
2
+ *  Copyright (C) 2005 - 2006 Tomasz Kojm <tkojm@clamav.net>
3 3
  *
4 4
  *  Header structures based on ELF: Executable and Linkable Format, Portable
5 5
  *  Formats Specification, Version 1.1
... ...
@@ -45,6 +45,17 @@ struct elf_file_hdr32 {
45 45
     uint16_t e_shstrndx;
46 46
 };
47 47
 
48
+struct elf_program_hdr32 {
49
+    uint32_t p_type;
50
+    uint32_t p_offset;
51
+    uint32_t p_vaddr;
52
+    uint32_t p_paddr;
53
+    uint32_t p_filesz;
54
+    uint32_t p_memsz;
55
+    uint32_t p_flags;
56
+    uint32_t p_align;
57
+};
58
+
48 59
 struct elf_section_hdr32 {
49 60
     uint32_t sh_name;
50 61
     uint32_t sh_type;