解压VBA流

VBA工程中,VBA代码是以压缩形式存放在复合文档中。

解压缩的代码如下:

Public Function Decompression(arrByte() As Byte) As Byte()
    '解压VBA流
    '算法说明:https://msdn.microsoft.com/en-us/library/cc313094(v=office.12).aspx
    Dim CompressedContainer() As Byte
    Dim SignatureByte As Byte
    Dim CompressedHeader As Integer
    Dim CompressedChunkSize As Integer
    Dim CompressedChunkSignature As Integer
    Dim CompressedChunkFlag As Integer
    Dim CompressedChunkData() As Byte
    Dim DecompressedChunk() As Byte
    Dim CompressedCurrent As Integer
    Dim DecompressedBuffer() As Byte
    Dim DecompressedStart As Long
    Dim DecompressedChunkSize As Integer
    
    If LBound(arrByte) <> 0 Then Exit Function
    
    CompressedContainer = arrByte
    SignatureByte = CompressedContainer(0)
    If SignatureByte <> 1 Then Exit Function
    CompressedCurrent = 1
    DecompressedStart = 0
    Do While CompressedCurrent < UBound(CompressedContainer)
        CopyMemory CompressedHeader, CompressedContainer(CompressedCurrent), 2
        CompressedChunkSize = ExtractCompressedChunkSize(CompressedHeader)
        CompressedChunkSignature = ExtractCompressedChunkSignature(CompressedHeader)
        CompressedChunkFlag = ExtractCompressedChunkFlag(CompressedHeader)
        If CompressedChunkSignature <> 3 Then Exit Function
        
        If CompressedChunkFlag = 0 Then
            Call DecompressingRawChunk
        Else
            ReDim CompressedChunkData(0 To CompressedChunkSize - 1)
            CopyMemory CompressedChunkData(0), CompressedContainer(CompressedCurrent + 2), CompressedChunkSize
            Call DecompressingTokenSequence(CompressedChunkData, DecompressedChunk)
            CompressedCurrent = CompressedCurrent + 2 + CompressedChunkSize
        End If
        DecompressedChunkSize = UBound(DecompressedChunk) + 1
        ReDim Preserve DecompressedBuffer(0 To DecompressedStart + DecompressedChunkSize - 1)
        CopyMemory DecompressedBuffer(DecompressedStart), DecompressedChunk(0), DecompressedChunkSize
    Loop
    
    Decompression = DecompressedBuffer
End Function

Private Sub DecompressingTokenSequence(CompressedData() As Byte, DecompressedChunk() As Byte)
    Dim i As Integer
    Dim FlagByte As Byte
    Dim FlagBit As Byte
    Dim Index As Integer
    Dim DecompressedCurrent As Integer
    Dim CompressedCurrent As Integer
    Dim CompressedEnd As Integer
    Dim CopyToken As Long
    Dim Offset As Integer
    Dim Length As Integer
    
    ReDim DecompressedChunk(0 To 4098)
    CompressedEnd = UBound(CompressedData)
    DecompressedCurrent = 0
    CompressedCurrent = 0
    Index = 0
    
    Do
        If Index = 0 Then
            FlagByte = CompressedData(CompressedCurrent)
            CompressedCurrent = CompressedCurrent + 1
            If CompressedCurrent > CompressedEnd Then Exit Do
        End If
        
        FlagBit = ExtractFlagBit(Index, FlagByte)
        
        If FlagBit = 0 Then
            DecompressedChunk(DecompressedCurrent) = CompressedData(CompressedCurrent)
            DecompressedCurrent = DecompressedCurrent + 1
            CompressedCurrent = CompressedCurrent + 1
        Else
            CopyMemory CopyToken, CompressedData(CompressedCurrent), 2
            Call UnpackCopyToken(CopyToken, DecompressedCurrent, Offset, Length)
            For i = 1 To Length
                DecompressedChunk(DecompressedCurrent) = DecompressedChunk(DecompressedCurrent - Offset)
                DecompressedCurrent = DecompressedCurrent + 1
            Next
            CompressedCurrent = CompressedCurrent + 2
        End If
        
        Index = Index + 1
        If Index > 7 Then Index = 0
    Loop Until CompressedCurrent > CompressedEnd
    
    ReDim Preserve DecompressedChunk(0 To DecompressedCurrent - 1)
End Sub

Private Function ExtractFlagBit(Index As Integer, FlagByte As Byte) As Byte
    ExtractFlagBit = (FlagByte And (2 ^ Index)) / 2 ^ Index
End Function

Private Sub DecompressingRawChunk()
    '暂未遇到此种情况,有待日后遇到再调试
    Stop
End Sub

Private Function ExtractCompressedChunkSize(Header As Integer) As Integer
    ExtractCompressedChunkSize = (Header And &HFFF) + 1
End Function

Private Function ExtractCompressedChunkSignature(Header As Integer) As Integer
    Dim Temp As Integer
    Temp = Header And &H7000
    ExtractCompressedChunkSignature = Temp / &H1000
End Function

Private Function ExtractCompressedChunkFlag(Header As Integer) As Integer
    Dim Temp As Integer
    Temp = Header And &H8000
    ExtractCompressedChunkFlag = Temp / &H8000
End Function

Private Sub UnpackCopyToken(CopyToken As Long, DecompressedCurrent As Integer, Out_Offset As Integer, Out_Length As Integer)
    Dim bitCount As Integer
    
    bitCount = -Int(-Log(DecompressedCurrent) / Log(2))
    If bitCount < 4 Then bitCount = 4
    bitCount = 16 - bitCount
    Out_Length = CopyToken And (2 ^ bitCount - 1)
    Out_Offset = (CopyToken - Out_Length) / 2 ^ bitCount
    Out_Length = Out_Length + 3
    Out_Offset = Out_Offset + 1
End Sub

 

posted @ 2017-01-20 13:07  wcymiss  阅读(512)  评论(0编辑  收藏  举报