Правильно преобразовать строку в поток

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

Обновление этого кода работает, за исключением того, что размер вывода составляет 500 КБ, а текст - всего 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. Как создать новый гайд?
  6. В блоке кода №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, это кажется ужасной идеей. Если вывод каждый раз превышает 559 КБ, это похоже на переполнение буфера, которое только и ждет, чтобы произойти.

Я думаю, что это решает ужасную проблему размера буфера.

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 29.01.2019 05:25

@jmcilhinney Возможно, вы правы, но как мне изменить свой код, чтобы получить работающее решение? Источник представляет собой сжатый gzip (двоичный файл), а вывод - текст.

cybernard 29.01.2019 05:43

Вы проводите некоторое исследование кодирования текста и убедитесь, что вы используете совместимое кодирование на каждом конце. .NET стандартно предлагает несколько кодировок через класс System.Text.Encoding. Вы знаете, что они все на самом деле означают? Если у вас возникли проблемы с кодировкой, вы должны выяснить это. Вы точно знаете, какой кодировке соответствует опубликованный вами код VBS? Опять же, вы должны узнать.

jmcilhinney 29.01.2019 05:56

@jmcilhinney Источник является прямым двоичным кодом, а не UTF. Поэтому Encoding.Unicode.GetBytes(value) кажется неподходящим для работы, но что мне использовать вместо него. Я знаю, что такое UTF (боль в...), который занимает 2 или более байтов для представления одного символа, поэтому вы можете иметь каждый символ на любом языке. Я потратил как минимум 2 дня, чтобы добраться до этого места. Я пробовал (погуглил) десятки способов передать мою двоичную строку в поток, и они терпят неудачу.

cybernard 29.01.2019 15:02

@cybernard Что такое «прямой двоичный код», как уже сказал jmcilhinney, кодировки должны совпадать.

user692942 29.01.2019 23:46

Просто чтобы внести ясность: насколько я понимаю, вам нужно обрабатывать данные gzip из vbscript. Поскольку vbscript никогда не получал встроенной поддержки gzip и теперь умирает, поэтому никогда не будет, вы надеетесь написать метод .Net, который вы можете вызывать из vbscript, который может распаковывать данные gzip для вас. Таким образом, любое решение, позволяющее распаковывать данные gzip, подойдет, верно?

Joel Coehoorn 30.01.2019 00:37

@JoelCoehoorn в основном СинийМонахMN имеет правильную идею. Но да, это правильная мысль. Он должен получить строку, а не файл, и вернуть строку. За исключением части «надеюсь», потому что процесс завершен на 90%, и у меня есть рабочий код прототипа. Я потрачу большую часть завтрашнего дня, работая над этим.

cybernard 30.01.2019 04:55
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
7
397
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Прошло слишком много времени с тех пор, как я писал vbscript, поэтому я больше не знаю достаточно, чтобы исправлять ошибки. Однако я могу указать на некоторые серьезные недостатки в части vbscript этого кода.

Он начинается с чтения до 900 байт из файла .gz, независимо от фактической длины файла. Все, что длиннее 900 байт, не будет прочитано.

Он выполняет это чтение в двоичном режиме. Двоичный режим игнорирует любой набор символов или информацию о кодировке и просто считывает необработанные байты, что подходит для файла .gz. Однако следующее, что происходит с этими данными, — это использование функции Len(), предназначенной для струны, а не для двоичных данных; Len() — это нет, соответствующая функция здесь. Кроме того, данные затем используются в цикле For через функцию Mid(). 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 для новой функции, только для нового интерфейса или класса.
  • Чтобы создать новый GUID, вы можете просто скопировать существующий и изменить его часть (цифры от 0 до F включительно), чтобы он был уникальным, или вы можете перейти к https://www.guidgenerator.com/ или выбрать «Создать GUID» в меню «Инструменты» Visual Studio.
  • Если вы можете прояснить свою проблему с длиной данных на основе нового кода (если проблема все еще существует), я мог бы ответить.

Хорошо, это выглядит довольно хорошо. Завтра сделаю тест. Однако как я могу добавить обработку ошибок? Скажем, кто-то передает строку 00, теперь gzip выдает ошибку, которую нужно обработать и передать обратно. Кроме того, что произойдет, если x = objStream.Read(342737) будет сокращено до Read(1000), поскольку файл больше, это вызовет ошибку, которую мне нужно будет обработать.

cybernard 30.01.2019 05:03

Да, ваш код vb.net проверен и исправен. Я не знаю почему, но без ((x)) мне отказывают в доступе. Я пробовал все комбинации byval, byRef, value, value(), byte и byte(), которые я мог найти, но они недоступны. Однако это избавляет меня от необходимости инвертировать мой буфер вручную, поэтому я сохраняю его.

cybernard 31.01.2019 00:51

@cybernard 342737 был просто для тестирования. Вы должны использовать adReadAll для чтения всего, что находится в потоке, вместо фиксированной длины. Вы можете обрабатывать ошибки в VBScript с помощью On Error Resume Next и в VB.NET с помощью try и catch.

BlueMonkMN 02.02.2019 02:24

Другие вопросы по теме