1 /*
2  * image.c:
3  * Attempt to find GIF/JPEG/PNG data embedded in buffers.
4  *
5  * Copyright (c) 2001 Chris Lightfoot.
6  * Email: chris@ex-parrot.com; WWW: http://www.ex-parrot.com/~chris/
7  *
8  */
9 
10 #include "compat/compat.h"
11 
12 #include <stdio.h>
13 #include <stdlib.h> /* On many systems (Darwin...), stdio.h is a prerequisite. */
14 #include <string.h>
15 #include <sys/types.h>
16 
17 #include <netinet/in.h> /* ntohl */
18 
19 #include "common/util.h"
20 
21 #include "pngformat.h"
22 
23 /* If we run out of space, put us back to the last candidate GIF header. */
24 #define spaceleft       if (block >= data + len) return gifhdr /* > ?? */
25 
find_gif_image(const unsigned char * data,const size_t len,unsigned char ** gifdata,size_t * giflen)26 unsigned char *find_gif_image(const unsigned char *data, const size_t len, unsigned char **gifdata, size_t *giflen)
27 {
28     unsigned char *gifhdr, *block;
29     int ncolours;
30 
31     *gifdata = NULL;
32 
33     if (len < 6) return (unsigned char*)data;
34 
35     gifhdr = memstr(data, len, (unsigned char*)"GIF89a", 6);
36     if (!gifhdr) gifhdr = memstr(data, len, (unsigned char*)"GIF87a", 6);
37     if (!gifhdr) return (unsigned char*)(data + len - 6);
38     if (data + len - gifhdr < 14) return gifhdr; /* no space for header */
39 
40     ncolours = (1 << ((gifhdr[10] & 0x7) + 1));
41     /* printf("gif header %d colours\n", ncolours); */
42     block = gifhdr + 13;
43     spaceleft;
44     if (gifhdr[10] & 0x80) block += 3 * ncolours; /* global colour table */
45     spaceleft;
46 
47     do {
48          /* printf("gifhdr = %p block = %p off = %u %02x\n", gifhdr, block, block - gifhdr, (unsigned int)*block); */
49         switch (*block) {
50             case 0x2c:
51                 /* image block */
52                 /* printf("image data\n"); */
53                 if (block + 9 > data + len) return gifhdr;
54                 if (block[9] & 0x80) {
55                     /* local colour table */
56                     block += 3 * ((1 << ((gifhdr[9] & 0x7) + 1)));
57                     spaceleft;
58                 }
59                 block += 10;
60                 ++block;        /* lzw code size */
61                 do {
62                     spaceleft;
63                     block += *block + 1;
64                     spaceleft;
65                 } while (*block);
66                 ++block;
67                 spaceleft;
68                 /*gotimgblock = 1;*/
69 
70                 break;
71 
72             case 0x21:
73                 /* extension */
74                 ++block;
75                 spaceleft;
76                 if (*block == 0xf9) {
77                     /* graphic control */
78                     /* printf("graphic control\n"); */
79                     ++block;
80                     spaceleft;
81                     block += *block + 2;
82                     spaceleft;
83                     break;
84                 } else if (*block == 0xfe) {
85                     /* comment */
86                     /* printf("comment\n"); */
87                     ++block;
88                     do {
89                         spaceleft;
90                         block += *block + 1;
91                         spaceleft;
92                     } while (*block);
93                     ++block;
94                     spaceleft;
95                 } else if (*block == 0x01) {
96                     /* text label */
97                     /* printf("text label\n"); */
98                     ++block;
99                     spaceleft;
100                     if (*block != 12) return gifhdr + 6;
101                     block += 13;
102                     do {
103                         spaceleft;
104                         block += *block + 1;
105                         spaceleft;
106                     } while (*block);
107                     ++block;
108                     spaceleft;
109                 } else if (*block == 0xff) {
110                     /* printf("application extension\n"); */
111                     ++block;
112                     spaceleft;
113                     if (*block != 11) return gifhdr + 6;
114                     block += 12;
115                     do {
116                         spaceleft;
117                         /* printf("app extension data %d bytes\n", (int)*block); */
118                         block += *block + 1;
119                         spaceleft;
120                     } while (*block);
121                     ++block;
122                     spaceleft;
123                 } else {
124                     /* printf("unknown extension block\n"); */
125                     return gifhdr + 6;
126                 }
127                 break;
128 
129             case 0x3b:
130                 /* end of file block: we win. */
131                 /* printf("gif data from %p to %p\n", gifhdr, block); */
132                 *gifdata = gifhdr;
133                 *giflen = block - gifhdr + 1;
134                 return block + 1;
135                 break;
136 
137             default:
138                 /* printf("unknown block %02x\n", *block); */
139                 return gifhdr + 6;
140         }
141     } while (1);
142 }
143 
144 enum jpeg_marker {
145     SOI = 0xD8,
146     DHT = 0xC4,
147     DAC = 0xCC,
148     DQT = 0xDB,
149     DRI = 0xDD,
150     DHP = 0xDE,
151     EXP = 0xDF,
152     SOS = 0xDA,
153     COM = 0xFE,
154     EOI = 0xD9,
155 
156     RST0 = 0xD0,
157     RST1 = 0xD1,
158     RST2 = 0xD2,
159     RST3 = 0xD3,
160     RST4 = 0xD4,
161     RST5 = 0xD5,
162     RST6 = 0xD6,
163     RST7 = 0xD7,
164 
165     SOF0 = 0xC0, SOF1 = 0xC1, SOF2 = 0xC2, SOF3 = 0xC3, SOF5 = 0xC5, SOF6 = 0xC6, SOF7 = 0xC7,
166     SOF8 = 0xC8, SOF9 = 0xC9, SOFA = 0xCA, SOFB = 0xCB, SOFD = 0xCD, SOFE = 0xCE, SOFF = 0xCF,
167 
168     APP0 = 0xE0, APP1 = 0xE1, APP2 = 0xE2, APP3 = 0xE3, APP4 = 0xE4, APP5 = 0xE5, APP6 = 0xE6, APP7 = 0xE7,
169     APP8 = 0xE8, APP9 = 0xE9, APPA = 0xEA, APPB = 0xEB, APPC = 0xEC, APPD = 0xED, APPE = 0xEE, APPF = 0xEF,
170 
171     JPG0 = 0xF0, JPG1 = 0xF1, JPG2 = 0xF2, JPG3 = 0xF3, JPG4 = 0xF4, JPG5 = 0xF5, JPG6 = 0xF6,
172     JPG7 = 0xF7, JPG8 = 0xF8, JPG9 = 0xF9, JPGA = 0xFA, JPGB = 0xFB, JPGC = 0xFC, JPGD = 0xFD
173 };
174 
175 typedef struct {
176     unsigned char start_of_marker;     /* always 0xFF */
177     unsigned char marker_type;
178     unsigned char length[2];
179 } jpg_segment_t;
180 
181 /* If we run out of space, put us back to the last candidate JPEG header. */
182 #define jpegcount(c)    ((*(c) << 8) | *((c) + 1))
183 
is_jpeg_segment(jpg_segment_t * segment)184 unsigned int is_jpeg_segment(jpg_segment_t* segment)
185 {
186     if (segment->start_of_marker != 0xFF) {
187         return 0;
188     }
189 
190     /*
191      * Check for markers
192      */
193     switch (segment->marker_type) {
194 
195         /* without payload */
196         case SOI:
197         case EOI:
198         case RST0:
199         case RST1:
200         case RST2:
201         case RST3:
202         case RST4:
203         case RST5:
204         case RST6:
205         case RST7:
206         case DHP:
207         case EXP:
208         case DAC:
209             return 4;
210 
211         /* constant payload */
212         case DRI:
213             return 4 + 4;
214 
215         /* variable payload */
216         case DHT:
217         case DQT:
218         case SOS:
219         case COM:
220 
221         case SOF0:case SOF1:case SOF2:case SOF3:case SOF5:case SOF6:case SOF7:
222         case SOF8:case SOF9:case SOFA:case SOFB:case SOFD:case SOFE:case SOFF:
223 
224         case APP0:case APP1:case APP2:case APP3:case APP4:case APP5:case APP6:case APP7:
225         case APP8:case APP9:case APPA:case APPB:case APPC:case APPD:case APPE:case APPF:
226 
227         case JPG0:case JPG1:case JPG2:case JPG3:case JPG4:case JPG5:case JPG6:
228         case JPG7:case JPG8:case JPG9:case JPGA:case JPGB:case JPGC:case JPGD:
229 
230             return (((segment->length[0] << 8) & 0xFF00) | segment->length[1]) + 2;
231     }
232 
233     return 0;
234 }
235 
find_jpeg_image(const unsigned char * data,const size_t len,unsigned char ** jpegdata,size_t * jpeglen)236 unsigned char *find_jpeg_image(const unsigned char *data, const size_t len, unsigned char **jpegdata, size_t *jpeglen)
237 {
238     unsigned char *jpeghdr;
239 
240     *jpegdata = NULL;
241     *jpeglen = 0;
242 
243     if (data == NULL) {
244         return NULL;
245     }
246 
247     /* find SOI marker */
248     jpeghdr = memstr(data, len, (unsigned char*)"\xff\xd8", 2);
249     if (!jpeghdr) return (unsigned char*)(data + len - 1);
250 
251     jpg_segment_t* segment;
252     unsigned int segment_lenght = 2;
253     unsigned char *block = jpeghdr;
254 
255     do {
256         /* more data to advance ? */
257         if (data + len < block + segment_lenght) {
258             break;
259         }
260 
261         /* advance to next block */
262         block = block + segment_lenght;
263         segment = (jpg_segment_t*) block;
264         segment_lenght = is_jpeg_segment(segment);
265 
266         /*
267          * start of scan
268          *
269          * XXX: dunno how to parse...
270          */
271         if (segment->marker_type == SOS) {
272             block = memstr(block, len - (block - data), (unsigned char*)"\xff\xd9", 2);
273 
274             if (block) {
275                 *jpegdata = jpeghdr;
276                 *jpeglen = block + 2 - jpeghdr;
277                 return block + 2;
278             } else break;
279         }
280 
281     } while (segment_lenght != 0);
282 
283     return jpeghdr;
284 }
285 
286 /* find_png_eoi BUFFER LEN
287  * Returns the first position in BUFFER of LEN bytes after the end of the image
288  * or NULL if end of image not found. */
find_png_eoi(unsigned char * buffer,const size_t len)289 unsigned char *find_png_eoi(unsigned char *buffer, const size_t len) {
290     unsigned char *end_data, *data, chunk_code[PNG_CODE_LEN + 1];
291     struct png_chunk chunk;
292     u_int32_t datalen;
293 
294     /* Move past the PNG header */
295     data = (buffer + PNG_SIG_LEN);
296     end_data = (buffer + len - (sizeof(struct png_chunk) + PNG_CRC_LEN));
297 
298     while (data <= end_data) {
299         memcpy(&chunk, data, sizeof chunk);
300 /*        chunk = (struct png_chunk *)data; */ /* can't do that. */
301         memset(chunk_code, '\0', PNG_CODE_LEN + 1);
302         memcpy(chunk_code, chunk.code, PNG_CODE_LEN);
303 
304         datalen = ntohl(chunk.datalen);
305 
306         if (!strncasecmp((char*)chunk_code, "iend", PNG_CODE_LEN))
307             return (unsigned char *)(data + sizeof(struct png_chunk) + PNG_CRC_LEN);
308 
309         /* Would this push us off the end of the buffer? */
310         if (datalen > (len - (data - buffer)))
311             return NULL;
312 
313         data += (sizeof(struct png_chunk) + datalen + PNG_CRC_LEN);
314     }
315 
316     return NULL;
317 }
318 
319 /* find_png_image DATA LEN PNGDATA PNGLEN
320  * Look for PNG images in LEN bytes buffer DATA. */
find_png_image(const unsigned char * data,const size_t len,unsigned char ** pngdata,size_t * pnglen)321 unsigned char *find_png_image(const unsigned char *data, const size_t len, unsigned char **pngdata, size_t *pnglen) {
322     unsigned char *pnghdr, *data_end, *png_eoi;
323 
324     *pngdata = NULL;
325 
326     if (len < PNG_SIG_LEN)
327        return (unsigned char*)data;
328 
329     pnghdr = memstr(data, len, (unsigned char*)"\x89\x50\x4e\x47\x0d\x0a\x1a\x0a", PNG_SIG_LEN);
330     if (!pnghdr)
331         return (unsigned char*)(data + len - PNG_SIG_LEN);
332 
333     data_end = (unsigned char *)(data + len);
334 
335     if ((png_eoi = find_png_eoi(pnghdr, (data_end - pnghdr))) == NULL)
336         return pnghdr;
337 
338     *pngdata = pnghdr;
339     *pnglen = (png_eoi - pnghdr);
340     return png_eoi;
341 }
342 
343 
344 #if 0
345 #include <unistd.h>
346 #include <fcntl.h>
347 
348 int main(int argc, char **argv) {
349     unsigned char buf[262144];
350     char **a;
351     for (a = argv + 1; *a; ++a) {
352         unsigned char *p, *img;
353         size_t len;
354         int fd = open(*a, O_RDONLY);
355         read(fd, buf + rand() % 256, 261000);
356         /* printf("jpeg file %s\n", *a); */
357         p = buf;
358         do {
359             /* printf("--> now p = %p\n", p); */
360             p = find_jpeg_image(p, 262144 - (p - buf), &img, &len);
361             if (img) /* printf("   found image %p len %u\n", img, len); */
362         } while (p);
363     }
364 }
365 #endif
366