【发布时间】:2019-01-30 09:13:11
【问题描述】:
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 的正确值。但是,它说标题是坏的。
以上代码需要编译测试
"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))
这行听到似乎错误地将我的字符串转换为流。
目标是发送压缩字符串并返回未压缩字符串。
上面的代码是用这个代码调用的
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 标头中仍有幻数不正确。
将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 函数知道,因为文本被压缩,输入大小与输出大小无关。
其他问题
- 可以更有效地编码吗?
- 如何防止恶意代码注入?
- 将输出限制为正确的大小?
- 如果我添加新功能,是否需要更多 Guid?
- 如何生成新的 Guid?
- 在代码块 #3 中,我将 X 转换为字符串 t 并在不进行转换的情况下传输值。
输出大小似乎基于错误信息。
intOutputLength=zipStream.Read(buffer, 0, buffer.Length)
End Using
Decompress=System.Text.Encoding.Default.GetString(buffer, 0, intOutputLength)
至少这减少了返回主程序的数据量。
Dim msgLength As Integer = BitConverter.ToInt32(gzBuffer, 0)
如果我没看错,msgLength 是由输入流的前 4 个字符决定的吗?由于 GZip 标头始终是 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
【问题讨论】:
-
没有将
String转换为Stream这样的事情。您创建一个Stream,将String转换为Byte数组,然后将Byte数组写入Stream。从String到Byte数组时,必须指定编码。如果您传输生成的二进制数据并希望在之后转换回文本,则需要在两个方向上使用相同的编码。最可能的问题是您不是。 -
@jmcilhinney 您可能是对的,但是如何更改我的代码以获得有效的解决方案?源是 gzip 压缩(二进制),输出是文本。
-
您对文本编码进行了一些研究,并确保您在每一端都使用了兼容的编码。 .NET 通过
System.Text.Encoding类提供多种编码作为标准。你知道它们的真正含义吗?如果您遇到编码问题,您应该找出答案。您确切知道您发布的 VBS 代码对应的编码是什么吗?再次,您应该找出答案。 -
@jmcilhinney 源代码是直接二进制,而不是 UTF 任何东西。因此,Encoding.Unicode.GetBytes(value) 似乎不适合这项工作,但我用什么来代替它。我知道 UTF 是什么(这很痛苦),它需要 2 个或更多字节来表示一个字符,这样你就可以拥有每种语言的每个字符。我花了至少 2 天时间才走到这一步。我已经尝试(谷歌搜索)几十种方法将我的二进制字符串放入流中,但都失败了。
-
@cybernard 究竟什么是“直接二进制”,因为 jmcilhinney 已经说过编码必须匹配。