将字符串正确转换为流

Convert a string into a stream correctly

Imports System
Imports System.Runtime.InteropServices
Imports Microsoft.Win32
Imports System.IO
Imports System.IO.Compression
Imports System.Text

Namespace WindowScriptingObject
    <Guid("7448E08D-ED0F-4E23-B528-91937BB41756"), _
        InterfaceType(ComInterfaceType.InterfaceIsIDispatch)> _
        Public Interface _WindowScriptingObject
        <DispId(1)> Function Decompress(ByVal value as String) As String
    End Interface

    <Guid("B146BF9E-78FC-4DB0-ABFE-9FF026B43E4D"), _
        ClassInterface(ClassInterfaceType.None), _
        ProgId("WindowScriptingObject")> Public Class WindowScriptingObject
    Implements _WindowScriptingObject

    Public WindowScriptingObject()
        Public Function Decompress(ByVal value as string) As String Implements _WindowScriptingObject.Decompress
            Dim x As String
            '     on error resume next
            Dim xstream As New MemoryStream(Encoding.Unicode.GetBytes(value))
            Dim mem2 As New IO.MemoryStream()
            'Dim streamMe As New StreamWriter(mem2,Encoding.UTF8)
            'streamMe.Write(value)
            'StreamMe.Close()
            'mem2.Position=0

            Dim gz As New System.IO.Compression.GZipStream(xstream, IO.Compression.CompressionMode.Decompress)

            Dim sr As New IO.StreamReader(gz)
            x = sr.ReadLine

            sr.Close()
            'End Using

            Decompress = x
        End Function
    End Class
End Namespace

我验证了我发送的字符串包含来自我的 VBScript 的正确值。但是,它说 header 不好。

以上代码需要编译测试

"C:\Windows\Microsoft.NET\Framework\v4.0.30319\vbc.exe" /target:library /out:"%userprofile%\desktop\t.dll" "%userprofile%\desktop\t.txt" /verbose

然后注册

"C:\Windows\Microsoft.NET\Framework\v4.0.30319\regasm" /codebase "%userprofile%\desktop\t.dll" /tlb:"%userprofile%\desktop\t.tlb" /v

然后调用

c:\windows\SysWOW64\cscript.exe old.vbs

我放入代码以从文件中读取内容,尽管这不是最终目标。当我这样做时,文件正确解压缩。

Dim xstream As New MemoryStream(Encoding.Unicode.GetBytes(value))

这行似乎错误地将我的字符串转换为流。

目标是发送压缩字符串和 return 未压缩字符串。

上面的代码是用这段代码调用的

Const adTypeBinary = 1
Set wso = CreateObject("WindowScriptingObject")
Dim objStream
Set objStream = CreateObject("ADODB.Stream")
objStream.Type = adTypeBinary
objStream.Open
objStream.LoadFromFile "e:\download\result.gz"
'objStream.Charset = "Windows-1252" 
x = objStream.Read(900)
objStream.Close

For i=1 To Len(x) 
    t = t & Chr(AscW(Mid(x, i, 1)) And 255)
    t = t & Chr((AscW(Mid(x, i, 1)) And 65280)/256)
Next
MsgBox wso.Decompress(t), , "vbs"

我试过了,甚至将字符串转换为 base64 以使其工作。

Dim gzBuffer As Byte() = Convert.FromBase64String(value)
    Using ms As New MemoryStream()
        Dim msgLength As Integer = BitConverter.ToInt32(gzBuffer, 0)
        ms.Write(gzBuffer, 4, gzBuffer.Length - 4)

        Dim buffer As Byte() = New Byte(msgLength - 1) {}
        ms.Position = 0
        Using zipStream As New System.IO.Compression.GZipStream(ms, System.IO.Compression.CompressionMode.Decompress)
              zipStream.Read(buffer, 0, buffer.Length)
        End Using
    Decompress=System.Text.Encoding.Unicode.GetString(buffer, 0, buffer.Length)
    End Using

数据没有正确转换,因为我在 GZip 中仍然有幻数 header 不正确。

将base64编码值转储到在线解码器中,我传入的字符串与解码值匹配。

版本 2 强迫我对其进行 base64 编码,但随后它就起作用了。 我该如何消除这种烦恼。

Imports System
Imports System.Runtime.InteropServices
Imports Microsoft.Win32
Imports System.IO
Imports System.IO.Compression
Imports System.Text

