// We use it instead of direct PPM.DecodeChar call to be sure that // we reset PPM structures in case of corrupt data. It is important, // because these structures can be invalid after PPM.DecodeChar returned -1. inline int Unpack::SafePPMDecodeChar() { int Ch=PPM.DecodeChar(); if (Ch==-1) // Corrupt PPM data found. { PPM.CleanUp(); // Reset possibly corrupt PPM data structures. UnpBlockType=BLOCK_LZ; // Set faster and more fail proof LZ mode. } return(Ch); } void Unpack::Unpack29(bool Solid) { static unsigned char LDecode[]={0,1,2,3,4,5,6,7,8,10,12,14,16,20,24,28,32,40,48,56,64,80,96,112,128,160,192,224}; static unsigned char LBits[]= {0,0,0,0,0,0,0,0,1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5}; static int DDecode[DC]; static byte DBits[DC]; static int DBitLengthCounts[]= {4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,14,0,12}; static unsigned char SDDecode[]={0,4,8,16,32,64,128,192}; static unsigned char SDBits[]= {2,2,3, 4, 5, 6, 6, 6}; unsigned int Bits; if (DDecode[1]==0) { int Dist=0,BitLength=0,Slot=0; for (int I=0;IReadBorder) { if (!UnpReadBuf30()) break; } if (((WrPtr-UnpPtr) & MaxWinMask)<260 && WrPtr!=UnpPtr) { UnpWriteBuf30(); if (WrittenFileSize>DestUnpSize) return; if (Suspended) { FileExtracted=false; return; } } if (UnpBlockType==BLOCK_PPM) { // Here speed is critical, so we do not use SafePPMDecodeChar, // because sometimes even the inline function can introduce // some additional penalty. int Ch=PPM.DecodeChar(); if (Ch==-1) // Corrupt PPM data found. { PPM.CleanUp(); // Reset possibly corrupt PPM data structures. UnpBlockType=BLOCK_LZ; // Set faster and more fail proof LZ mode. break; } if (Ch==PPMEscChar) { int NextCh=SafePPMDecodeChar(); if (NextCh==0) // End of PPM encoding. { if (!ReadTables30()) break; continue; } if (NextCh==-1) // Corrupt PPM data found. break; if (NextCh==2) // End of file in PPM mode. break; if (NextCh==3) // Read VM code. { if (!ReadVMCodePPM()) break; continue; } if (NextCh==4) // LZ inside of PPM. { unsigned int Distance=0,Length; bool Failed=false; for (int I=0;I<4 && !Failed;I++) { int Ch=SafePPMDecodeChar(); if (Ch==-1) Failed=true; else if (I==3) Length=(byte)Ch; else Distance=(Distance<<8)+(byte)Ch; } if (Failed) break; CopyString(Length+32,Distance+2); continue; } if (NextCh==5) // One byte distance match (RLE) inside of PPM. { int Length=SafePPMDecodeChar(); if (Length==-1) break; CopyString(Length+4,1); continue; } // If we are here, NextCh must be 1, what means that current byte // is equal to our 'escape' byte, so we just store it to Window. } Window[UnpPtr++]=Ch; continue; } uint Number=DecodeNumber(Inp,&BlockTables.LD); if (Number<256) { Window[UnpPtr++]=(byte)Number; continue; } if (Number>=271) { uint Length=LDecode[Number-=271]+3; if ((Bits=LBits[Number])>0) { Length+=Inp.getbits()>>(16-Bits); Inp.addbits(Bits); } uint DistNumber=DecodeNumber(Inp,&BlockTables.DD); uint Distance=DDecode[DistNumber]+1; if ((Bits=DBits[DistNumber])>0) { if (DistNumber>9) { if (Bits>4) { Distance+=((Inp.getbits()>>(20-Bits))<<4); Inp.addbits(Bits-4); } if (LowDistRepCount>0) { LowDistRepCount--; Distance+=PrevLowDist; } else { uint LowDist=DecodeNumber(Inp,&BlockTables.LDD); if (LowDist==16) { LowDistRepCount=LOW_DIST_REP_COUNT-1; Distance+=PrevLowDist; } else { Distance+=LowDist; PrevLowDist=LowDist; } } } else { Distance+=Inp.getbits()>>(16-Bits); Inp.addbits(Bits); } } if (Distance>=0x2000) { Length++; if (Distance>=0x40000) Length++; } InsertOldDist(Distance); LastLength=Length; CopyString(Length,Distance); continue; } if (Number==256) { if (!ReadEndOfBlock()) break; continue; } if (Number==257) { if (!ReadVMCode()) break; continue; } if (Number==258) { if (LastLength!=0) CopyString(LastLength,OldDist[0]); continue; } if (Number<263) { uint DistNum=Number-259; uint Distance=OldDist[DistNum]; for (uint I=DistNum;I>0;I--) OldDist[I]=OldDist[I-1]; OldDist[0]=Distance; uint LengthNumber=DecodeNumber(Inp,&BlockTables.RD); int Length=LDecode[LengthNumber]+2; if ((Bits=LBits[LengthNumber])>0) { Length+=Inp.getbits()>>(16-Bits); Inp.addbits(Bits); } LastLength=Length; CopyString(Length,Distance); continue; } if (Number<272) { uint Distance=SDDecode[Number-=263]+1; if ((Bits=SDBits[Number])>0) { Distance+=Inp.getbits()>>(16-Bits); Inp.addbits(Bits); } InsertOldDist(Distance); LastLength=2; CopyString(2,Distance); continue; } } UnpWriteBuf30(); } // Return 'false' to quit unpacking the current file or 'true' to continue. bool Unpack::ReadEndOfBlock() { uint BitField=Inp.getbits(); bool NewTable,NewFile=false; // "1" - no new file, new table just here. // "00" - new file, no new table. // "01" - new file, new table (in beginning of next file). if ((BitField & 0x8000)!=0) { NewTable=true; Inp.addbits(1); } else { NewFile=true; NewTable=(BitField & 0x4000)!=0; Inp.addbits(2); } TablesRead3=!NewTable; // Quit immediately if "new file" flag is set. If "new table" flag // is present, we'll read the table in beginning of next file // based on 'TablesRead3' 'false' value. if (NewFile) return false; return ReadTables30(); // Quit only if we failed to read tables. } bool Unpack::ReadVMCode() { // Entire VM code is guaranteed to fully present in block defined // by current Huffman table. Compressor checks that VM code does not cross // Huffman block boundaries. uint FirstByte=Inp.getbits()>>8; Inp.addbits(8); uint Length=(FirstByte & 7)+1; if (Length==7) { Length=(Inp.getbits()>>8)+7; Inp.addbits(8); } else if (Length==8) { Length=Inp.getbits(); Inp.addbits(16); } if (Length==0) return false; Array VMCode(Length); for (uint I=0;I=ReadTop-1 && !UnpReadBuf30() && I>8; Inp.addbits(8); } return AddVMCode(FirstByte,&VMCode[0],Length); } bool Unpack::ReadVMCodePPM() { uint FirstByte=SafePPMDecodeChar(); if ((int)FirstByte==-1) return false; uint Length=(FirstByte & 7)+1; if (Length==7) { int B1=SafePPMDecodeChar(); if (B1==-1) return false; Length=B1+7; } else if (Length==8) { int B1=SafePPMDecodeChar(); if (B1==-1) return false; int B2=SafePPMDecodeChar(); if (B2==-1) return false; Length=B1*256+B2; } if (Length==0) return false; Array VMCode(Length); for (uint I=0;IFilters30.Size() || FiltPos>OldFilterLengths.Size()) return false; LastFilter=FiltPos; bool NewFilter=(FiltPos==Filters30.Size()); UnpackFilter30 *StackFilter=new UnpackFilter30; // New filter for PrgStack. UnpackFilter30 *Filter; if (NewFilter) // New filter code, never used before since VM reset. { if (FiltPos>MAX3_UNPACK_FILTERS) { // Too many different filters, corrupt archive. delete StackFilter; return false; } Filters30.Add(1); Filters30[Filters30.Size()-1]=Filter=new UnpackFilter30; StackFilter->ParentFilter=(uint)(Filters30.Size()-1); // Reserve one item to store the data block length of our new filter // entry. We'll set it to real block length below, after reading it. // But we need to initialize it now, because when processing corrupt // data, we can access this item even before we set it to real value. OldFilterLengths.Push(0); } else // Filter was used in the past. { Filter=Filters30[FiltPos]; StackFilter->ParentFilter=FiltPos; } uint EmptyCount=0; for (uint I=0;I0) PrgStack[I]=NULL; } if (EmptyCount==0) { if (PrgStack.Size()>MAX3_UNPACK_FILTERS) { delete StackFilter; return false; } PrgStack.Add(1); EmptyCount=1; } size_t StackPos=PrgStack.Size()-EmptyCount; PrgStack[StackPos]=StackFilter; uint BlockStart=RarVM::ReadData(VMCodeInp); if ((FirstByte & 0x40)!=0) BlockStart+=258; StackFilter->BlockStart=(uint)((BlockStart+UnpPtr)&MaxWinMask); if ((FirstByte & 0x20)!=0) { StackFilter->BlockLength=RarVM::ReadData(VMCodeInp); // Store the last data block length for current filter. OldFilterLengths[FiltPos]=StackFilter->BlockLength; } else { // Set the data block size to same value as the previous block size // for same filter. It is possible for corrupt data to access a new // and not filled yet item of OldFilterLengths array here. This is why // we set new OldFilterLengths items to zero above. StackFilter->BlockLength=FiltPosNextWindow=WrPtr!=UnpPtr && ((WrPtr-UnpPtr)&MaxWinMask)<=BlockStart; // DebugLog("\nNextWindow: UnpPtr=%08x WrPtr=%08x BlockStart=%08x",UnpPtr,WrPtr,BlockStart); memset(StackFilter->Prg.InitR,0,sizeof(StackFilter->Prg.InitR)); StackFilter->Prg.InitR[4]=StackFilter->BlockLength; if ((FirstByte & 0x10)!=0) // Set registers to optional parameters if any. { uint InitMask=VMCodeInp.fgetbits()>>9; VMCodeInp.faddbits(7); for (uint I=0;I<7;I++) if (InitMask & (1<Prg.InitR[I]=RarVM::ReadData(VMCodeInp); } if (NewFilter) { uint VMCodeSize=RarVM::ReadData(VMCodeInp); if (VMCodeSize>=0x10000 || VMCodeSize==0 || VMCodeInp.InAddr+VMCodeSize>CodeSize) return false; Array VMCode(VMCodeSize); for (uint I=0;I>8; VMCodeInp.faddbits(8); } VM.Prepare(&VMCode[0],VMCodeSize,&Filter->Prg); } StackFilter->Prg.Type=Filter->Prg.Type; return true; } bool Unpack::UnpReadBuf30() { int DataSize=ReadTop-Inp.InAddr; // Data left to process. if (DataSize<0) return false; if (Inp.InAddr>BitInput::MAX_SIZE/2) { // If we already processed more than half of buffer, let's move // remaining data into beginning to free more space for new data // and ensure that calling function does not cross the buffer border // even if we did not read anything here. Also it ensures that read size // is not less than CRYPT_BLOCK_SIZE, so we can align it without risk // to make it zero. if (DataSize>0) memmove(Inp.InBuf,Inp.InBuf+Inp.InAddr,DataSize); Inp.InAddr=0; ReadTop=DataSize; } else DataSize=ReadTop; int ReadCode=UnpIO->UnpRead(Inp.InBuf+DataSize,BitInput::MAX_SIZE-DataSize); if (ReadCode>0) ReadTop+=ReadCode; ReadBorder=ReadTop-30; return ReadCode!=-1; } void Unpack::UnpWriteBuf30() { uint WrittenBorder=(uint)WrPtr; uint WriteSize=(uint)((UnpPtr-WrittenBorder)&MaxWinMask); for (size_t I=0;INextWindow) { flt->NextWindow=false; continue; } unsigned int BlockStart=flt->BlockStart; unsigned int BlockLength=flt->BlockLength; if (((BlockStart-WrittenBorder)&MaxWinMask)ParentFilter]->Prg; VM_PreparedProgram *Prg=&flt->Prg; ExecuteCode(Prg); byte *FilteredData=Prg->FilteredData; unsigned int FilteredDataSize=Prg->FilteredDataSize; delete PrgStack[I]; PrgStack[I]=NULL; while (I+1BlockStart!=BlockStart || NextFilter->BlockLength!=FilteredDataSize || NextFilter->NextWindow) break; // Apply several filters to same data block. VM.SetMemory(0,FilteredData,FilteredDataSize); VM_PreparedProgram *ParentPrg=&Filters30[NextFilter->ParentFilter]->Prg; VM_PreparedProgram *NextPrg=&NextFilter->Prg; ExecuteCode(NextPrg); FilteredData=NextPrg->FilteredData; FilteredDataSize=NextPrg->FilteredDataSize; I++; delete PrgStack[I]; PrgStack[I]=NULL; } UnpIO->UnpWrite(FilteredData,FilteredDataSize); UnpSomeRead=true; WrittenFileSize+=FilteredDataSize; WrittenBorder=BlockEnd; WriteSize=uint((UnpPtr-WrittenBorder)&MaxWinMask); } else { // Current filter intersects the window write border, so we adjust // the window border to process this filter next time, not now. for (size_t J=I;JNextWindow) flt->NextWindow=false; } WrPtr=WrittenBorder; return; } } } UnpWriteArea(WrittenBorder,UnpPtr); WrPtr=UnpPtr; } void Unpack::ExecuteCode(VM_PreparedProgram *Prg) { Prg->InitR[6]=(uint)WrittenFileSize; VM.Execute(Prg); } bool Unpack::ReadTables30() { byte BitLength[BC]; byte Table[HUFF_TABLE_SIZE30]; if (Inp.InAddr>ReadTop-25) if (!UnpReadBuf30()) return(false); Inp.faddbits((8-Inp.InBit)&7); uint BitField=Inp.fgetbits(); if (BitField & 0x8000) { UnpBlockType=BLOCK_PPM; return(PPM.DecodeInit(this,PPMEscChar)); } UnpBlockType=BLOCK_LZ; PrevLowDist=0; LowDistRepCount=0; if (!(BitField & 0x4000)) memset(UnpOldTable,0,sizeof(UnpOldTable)); Inp.faddbits(2); for (uint I=0;I> 12); Inp.faddbits(4); if (Length==15) { uint ZeroCount=(byte)(Inp.fgetbits() >> 12); Inp.faddbits(4); if (ZeroCount==0) BitLength[I]=15; else { ZeroCount+=2; while (ZeroCount-- > 0 && IReadTop-5) if (!UnpReadBuf30()) return(false); uint Number=DecodeNumber(Inp,&BlockTables.BD); if (Number<16) { Table[I]=(Number+UnpOldTable[I]) & 0xf; I++; } else if (Number<18) { uint N; if (Number==16) { N=(Inp.fgetbits() >> 13)+3; Inp.faddbits(3); } else { N=(Inp.fgetbits() >> 9)+11; Inp.faddbits(7); } if (I==0) return false; // We cannot have "repeat previous" code at the first position. else while (N-- > 0 && I> 13)+3; Inp.faddbits(3); } else { N=(Inp.fgetbits() >> 9)+11; Inp.faddbits(7); } while (N-- > 0 && IReadTop) return false; MakeDecodeTables(&Table[0],&BlockTables.LD,NC30); MakeDecodeTables(&Table[NC30],&BlockTables.DD,DC30); MakeDecodeTables(&Table[NC30+DC30],&BlockTables.LDD,LDC30); MakeDecodeTables(&Table[NC30+DC30+LDC30],&BlockTables.RD,RC30); memcpy(UnpOldTable,Table,sizeof(UnpOldTable)); return true; } void Unpack::UnpInitData30(bool Solid) { if (!Solid) { TablesRead3=false; memset(UnpOldTable,0,sizeof(UnpOldTable)); PPMEscChar=2; UnpBlockType=BLOCK_LZ; } InitFilters30(Solid); } void Unpack::InitFilters30(bool Solid) { if (!Solid) { OldFilterLengths.SoftReset(); LastFilter=0; for (size_t I=0;I