git-svn: trunk@651
Tomasz Kojm authored on 2004/07/06 08:50:55... | ... |
@@ -1,3 +1,12 @@ |
1 |
+Tue Jul 6 01:46:41 CEST 2004 (tk) |
|
2 |
+---------------------------------- |
|
3 |
+ * libclamav: pe, upx: add big-endian support |
|
4 |
+ * libclamav: activate PE and UPX code (new scan option CL_PE). UPX code |
|
5 |
+ still needs some corrections in NRV2D/E decompression routines. |
|
6 |
+ * clamd: new directive ScanPE |
|
7 |
+ * clamscan: new option --no-pe |
|
8 |
+ * docs: update manual pages |
|
9 |
+ |
|
1 | 10 |
Sun Jul 4 16:52:45 CEST 2004 (tk) |
2 | 11 |
---------------------------------- |
3 | 12 |
* libclamav: Do not scan mail files twice. Separate archive and mail |
... | ... |
@@ -311,6 +311,13 @@ int acceptloop_th(int socketd, struct cl_node *root, const struct cfgstruct *cop |
311 | 311 |
logg("Archive support disabled.\n"); |
312 | 312 |
} |
313 | 313 |
|
314 |
+ if(cfgopt(copt, "ScanPE")) { |
|
315 |
+ logg("Portable Executable support enabled.\n"); |
|
316 |
+ options |= CL_PE; |
|
317 |
+ } else { |
|
318 |
+ logg("Portable Executable support disabled.\n"); |
|
319 |
+ } |
|
320 |
+ |
|
314 | 321 |
if(cfgopt(copt, "ScanMail")) { |
315 | 322 |
logg("Mail files support enabled.\n"); |
316 | 323 |
options |= CL_MAIL; |
... | ... |
@@ -200,7 +200,8 @@ void help(void) |
200 | 200 |
mprintf(" --stdout Write to stdout instead of stderr\n"); |
201 | 201 |
mprintf(" (this help is always written to stdout)\n"); |
202 | 202 |
mprintf("\n"); |
203 |
- mprintf(" --tempdir=DIRECTORY create temporary files in DIRECTORY\n"); |
|
203 |
+ mprintf(" --tempdir=DIRECTORY Create temporary files in DIRECTORY\n"); |
|
204 |
+ mprintf(" --leave-temps Do not remove temporary files\n"); |
|
204 | 205 |
mprintf(" --database=FILE/DIR -d FILE/DIR Load virus database from FILE or load\n"); |
205 | 206 |
mprintf(" all .cvd and .db[2] files from DIR\n"); |
206 | 207 |
mprintf(" --log=FILE -l FILE Save scan report to FILE\n"); |
... | ... |
@@ -219,6 +220,7 @@ void help(void) |
219 | 219 |
mprintf(" --no-summary Disable summary at end of scanning\n"); |
220 | 220 |
mprintf(" --mbox -m Treat stdin as a mailbox\n"); |
221 | 221 |
mprintf("\n"); |
222 |
+ mprintf(" --no-pe Disable PE analysis\n"); |
|
222 | 223 |
mprintf(" --no-ole2 Disable OLE2 support\n"); |
223 | 224 |
mprintf(" --no-html Disable HTML support\n"); |
224 | 225 |
mprintf(" --no-archive Disable libclamav archive support\n"); |
... | ... |
@@ -422,6 +422,11 @@ int scanfile(const char *filename, struct cl_node *root, const struct passwd *us |
422 | 422 |
if(optl(opt, "block-encrypted")) |
423 | 423 |
options |= CL_ENCRYPTED; |
424 | 424 |
|
425 |
+ if(optl(opt, "no-pe")) |
|
426 |
+ options &= ~CL_PE; |
|
427 |
+ else |
|
428 |
+ options |= CL_PE; |
|
429 |
+ |
|
425 | 430 |
if(optl(opt, "no-ole2")) |
426 | 431 |
options &= ~CL_OLE2; |
427 | 432 |
else |
... | ... |
@@ -818,6 +823,9 @@ int checkfile(const char *filename, const struct cl_node *root, const struct cl_ |
818 | 818 |
int fd, ret; |
819 | 819 |
const char *virname; |
820 | 820 |
|
821 |
+ |
|
822 |
+ mprintf("*Scanning %s\n", filename); |
|
823 |
+ |
|
821 | 824 |
if((fd = open(filename, O_RDONLY)) == -1) { |
822 | 825 |
mprintf("@Can't open file %s\n", filename); |
823 | 826 |
return 54; |
... | ... |
@@ -159,6 +159,11 @@ Close the connection when this limit is exceeded. |
159 | 159 |
.br |
160 | 160 |
Default: disabled. |
161 | 161 |
.TP |
162 |
+\fBScanPE\fR |
|
163 |
+PE stands for Portable Executable \- it's an executable file format used in all 32\-bit versions of Windows operating systems. This option allows ClamAV to perform a deeper analysis of executable files and it's also required for decompression of popular executable packers such as UPX. |
|
164 |
+.br |
|
165 |
+Default: enabled. |
|
166 |
+.TP |
|
162 | 167 |
\fBScanOLE2\fR |
163 | 168 |
Enables scanning of Microsoft Office document macros. |
164 | 169 |
.br |
... | ... |
@@ -40,6 +40,9 @@ Save scan report to FILE. |
40 | 40 |
\fB\-\-tempdir=DIRECTORY\fR |
41 | 41 |
Create temporary files in DIRECTORY. Directory must be writeable for the 'clamav' user or unprivileged user running clamscan. |
42 | 42 |
.TP |
43 |
+\fB\-\-leave\-temps\fR |
|
44 |
+Do not remove temporary files. |
|
45 |
+.TP |
|
43 | 46 |
\fB\-r, \-\-recursive\fR |
44 | 47 |
Scan directories recursively. All the subdirectories in the given directory will be scanned. |
45 | 48 |
.TP |
... | ... |
@@ -67,7 +70,8 @@ Remove infected files. \fBBe careful.\fR |
67 | 67 |
\fB\-\-move=DIRECTORY\fR |
68 | 68 |
Move infected files into DIRECTORY. Directory must be writeable for the 'clamav' user or unprivileged user running clamscan. |
69 | 69 |
.TP |
70 |
-EXTRACTION OPTIONS: |
|
70 |
+\fB\-\-no\-pe\fR |
|
71 |
+PE stands for Portable Executable \- it's an executable file format used in all 32\-bit versions of Windows operating systems. By default ClamAV performs deeper analysis of executable files and tries to decompress popular executable packers such as UPX. This option \fBdisables\fR PE support and is not recommended ! |
|
71 | 72 |
.TP |
72 | 73 |
\fB\-\-no\-ole2\fR |
73 | 74 |
Disable support for Microsoft Office document files. |
... | ... |
@@ -133,22 +133,33 @@ MaxDirectoryRecursion 15 |
133 | 133 |
# Do not remove temporary files (for debug purposes). |
134 | 134 |
#LeaveTemporaryFiles |
135 | 135 |
|
136 |
+ |
|
137 |
+## |
|
138 |
+## Executable files |
|
139 |
+## |
|
140 |
+ |
|
141 |
+# PE stands for Portable Executable - it's an executable file format used |
|
142 |
+# in all 32-bit versions of Windows operating systems. This option allows |
|
143 |
+# ClamAV to perform a deeper analysis of executable files and it's also |
|
144 |
+# required for decompression of popular executable packers such as UPX. |
|
145 |
+ScanPE |
|
146 |
+ |
|
136 | 147 |
## |
137 |
-## Document scanning |
|
148 |
+## Documents |
|
138 | 149 |
## |
139 | 150 |
|
140 | 151 |
# This option enables scanning of Microsoft Office document macros. |
141 | 152 |
ScanOLE2 |
142 | 153 |
|
143 | 154 |
## |
144 |
-## Mail support |
|
155 |
+## Mail files |
|
145 | 156 |
## |
146 | 157 |
|
147 | 158 |
# Uncomment this option if you are going to scan mail files. |
148 | 159 |
#ScanMail |
149 | 160 |
|
150 | 161 |
## |
151 |
-## HTML support |
|
162 |
+## HTML |
|
152 | 163 |
## |
153 | 164 |
|
154 | 165 |
# This option enables HTML detection and normalisation. It's highly |
... | ... |
@@ -156,7 +167,7 @@ ScanOLE2 |
156 | 156 |
ScanHTML |
157 | 157 |
|
158 | 158 |
## |
159 |
-## Archive support |
|
159 |
+## Archives |
|
160 | 160 |
## |
161 | 161 |
|
162 | 162 |
|
... | ... |
@@ -45,6 +45,23 @@ |
45 | 45 |
#define UPX_NRV2D "\x8b\x1e\x83\xee\xfc\x11\xdb\x11\xc9\x01\xdb\x75\x07\x8b\x1e\x83\xee\xfc\x11\xdb\x11\xc9\x75\x20" |
46 | 46 |
#define UPX_NRV2E "\x83\xf0\xff\x74\x75\xd1\xf8\x89\xc5\xeb\x0b\x01\xdb\x75\x07\x8b\x1e\x83\xee\xfc\x11\xdb\x72\xcc" |
47 | 47 |
|
48 |
+#if WORDS_BIGENDIAN == 0 |
|
49 |
+#define EC16(v) (v) |
|
50 |
+#define EC32(v) (v) |
|
51 |
+#else |
|
52 |
+static inline uint16_t EC16(uint16_t v) |
|
53 |
+{ |
|
54 |
+ return ((v >> 8) + (v << 8)); |
|
55 |
+} |
|
56 |
+ |
|
57 |
+static inline uint32_t EC32(uint32_t v) |
|
58 |
+{ |
|
59 |
+ return ((v >> 24) | ((v & 0x00FF0000) >> 8) | ((v & 0x0000FF00) << 8) | (v << 24)); |
|
60 |
+} |
|
61 |
+#endif |
|
62 |
+ |
|
63 |
+extern short cli_leavetemps_flag; |
|
64 |
+ |
|
48 | 65 |
struct pe_image_file_hdr { |
49 | 66 |
uint32_t Magic; |
50 | 67 |
uint16_t Machine; |
... | ... |
@@ -120,7 +137,7 @@ static uint32_t cli_rawaddr(uint32_t rva, struct pe_image_section_hdr *shp, uint |
120 | 120 |
|
121 | 121 |
|
122 | 122 |
for(i = 0; i < nos; i++) { |
123 |
- if(shp[i].VirtualAddress <= rva && shp[i].VirtualAddress + shp[i].SizeOfRawData > rva) { |
|
123 |
+ if(EC32(shp[i].VirtualAddress) <= rva && EC32(shp[i].VirtualAddress) + EC32(shp[i].SizeOfRawData) > rva) { |
|
124 | 124 |
found = 1; |
125 | 125 |
break; |
126 | 126 |
} |
... | ... |
@@ -131,7 +148,7 @@ static uint32_t cli_rawaddr(uint32_t rva, struct pe_image_section_hdr *shp, uint |
131 | 131 |
return -1; |
132 | 132 |
} |
133 | 133 |
|
134 |
- return rva - shp[i].VirtualAddress + shp[i].PointerToRawData; |
|
134 |
+ return rva - EC32(shp[i].VirtualAddress) + EC32(shp[i].PointerToRawData); |
|
135 | 135 |
} |
136 | 136 |
|
137 | 137 |
static int cli_ddump(int desc, int offset, int size, const char *file) |
... | ... |
@@ -144,19 +161,19 @@ static int cli_ddump(int desc, int offset, int size, const char *file) |
144 | 144 |
|
145 | 145 |
if((pos = lseek(desc, 0, SEEK_CUR)) == -1) { |
146 | 146 |
cli_dbgmsg("Invalid descriptor\n"); |
147 |
- return CL_EIO; |
|
147 |
+ return -1; |
|
148 | 148 |
} |
149 | 149 |
|
150 | 150 |
if(lseek(desc, offset, SEEK_SET) == -1) { |
151 | 151 |
cli_dbgmsg("lseek() failed\n"); |
152 | 152 |
lseek(desc, pos, SEEK_SET); |
153 |
- return CL_EIO; |
|
153 |
+ return -1; |
|
154 | 154 |
} |
155 | 155 |
|
156 | 156 |
if((ndesc = open(file, O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU)) < 0) { |
157 | 157 |
cli_dbgmsg("Can't create file %s\n", file); |
158 | 158 |
lseek(desc, pos, SEEK_SET); |
159 |
- return CL_EIO; |
|
159 |
+ return -1; |
|
160 | 160 |
} |
161 | 161 |
|
162 | 162 |
while((bread = read(desc, buff, FILEBUFF)) > 0) { |
... | ... |
@@ -166,7 +183,7 @@ static int cli_ddump(int desc, int offset, int size, const char *file) |
166 | 166 |
lseek(desc, pos, SEEK_SET); |
167 | 167 |
close(ndesc); |
168 | 168 |
unlink(file); |
169 |
- return CL_EIO; |
|
169 |
+ return -1; |
|
170 | 170 |
} |
171 | 171 |
break; |
172 | 172 |
} else { |
... | ... |
@@ -175,7 +192,7 @@ static int cli_ddump(int desc, int offset, int size, const char *file) |
175 | 175 |
lseek(desc, pos, SEEK_SET); |
176 | 176 |
close(ndesc); |
177 | 177 |
unlink(file); |
178 |
- return CL_EIO; |
|
178 |
+ return -1; |
|
179 | 179 |
} |
180 | 180 |
} |
181 | 181 |
sum += bread; |
... | ... |
@@ -186,17 +203,38 @@ static int cli_ddump(int desc, int offset, int size, const char *file) |
186 | 186 |
return 0; |
187 | 187 |
} |
188 | 188 |
|
189 |
-int cli_scanpe(int desc, const char **virname, long int *scanned, const struct cl_node *root, const struct cl_limits *limits, int options, int *reclev) |
|
189 |
+int cli_memstr(const char *haystack, int hs, const char *needle, int ns) |
|
190 |
+{ |
|
191 |
+ const char *pt; |
|
192 |
+ int n; |
|
193 |
+ |
|
194 |
+ if(!memcmp(haystack, needle, ns)) |
|
195 |
+ return 1; |
|
196 |
+ |
|
197 |
+ pt = haystack; |
|
198 |
+ n = hs; |
|
199 |
+ while(n && (pt = memchr(pt, needle[0], n))) { |
|
200 |
+ n--; |
|
201 |
+ if(!memcmp(pt, needle, ns)) |
|
202 |
+ return 1; |
|
203 |
+ } |
|
204 |
+ |
|
205 |
+ return 0; |
|
206 |
+} |
|
207 |
+ |
|
208 |
+int cli_scanpe(int desc, const char **virname, long int *scanned, const struct cl_node *root, const struct cl_limits *limits, int options, int *arec, int *mrec) |
|
190 | 209 |
{ |
191 | 210 |
uint16_t e_magic; /* DOS signature ("MZ") */ |
211 |
+ uint16_t nsections; |
|
192 | 212 |
uint32_t e_lfanew; /* address of new exe header */ |
193 | 213 |
uint32_t ep; /* entry point (raw) */ |
214 |
+ uint32_t timestamp; |
|
194 | 215 |
struct pe_image_file_hdr file_hdr; |
195 | 216 |
struct pe_image_optional_hdr optional_hdr; |
196 | 217 |
struct pe_image_section_hdr *section_hdr; |
197 | 218 |
struct stat sb; |
198 | 219 |
char sname[9], buff[24], *tempfile; |
199 |
- int i, found; |
|
220 |
+ int i, found, upx_success = 0; |
|
200 | 221 |
int (*upxfn)(char *, int , char *, int) = NULL; |
201 | 222 |
|
202 | 223 |
|
... | ... |
@@ -205,38 +243,38 @@ int cli_scanpe(int desc, const char **virname, long int *scanned, const struct c |
205 | 205 |
return CL_EIO; |
206 | 206 |
} |
207 | 207 |
|
208 |
- if(e_magic != IMAGE_DOS_SIGNATURE) { |
|
208 |
+ if(EC16(e_magic) != IMAGE_DOS_SIGNATURE) { |
|
209 | 209 |
cli_dbgmsg("Invalid DOS signature\n"); |
210 |
- return -1; |
|
210 |
+ return CL_CLEAN; |
|
211 | 211 |
} |
212 | 212 |
|
213 | 213 |
lseek(desc, 58, SEEK_CUR); /* skip to the end of the DOS header */ |
214 | 214 |
|
215 | 215 |
if(read(desc, &e_lfanew, sizeof(e_lfanew)) != sizeof(e_lfanew)) { |
216 | 216 |
cli_dbgmsg("Can't read new header address\n"); |
217 |
- return -1; |
|
217 |
+ return CL_EIO; |
|
218 | 218 |
} |
219 | 219 |
|
220 |
+ e_lfanew = EC32(e_lfanew); |
|
220 | 221 |
cli_dbgmsg("e_lfanew == %d\n", e_lfanew); |
221 | 222 |
if(!e_lfanew) { |
222 | 223 |
cli_dbgmsg("Not a PE file\n"); |
223 |
- return -2; |
|
224 |
+ return CL_CLEAN; |
|
224 | 225 |
} |
225 |
- |
|
226 | 226 |
lseek(desc, e_lfanew, SEEK_SET); |
227 | 227 |
|
228 | 228 |
if(read(desc, &file_hdr, sizeof(struct pe_image_file_hdr)) != sizeof(struct pe_image_file_hdr)) { |
229 | 229 |
cli_dbgmsg("Can't read file header\n"); |
230 |
- return -1; |
|
230 |
+ return CL_EIO; |
|
231 | 231 |
} |
232 | 232 |
|
233 |
- if(file_hdr.Magic != IMAGE_NT_SIGNATURE) { |
|
233 |
+ if(EC32(file_hdr.Magic) != IMAGE_NT_SIGNATURE) { |
|
234 | 234 |
cli_dbgmsg("Invalid PE signature (probably NE file)\n"); |
235 |
- return -2; |
|
235 |
+ return CL_CLEAN; |
|
236 | 236 |
} |
237 | 237 |
|
238 | 238 |
/* cli_dbgmsg("Machine type: "); */ |
239 |
- switch(file_hdr.Machine) { |
|
239 |
+ switch(EC16(file_hdr.Machine)) { |
|
240 | 240 |
case 0x14c: |
241 | 241 |
cli_dbgmsg("Machine type: 80386\n"); |
242 | 242 |
break; |
... | ... |
@@ -265,35 +303,38 @@ int cli_scanpe(int desc, const char **virname, long int *scanned, const struct c |
265 | 265 |
cli_warnmsg("Unknown machine type in PE header\n"); |
266 | 266 |
} |
267 | 267 |
|
268 |
- cli_dbgmsg("NumberOfSections: %d\n", file_hdr.NumberOfSections); |
|
269 |
- cli_dbgmsg("TimeDateStamp: %s", ctime((time_t *) &file_hdr.TimeDateStamp)); |
|
268 |
+ nsections = EC16(file_hdr.NumberOfSections); |
|
269 |
+ cli_dbgmsg("NumberOfSections: %d\n", nsections); |
|
270 |
+ |
|
271 |
+ timestamp = EC32(file_hdr.TimeDateStamp); |
|
272 |
+ cli_dbgmsg("TimeDateStamp: %s", ctime((time_t *) ×tamp)); |
|
270 | 273 |
|
271 |
- cli_dbgmsg("SizeOfOptionalHeader: %d\n", file_hdr.SizeOfOptionalHeader); |
|
274 |
+ cli_dbgmsg("SizeOfOptionalHeader: %d\n", EC16(file_hdr.SizeOfOptionalHeader)); |
|
272 | 275 |
|
273 |
- if(file_hdr.SizeOfOptionalHeader != sizeof(struct pe_image_optional_hdr)) { |
|
276 |
+ if(EC16(file_hdr.SizeOfOptionalHeader) != sizeof(struct pe_image_optional_hdr)) { |
|
274 | 277 |
cli_warnmsg("Broken PE header detected.\n"); |
275 |
- return -1; |
|
278 |
+ return CL_CLEAN; |
|
276 | 279 |
} |
277 | 280 |
|
278 | 281 |
if(read(desc, &optional_hdr, sizeof(struct pe_image_optional_hdr)) != sizeof(struct pe_image_optional_hdr)) { |
279 | 282 |
cli_dbgmsg("Can't optional file header\n"); |
280 |
- return -1; |
|
283 |
+ return CL_EIO; |
|
281 | 284 |
} |
282 | 285 |
|
283 | 286 |
cli_dbgmsg("MajorLinkerVersion: %d\n", optional_hdr.MajorLinkerVersion); |
284 | 287 |
cli_dbgmsg("MinorLinkerVersion: %d\n", optional_hdr.MinorLinkerVersion); |
285 |
- cli_dbgmsg("SizeOfCode: %d\n", optional_hdr.SizeOfCode); |
|
286 |
- cli_dbgmsg("SizeOfInitializedData: %d\n", optional_hdr.SizeOfInitializedData); |
|
287 |
- cli_dbgmsg("SizeOfUninitializedData: %d\n", optional_hdr.SizeOfUninitializedData); |
|
288 |
- cli_dbgmsg("AddressOfEntryPoint: 0x%x\n", optional_hdr.AddressOfEntryPoint); |
|
289 |
- cli_dbgmsg("SectionAlignment: %d\n", optional_hdr.SectionAlignment); |
|
290 |
- cli_dbgmsg("FileAlignment: %d\n", optional_hdr.FileAlignment); |
|
291 |
- cli_dbgmsg("MajorSubsystemVersion: %d\n", optional_hdr.MajorSubsystemVersion); |
|
292 |
- cli_dbgmsg("MinorSubsystemVersion: %d\n", optional_hdr.MinorSubsystemVersion); |
|
293 |
- cli_dbgmsg("SizeOfImage: %d\n", optional_hdr.SizeOfImage); |
|
294 |
- cli_dbgmsg("SizeOfHeaders: %d\n", optional_hdr.SizeOfHeaders); |
|
295 |
- |
|
296 |
- switch(optional_hdr.Subsystem) { |
|
288 |
+ cli_dbgmsg("SizeOfCode: %d\n", EC32(optional_hdr.SizeOfCode)); |
|
289 |
+ cli_dbgmsg("SizeOfInitializedData: %d\n", EC32(optional_hdr.SizeOfInitializedData)); |
|
290 |
+ cli_dbgmsg("SizeOfUninitializedData: %d\n", EC32(optional_hdr.SizeOfUninitializedData)); |
|
291 |
+ cli_dbgmsg("AddressOfEntryPoint: 0x%x\n", EC32(optional_hdr.AddressOfEntryPoint)); |
|
292 |
+ cli_dbgmsg("SectionAlignment: %d\n", EC32(optional_hdr.SectionAlignment)); |
|
293 |
+ cli_dbgmsg("FileAlignment: %d\n", EC32(optional_hdr.FileAlignment)); |
|
294 |
+ cli_dbgmsg("MajorSubsystemVersion: %d\n", EC16(optional_hdr.MajorSubsystemVersion)); |
|
295 |
+ cli_dbgmsg("MinorSubsystemVersion: %d\n", EC16(optional_hdr.MinorSubsystemVersion)); |
|
296 |
+ cli_dbgmsg("SizeOfImage: %d\n", EC32(optional_hdr.SizeOfImage)); |
|
297 |
+ cli_dbgmsg("SizeOfHeaders: %d\n", EC32(optional_hdr.SizeOfHeaders)); |
|
298 |
+ |
|
299 |
+ switch(EC16(optional_hdr.Subsystem)) { |
|
297 | 300 |
case 1: |
298 | 301 |
cli_dbgmsg("Subsystem: Native (a driver ?)\n"); |
299 | 302 |
break; |
... | ... |
@@ -313,16 +354,16 @@ int cli_scanpe(int desc, const char **virname, long int *scanned, const struct c |
313 | 313 |
cli_warnmsg("Unknown subsystem in PE header\n"); |
314 | 314 |
} |
315 | 315 |
|
316 |
- cli_dbgmsg("NumberOfRvaAndSizes: %d\n", optional_hdr.NumberOfRvaAndSizes); |
|
316 |
+ cli_dbgmsg("NumberOfRvaAndSizes: %d\n", EC32(optional_hdr.NumberOfRvaAndSizes)); |
|
317 | 317 |
|
318 |
- section_hdr = (struct pe_image_section_hdr *) cli_calloc(file_hdr.NumberOfSections, sizeof(struct pe_image_section_hdr)); |
|
318 |
+ section_hdr = (struct pe_image_section_hdr *) cli_calloc(nsections, sizeof(struct pe_image_section_hdr)); |
|
319 | 319 |
|
320 | 320 |
if(!section_hdr) { |
321 | 321 |
cli_dbgmsg("Can't allocate memory for section headers\n"); |
322 | 322 |
return CL_EMEM; |
323 | 323 |
} |
324 | 324 |
|
325 |
- for(i = 0; i < file_hdr.NumberOfSections; i++) { |
|
325 |
+ for(i = 0; i < nsections; i++) { |
|
326 | 326 |
|
327 | 327 |
if(read(desc, §ion_hdr[i], sizeof(struct pe_image_section_hdr)) != sizeof(struct pe_image_section_hdr)) { |
328 | 328 |
cli_dbgmsg("Can't read section header\n"); |
... | ... |
@@ -335,15 +376,15 @@ int cli_scanpe(int desc, const char **virname, long int *scanned, const struct c |
335 | 335 |
sname[8] = 0; |
336 | 336 |
cli_dbgmsg("------------------------------------\n"); |
337 | 337 |
cli_dbgmsg("Section name: %s\n", sname); |
338 |
- cli_dbgmsg("VirtualSize: %d\n", section_hdr[i].VirtualSize); |
|
339 |
- cli_dbgmsg("VirtualAddress: 0x%x\n", section_hdr[i].VirtualAddress); |
|
340 |
- cli_dbgmsg("Section size: %d\n", section_hdr[i].SizeOfRawData); |
|
341 |
- cli_dbgmsg("PointerToRawData: 0x%x (%d)\n", section_hdr[i].PointerToRawData, section_hdr[i].PointerToRawData); |
|
338 |
+ cli_dbgmsg("VirtualSize: %d\n", EC32(section_hdr[i].VirtualSize)); |
|
339 |
+ cli_dbgmsg("VirtualAddress: 0x%x\n", EC32(section_hdr[i].VirtualAddress)); |
|
340 |
+ cli_dbgmsg("SizeOfRawData: %d\n", EC32(section_hdr[i].SizeOfRawData)); |
|
341 |
+ cli_dbgmsg("PointerToRawData: 0x%x (%d)\n", EC32(section_hdr[i].PointerToRawData), EC32(section_hdr[i].PointerToRawData)); |
|
342 | 342 |
|
343 |
- if(section_hdr[i].Characteristics & 0x20) { |
|
343 |
+ if(EC32(section_hdr[i].Characteristics) & 0x20) { |
|
344 | 344 |
cli_dbgmsg("Section contains executable code\n"); |
345 | 345 |
|
346 |
- if(section_hdr[i].VirtualSize < section_hdr[i].SizeOfRawData) { |
|
346 |
+ if(EC32(section_hdr[i].VirtualSize) < EC32(section_hdr[i].SizeOfRawData)) { |
|
347 | 347 |
cli_dbgmsg("Section contains free space\n"); |
348 | 348 |
/* |
349 | 349 |
cli_dbgmsg("Dumping %d bytes\n", section_hdr.SizeOfRawData - section_hdr.VirtualSize); |
... | ... |
@@ -353,7 +394,7 @@ int cli_scanpe(int desc, const char **virname, long int *scanned, const struct c |
353 | 353 |
} |
354 | 354 |
} |
355 | 355 |
|
356 |
- if(section_hdr[i].Characteristics & 0x20000000) |
|
356 |
+ if(EC32(section_hdr[i].Characteristics) & 0x20000000) |
|
357 | 357 |
cli_dbgmsg("Section's memory is executable\n"); |
358 | 358 |
|
359 | 359 |
/* |
... | ... |
@@ -373,87 +414,136 @@ int cli_scanpe(int desc, const char **virname, long int *scanned, const struct c |
373 | 373 |
return CL_EIO; |
374 | 374 |
} |
375 | 375 |
|
376 |
- ep = cli_rawaddr(optional_hdr.AddressOfEntryPoint, section_hdr, file_hdr.NumberOfSections); |
|
376 |
+ ep = cli_rawaddr(EC32(optional_hdr.AddressOfEntryPoint), section_hdr, nsections); |
|
377 | 377 |
|
378 |
- if(section_hdr[i].PointerToRawData + section_hdr[i].SizeOfRawData > sb.st_size || ep == -1) { |
|
379 |
- cli_warnmsg("Possibly broken PE file\n"); |
|
378 |
+ if(EC32(section_hdr[i].PointerToRawData) + EC32(section_hdr[i].SizeOfRawData) > sb.st_size || ep == -1) { |
|
379 |
+ cli_dbgmsg("Possibly broken PE file\n"); |
|
380 | 380 |
free(section_hdr); |
381 | 381 |
return CL_CLEAN; |
382 | 382 |
} |
383 | 383 |
|
384 | 384 |
cli_dbgmsg("EntryPoint offset: 0x%x (%d)\n", ep, ep); |
385 | 385 |
|
386 |
+ |
|
387 |
+ /* UPX support */ |
|
388 |
+ |
|
389 |
+ /* try to detect UPX code */ |
|
390 |
+ |
|
386 | 391 |
if(lseek(desc, ep + 0x78, SEEK_SET) == -1) { |
387 | 392 |
cli_dbgmsg("lseek() failed\n"); |
388 | 393 |
free(section_hdr); |
389 | 394 |
return CL_EIO; |
390 | 395 |
} |
391 | 396 |
|
392 |
- if(read(desc, buff, 24) != 24) { |
|
393 |
- cli_dbgmsg("Can't read 24 bytes at 0x%x (%d)\n", ep + 0x78, ep + 0x78); |
|
397 |
+ if(read(desc, buff, 13) != 13) { |
|
398 |
+ cli_dbgmsg("UPX: Can't read 13 bytes at 0x%x (%d)\n", ep + 0x78, ep + 0x78); |
|
394 | 399 |
} else { |
395 |
- if(!memcmp(buff, UPX_NRV2B, 24)) { |
|
396 |
- cli_dbgmsg("UPX: NRV2B decompressor detected\n"); |
|
400 |
+ if(cli_memstr(UPX_NRV2B, 24, buff, 13)) { |
|
401 |
+ cli_dbgmsg("UPX: Looks like a NRV2B decompressor\n"); |
|
397 | 402 |
upxfn = upx_inflate2b; |
398 |
- } else if(!memcmp(buff, UPX_NRV2D, 24)) { |
|
399 |
- cli_dbgmsg("UPX: NRV2D decompressor detected\n"); |
|
403 |
+ } else if(cli_memstr(UPX_NRV2D, 24, buff, 13)) { |
|
404 |
+ cli_dbgmsg("UPX: Looks like a NRV2D decompressor\n"); |
|
400 | 405 |
upxfn = upx_inflate2d; |
401 |
- } else if(!memcmp(buff, UPX_NRV2E, 24)) { |
|
402 |
- cli_dbgmsg("UPX: NRV2E decompressor detected\n"); |
|
406 |
+ } else if(cli_memstr(UPX_NRV2E, 24, buff, 13)) { |
|
407 |
+ cli_dbgmsg("UPX: Looks like a NRV2E decompressor\n"); |
|
403 | 408 |
upxfn = upx_inflate2e; |
404 | 409 |
} |
405 | 410 |
} |
406 | 411 |
|
407 |
- if(upxfn) { |
|
408 |
- /* try to find the first section with physical size == 0 */ |
|
409 |
- found = 0; |
|
410 |
- for(i = 0; i < file_hdr.NumberOfSections; i++) { |
|
411 |
- if(!section_hdr[i].SizeOfRawData) { |
|
412 |
- found = 1; |
|
413 |
- break; |
|
414 |
- } |
|
412 |
+ /* try to find the first section with physical size == 0 */ |
|
413 |
+ found = 0; |
|
414 |
+ for(i = 0; i < nsections - 1; i++) { |
|
415 |
+ if(!section_hdr[i].SizeOfRawData) { |
|
416 |
+ found = 1; |
|
417 |
+ cli_dbgmsg("UPX: empty section found - assuming UPX compression\n"); |
|
418 |
+ break; |
|
419 |
+ } |
|
420 |
+ } |
|
421 |
+ |
|
422 |
+ if(found) { |
|
423 |
+ int ssize, dsize; |
|
424 |
+ char *src, *dest; |
|
425 |
+ |
|
426 |
+ strncpy(sname, section_hdr[i].Name, 8); |
|
427 |
+ sname[8] = 0; |
|
428 |
+ cli_dbgmsg("UPX: Section %d name: %s\n", i, sname); |
|
429 |
+ strncpy(sname, section_hdr[i + 1].Name, 8); |
|
430 |
+ sname[8] = 0; |
|
431 |
+ cli_dbgmsg("UPX: Section %d name: %s\n", i + 1, sname); |
|
432 |
+ |
|
433 |
+ if(strncmp(section_hdr[i].Name, "UPX0", 4) || strncmp(section_hdr[i + 1].Name, "UPX1", 4)) |
|
434 |
+ cli_dbgmsg("UPX: Possibly hacked UPX section headers\n"); |
|
435 |
+ |
|
436 |
+ /* we assume (i + 1) is UPX1 */ |
|
437 |
+ ssize = EC32(section_hdr[i + 1].SizeOfRawData); |
|
438 |
+ dsize = EC32(section_hdr[i].VirtualSize) + EC32(section_hdr[i + 1].VirtualSize); |
|
439 |
+ |
|
440 |
+ /* FIXME: use file operations in case of big files */ |
|
441 |
+ if((src = (char *) cli_malloc(ssize)) == NULL) { |
|
442 |
+ free(section_hdr); |
|
443 |
+ return CL_EMEM; |
|
415 | 444 |
} |
416 | 445 |
|
417 |
- if(found) { |
|
418 |
- uint32_t ssize, dsize; |
|
419 |
- char *src, *dest; |
|
446 |
+ if((dest = (char *) cli_calloc(dsize, sizeof(char))) == NULL) { |
|
447 |
+ free(section_hdr); |
|
448 |
+ free(src); |
|
449 |
+ return CL_EMEM; |
|
450 |
+ } |
|
420 | 451 |
|
421 |
- /* we assume (i + 1) is UPX1 */ |
|
422 |
- if(strncmp(section_hdr[i].Name, "UPX0", 4) || strncmp(section_hdr[i + 1].Name, "UPX1", 4)) |
|
423 |
- cli_dbgmsg("Possibly hacked UPX section headers\n"); |
|
452 |
+ lseek(desc, EC32(section_hdr[i + 1].PointerToRawData), SEEK_SET); |
|
453 |
+ if(read(desc, src, ssize) != ssize) { |
|
454 |
+ cli_dbgmsg("Can't read raw data of section %d\n", i + 1); |
|
455 |
+ free(section_hdr); |
|
456 |
+ free(src); |
|
457 |
+ free(dest); |
|
458 |
+ return CL_EIO; |
|
459 |
+ } |
|
424 | 460 |
|
425 |
- /* FIXME: use file operations in case of big files */ |
|
426 |
- ssize = section_hdr[i + 1].SizeOfRawData; |
|
427 |
- dsize = section_hdr[i].VirtualSize + section_hdr[i + 1].VirtualSize; |
|
428 |
- if((src = (char *) malloc(ssize)) == NULL) { |
|
429 |
- free(section_hdr); |
|
430 |
- return CL_EMEM; |
|
461 |
+ if(upxfn) { |
|
462 |
+ if(upxfn(src, ssize, dest, dsize)) { |
|
463 |
+ cli_dbgmsg("UPX: Prefered decompressor failed\n"); |
|
464 |
+ } else { |
|
465 |
+ upx_success = 1; |
|
466 |
+ cli_dbgmsg("UPX: Successfuly decompressed\n"); |
|
431 | 467 |
} |
468 |
+ } |
|
432 | 469 |
|
433 |
- if((dest = (char *) malloc(dsize)) == NULL) { |
|
434 |
- free(section_hdr); |
|
435 |
- free(src); |
|
436 |
- return CL_EMEM; |
|
470 |
+ if(!upx_success && upxfn != upx_inflate2b) { |
|
471 |
+ if(upx_inflate2b(src, ssize, dest, dsize)) { |
|
472 |
+ cli_dbgmsg("UPX: NRV2B decompressor failed\n"); |
|
473 |
+ } else { |
|
474 |
+ upx_success = 1; |
|
475 |
+ cli_dbgmsg("UPX: Successfuly decompressed with NRV2B\n"); |
|
437 | 476 |
} |
477 |
+ } |
|
438 | 478 |
|
439 |
- lseek(desc, section_hdr[i + 1].PointerToRawData, SEEK_SET); |
|
440 |
- if(read(desc, src, ssize) != ssize) { |
|
441 |
- cli_dbgmsg("Can't read raw data of section %d\n", i + 1); |
|
442 |
- free(section_hdr); |
|
443 |
- free(src); |
|
444 |
- free(dest); |
|
445 |
- return CL_EMEM; |
|
479 |
+ if(!upx_success && upxfn != upx_inflate2d) { |
|
480 |
+ if(upx_inflate2d(src, ssize, dest, dsize)) { |
|
481 |
+ cli_dbgmsg("UPX: NRV2D decompressor failed\n"); |
|
482 |
+ } else { |
|
483 |
+ upx_success = 1; |
|
484 |
+ cli_dbgmsg("UPX: Successfuly decompressed with NRV2D\n"); |
|
446 | 485 |
} |
486 |
+ } |
|
447 | 487 |
|
448 |
- if(upxfn(src, ssize, dest, dsize)) { |
|
449 |
- cli_dbgmsg("UPX decompression failed\n"); |
|
488 |
+ if(!upx_success && upxfn != upx_inflate2e) { |
|
489 |
+ if(upx_inflate2e(src, ssize, dest, dsize)) { |
|
490 |
+ cli_dbgmsg("UPX: NRV2E decompressor failed\n"); |
|
450 | 491 |
} else { |
451 |
- int ndesc; |
|
492 |
+ upx_success = 1; |
|
493 |
+ cli_dbgmsg("UPX: Successfuly decompressed with NRV2E\n"); |
|
494 |
+ } |
|
495 |
+ } |
|
452 | 496 |
|
453 |
- tempfile = cl_gentemp(NULL); |
|
497 |
+ if(!upx_success) { |
|
498 |
+ cli_dbgmsg("UPX: All decompressors failed\n"); |
|
499 |
+ } else { |
|
500 |
+ int ndesc; |
|
454 | 501 |
|
502 |
+ if(cli_leavetemps_flag) { |
|
503 |
+ tempfile = cl_gentemp(NULL); |
|
455 | 504 |
if((ndesc = open(tempfile, O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU)) < 0) { |
456 |
- cli_dbgmsg("Can't create file %s\n", tempfile); |
|
505 |
+ cli_dbgmsg("UPX: Can't create file %s\n", tempfile); |
|
457 | 506 |
free(section_hdr); |
458 | 507 |
free(src); |
459 | 508 |
free(dest); |
... | ... |
@@ -469,19 +559,20 @@ int cli_scanpe(int desc, const char **virname, long int *scanned, const struct c |
469 | 469 |
} |
470 | 470 |
|
471 | 471 |
close(ndesc); |
472 |
- |
|
473 |
- /* TODO: scan and unlink file */ |
|
474 |
- |
|
475 |
- /* unlink(tempfile); */ |
|
472 |
+ cli_dbgmsg("UPX: Decompressed data saved in %s\n", tempfile); |
|
476 | 473 |
free(tempfile); |
477 | 474 |
} |
478 | 475 |
|
479 |
- free(src); |
|
480 |
- free(dest); |
|
481 |
- |
|
482 |
- } else { |
|
483 |
- cli_dbgmsg("UPX sections not found\n"); |
|
476 |
+ if(cl_scanbuff(dest, dsize, virname, root) == CL_VIRUS) { |
|
477 |
+ free(section_hdr); |
|
478 |
+ free(src); |
|
479 |
+ free(dest); |
|
480 |
+ return CL_VIRUS; |
|
481 |
+ } |
|
484 | 482 |
} |
483 |
+ |
|
484 |
+ free(src); |
|
485 |
+ free(dest); |
|
485 | 486 |
} |
486 | 487 |
|
487 | 488 |
/* to be continued ... */ |
... | ... |
@@ -21,6 +21,6 @@ |
21 | 21 |
|
22 | 22 |
#include "clamav.h" |
23 | 23 |
|
24 |
-int cli_scanpe(int desc, const char **virname, long int *scanned, const struct cl_node *root, const struct cl_limits *limits, int options, int *reclev); |
|
24 |
+int cli_scanpe(int desc, const char **virname, long int *scanned, const struct cl_node *root, const struct cl_limits *limits, int options, int *arec, int *mrec); |
|
25 | 25 |
|
26 | 26 |
#endif |
... | ... |
@@ -54,6 +54,7 @@ extern short cli_leavetemps_flag; |
54 | 54 |
#include "ole2_extract.h" |
55 | 55 |
#include "vba_extract.h" |
56 | 56 |
#include "msexpand.h" |
57 |
+#include "pe.h" |
|
57 | 58 |
#include "filetypes.h" |
58 | 59 |
#include "htmlnorm.h" |
59 | 60 |
|
... | ... |
@@ -70,6 +71,7 @@ extern short cli_leavetemps_flag; |
70 | 70 |
#define SCAN_MAIL (options & CL_MAIL) |
71 | 71 |
#define SCAN_OLE2 (options & CL_OLE2) |
72 | 72 |
#define SCAN_HTML (options & CL_HTML) |
73 |
+#define SCAN_PE (options & CL_PE) |
|
73 | 74 |
#define DISABLE_RAR (options & CL_DISABLERAR) |
74 | 75 |
#define DETECT_ENCRYPTED (options & CL_ENCRYPTED) |
75 | 76 |
|
... | ... |
@@ -1048,8 +1050,8 @@ static int cli_magic_scandesc(int desc, const char **virname, long int *scanned, |
1048 | 1048 |
|
1049 | 1049 |
switch(type) { |
1050 | 1050 |
case CL_DOSEXE: |
1051 |
- /* temporarily the return code is ignored */ |
|
1052 |
- cli_scanpe(desc, virname, scanned, root, limits, options, arec, mrec); |
|
1051 |
+ if(SCAN_PE) |
|
1052 |
+ ret = cli_scanpe(desc, virname, scanned, root, limits, options, arec, mrec); |
|
1053 | 1053 |
break; |
1054 | 1054 |
|
1055 | 1055 |
case CL_RARFILE: |
... | ... |
@@ -1150,6 +1152,9 @@ static int cli_scanfile(const char *filename, const char **virname, unsigned lon |
1150 | 1150 |
{ |
1151 | 1151 |
int fd, ret; |
1152 | 1152 |
|
1153 |
+ |
|
1154 |
+ cli_dbgmsg("Scanning %s\n", filename); |
|
1155 |
+ |
|
1153 | 1156 |
/* internal version of cl_scanfile with arec/mrec preserved */ |
1154 | 1157 |
if((fd = open(filename, O_RDONLY)) == -1) |
1155 | 1158 |
return CL_EOPEN; |
... | ... |
@@ -1165,6 +1170,8 @@ int cl_scanfile(const char *filename, const char **virname, unsigned long int *s |
1165 | 1165 |
int fd, ret; |
1166 | 1166 |
|
1167 | 1167 |
|
1168 |
+ cli_dbgmsg("Scanning %s\n", filename); |
|
1169 |
+ |
|
1168 | 1170 |
if((fd = open(filename, O_RDONLY)) == -1) |
1169 | 1171 |
return CL_EOPEN; |
1170 | 1172 |
|
... | ... |
@@ -36,6 +36,9 @@ |
36 | 36 |
** bloatness. My gratitude to whoever wrote it. |
37 | 37 |
*/ |
38 | 38 |
|
39 |
+#if HAVE_CONFIG_H |
|
40 |
+#include "clamav-config.h" |
|
41 |
+#endif |
|
39 | 42 |
|
40 | 43 |
#include <stdio.h> |
41 | 44 |
#include <stdlib.h> |
... | ... |
@@ -44,17 +47,33 @@ |
44 | 44 |
#include <unistd.h> |
45 | 45 |
#include <string.h> |
46 | 46 |
|
47 |
+#include "cltypes.h" |
|
48 |
+ |
|
47 | 49 |
/* [doubleebx] */ |
48 | 50 |
|
49 |
-static int doubleebx(char *src, int *myebx, int *scur, int ssize) |
|
51 |
+static int doubleebx(char *src, int32_t *myebx, int *scur, int ssize) |
|
50 | 52 |
{ |
51 |
- int oldebx = *myebx; |
|
53 |
+ int32_t oldebx = *myebx; |
|
54 |
+#if WORDS_BIGENDIAN == 1 |
|
55 |
+ char *pt; |
|
56 |
+ int32_t shift, i = 0; |
|
57 |
+#endif |
|
52 | 58 |
|
53 | 59 |
*myebx*=2; |
54 | 60 |
if ( !(oldebx & 0x7fffffff)) { |
55 | 61 |
if (*scur<0 || ssize-*scur<4) |
56 | 62 |
return 0; |
63 |
+#if WORDS_BIGENDIAN == 0 |
|
57 | 64 |
oldebx = *(int*)(src+*scur); |
65 |
+#else |
|
66 |
+ oldebx = 0; |
|
67 |
+ pt = src + *scur; |
|
68 |
+ for(shift = 0; shift < 32; shift += 8) { |
|
69 |
+ oldebx |= (pt[i] & 0xff ) << shift; |
|
70 |
+ i++; |
|
71 |
+ } |
|
72 |
+#endif |
|
73 |
+ |
|
58 | 74 |
*myebx = oldebx*2+1; |
59 | 75 |
*scur+=4; |
60 | 76 |
} |
... | ... |
@@ -65,9 +84,8 @@ static int doubleebx(char *src, int *myebx, int *scur, int ssize) |
65 | 65 |
|
66 | 66 |
int upx_inflate2b(char *src, int ssize, char *dst, int dsize) |
67 | 67 |
{ |
68 |
- int backbytes, backsize, unp_offset = -1, i; |
|
69 |
- int myebx = 0; |
|
70 |
- int scur=0, dcur=0; |
|
68 |
+ int32_t backbytes, unp_offset = -1, myebx = 0; |
|
69 |
+ int scur=0, dcur=0, i, backsize; |
|
71 | 70 |
|
72 | 71 |
while (1) { |
73 | 72 |
while (doubleebx(src, &myebx, &scur, ssize)) { |
... | ... |
@@ -114,6 +132,7 @@ int upx_inflate2b(char *src, int ssize, char *dst, int dsize) |
114 | 114 |
backsize++; |
115 | 115 |
|
116 | 116 |
backsize++; |
117 |
+ |
|
117 | 118 |
for (i = 0; i < backsize; i++) { |
118 | 119 |
if (dcur+i<0 || dcur+i>=dsize || dcur+unp_offset+i<0 || dcur+unp_offset+i>=dsize) |
119 | 120 |
return -1; |
... | ... |
@@ -126,9 +145,8 @@ int upx_inflate2b(char *src, int ssize, char *dst, int dsize) |
126 | 126 |
|
127 | 127 |
int upx_inflate2d(char *src, int ssize, char *dst, int dsize) |
128 | 128 |
{ |
129 |
- int backbytes, backsize, unp_offset = -1, i; |
|
130 |
- int myebx = 0; |
|
131 |
- int scur=0, dcur=0; |
|
129 |
+ int32_t backbytes, unp_offset = -1, myebx = 0; |
|
130 |
+ int scur=0, dcur=0, i, backsize; |
|
132 | 131 |
|
133 | 132 |
while (1) { |
134 | 133 |
while (doubleebx(src, &myebx, &scur, ssize)) { |
... | ... |
@@ -192,9 +210,8 @@ int upx_inflate2d(char *src, int ssize, char *dst, int dsize) |
192 | 192 |
|
193 | 193 |
int upx_inflate2e(char *src, int ssize, char *dst, int dsize) |
194 | 194 |
{ |
195 |
- int backbytes, backsize, unp_offset = -1, i; |
|
196 |
- int myebx = 0; |
|
197 |
- int scur=0, dcur=0; |
|
195 |
+ int32_t backbytes, unp_offset = -1, myebx = 0; |
|
196 |
+ int scur=0, dcur=0, i, backsize; |
|
198 | 197 |
|
199 | 198 |
while (1) { |
200 | 199 |
while (doubleebx(src, &myebx, &scur, ssize)) { |
... | ... |
@@ -62,6 +62,7 @@ struct cfgstruct *parsecfg(const char *cfgfile, int messages) |
62 | 62 |
{"PidFile", OPT_STR}, |
63 | 63 |
{"TemporaryDirectory", OPT_STR}, |
64 | 64 |
{"MaxFileSize", OPT_COMPSIZE}, |
65 |
+ {"ScanPE", OPT_NOARG}, |
|
65 | 66 |
{"ScanMail", OPT_NOARG}, |
66 | 67 |
{"ScanHTML", OPT_NOARG}, |
67 | 68 |
{"ScanOLE2", OPT_NOARG}, |