【问题标题】:XMLHTTP Request (POST) to retrieve data from web site using VBScriptXMLHTTPRequest (POST) 使用 VBScript 从网站检索数据
【发布时间】:2013-08-23 00:39:39
【问题描述】:

亲爱的同学们!我需要你的帮助。

首先,这不是广告。我尝试在火车票网站http://booking.uz.gov.ua/en/ 上发出 POST 请求,以了解当前日期是否有票。但是……有问题。我正在使用 VBScript 发出请求。为了知道在网站上发送什么 HTTP Header 和 POST 请求,我使用了 Chrome 内置开发工具。

这是我的脚本:

    Dim URL
    Dim URL2
    Dim URL3
    Dim sRequest
    Dim sCookies

    'This is web page where I need to enter information.
    URL = "http://booking.uz.gov.ua/en/"
    'This is path that Chrome shows to send POST request.
    URL2 = "http://booking.uz.gov.ua/en/purchase/search/"
    'Optional URL, Chrome shows this link near of URL2. I think this is .js that works                 on info I enter on web site (URL).
    'URL3 = "http://booking.uz.gov.ua/i/js/common.138.js"
    'POST request that Chrome shows to send.
    sRequest ="station_id_from=2200001&station_id_till=2208001&station_from=Kyiv&station_till=Odesa&date_        dep=09.19.2013&time_dep=00%3A00&search="
    'Here I'm using GET request to retrieve Set-Cookie Header (SessionID first of all)         to reuse in my second POST request.
    sCookies = GetSetHeader(URL)
    'Here I'm calling function to make POST request.
    Result = HTTPPost(URL2, sRequest)

    Function GetSetHeader(URL)
       Set objhttp = CreateObject("Microsoft.XmlHttp")
       objhttp.open "GET", URL, FALSE
       objhttp.Send
       'I'm getting only SessionID + other cookies that Chrome shows.
       GetSetHeader = Left (objhttp.getResponseHeader("Set-Cookie"), 38) & " " &         "HTTPSERVERID=server1; _gv_lang=en;         __utma=31515437.675496133.1376934004.1376934004.1376934004.1;         __utmb=31515437.2.10.1376934004; __utmc=31515437; __utmz=31515437.1376934004.1.1.utmcsr=        (direct)|utmccn=(direct)|utmcmd=(none)"
    End Function

    Function HTTPPost(URL2, sRequest) 
       'Header I just took from Chrome.
       Set objhttp = CreateObject("Microsoft.XmlHttp")
       objHTTP.open "POST", URL2, false
       objHTTP.setRequestHeader "Connection", "keep-alive"
       objHTTP.setRequestHeader "Host", "booking.uz.gov.ua"
       objHTTP.setRequestHeader "Connection", "keep-alive"
       objHTTP.setRequestHeader "Content-Length", "Len(Request)"
       objHTTP.setRequestHeader "GV-Token", "64214392f178b9f91e3b61a069915cd1"
       objHTTP.setRequestHeader "Origin", "http://booking.uz.gov.ua"
       objHTTP.setRequestHeader "User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64)         AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36"
       objHTTP.setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
       objHTTP.setRequestHeader "GV-Unique-Host", "1"
       objHTTP.setRequestHeader "GV-Ajax", "1"
       objHTTP.setRequestHeader "GV-Screen", "1366x768"
       objHTTP.setRequestHeader "GV-Referer", "http://booking.uz.gov.ua/en/"
       objHTTP.setRequestHeader "Accept", "*/*"
       objHTTP.setRequestHeader "Referer", "http://booking.uz.gov.ua/en/"
       objHTTP.setRequestHeader "Accept-Encoding", "gzip,deflate,sdch"
       objHTTP.setRequestHeader "Accept-Language", "ru-RU,ru;q=0.8,en-        US;q=0.6,en;q=0.4"
       'Here I use cookies retrieved with first GET request.
       objHTTP.setRequestHeader "Cookie", "sCookies"
       objHTTP.send sRequest
       'I use this msg to check that right cookies send with POST request.
        WScript.Echo sCookies
        HTTPPost =  objHttp.responseText
       'Write answer to TXT file.
       Set FSO = CreateObject("Scripting.FileSystemObject")
       Set oFile = FSO.OpenTextFile("D:\Results.txt", 2, True)
       oFile.Write(objHttp.responseText)
       oFile.Close
       Set oFile = Nothing
       Set FSO = Nothing
    end Function