Namespace WindowScriptingObject
    <Guid("7448E08D-ED0F-4E23-B528-91937BB41756"), _
        InterfaceType(ComInterfaceType.InterfaceIsIDispatch)> _
        Public Interface _WindowScriptingObject
        <DispId(1)> Function Decompress(ByVal value as String) As String
    End Interface

    <Guid("B146BF9E-78FC-4DB0-ABFE-9FF026B43E4D"), _
        ClassInterface(ClassInterfaceType.None), _
        ProgId("WindowScriptingObject")> Public Class WindowScriptingObject
    Implements _WindowScriptingObject

    Public WindowScriptingObject()
        Public Function Decompress(ByVal value as string) As String Implements _WindowScriptingObject.Decompress
            Dim x As String
            '     on error resume next
    Dim gzBuffer As Byte() = Convert.FromBase64String(value)
    Using ms As New MemoryStream()
        Dim msgLength As Integer = BitConverter.ToInt32(gzBuffer, 0)
        ms.Write(gzBuffer, 0, gzBuffer.Length)

        Dim buffer As Byte() = New Byte(msgLength - 1) {}
        ms.Position = 0
        Using zipStream As New System.IO.Compression.GZipStream(ms, System.IO.Compression.CompressionMode.Decompress)
              zipStream.Read(buffer, 0, buffer.Length)
        End Using
    Decompress=System.Text.Encoding.ASCII.GetString(buffer, 0, buffer.Length)
    End Using
'            Dim xstream As New MemoryStream(value.ToArray())
            Dim mem2 As New IO.MemoryStream()
            'Dim streamMe As New StreamWriter(mem2,Encoding.UTF8)
            'streamMe.Write(value)
            'StreamMe.Close()
            'mem2.Position=0

            'Dim gz As New System.IO.Compression.GZipStream(xstream, IO.Compression.CompressionMode.Decompress)

            'Dim sr As New IO.StreamReader(gz)
           ' x = sr.ReadLine

            'sr.Close()
            'End Using

            'Decompress = x
        End Function
    End Class
End Namespace

除了输出大小为 500K 且只有 3100 字节的文本外,更新此代码有效。

Imports System
Imports System.Runtime.InteropServices
Imports Microsoft.Win32
Imports System.IO
Imports System.IO.Compression
Imports System.Text

Namespace WindowScriptingObject
    <Guid("7448E08D-ED0F-4E23-B528-91937BB41756"), _
        InterfaceType(ComInterfaceType.InterfaceIsIDispatch)> _
        Public Interface _WindowScriptingObject
        <DispId(1)> Function Decompress(ByVal value as string) As String
    End Interface

    <Guid("B146BF9E-78FC-4DB0-ABFE-9FF026B43E4D"), _
        ClassInterface(ClassInterfaceType.None), _
        ProgId("WindowScriptingObject")> Public Class WindowScriptingObject
    Implements _WindowScriptingObject

    Public WindowScriptingObject()
        Public Function Decompress(ByVal value as string) As String Implements _WindowScriptingObject.Decompress
            '     on error resume next
    Dim gzBuffer() As Byte = System.Text.Encoding.Default.Getbytes(value)

    Using ms As New MemoryStream()
        Dim msgLength As Integer = BitConverter.ToInt32(gzBuffer, 0)
        ms.Write(gzBuffer, 0, gzBuffer.Length)
 msgbox(msgLength)
        Dim buffer As Byte() = New Byte(msgLength - 1) {}
        ms.Position = 0

        Using zipStream As New System.IO.Compression.GZipStream(ms, System.IO.Compression.CompressionMode.Decompress)
              zipStream.Read(buffer, 0, buffer.Length)
        End Using
    Decompress=System.Text.Encoding.Default.GetString(buffer, 0, buffer.Length)
    End Using

        End Function
    End Class
End Namespace

由于某些原因,msgLength 的大小为 559,903,解压后的文本大约为 3100 字节。这意味着 BitConverter.toint32 出现故障,因为 gzBuffer 是 865 字节。最终输出大小只有 GZIPStream 函数知道,因为文本是压缩的,输入大小与输出大小无关。

其他问题

  1. 这可以更有效地编码吗?
  2. 如何防止恶意代码注入?
  3. 将输出限制为正确的大小?
  4. 如果我添加新功能,是否需要更多 Guid?
  5. 如何生成新的 Guid?
  6. 在代码块 #3 中,我将 X 转换为字符串 t 并在不进行转换的情况下传输值。

输出大小似乎基于错误信息。

intOutputLength=zipStream.Read(buffer, 0, buffer.Length)
End Using
Decompress=System.Text.Encoding.Default.GetString(buffer, 0, intOutputLength)

至少这样可以减少主程序的数据量return。

Dim msgLength As Integer = BitConverter.ToInt32(gzBuffer, 0)

如果我没看错的话,msgLength 是由输入流的前 4 个字符决定的?因为 GZip header 总是 1f 8b 08 00 这似乎是一个可怕的想法。如果输出每次都大于 559k,则看起来像是等待发生的缓冲区溢出。

我认为这解决了可怕的缓冲区大小问题。

Imports System
Imports System.Runtime.InteropServices
Imports Microsoft.Win32
Imports System.IO
Imports System.IO.Compression
Imports System.Text


