Quantcast
Channel: C#タグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 8895

ライブラリを使わずにPDFを分析してみた(自分用)

$
0
0

Passなし Encryptionありの Rev=3. RC4(40bit or 128bit) × /FlateDecodeに対応。
(平文未動確)

画面キャプチャ

https://www.adobe.com/content/dam/acom/en/devnet/pdf/PDF32000_2008.pdf
を読み込ませた結果

image.png

参考

ソースコード

処理がかなり雑です。というか正常な手順を踏まずにデータ解析処理してますので真似はしないでください。

RC4は別途: https://qiita.com/kob58im/items/e20e35d9b11f39ce5da5

バグあり:オブジェクトの外まで検索してしまっている箇所が残っていそう。
誤った子ノードを探してしまい、無限に再帰するケースがある。
暫定処置で再帰の深さを制限している。

PdfStructViewer.cs
usingSystem;usingSystem.Collections;usingSystem.Collections.Generic;usingSystem.Drawing;usingSystem.IO;usingSystem.IO.Compression;//using System.Reflection;//using System.Runtime.InteropServices;usingSystem.Text;usingSystem.Text.RegularExpressions;usingSystem.Windows.Forms;usingSystem.Security.Cryptography;// MD5CryptoServiceProviderusingMyUtil;classPdfStructViewer:Form{ListViewlsv;TreeViewtrv;SplitContainerspl;SplitContainersplL;SplitContainersplR;RichTextBoxrtxt;// object viewRichTextBoxrtxtSt;// stream...endstreamFileStream_pdfFs;MD5CryptoServiceProvider_md5sp;byte[]_baseKey;int[]_xref;int_rootObjRef;bool_lsvEventByProgram;classPageObjId{publicintidContents;publicintidResources;publicPageObjId(){idContents=-1;idResources=-1;}}PdfStructViewer(stringpath){Text="PDF Struct viewer";ClientSize=newSize(700,500);spl=newSplitContainer();spl.Dock=DockStyle.Fill;spl.Orientation=Orientation.Vertical;Controls.Add(spl);splL=newSplitContainer();splL.Dock=DockStyle.Fill;splL.Orientation=Orientation.Horizontal;spl.Panel1.Controls.Add(splL);splR=newSplitContainer();splR.Dock=DockStyle.Fill;splR.Orientation=Orientation.Horizontal;spl.Panel2.Controls.Add(splR);lsv=newListView();lsv.View=View.Details;lsv.FullRowSelect=true;lsv.HideSelection=false;lsv.MultiSelect=false;lsv.GridLines=true;lsv.Dock=DockStyle.Fill;lsv.Columns.Add("No.",100,HorizontalAlignment.Left);lsv.Columns.Add("Address",400,HorizontalAlignment.Left);splL.Panel1.Controls.Add(lsv);trv=newTreeView();trv.HideSelection=false;trv.Dock=DockStyle.Fill;splL.Panel2.Controls.Add(trv);rtxt=newRichTextBox();rtxt.ReadOnly=true;rtxt.Multiline=true;rtxt.WordWrap=false;rtxt.ScrollBars=RichTextBoxScrollBars.Both;rtxt.Dock=DockStyle.Fill;splR.Panel1.Controls.Add(rtxt);rtxtSt=newRichTextBox();rtxtSt.ReadOnly=true;rtxtSt.Multiline=true;rtxtSt.WordWrap=false;rtxtSt.ScrollBars=RichTextBoxScrollBars.Both;rtxtSt.Dock=DockStyle.Fill;splR.Panel2.Controls.Add(rtxtSt);// -----------------ParsePdfStruct(path);ParseTreeFromRoot();lsv.SelectedIndexChanged+=Lsv_SelectedIndexChanged;trv.AfterSelect+=Trv_AfterSelect;}voidTrv_AfterSelect(objectsender,EventArgse){TreeNodenode=trv.SelectedNode;if(node==null){return;}intid=(int)node.Tag;_lsvEventByProgram=true;try{lsv.Items[id].Selected=true;lsv.EnsureVisible(id);}finally{_lsvEventByProgram=false;}}voidLsv_SelectedIndexChanged(objectsender,EventArgse){varitems=lsv.SelectedItems;if(!_lsvEventByProgram){trv.SelectedNode=null;}if(items.Count!=1){return;}ListViewItemitem=items[0];Console.WriteLine(item.SubItems[0].Text);intaddr=Convert.ToInt32(item.SubItems[1].Text.Substring(2),16);_pdfFs.Position=addr;stringobjData=GetPreviewData(_pdfFs);rtxt.Text=objData;_pdfFs.Position=addr;byte[]streamData=ParseStreamBlock(_pdfFs,_baseKey,_md5sp);rtxtSt.Text=ConvertBytesToString(streamData);}TreeNodeMakeTreeNode(stringname,intid){returnMakeTreeNode(name,id,0);}TreeNodeMakeTreeNode(stringname,intid,intpageOffset){stringpageInfo="";if(pageOffset>0){pageInfo="("+pageOffset.ToString()+")";}TreeNodenode=newTreeNode("["+id.ToString()+"]"+name+pageInfo);node.Tag=id;returnnode;}int_tmpCallDepth;voidParseTreeFromRoot(){TreeNoderootNode=MakeTreeNode("Root",_rootObjRef);trv.Nodes.Add(rootNode);varrootDict=ParseRootObj(_pdfFs,_xref[_rootObjRef]);intobjRef=(int)rootDict["/Pages"];TreeNodepagesNode=MakeTreeNode("Pages",objRef);rootNode.Nodes.Add(pagesNode);_tmpCallDepth=0;ParseKidsRecursive(pagesNode,objRef,1);}intParseKidsRecursive(TreeNodeparentNode,intparentId,intpageOffset){intpageCount=0;_tmpCallDepth++;if(_tmpCallDepth<=7){PageObjIdcontentId;int[]pagesKids=ParsePagesObj(_pdfFs,_xref[parentId],outpageCount,outcontentId);if(contentId.idContents>=0){TreeNodepageNode=MakeTreeNode("Contents",contentId.idContents);parentNode.Nodes.Add(pageNode);}if(contentId.idResources>=0){TreeNodepageNode=MakeTreeNode("Resources",contentId.idResources);parentNode.Nodes.Add(pageNode);}foreach(intsubObjRefinpagesKids){TreeNodepageNode=MakeTreeNode("Kids",subObjRef,pageOffset);parentNode.Nodes.Add(pageNode);pageOffset+=ParseKidsRecursive(pageNode,subObjRef,pageOffset);}}else{Console.WriteLine("Skipped:"+parentId.ToString());}_tmpCallDepth--;returnpageCount;}voidParsePdfStruct(stringpath){_pdfFs=newFileStream(path,FileMode.Open,FileAccess.Read);inttrailerAddr;longpos=GetXRefAddress(_pdfFs,outtrailerAddr,500);Console.WriteLine("xref address: 0x"+pos.ToString("X"));_xref=ParseXRefTable(_pdfFs,(int)pos);vartrailerDict=ParseTrailer(_pdfFs,trailerAddr);lsv.BeginUpdate();try{lsv.Items.Clear();for(inti=0;i<_xref.Length;i++){lsv.Items.Add(newListViewItem(newstring[]{i.ToString(),"0x"+_xref[i].ToString("X")}));}}finally{lsv.EndUpdate();}_baseKey=null;_md5sp=newMD5CryptoServiceProvider();if(trailerDict.ContainsKey("/Encrypt")){byte[]id=(byte[])trailerDict["/ID"];intencObjNum=(int)trailerDict["/Encrypt"];Console.Write("Encrypt obj num:");Console.WriteLine(encObjNum.ToString());_pdfFs.Position=_xref[encObjNum];Dictionary<string,object>encryptionDict;ParseEncryptObj(_pdfFs,outencryptionDict);_baseKey=CalcBaseKey(encryptionDict,id,_md5sp);}_rootObjRef=(int)trailerDict["/Root"];//DumpIntArrayWithIndex(pagesKids,false);}// read /-?[0-9]+/ in binary// てきとうstaticboolReadIntegerDigits(FileStreamfs,outlongdigits){varsb=newStringBuilder();intb=fs.ReadByte();boolretCode=false;if(b=='-'){sb.Append((char)b);b=fs.ReadByte();}if(IsDigit(b)){retCode=true;}do{sb.Append((char)b);b=fs.ReadByte();}while(IsDigit(b));if(b>=0){// EOFでなければ1文字戻すfs.Position--;}strings=sb.ToString();if(retCode){digits=Convert.ToInt64(s);}else{digits=0;}returnretCode;}// read /\/[A-Za-z_][A-Za-z0-9]*/ in binary// てきとうstaticboolReadKeyword(FileStreamfs,outstringret){varsb=newStringBuilder();intb=fs.ReadByte();if(b=='/'){sb.Append((char)b);}else{if(b>=0){// EOFでなければ1文字戻すfs.Position--;}ret=null;returnfalse;}b=fs.ReadByte();if(IsAlphabet(b)){sb.Append((char)b);}else{ret=null;returnfalse;}b=fs.ReadByte();while(IsAlphabet(b)||IsDigit(b)){sb.Append((char)b);b=fs.ReadByte();}if(b>=0){// EOFでなければ1文字戻すfs.Position--;}ret=sb.ToString();returntrue;}// read /([ \t\r\n]+|\b)/// 空白文字を読み捨てるstaticboolReadSpaces(FileStreamfs){intb;do{b=fs.ReadByte();}while(b==' '||b=='\t'||b=='\r'||b=='\n');if(b>=0){// EOFでなければ1文字戻すfs.Position--;}returntrue;}staticboolReadFixedSequence(FileStreamfs,strings){byte[]a=Encoding.ASCII.GetBytes(s);// expected datafor(inti=0;i<a.Length;i++){intb=fs.ReadByte();if(b!=a[i]){returnfalse;}}returntrue;}// read postscript string object (...) in binary// e.g.://  (xxx)//  (xxx(xxx)xxx)//  (xxx\)xxx)staticboolReadPsString(FileStreamfs,outbyte[]ret){intnCount=0;// Count of open '('boolescape=false;vart=newList<byte>();intb=fs.ReadByte();if(b!='('){if(b>=0){// EOFでなければ1文字戻すfs.Position--;}ret=null;returnfalse;}nCount++;while(nCount>0){b=fs.ReadByte();if(b<0){// EOFret=null;returnfalse;}if(!escape){switch(b){case'(':nCount++;t.Add((byte)b);break;case')':nCount--;if(nCount>0){t.Add((byte)b);}break;case'\\':escape=true;break;default:t.Add((byte)b);break;}}else{escape=false;// \000 - \377 がケアできていないswitch(b){case't':t.Add((byte)'\t');break;case'r':t.Add((byte)'\r');break;case'n':t.Add((byte)'\n');break;case'b':t.Add((byte)'\b');break;case'f':t.Add((byte)'\f');break;default:t.Add((byte)b);break;}}}ret=t.ToArray();returntrue;}// 未実装: read /<([ \r\n\t]*[0-9A-Fa-f]{2}[ \r\n\t]*)*>/ in binary// read /<([0-9A-Fa-f]{2})+>/ in binarystaticboolReadHexString(FileStreamfs,outbyte[]ret){intb1=fs.ReadByte();if(b1!='<'){if(b1>=0){// EOFでなければ1文字戻すfs.Position--;}ret=null;returnfalse;}vart=newList<byte>();while(true){b1=fs.ReadByte();if(b1=='>'){// end mark (正常終了)break;}if(b1<0){ret=null;returnfalse;}// EOFintb2=fs.ReadByte();if(b2<0){ret=null;returnfalse;}// EOFintx=DecodeHexChar(b1,b2);if(x<0){// invalid formatret=null;returnfalse;}t.Add((byte)x);}ret=t.ToArray();returntrue;}staticboolReadPsOrHexString(FileStreamfs,outbyte[]ret){ret=null;intb=fs.ReadByte();if(b>=0){fs.Position--;}if(b=='('){returnReadPsString(fs,outret);}elseif(b=='<'){returnReadHexString(fs,outret);}else{returnfalse;}}staticboolIsSpacerChar(intx){return(x==' ')||(x=='\t')||(x=='\r')||(x=='\n');}staticboolIsDigit(intx){return('0'<=x&&x<='9');}staticboolIsAlphabet(intx){return('A'<=x&&x<='Z')||('a'<=x&&x<='z');}staticboolIsPrintableChar(byteb){if(b=='\t'||b=='\n'||b=='\n'){returntrue;}if(0x20<=b&&b<=0x7E){returntrue;}returnfalse;}staticintDecodeHexChar(intx){if('0'<=x&&x<='9'){returnx-'0';}if('A'<=x&&x<='F'){return(x-'A')+10;}if('a'<=x&&x<='f'){return(x-'a')+10;}return-1;}staticintDecodeHexChar(intx1,intx2){x1=DecodeHexChar(x1);if(x1<0){return-1;}x2=DecodeHexChar(x2);if(x2<0){return-1;}return(x1<<4)|x2;}staticstringConvertBytesToString(byte[]data){if(data==null){return"";}varsb=newStringBuilder(data.Length);foreach(bytebindata){if(IsPrintableChar(b)){sb.Append((char)b);}else{sb.Append('?');}}returnsb.ToString();}staticboolParseEncryptObj(FileStreamfs,outDictionary<string,object>dict){dict=null;longobjId;longgenId;boolretCode;if(!ReadIntegerDigits(fs,outobjId)){returnfalse;}ReadSpaces(fs);if(!ReadIntegerDigits(fs,outgenId)){returnfalse;}ReadSpaces(fs);if(!ReadFixedSequence(fs,"obj")){returnfalse;}ReadSpaces(fs);if(!ReadFixedSequence(fs,"<<")){returnfalse;}ReadSpaces(fs);dict=newDictionary<string,object>();dict["/Length"]=40;while(true){stringkwd;retCode=ReadKeyword(fs,outkwd);if(!retCode){break;}Console.Write(kwd??"null");Console.Write(" ");ReadSpaces(fs);switch(kwd){case"/Filter":{stringkwd2;if(!ReadKeyword(fs,outkwd2)){returnfalse;}Console.WriteLine(kwd2??"null");dict[kwd]=kwd2;ReadSpaces(fs);}break;case"/V":case"/R":case"/P":case"/Length":{longnumValue;if(!ReadIntegerDigits(fs,outnumValue)){returnfalse;}if(kwd=="/Length"&&(numValue<40||numValue%8!=0)){returnfalse;}Console.WriteLine(numValue);dict[kwd]=numValue;ReadSpaces(fs);}break;case"/O":case"/U":{byte[]content;if(!ReadPsOrHexString(fs,outcontent)){returnfalse;}dict[kwd]=content;ReadSpaces(fs);Console.Write("#");Console.WriteLine(content.Length);if(content.Length!=32){returnfalse;}}break;default:break;}}if(!dict.ContainsKey("/O")){returnfalse;}if(!dict.ContainsKey("/P")){returnfalse;}if(!dict.ContainsKey("/R")){returnfalse;}returntrue;}staticbyte[]Int32ToBytesLE(uintx){byte[]a=newbyte[4];a[0]=(byte)(x&0xFF);a[1]=(byte)((x>>8)&0xFF);a[2]=(byte)((x>>16)&0xFF);a[3]=(byte)((x>>24)&0xFF);returna;}staticbyte[]CalcBaseKey(Dictionary<string,object>encryptDict,byte[]id,MD5CryptoServiceProvidermd5sp){if(id.Length!=16){returnnull;}byte[]pswdPadding=newbyte[]{0x28,0xBF,0x4E,0x5E,0x4E,0x75,0x8A,0x41,0x64,0x00,0x4E,0x56,0xFF,0xFA,0x01,0x08,0x2E,0x2E,0x00,0xB6,0xD0,0x68,0x3E,0x80,0x2F,0x0C,0xA9,0xFE,0x64,0x53,0x69,0x7A};// 32 + O 32 + P 4 + ID 16byte[]data=newbyte[200];intpos=0;intn=0;n=pswdPadding.Length;Array.Copy(pswdPadding,0,data,pos,n);pos+=n;n=32;Array.Copy((byte[])encryptDict["/O"],0,data,pos,n);pos+=n;n=4;Array.Copy(Int32ToBytesLE((uint)(long)encryptDict["/P"]),0,data,pos,n);pos+=n;n=16;//id.Length;Array.Copy(id,0,data,pos,n);pos+=n;Array.Resize(refdata,pos);byte[]baseKey=md5sp.ComputeHash(data);for(inti=0;i<50;i++){Array.Resize(refbaseKey,(int)(long)encryptDict["/Length"]/8);baseKey=md5sp.ComputeHash(baseKey);}Array.Resize(refbaseKey,(int)(long)encryptDict["/Length"]/8);returnbaseKey;}staticbyte[]CalcObjKey(byte[]baseKey,uintobjId,uintgenId,MD5CryptoServiceProvidermd5sp){//byte[] sAlT = Encoding.ASCII.GetBytes("sAlT"); // AES使用時byte[]data=newbyte[baseKey.Length+5];//+sAlT.Length];intpos=0;intn;n=baseKey.Length;Array.Copy(baseKey,0,data,pos,n);pos+=n;n=3;Array.Copy(Int32ToBytesLE(objId),0,data,pos,n);pos+=n;n=2;Array.Copy(Int32ToBytesLE(genId),0,data,pos,n);pos+=n;//n = sAlT.Length;//Array.Copy(sAlT,0,data,pos,n);//pos += n;//Console.Write("objkey before hash:");//DumpByteArray(data,data.Length);byte[]objKey=md5sp.ComputeHash(data);// 16byteif(objKey.Length>baseKey.Length+5){Array.Resize(refobjKey,baseKey.Length+5);}Console.Write("ObjKeyLen:");Console.WriteLine(objKey.Length);returnobjKey;}staticintGetXRefAddress(FileStreamfs,outinttrailerAddress,intsearchBytesLen){byte[]buf=newbyte[searchBytesLen];fs.Seek(-searchBytesLen,SeekOrigin.End);longtmpPos=fs.Position;intn=fs.Read(buf,0,buf.Length);intiEof=SearchBackward(buf,"%%EOF");intiStartXref=SearchBackward(buf,"startxref");trailerAddress=SearchBackward(buf,"trailer");if(trailerAddress>=0){trailerAddress+=(int)tmpPos;}if(iEof>=0&&iStartXref>=0&&iEof>=iStartXref){iStartXref+="startxref".Length;if(buf[iStartXref]=='\r'){iStartXref++;}if(buf[iStartXref]=='\n'){iStartXref++;}}else{return-1;}returnParseBufferInt32(buf,iStartXref);}// 負数非対応staticintParseBufferInt32(byte[]buf,intindex){varsb=newStringBuilder();while(index<buf.Length&&IsDigit(buf[index])){sb.Append((char)buf[index]);index++;}strings=sb.ToString();if(s.Length==0){return-1;}returnConvert.ToInt32(s);}staticint[]ParseXRefTable(FileStreamfs,intheadPos){int[]offsetTable;fs.Position=headPos+4;// +4 : length of "xref"boolretCode;longn;{longstartIndex;ReadSpaces(fs);retCode=ReadIntegerDigits(fs,outstartIndex);if(!retCode||startIndex!=0){Console.WriteLine("not supported xref data.");returnnull;}ReadSpaces(fs);retCode=ReadIntegerDigits(fs,outn);if(n<=0){Console.WriteLine("not supported xref data.");returnnull;}}offsetTable=newint[n];ReadSpaces(fs);for(inti=0;i<n;i++){longpos;longgenId;retCode=ReadIntegerDigits(fs,outpos);if(!retCode||pos<0){returnnull;}offsetTable[i]=(int)pos;ReadSpaces(fs);retCode=ReadIntegerDigits(fs,outgenId);if(!retCode||genId<0){returnnull;}ReadSpaces(fs);intb=fs.ReadByte();if(b!='n'&&b!='f'){returnnull;}ReadSpaces(fs);}returnoffsetTable;}staticint[]ParsePagesObj(FileStreamfs,intpagesPos,outintpageCount,outPageObjIdsubObjIds){//var dict = new Dictionary<string,object>();pageCount=1;subObjIds=newPageObjId();constintBufSize=1000;byte[]buf=newbyte[BufSize];fs.Position=pagesPos;intn=fs.Read(buf,0,buf.Length);if(n<=0){returnnull;}Array.Resize(refbuf,n);intendobjPos=Search(buf,0,"endobj");if(endobjPos>=0){Array.Resize(refbuf,endobjPos+"endobj".Length);}//  /Kids[nnnnn 0 R nnnnn 0 R ...]int[]ret=newint[0];{conststringKeyName="/Kids";intiKids=Search(buf,0,KeyName);if(iKids>=0){iKids+=KeyName.Length;while(IsSpacerChar(buf[iKids])){iKids++;}if(buf[iKids]=='['){iKids++;intiRight=Search(buf,iKids,"]");if(iRight>=iKids+1){strings=Encoding.ASCII.GetString(buf,iKids,iRight-iKids);//Console.WriteLine(s);Regexr=newRegex(@"([0-9]+)[ \r\n\t]+([0-9]+)[ \r\n\t]+R\b");MatchCollectionmc=r.Matches(s);ret=newint[mc.Count];for(inti=0;i<mc.Count;i++){ret[i]=Convert.ToInt32(mc[i].Groups[1].Value);}}}}}inttmp;if(FindUIntOfKeyInBuffer(buf,"/Count",outtmp)){pageCount=tmp;}if(FindUIntOfKeyInBuffer(buf,"/Contents",outtmp)){subObjIds.idContents=tmp;}if(FindUIntOfKeyInBuffer(buf,"/Resources",outtmp)){subObjIds.idResources=tmp;}returnret;}staticDictionary<string,object>ParseRootObj(FileStreamfs,intpagesPos){vardict=newDictionary<string,object>();constintBufSize=1000;byte[]buf=newbyte[BufSize];fs.Position=pagesPos;intn=fs.Read(buf,0,buf.Length);if(n<=0){returnnull;}Array.Resize(refbuf,n);inttmp;if(FindUIntOfKeyInBuffer(buf,"/Pages",outtmp)){dict["/Pages"]=tmp;}returndict;}staticDictionary<string,object>ParseTrailer(FileStreamfs,inttrailerPos){if(trailerPos<0){returnnull;}vardict=newDictionary<string,object>();constintBufSize=1000;byte[]buf=newbyte[BufSize];//Console.WriteLine(trailerPos.ToString("X"));fs.Position=trailerPos;intn=fs.Read(buf,0,buf.Length);if(n<=0){returnnull;}Array.Resize(refbuf,n);inttmp;if(FindUIntOfKeyInBuffer(buf,"/Encrypt",outtmp)){dict["/Encrypt"]=tmp;}else{Console.WriteLine("enc not found");}if(FindUIntOfKeyInBuffer(buf,"/Root",outtmp)){dict["/Root"]=tmp;}if(FindUIntOfKeyInBuffer(buf,"/Info",outtmp)){dict["/Info"]=tmp;}{conststringKeyName="/ID";intiID=Search(buf,0,KeyName);if(iID>=0){// /ID [<HexStr>iID+=KeyName.Length;while(IsSpacerChar(buf[iID])){iID++;}if(buf[iID]=='['){iID++;}byte[]idData1;fs.Position=trailerPos+iID;boolretCode=ReadHexString(fs,outidData1);if(retCode){dict[KeyName]=idData1;}else{Console.WriteLine("id parse failed");}}else{Console.WriteLine("id not found");}}returndict;}staticboolFindUIntOfKeyInBuffer(byte[]buf,stringkeyName,outintret){intindex=Search(buf,0,keyName);if(index>=0){index+=keyName.Length;while(IsSpacerChar(buf[index])){index++;}ret=ParseBufferInt32(buf,index);if(ret>=0){returntrue;}}ret=-1;returnfalse;}staticintSearch(byte[]data,intstartI,strings){if(s==""){return-1;}byte[]w=Encoding.ASCII.GetBytes(s);intpos=0;inti=startI;while(i<data.Length){if(data[i]==w[pos]){pos++;i++;if(pos==w.Length){returni-w.Length;}}else{i-=pos;i++;pos=0;}}return-1;}staticintSearchBackward(byte[]data,strings){if(s==""){return-1;}byte[]w=Encoding.ASCII.GetBytes(s);intpos=0;inti=data.Length-1;while(i>=0){if(data[i]==w[(w.Length-1)-pos]){pos++;if(pos==w.Length){returni;}i--;}else{i+=pos-1;pos=0;}}return-1;}staticbyte[]ParseStreamBlock(FileStreamfs,byte[]baseKey,MD5CryptoServiceProvidermd5sp){//nnnn 0 obj <</Filter/FlateDecode/Lengthvardict=newDictionary<string,object>();longobjId;longgenId;boolretCode;if(!ReadIntegerDigits(fs,outobjId)){returnnull;}ReadSpaces(fs);if(!ReadIntegerDigits(fs,outgenId)){returnnull;}ReadSpaces(fs);if(!ReadFixedSequence(fs,"obj")){returnnull;}ReadSpaces(fs);if(!ReadFixedSequence(fs,"<<")){returnnull;}ReadSpaces(fs);while(true){stringkwd;retCode=ReadKeyword(fs,outkwd);if(!retCode){break;}//Console.Write(kwd??"null");//Console.Write(" ");ReadSpaces(fs);switch(kwd){case"/Filter":{stringkwd2;if(!ReadKeyword(fs,outkwd2)){returnnull;}//Console.WriteLine(kwd2??"null");dict[kwd]=kwd2;ReadSpaces(fs);}break;case"/Length":case"/Length1":{longnumValue;if(!ReadIntegerDigits(fs,outnumValue)){returnnull;}dict[kwd]=numValue;//Console.WriteLine(numValue);ReadSpaces(fs);}break;default:{// 未知のパラメータreturnnull;}//break;}}if(!ReadFixedSequence(fs,">>")){returnnull;}ReadSpaces(fs);if(!ReadFixedSequence(fs,"stream")){returnnull;}ReadSpaces(fs);// <-- 読み捨てるべきではないデータも読み捨ててしまいそうif(!dict.ContainsKey("/Length")){returnnull;}intlength=(int)(long)dict["/Length"];byte[]decrypted;if(baseKey==null){// no encryptiondecrypted=newByte[length];fs.Read(decrypted,0,length);}else{decrypted=DecryptStream(fs,baseKey,(int)objId,(int)genId,length,md5sp);}byte[]ret;if(dict.ContainsKey("/Filter")&&(string)dict["/Filter"]=="/FlateDecode"){ret=newbyte[0];using(varms=newMemoryStream(decrypted)){intheader1=ms.ReadByte();intheader2=ms.ReadByte();if(header1<0||header2<0){returnnull;}if((header1&0x0F)!=0x08){returnnull;}if(((header1<<8)+header2)%31!=0){returnnull;}byte[]buffer=newbyte[1000];using(DeflateStreamds=newDeflateStream(ms,CompressionMode.Decompress)){while(true){intnRead;try{nRead=ds.Read(buffer,0,buffer.Length);}catch(Exceptione){Console.WriteLine(e);returnnull;}if(nRead<=0){break;}intnSize=ret.Length;Array.Resize(refret,nSize+nRead);Array.Copy(buffer,0,ret,nSize,nRead);}}}}else{ret=decrypted;}returnret;}staticbyte[]DecryptStream(FileStreamfs,byte[]baseKey,intobjId,intgenId,intlength,MD5CryptoServiceProvidermd5sp){byte[]objKey=CalcObjKey(baseKey,(uint)objId,(uint)genId,md5sp);//Console.WriteLine(objKey.Length);byte[]buf=newbyte[length];fs.Read(buf,0,length);byte[]plain=newbyte[length];using(MemoryStreamms=newMemoryStream(buf))using(MemoryStreammsDest=newMemoryStream(plain)){Arcfourarcfour=Arcfour.CreateArcfour(objKey);arcfour.Encrypt(ms,msDest,buf.Length);//decrypt}//Console.Write("mod31: ");//Console.WriteLine((plain[0]*256 + plain[1])%31);//DumpByteArray(plain,50);returnplain;}// endobjが見つかるまでのデータをASCIIにして出力。(単語単位では検索してないのでよくない)// 可読でない文字は ? に置き換える。// stream...endstreamはそのまま出力。staticstringGetPreviewData(FileStreamfs){constintMaxBufSize=1000;byte[]buf=newbyte[MaxBufSize];byte[]endobj=Encoding.ASCII.GetBytes("endobj");intn=fs.Read(buf,0,buf.Length);if(n<endobj.Length){return"";}varsb=newStringBuilder(n);intpos=0;for(inti=0;i<n;i++){if(IsPrintableChar(buf[i])){sb.Append((char)buf[i]);}else{sb.Append('?');}if(buf[i]==endobj[pos]){pos++;if(pos==endobj.Length){break;}}else{// たまたま "endobj" を構成する文字がすべて別の文字なので、検索アンマッチ時は単純に pos=0に戻しているだけでよいが、// "aab"とかだと "aaab"から検索していると"aa"のあと"ab"から検索が始まってとりこぼすおそれがある。pos=0;}}returnsb.ToString();}[STAThread]staticvoidMain(string[]args){if(args.Length!=1){Console.WriteLine("Argument error.");return;}Application.Run(newPdfStructViewer(args[0]));}staticvoidDumpIntArrayWithIndex(int[]a,boolhex){DumpIntArrayWithIndex(a,0,a.Length-1,hex);}staticvoidDumpIntArrayWithIndex(int[]a,intfromI,intendI,boolhex){if(fromI<0){fromI=0;}for(inti=fromI;i<a.Length&&i<=endI;i++){Console.Write(i.ToString());if(hex){Console.Write(": 0x");Console.WriteLine(a[i].ToString("X"));}else{Console.Write(": ");Console.WriteLine(a[i].ToString(""));}}}staticvoidDumpByteArray(byte[]a,intn){intcnt=0;foreach(bytebina){cnt++;Console.Write(b.ToString("X2"));if(cnt>=n)break;}Console.WriteLine();}}

Viewing all articles
Browse latest Browse all 8895

Trending Articles