我不能让我的脚本工作。如果我使用 URL2 发送请求,我会得到空的 TXT 文件。如果我使用 URL3(Chrome 显示给我的脚本路径)发送请求,我只会在我的 TXT 文件中收到 common.138.js 的内容。但我希望收到 JSON 类型的信息,如 Chrome 响应中所示。

我注意到了, 首先,如果刷新网站,并尝试重新发送旧请求,我给我一个错误:

NetworkError: 400 Bad Request - http://booking.uz.gov.ua/en/purchase/search/"

可能是因为 SessionID 改变了。

其次,我不能简单地写站名,我需要从下拉列表中选择它(在 UI 模式下使用此站点时)。或者我收到一个错误 - 从下拉列表中选择一个出发点。

第三,如果尝试通过单击站点上的按钮进行搜索来发送请求,我会收到错误状态代码:400 错误请求。我认为 SessionID 的时间已过期。

有一个使用 InternetExplorer.Application 系统对象的工作脚本,但它没有决定。我想通过发送请求使其工作。将来想尝试在php上做(作为学习过程的一部分)。

也许这是对像我这样的人的某种防御???有办法让我的脚本工作???也许 GET 和 POST 请求之间的 SessionID 会发生变化?或者也许 VBScript 无法解决它,我需要 PHP,例如???

我不知道如何解决这个问题。请帮帮我。睡不着。不能吃。非常感谢。