Namespace WindowScriptingObject
    <Guid("7448E08D-ED0F-4E23-B528-91937BB41756"), _
        InterfaceType(ComInterfaceType.InterfaceIsIDispatch)> _
        Public Interface _WindowScriptingObject
        <DispId(1)> Function Decompress(ByVal value as string) As String
    End Interface


    <Guid("B146BF9E-78FC-4DB0-ABFE-9FF026B43E4D"), _
        ClassInterface(ClassInterfaceType.None), _
        ProgId("WindowScriptingObject")> Public Class WindowScriptingObject
    Implements _WindowScriptingObject


    Public WindowScriptingObject()
        Public Function Decompress(ByVal value as string) As String Implements _WindowScriptingObject.Decompress
            '     on error resume next
    Dim gzBuffer() As Byte = System.Text.Encoding.Default.Getbytes(value)
    dim intOutputLength as integer
    Dim intBlock as integer
    Decompress=""
    Using ms As New MemoryStream()
        Dim msgLength As Integer = 4096
        ms.Write(gzBuffer, 0, gzBuffer.Length)

        Dim buffer As Byte() = New Byte(4096) {}
        ms.Position = 0

        Using zipStream As New System.IO.Compression.GZipStream(ms, System.IO.Compression.CompressionMode.Decompress)
        intOutputLength=0
        intBlock=4096
        while intBlock=4096
              intBlock=zipStream.Read(buffer, 0, buffer.Length)
            Decompress+=System.Text.Encoding.Default.GetString(buffer, 0, intBlock)
            intOutputLength+=intBlock
        end while 
        End Using

    End Using

        End Function
    End Class
End Namespace

太久没写vbscript了,所以我知道的不够多,无法给出修复。但是,我可以指出此代码的 vbscript 部分中的一些严重缺陷

它首先从 .gz 文件中读取最多 900 个字节,而不考虑文件的实际长度。任何超过 900 字节的内容都不会被读取。

它以二进制模式执行此读取。二进制模式忽略任何字符集或编码信息,只读取原始字节,适合 .gz 文件。但是,此数据接下来发生的事情是使用 Len() 函数,该函数用于 strings,而不是二进制数据; Len() 不是 此处的适当函数。此外,接下来通过 Mid() 函数在 For 循环中使用数据。 Mid() 同样 仅用于字符串 ,并且 x 变体 不是字符串 。 vbscript 字符串对象不仅仅是原始字符;它们包括编码、长度和字符缓冲区等元数据,而这些字符串函数依赖于使用所有元数据正确构造的对象。

不可能这个 vbscript 产生正确的结果。在解决这个问题之前,即使查看 vb.net 代码也毫无意义。同样,我太过分了,无法提出真正的解决方案,但我建议尝试将未更改的字节数组而不是字符串传递给 .Net 端。

通过将 VB.NET 函数和界面更改为如下所示(主要是更改参数类型),我能够使您的代码正常工作:

<Guid("7448E08E-ED0F-4E23-B528-91937BB41756"),
        InterfaceType(ComInterfaceType.InterfaceIsIDispatch)>
Public Interface _WindowScriptingObject
   <DispId(1)> Function Decompress(ByVal value As Byte()) As String
End Interface

Public Function Decompress(ByVal value As Byte()) As String Implements _WindowScriptingObject.Decompress
   Using xstream As New MemoryStream(value)
      Using gz As New System.IO.Compression.GZipStream(xstream, IO.Compression.CompressionMode.Decompress)
         Using sr As New IO.StreamReader(gz)
            Return sr.ReadLine()
         End Using
      End Using
   End Using
End Function

我的测试 VBS 是这样的

Const adTypeBinary = 1
Dim wso
Set wso = CreateObject("WindowScriptingObject")
Dim objStream, x
Set objStream = CreateObject("ADODB.Stream")
objStream.Type = adTypeBinary
objStream.Open
objStream.LoadFromFile "c:\users\bluem\desktop\Notes.txt.gz"
x = objStream.Read(342737)
objStream.Close
WScript.StdOut.WriteLine wso.Decompress((x))

我不完全确定为什么我需要将 x 参数括在两组括号中,但我认为这与强制参数通过值而不是通过引用传递有关,并有助于它转换到一个字节数组。在我添加额外的一对括号之前出现错误。

编辑: 回答您的其他一些问题:

  • 我认为您不需要为新功能创建新的 GUID,只需为新界面或 class。
  • 要创建一个新的 GUID,您只需复制一个现有的 GUID 并将其部分更改为唯一(包括 0 和 F 之间的数字),或者您可以转至 https://www.guidgenerator.com/ 或者您可以 select "Create GUID" 来自 Visual Studio 的工具菜单。
  • 如果你能根据新代码弄清楚你的数据长度问题(如果问题仍然存在),我也许能回答。