diff --git a/configure.ac b/configure.ac index 3b52bf4..56479cb 100644 --- a/configure.ac +++ b/configure.ac @@ -18,6 +18,19 @@ AC_C_CONST AC_C_INLINE AC_TYPE_SIZE_T +# Use OpenCV? +ac_cv_my_have_opencv="no" +save_CPPFLAGS="${CPPFLAGS}" +AC_PATH_PROG(OPENCV_CONFIG, opencv-config, no) +if test "${OPENCV_CONFIG}" != "no"; then + CPPFLAGS="${CPPFLAGS} `opencv-config --cflags`" +fi +AC_CHECK_HEADERS(cv.h, + [ac_cv_my_have_opencv="yes"], + [ac_cv_my_have_opencv="no"]) +CPPFLAGS="${save_CPPFLAGS}" +AM_CONDITIONAL(USE_OPENCV, test "${ac_cv_my_have_opencv}" = "yes") + # Use Imlib2? ac_cv_my_have_imlib2="no" save_CPPFLAGS="${CPPFLAGS}" @@ -31,19 +44,6 @@ AC_CHECK_HEADERS(Imlib2.h, CPPFLAGS="${save_CPPFLAGS}" AM_CONDITIONAL(USE_IMLIB2, test "${ac_cv_my_have_imlib2}" = "yes") -# Use Imlib2? -ac_cv_my_have_opencv="no" -save_CPPFLAGS="${CPPFLAGS}" -AC_PATH_PROG(OPENCV_CONFIG, opencv-config, no) -if test "${OPENCV_CONFIG}" != "no"; then - CPPFLAGS="${CPPFLAGS} `opencv-config --cflags`" -fi -AC_CHECK_HEADERS(cv.h, - [ac_cv_my_have_opencv="yes"], - [ac_cv_my_have_opencv="no"]) -CPPFLAGS="${save_CPPFLAGS}" -AM_CONDITIONAL(USE_OPENCV, test "${ac_cv_my_have_opencv}" = "yes") - if test "${ac_cv_my_have_imlib2}" = "no" -a "${ac_cv_my_have_opencv}" = "no"; then AC_MSG_ERROR([[cannot find Imlib2 or OpenCV, please install one of them]]) fi diff --git a/share/font_phpbb.png b/share/font_phpbb.png new file mode 100644 index 0000000..3544a6b Binary files /dev/null and b/share/font_phpbb.png differ diff --git a/src/Makefile.am b/src/Makefile.am index 6a098f0..5dbe48a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -3,16 +3,16 @@ NULL = bin_PROGRAMS = pwntcha pwntcha_CFLAGS = $(ADDITIONAL_CFLAGS) -Wall -O6 pwntcha_LDFLAGS = $(ADDITIONAL_LDFLAGS) -pwntcha_SOURCES = main.c image.c filters.c slashdot.c common.h +pwntcha_SOURCES = main.c image.c filters.c common.h slashdot.c phpbb.c test.c -if USE_IMLIB2 -ADDITIONAL_CFLAGS = `imlib2-config --cflags` -DX_DISPLAY_MISSING=1 -ADDITIONAL_LDFLAGS = `imlib2-config --libs` -else if USE_OPENCV ADDITIONAL_CFLAGS = `opencv-config --cflags` ADDITIONAL_LDFLAGS = `opencv-config --libs opencv highgui` else +if USE_IMLIB2 +ADDITIONAL_CFLAGS = `imlib2-config --cflags` -DX_DISPLAY_MISSING=1 +ADDITIONAL_LDFLAGS = `imlib2-config --libs` +else ADDITIONAL_CFLAGS = ADDITIONAL_LDFLAGS = endif diff --git a/src/common.h b/src/common.h index a980ba2..8abea54 100644 --- a/src/common.h +++ b/src/common.h @@ -18,11 +18,15 @@ struct image }; /* available CAPTCHA decoders */ -char * decode_slashdot(struct image *img); +char *decode_phpbb(struct image *img); +char *decode_slashdot(struct image *img); +char *decode_test(struct image *img); /* image operations */ -struct image * image_load(char *name); -struct image * image_new(int width, int height); +struct image *image_load(char *name); +struct image *image_new(int width, int height); +void image_free(struct image *img); +void image_display(struct image *img); int getgray(struct image *img, int x, int y, int *g); int getpixel(struct image *img, int x, int y, int *r, int *g, int *b); int setpixel(struct image *img, int x, int y, int r, int g, int b); @@ -31,8 +35,9 @@ int setpixel(struct image *img, int x, int y, int r, int g, int b); void filter_flood_fill(struct image *img, int x, int y, int r, int g, int b); struct image *filter_fill_holes(struct image *img); struct image *filter_detect_lines(struct image *img); -struct image *filter_equalize(struct image *img); +struct image *filter_equalize(struct image *img, int threshold); struct image *filter_trick(struct image *img); struct image *filter_smooth(struct image *img); struct image *filter_median(struct image *img); +struct image *filter_contrast(struct image *img); diff --git a/src/filters.c b/src/filters.c index c3aedf0..44501d3 100644 --- a/src/filters.c +++ b/src/filters.c @@ -142,7 +142,7 @@ struct image *filter_detect_lines(struct image *img) return dst; } -struct image *filter_equalize(struct image *img) +struct image *filter_equalize(struct image *img, int threshold) { struct image *dst; int x, y; @@ -154,7 +154,7 @@ struct image *filter_equalize(struct image *img) for(x = 0; x < img->width; x++) { getpixel(img, x, y, &r, &g, &b); - if(r < 200) r = 50; else r = 200; + if(r < threshold) r = 0; else r = 255; setpixel(dst, x, y, r, r, r); } @@ -240,7 +240,7 @@ struct image *filter_smooth(struct image *img) struct image *filter_median(struct image *img) { -#define MSIZE 4 +#define MSIZE 3 struct image *dst; int x, y, i, j, val[MSIZE*MSIZE]; int r, g, b; @@ -278,3 +278,36 @@ struct image *filter_median(struct image *img) return dst; } +struct image *filter_contrast(struct image *img) +{ + struct image *dst; + int histo[256]; + int x, y, i, min = 255, max = 0; + int r, g, b; + + dst = image_new(img->width, img->height); + + for(y = 0; y < img->height; y++) + for(x = 0; x < img->width; x++) + { + getgray(img, x, y, &r); + if(r < min) min = r; + if(r > max) max = r; + } + + if(min == max) + histo[min] = 127; + else + for(i = min; i < max; i++) + histo[i] = (i - min) * 255 / (max - min); + + for(y = 0; y < img->height; y++) + for(x = 0; x < img->width; x++) + { + getgray(img, x, y, &r); + setpixel(dst, x, y, histo[r], histo[r], histo[r]); + } + + return dst; +} + diff --git a/src/image.c b/src/image.c index f580eec..f9d47ac 100644 --- a/src/image.c +++ b/src/image.c @@ -15,79 +15,93 @@ #include "config.h" #include "common.h" -#if defined(HAVE_IMLIB2_H) -# include -#elif defined(HAVE_CV_H) +#if defined(HAVE_CV_H) # include # include +#elif defined(HAVE_IMLIB2_H) +# include #else # error "No imaging library" #endif -struct image * image_load(char *name) +struct image *image_load(char *name) { - struct image * img; -#if defined(HAVE_IMLIB2_H) + struct image *img; +#if defined(HAVE_CV_H) + IplImage *priv = cvLoadImage(name, -1); +#elif defined(HAVE_IMLIB2_H) Imlib_Image priv = imlib_load_image(name); -#elif defined(HAVE_CV_H) - IplImage * priv = cvLoadImage(name, -1); #endif if(!priv) return NULL; img = malloc(sizeof(struct image)); -#if defined(HAVE_IMLIB2_H) +#if defined(HAVE_CV_H) + img->width = priv->width; + img->height = priv->height; + img->pitch = priv->widthStep; + img->channels = priv->nChannels; + img->pixels = priv->imageData; +#elif defined(HAVE_IMLIB2_H) imlib_context_set_image(priv); img->width = imlib_image_get_width(); img->height = imlib_image_get_height(); img->pitch = 4 * imlib_image_get_width(); img->channels = 4; img->pixels = (char *)imlib_image_get_data(); -#elif defined(HAVE_CV_H) - img->width = priv->width; - img->height = priv->height; - img->pitch = priv->widthStep; - img->channels = priv->nChannels; - img->pixels = priv->imageData; #endif img->priv = (void *)priv; return img; } -struct image * image_new(int width, int height) +struct image *image_new(int width, int height) { - struct image * img; -#if defined(HAVE_IMLIB2_H) + struct image *img; +#if defined(HAVE_CV_H) + IplImage *priv = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 3); +#elif defined(HAVE_IMLIB2_H) Imlib_Image priv = imlib_create_image(width, height); -#elif defined(HAVE_CV_H) - IplImage * priv = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 3); #endif if(!priv) return NULL; img = malloc(sizeof(struct image)); -#if defined(HAVE_IMLIB2_H) +#if defined(HAVE_CV_H) + img->width = priv->width; + img->height = priv->height; + img->pitch = priv->widthStep; + img->channels = priv->nChannels; + img->pixels = priv->imageData; +#elif defined(HAVE_IMLIB2_H) imlib_context_set_image(priv); img->width = imlib_image_get_width(); img->height = imlib_image_get_height(); img->pitch = 4 * imlib_image_get_width(); img->channels = 4; img->pixels = (char *)imlib_image_get_data(); -#elif defined(HAVE_CV_H) - img->width = priv->width; - img->height = priv->height; - img->pitch = priv->widthStep; - img->channels = priv->nChannels; - img->pixels = priv->imageData; #endif img->priv = (void *)priv; return img; } +void image_free(struct image *img) +{ +#if defined(HAVE_CV_H) + IplImage *iplimg; + iplimg = (IplImage *)img->priv; + cvReleaseImage(&iplimg); +#elif defined(HAVE_IMLIB2_H) + imlib_context_set_image(img->priv); + imlib_free_image(); +#endif + + free(img); +} + int getgray(struct image *img, int x, int y, int *g) { if(x < 0 || y < 0 || x >= img->width || y >= img->height) @@ -132,19 +146,21 @@ int setpixel(struct image *img, int x, int y, int r, int g, int b) void image_display(struct image *img) { -#if defined(HAVE_IMLIB2_H) +#if defined(HAVE_CV_H) + char name[BUFSIZ]; + sprintf(name, "Image %p (%i x %i)", img, img->width, img->height); + cvNamedWindow(name, 0); + cvShowImage(name, img->priv); + cvResizeWindow(name, img->width * 2, img->height * 2 + 50); + while((unsigned char)cvWaitKey(0) != 0x1b) + ; +#elif defined(HAVE_IMLIB2_H) //char name[BUFSIZ]; //static int i = 0; //sprintf(name, "image%i-%ix%i.png", i++, img->width, img->height); //imlib_context_set_image(img->priv); //imlib_save_image(name); //fprintf(stderr, "saved to %s\n", name); -#elif defined(HAVE_CV_H) - char name[BUFSIZ]; - sprintf(name, "Image %p (%i x %i)", img, img->width, img->height); - cvNamedWindow(name, 0); - cvShowImage(name, img->priv); - cvResizeWindow(name, 320, 120); #endif } diff --git a/src/main.c b/src/main.c index 6c2449d..7a317a8 100644 --- a/src/main.c +++ b/src/main.c @@ -11,6 +11,7 @@ #include #include +#include #include #include "config.h" @@ -87,7 +88,29 @@ int main(int argc, char *argv[]) continue; } - result = decode_slashdot(img); + if(!strcmp(mode, "test")) + result = decode_test(img); + else if(!strcmp(mode, "phpbb")) + result = decode_phpbb(img); + else if(!strcmp(mode, "slashdot")) + result = decode_slashdot(img); + else + { + if(img->width == 320 && img->height == 50) + result = decode_phpbb(img); + else if(img->height == 69) + result = decode_slashdot(img); + else + { + fprintf(stderr, "%s: could not guess CAPTCHA type\n", argv[0]); + printf("\n"); + image_free(img); + continue; + } + } + + image_free(img); + if(!result) { fprintf(stderr, "%s: sorry, decoding failed\n", argv[0]); diff --git a/src/phpbb.c b/src/phpbb.c new file mode 100644 index 0000000..6ae69e4 --- /dev/null +++ b/src/phpbb.c @@ -0,0 +1,136 @@ +/* + * phpbb.c: decode phpBB captchas + * $Id$ + * + * Copyright: (c) 2005 Sam Hocevar + * This program is free software; you can redistribute it and/or + * modify it under the terms of the Do What The Fuck You Want To + * Public License as published by Banlu Kemiyatorn. See + * http://sam.zoy.org/projects/COPYING.WTFPL for more details. + */ + +#include +#include +#include + +#include "config.h" +#include "common.h" + +/* Our macros */ +#define FONTNAME "share/font_phpbb.png" + +static struct image *find_glyphs(struct image *img); + +/* Global stuff */ +char *result; + +/* Main function */ +char *decode_phpbb(struct image *img) +{ + char all[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789"; + struct image *tmp1, *tmp2, *tmp3; + struct image *font = image_load(FONTNAME); + int x, y, i = 0; + int r, g, b; + int xmin, xmax, ymin, ymax, cur, offset = -1; + int distmin, distx, disty, distch; + + /* phpBB captchas have 6 characters */ + result = malloc(7 * sizeof(char)); + + if(!font) + { + fprintf(stderr, "cannot load font %s\n", FONTNAME); + exit(-1); + } + + tmp1 = filter_smooth(img); + tmp2 = filter_equalize(tmp1, 128); + tmp3 = image_new(img->width, img->height); + + for(x = 0; x < img->width; x++) + for(y = 0; y < img->height; y++) + { + getpixel(tmp2, x, y, &r, &g, &b); + if(r == 0 && offset == -1) + offset = x; + getpixel(img, x, y, &r, &g, &b); + setpixel(tmp3, x, y, 255, g, 255); + } + + strcpy(result, " "); + + for(cur = 0; cur < 6; cur++) + { + /* Try to find 1st letter */ + distmin = 999999999; + for(i = 0; i < 35; i++) + { + int localmin = 99999999, localx, localy; + xmin = i * 40; + ymin = 0; + xmax = i * 40 + 40; + ymax = 40; + for(y = 0; y < img->height - 40; y++) + { + x = offset - 3; + if(cur == 0) + x -= 10; + if(x < 0) + x = 0; + for(; x < offset + 3; x++) + { + int z, t, dist; + dist = 0; + for(t = 0; t < ymax - ymin; t++) + for(z = 0; z < xmax - xmin; z++) + { + int r2; + getgray(font, xmin + z, ymin + t, &r); + getgray(tmp2, x + z, y + t, &r2); + dist += (r - r2) * (r - r2); + } + if(dist < localmin) + { + localmin = dist; + localx = x; + localy = y; + } + } + } + if(localmin < distmin) + { + distmin = localmin; + distx = localx; + disty = localy; + distch = i; + } + } + + /* Print min glyph (debug) */ + xmin = distch * 40; + ymin = 0; + xmax = distch * 40 + 40; + ymax = 40; + for(y = 0; y < ymax - ymin; y++) + for(x = 0; x < xmax - xmin; x++) + { + int r2; + getpixel(font, xmin + x, ymin + y, &r2, &g, &b); + if(r2 > 128) continue; + getpixel(tmp3, distx + x, disty + y, &r, &g, &b); + setpixel(tmp3, distx + x, disty + y, r2, g, b); + } + + offset = distx + xmax - xmin; + result[cur] = all[distch]; + } + + image_free(tmp1); + image_free(tmp2); + image_free(tmp3); + image_free(font); + + return result; +} + diff --git a/src/slashdot.c b/src/slashdot.c index 60bfbb4..3ec6c4d 100644 --- a/src/slashdot.c +++ b/src/slashdot.c @@ -30,30 +30,48 @@ static struct image *find_glyphs(struct image *img); /* Global stuff */ struct { int xmin, ymin, xmax, ymax; } objlist[100]; -int objects = 0, first = -1, last = -1; +int objects, first, last; char *result; /* Main function */ char * decode_slashdot(struct image *img) { - struct image *tmp, *tmp2; + struct image *tmp1, *tmp2, *tmp3, *tmp4, *tmp5, *tmp6, *tmp7; + + /* Initialise local data */ + objects = 0; + first = -1; + last = -1; /* Slashdot captchas have 7 characters */ result = malloc(8 * sizeof(char)); /* Clean image a bit */ - tmp = filter_detect_lines(img); - tmp = filter_fill_holes(tmp); + tmp1 = filter_detect_lines(img); + tmp2 = filter_fill_holes(tmp1); /* Detect small objects to guess image orientation */ - tmp2 = filter_median(tmp); - tmp2 = filter_equalize(tmp2); - count_objects(tmp2); + tmp3 = filter_median(tmp2); + tmp4 = filter_equalize(tmp3, 200); + count_objects(tmp4); /* Invert rotation and find glyphs */ - tmp = rotate(tmp); - tmp = filter_median(tmp); - tmp = find_glyphs(tmp); + tmp5 = rotate(tmp2); + tmp6 = filter_median(tmp5); + tmp7 = find_glyphs(tmp6); + + /* Clean up our mess */ + image_free(tmp1); + image_free(tmp2); + image_free(tmp3); + image_free(tmp4); + image_free(tmp5); + image_free(tmp6); + image_free(tmp7); + + /* aaaaaaa means decoding failed */ + if(!strcmp(result, "aaaaaaa")) + result[0] = '\0'; return result; } diff --git a/src/test.c b/src/test.c new file mode 100644 index 0000000..f7f5c59 --- /dev/null +++ b/src/test.c @@ -0,0 +1,155 @@ +/* + * test.c: test captchas + * $Id$ + * + * Copyright: (c) 2004 Sam Hocevar + * This program is free software; you can redistribute it and/or + * modify it under the terms of the Do What The Fuck You Want To + * Public License as published by Banlu Kemiyatorn. See + * http://sam.zoy.org/projects/COPYING.WTFPL for more details. + */ + +#include +#include +#include +#include + +#include "config.h" +#include "common.h" + +/* Our macros */ +#define FONTNAME "share/font_phpbb.png" + +static struct image *find_glyphs(struct image *img); + +/* Global stuff */ +struct { int xmin, ymin, xmax, ymax; } objlist[100]; +int objects, first, last; +char *result; + +/* Main function */ +char * decode_test(struct image *img) +{ + struct image *tmp1, *tmp2, *tmp3, *tmp4, *tmp5, *tmp6, *tmp7; + + /* Initialise local data */ + objects = 0; + first = -1; + last = -1; + + /* phpBB captchas have 6 characters */ + result = malloc(7 * sizeof(char)); + + tmp1 = filter_smooth(img); + tmp2 = filter_median(tmp1); + tmp3 = filter_equalize(tmp2, 130); + tmp4 = filter_median(tmp3); + tmp5 = find_glyphs(tmp3); + + image_free(tmp1); + image_free(tmp2); + image_free(tmp3); + image_free(tmp4); + image_free(tmp5); + + return result; +} + +/* The following functions are local */ + +static struct image *find_glyphs(struct image *img) +{ + char all[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789"; + struct image *dst; + struct image *font = image_load(FONTNAME); + int x, y, i = 0; + int r, g, b; + int xmin, xmax, ymin, ymax, incell = 0, count = 0, cur = 0, offset = -1; + int distmin, distx, disty, distch; + + if(!font) + { + fprintf(stderr, "cannot load font %s\n", FONTNAME); + exit(-1); + } + + dst = image_new(img->width, img->height); + + for(x = 0; x < img->width; x++) + for(y = 0; y < img->height; y++) + { + getpixel(img, x, y, &r, &g, &b); + setpixel(dst, x, y, 255, g, 255); + if(r == 0 && offset == -1) + offset = x; + } + + strcpy(result, " "); + + while(cur < 6) + { + /* Try to find 1st letter */ + distmin = 999999999; + for(i = 0; i < 35; i++) + { + int localmin = 99999999, localx, localy; + xmin = i * 40; + ymin = 0; + xmax = i * 40 + 40; + ymax = 40; + for(y = 0; y < img->height - 40; y++) + { + x = offset - 5; + if(cur == 0) + x -= 15; + if(x < 0) + x = 0; + for(; x < offset + 10; x++) + { + int z, t, dist; + dist = 0; + for(t = 0; t < ymax - ymin; t++) + for(z = 0; z < xmax - xmin; z++) + { + int r2; + getgray(font, xmin + z, ymin + t, &r); + getgray(img, x + z, y + t, &r2); + dist += abs(r - r2); + } + if(dist < localmin) + { + localmin = dist; + localx = x; + localy = y; + } + } + } + if(localmin < distmin) + { + distmin = localmin; + distx = localx; + disty = localy; + distch = i; + } + } + + /* Print min glyph (debug) */ + xmin = distch * 40; + ymin = 0; + xmax = distch * 40 + 40; + ymax = 40; + for(y = 0; y < ymax - ymin; y++) + for(x = 0; x < xmax - xmin; x++) + { + getpixel(font, xmin + x, ymin + y, &r, &g, &b); + if(r > 128) continue; + setpixel(dst, distx + x, disty + y, r, g, b); + } + + offset = distx + xmax - xmin; + result[cur++] = all[distch]; + } + + return dst; +} +