【问题讨论】:

    标签: post vbscript get request xmlhttprequest


    【解决方案1】:

    您收到“400”是因为您向 UZ 网站发送了错误的 GV-Token 标头。 最终,GV-token 是某个会话相关变量的 md5(会话通过 _gv_sessid cookie 标识)。

    这个标记在 JavaScript 中被混淆并驻留在页面正文中,例如,

    ...
    $$_.$_=($$_.$_=$$_+"")[$$_.$_$]+($$_._$=$$_.$_[$$_.__$])+($$_.$$=($$_.$+"")[$$_.__$])+((!$$_)+"")[$$_._$$]+($$_.__=$$_.$_[$$_.$$_])
    ...
    

    计算结果类似于

    localStorage.setItem('gv-token',4619709a341b4ffdacce3dafd2f85af3)
    

    然后进行到所有 UZ Ajax 请求。

    所以我祝你反混淆快乐:))(不适合弱者)

    PS 还要确保通过应用配置或反射打开 .NET useUnsafeHeaderParsing

    UPD:正如我所见,这个话题仍然存在,所以我编写了反混淆代码 - 似乎基本的正则表达式和字符串搜索和替换就足够了。

    假设您在 pageHTML 中有 UZ 起始页 HTML,那么为了使事情正常运行,您需要在 C# 中使用类似(无有效性检查)的东西:

    混淆代码包含一些标记,每个标记为一个从 0 到 F 的十六进制数,它们可以直接替换。这是一个对应字典:

    var subsitutes = new Dictionary<string, string>
    {
        {"$$_.$$$", "7"},
        {"$$_.$$$$", "f"},
        {"$$_.$$$_", "e"},
        {"$$_.$$_", "6"},
        {"$$_.$$_$", "d"},
        {"$$_.$$__", "c"},
        {"$$_.$_$", "5"},
        {"$$_.$_$$", "b"},
        {"$$_.$_$_", "a"},
        {"$$_.$__", "4"},
        {"$$_.$__$", "9"},
        {"$$_.$___", "8"},
        {"$$_._$$", "3"},
        {"$$_._$_", "2"},
        {"$$_.__$" ,"1"},
        {"$$_.___", "0"},
    };
    

    然后通过使用正则表达式,我们得到了我们感兴趣的部分代码

    var scramble = Regex.Match(pageHTML, @"\$\$_\.\$\(\$\$_\.\$\((.*)\)\(\)\)\(\);");
    

    并将上面提到的标记替换为它们的真正含义

    var keysSorted = subsitutes.Keys.OrderByDescending(key => key.Length);   
    var halfBakedDeobfuscated = keysSorted.Aggregate(scramble.Groups[1].Value, (current, key) => current.Replace(key, subsitutes[key]));
    

    快完成了,扔掉一些垃圾

    var start = Regex.Escape(new string(new[] { '"', '\\', '\\', '\\', '"', ',', '\\', '\\', '"', '+' }) + "4+0+" + new string(new[] { '\"', '\\', '\\', '\\', '\"', '\"', '+' }));
    var end = Regex.Escape(new string(new[] { '+', '"', '\\', '\\', '\\', '"', ')' }));    
    var core = Regex.Match(halfBakedDeobfuscated, start + "(.*)" + end).Groups[1].Value;
    

    现在 core 包含几乎干净的 gvToken 版本,例如 7+0+f+a+7+7+9+8+5+7+e+b+3+3+a+8+3+c+7+8+3+b+d+d+e+f+4+8+7+7+f+7 所以最后一步是删除这些+ 符号

    var gvToken = string.Join(string.Empty, core.Split('+'));
    

    最后,gvToken 包含您需要提供给 UZ 网站的内容 - 一个类似 70fa779857eb33a83c783bddef4877f7 的字符串。

    不需要 JS 库,当然也不需要 InternetExplorer。

    【讨论】:

    • 你好,@Shorstok。感谢您的更新。由于问题是在不同的帐户下创建的,因此刚刚看到您更新了答案。我忘记了这个问题。但是你做了更新,我对这个问题重新产生了兴趣。不幸的是,在这段时间里,我没有在这个方向上提高我的技能。所以如何应用它不是很清楚。但我会试着弄清楚这些奇怪的词是什么意思,以及浏览器是如何工作的。我在Udacity - Building a Web Browser 上找到了课程。希望这将有助于我更深入地潜水。谢谢。祝你有美好的一天。
    • 还找到了一本书HTTP Programming Recipes for C# Bots。也许也会有所帮助。
    • 您是如何分别找出正确的PostDataAuthToken.Pattern 的?我试图从安全站点下载文档,但它没有公开 URL 字符串中的任何内容。当我想下载时,我得到的只是401 + authorisation token missing。我把登录页面的ResponseTextGetAllResponseHeaders()放在pastebin上。我找不到与授权令牌相关的任何内容。
    • 它是高度特定于站点的,但它要么在 cookie 中,要么通过 javascript 生成(或检索),或者(不可能)写入 HTML
    【解决方案2】:

    你在这里犯了错误:

    objHTTP.setRequestHeader "Content-Length", "Len(Request)"

    应该是:

    objHTTP.setRequestHeader "Content-Length", Len(Request)

    【讨论】:

      【解决方案3】:

      有趣,需要用到InternetExplorer.Application,例如列出powershell代码:

      $erroractionpreference = "Continue"
      $ie = New-Object -ComObject "InternetExplorer.Application"
      $ie.navigate("http://booking.uz.gov.ua/en/")
      $ie.visible = $true
      sleep 5
      while($ie.ReadyState -ne 4) {start-sleep -m 100}
      $ie.document.getElementByID("station_id_from").Value = "2200001"
      $ie.document.getElementByID("station_id_till").Value = "2208001"
      $ie.document.getElementsByName("station_from").Item(1).Value = "Kyiv"
      $ie.document.getElementsByName("station_till").Item(1).Value = "Odesa"
      $ie.document.getElementByID("date_dep").Value = "12.26.2014"
      $ie.document.getElementByID("time_dep").Value = "00:00"
      $ie.document.getElementByID("search").Click()  
      

      Cookie,包括 GV-Token,在这种情况下无需转移。我认为,有一种方法可以在没有 InternetExplorer.Application 的情况下编写,但可以使用您的代码模拟浏览器。需要探索一下。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-11-16
        • 1970-01-01
        • 2018-07-01
        • 2015-06-25
        • 2014-12-01
        相关资源
        最近更新 更多