{"id":9282,"date":"2024-07-07T00:04:13","date_gmt":"2024-07-07T00:04:13","guid":{"rendered":"https:\/\/www.hexacorn.com\/blog\/?p=9282"},"modified":"2024-07-07T21:40:44","modified_gmt":"2024-07-07T21:40:44","slug":"writing-a-frida-based-vbs-api-monitor","status":"publish","type":"post","link":"https:\/\/www.hexacorn.com\/blog\/2024\/07\/07\/writing-a-frida-based-vbs-api-monitor\/","title":{"rendered":"Writing a Frida-based VBS API monitor"},"content":{"rendered":"\n<p><strong>Update<\/strong><\/p>\n\n\n\n<p>See the updated version of the script <a href=\"https:\/\/www.hexacorn.com\/blog\/2024\/07\/07\/writing-a-frida-based-vbs-api-monitor-take-two\/\">here<\/a>.<\/p>\n\n\n\n<p><strong>Old Post<\/strong><\/p>\n\n\n\n<p>I love experimenting with Frida and I have presented a <a href=\"https:\/\/www.hexacorn.com\/blog\/2022\/02\/20\/delphi-api-monitoring-with-frida-part-3\/\" data-type=\"post\" data-id=\"8007\">few<\/a> <a href=\"https:\/\/www.hexacorn.com\/blog\/2024\/03\/31\/subfrida-v0-1\/\" data-type=\"post\" data-id=\"9130\">different<\/a> API Monitoring prototypes based on this framework a few times before&#8230;<\/p>\n\n\n\n<p>Today I will demonstrate how to write a quick &amp; dirty VBS\/VBScript (Visual Basic Script) API Monitor.<\/p>\n\n\n\n<p>As many of you probably know, when we execute <em>.vbs<\/em> scripts via <em>cscript.exe<\/em> or <em>wscript.exe <\/em>the actual dirty work is done by the <em>vbscript.dll<\/em>. From some quick poking around inside this library I can tell that many functions available to Visual Basic Script programmers are implemented as callbacks, and these callback routines rely on a <a href=\"https:\/\/learn.microsoft.com\/en-us\/windows\/win32\/winauto\/variant-structure\">VARIANT<\/a> structure. What it means in practice is that arguments are passed by means of using this structure, where the first field of the structure defines the type of the data being passed as an argument, followed by the actual data or offset to data. On a 64-bit version of Windows, and using the 64-bit version of <em>vbscript.dll<\/em>, the first field of this VARIANT structure is 8-bytes long (qword).<\/p>\n\n\n\n<p>The below screenshot from XDBG shows an example of such structure as seen in memory:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/www.hexacorn.com\/blog\/wp-content\/uploads\/2024\/07\/vbs1_variant.png\"><img decoding=\"async\" loading=\"lazy\" width=\"577\" height=\"29\" src=\"https:\/\/www.hexacorn.com\/blog\/wp-content\/uploads\/2024\/07\/vbs1_variant.png\" alt=\"\" class=\"wp-image-9284\" srcset=\"https:\/\/www.hexacorn.com\/blog\/wp-content\/uploads\/2024\/07\/vbs1_variant.png 577w, https:\/\/www.hexacorn.com\/blog\/wp-content\/uploads\/2024\/07\/vbs1_variant-300x15.png 300w, https:\/\/www.hexacorn.com\/blog\/wp-content\/uploads\/2024\/07\/vbs1_variant-500x25.png 500w\" sizes=\"(max-width: 577px) 100vw, 577px\" \/><\/a><\/figure>\n\n\n\n<p>The first qword is holding a value 8 which means it is a String. The address following this qword is the offset to the actual Unicode string.<\/p>\n\n\n\n<p>The VARIANT types are described all over the place, and sometimes these descriptions actually vary. This is because of the migration from the 32- to 64-bit architecture, plus some of the types have evolved over time. And on top of that, this is a shared commodity &#8212; win32 APIs use it, COM methods use it, VBS uses it, VB uses it and VBA as well. It&#8217;s a bit of a mess (f.ex. type &#8216;2&#8217; may be a <em>vbInteger<\/em> or <em>vbShort<\/em>, depending on where you look).<\/p>\n\n\n\n<p>It doesn&#8217;t matter from our perspective too much as we can build a list of known values (this is not a final list!) that is good enough for our purposes:<\/p>\n\n\n\n<ul>\n<li>0 &#8211; vbEmpty<\/li>\n\n\n\n<li>1 &#8211; vbNull<\/li>\n\n\n\n<li>2 &#8211; vbShort<\/li>\n\n\n\n<li>3 &#8211; vbInteger<\/li>\n\n\n\n<li>4 &#8211; vbSingle<\/li>\n\n\n\n<li>5 &#8211; vbDouble<\/li>\n\n\n\n<li>6 &#8211; vbCurrency<\/li>\n\n\n\n<li>7 &#8211; vbDate<\/li>\n\n\n\n<li>8 &#8211; vtString<\/li>\n\n\n\n<li>9 &#8211; vbObject<\/li>\n\n\n\n<li>10 &#8211; vbError<\/li>\n\n\n\n<li>11 &#8211; vbBoolean<\/li>\n\n\n\n<li>12 &#8211; vbVariant<\/li>\n\n\n\n<li>13 &#8211; vbDataObject<\/li>\n\n\n\n<li>14 &#8211; vbDecimal<\/li>\n\n\n\n<li>17 &#8211; vbByte<\/li>\n\n\n\n<li>17 &#8211; vbChar<\/li>\n\n\n\n<li>20 &#8211; vbLong<\/li>\n\n\n\n<li>36 &#8211; vbUserDefined<\/li>\n\n\n\n<li>8192 &#8211; vbArray<\/li>\n\n\n\n<li>16384 &#8211; vbByRef<\/li>\n\n\n\n<li>16392 &#8211; vbStringByRef<\/li>\n<\/ul>\n\n\n\n<p>Now that we know how the data is being passed to these callbacks, let&#8217;s find out where they reside inside the <em>vbscript.dll<\/em>.<\/p>\n\n\n\n<p>One way to find them is to analyze the library&#8217;s disassembled code and look for popular VBS function names f.ex. <em>MsgBox <\/em>and see where they are being referenced, f.ex.:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><a href=\"https:\/\/www.hexacorn.com\/blog\/wp-content\/uploads\/2024\/07\/vbs2.png\"><img decoding=\"async\" src=\"https:\/\/www.hexacorn.com\/blog\/wp-content\/uploads\/2024\/07\/vbs2-1024x225.png\" alt=\"\" class=\"wp-image-9285\" width=\"512\" srcset=\"https:\/\/www.hexacorn.com\/blog\/wp-content\/uploads\/2024\/07\/vbs2-1024x225.png 1024w, https:\/\/www.hexacorn.com\/blog\/wp-content\/uploads\/2024\/07\/vbs2-300x66.png 300w, https:\/\/www.hexacorn.com\/blog\/wp-content\/uploads\/2024\/07\/vbs2-768x169.png 768w, https:\/\/www.hexacorn.com\/blog\/wp-content\/uploads\/2024\/07\/vbs2-500x110.png 500w, https:\/\/www.hexacorn.com\/blog\/wp-content\/uploads\/2024\/07\/vbs2.png 1051w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<p>And yes, we can easily find a reference to <em>MsgBox<\/em> function inside a very large subroutine that appears to be handling all vbs functions really &#8211; with some additional analysis it is pretty clear that it is most likely the main dispatcher function. What&#8217;s also encouraging is the fact that further code analysis shows that the compiler was very nice to us &#8211; for all these string comparisons looking for vbs functions it generated code that is easy to process in a batch way. As you can see from the below snippet, if there is a string match, a pointer to the structure defining the function&#8217;s call back is always assigned to <em>rdi <\/em>register the same way:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full is-resized\"><a href=\"https:\/\/www.hexacorn.com\/blog\/wp-content\/uploads\/2024\/07\/vbs3.png\"><img decoding=\"async\" src=\"https:\/\/www.hexacorn.com\/blog\/wp-content\/uploads\/2024\/07\/vbs3.png\" alt=\"\" class=\"wp-image-9286\" width=\"512\" srcset=\"https:\/\/www.hexacorn.com\/blog\/wp-content\/uploads\/2024\/07\/vbs3.png 603w, https:\/\/www.hexacorn.com\/blog\/wp-content\/uploads\/2024\/07\/vbs3-300x219.png 300w, https:\/\/www.hexacorn.com\/blog\/wp-content\/uploads\/2024\/07\/vbs3-411x300.png 411w\" sizes=\"(max-width: 603px) 100vw, 603px\" \/><\/a><\/figure>\n\n\n\n<p>Because of this pattern we can easily enumerate all these repetitive sequences and start generating Frida handlers!<\/p>\n\n\n\n<p>So, we write a simple idapython script that will walk through all the chunks of the dispatcher function, find all occurrences of what looks like a function name comparison, and once these are found &#8211; we discover the offset to their callback structure, then use it to find the vbs function callback routine and then generate its generic Frida handler.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/www.hexacorn.com\/blog\/wp-content\/uploads\/2024\/07\/vbs7.png\"><img decoding=\"async\" loading=\"lazy\" width=\"301\" height=\"572\" src=\"https:\/\/www.hexacorn.com\/blog\/wp-content\/uploads\/2024\/07\/vbs7.png\" alt=\"\" class=\"wp-image-9290\" srcset=\"https:\/\/www.hexacorn.com\/blog\/wp-content\/uploads\/2024\/07\/vbs7.png 301w, https:\/\/www.hexacorn.com\/blog\/wp-content\/uploads\/2024\/07\/vbs7-158x300.png 158w\" sizes=\"(max-width: 301px) 100vw, 301px\" \/><\/a><\/figure>\n\n\n\n<p>Here&#8217;s the <a href=\"https:\/\/hexacorn.com\/d\/vbs_frida.py\">idapython script<\/a> that does it for you.<\/p>\n\n\n\n<p>Note: I used a 64-bit <em>vbscript.dll<\/em> from Windows 10 &#8211; in my case the dispatcher function is located at 0x180014DD0 &#8211; you may need to adjust the address to your version of Windows\/<em>vbscript.dll<\/em>.<\/p>\n\n\n\n<p>How does it all work in practice?<\/p>\n\n\n\n<p>I wrote a lame test vbs script that looks like this:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">MsgBox \"Find the PID of this process &amp; run Frida-Trace now\"\n\nb = \"ABCDEFGH\"\na = Right(b, 1) &amp; StrReverse (\"olle\") &amp; StrReverse (\"dlrow\") \na = Replace(a,\"world\",\"VBS_API_Mon\")\nMsgBox a\nMsgBox \"End of the demo\"<\/pre>\n\n\n\n<p>The first <em>MsgBox <\/em>is a helper function or a breakpoint of sort. Frida has problems hooking functions from libraries that are loaded dynamically (<em>vbscript.dll<\/em> is not statically linked to <em>cscript.exe<\/em>). As such, I force the <em>vbscript.dll<\/em> to be loaded first and this first <em>MsgBox<\/em> gives us time to run frida-trace on the cscript process.<\/p>\n\n\n\n<p>In other words:<\/p>\n\n\n\n<ul>\n<li>Run <em>cscript.exe<\/em> <em>test.vbs<\/em>; let it pause on the first <em>MsgBox<\/em><\/li>\n\n\n\n<li>Run Frida-trace with the PID of the <em>cscript.exe<\/em> process<\/li>\n\n\n\n<li>Click the OK in the Message Box &#8211; now you are monitoring all the calls<\/li>\n<\/ul>\n\n\n\n<p>Here it is in action:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full is-resized\"><a href=\"https:\/\/www.hexacorn.com\/blog\/wp-content\/uploads\/2024\/07\/vbs5.png\"><img decoding=\"async\" src=\"https:\/\/www.hexacorn.com\/blog\/wp-content\/uploads\/2024\/07\/vbs5.png\" alt=\"\" class=\"wp-image-9287\" width=\"512\" srcset=\"https:\/\/www.hexacorn.com\/blog\/wp-content\/uploads\/2024\/07\/vbs5.png 569w, https:\/\/www.hexacorn.com\/blog\/wp-content\/uploads\/2024\/07\/vbs5-300x286.png 300w, https:\/\/www.hexacorn.com\/blog\/wp-content\/uploads\/2024\/07\/vbs5-315x300.png 315w\" sizes=\"(max-width: 569px) 100vw, 569px\" \/><\/a><\/figure>\n\n\n\n<p>We can see that basic function invocations are handled relatively well &#8211; we could extract all the strings being passed to string functions that handle actual strings. There is definitely a limitation when it comes to operators and functions not operating on strings.<\/p>\n\n\n\n<p>The two MsgBox invocations are of special interest:<\/p>\n\n\n\n<ul>\n<li>MsgBox a &#8212; not handled well at the moment<\/li>\n\n\n\n<li>MsgBox &#8220;End of the demo&#8221; &#8211; handled well<\/li>\n<\/ul>\n\n\n\n<p>It turns out that apart from the VARIANT structure there are also some other bits at play &#8212; ones that I didn&#8217;t explore yet.<\/p>\n\n\n\n<p>It all took around 4 hours. I knew nothing about <em>vbscript.dll<\/em> internals prior to this exercise. The rapid prototyping power that Frida gives us cannot be understated&#8230;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Update See the updated version of the script here. Old Post I love experimenting with Frida and I have presented a few different API Monitoring prototypes based on this framework a few times before&#8230; Today I will demonstrate how to &hellip; <a href=\"https:\/\/www.hexacorn.com\/blog\/2024\/07\/07\/writing-a-frida-based-vbs-api-monitor\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[92,41],"tags":[],"_links":{"self":[{"href":"https:\/\/www.hexacorn.com\/blog\/wp-json\/wp\/v2\/posts\/9282"}],"collection":[{"href":"https:\/\/www.hexacorn.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.hexacorn.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.hexacorn.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.hexacorn.com\/blog\/wp-json\/wp\/v2\/comments?post=9282"}],"version-history":[{"count":7,"href":"https:\/\/www.hexacorn.com\/blog\/wp-json\/wp\/v2\/posts\/9282\/revisions"}],"predecessor-version":[{"id":9299,"href":"https:\/\/www.hexacorn.com\/blog\/wp-json\/wp\/v2\/posts\/9282\/revisions\/9299"}],"wp:attachment":[{"href":"https:\/\/www.hexacorn.com\/blog\/wp-json\/wp\/v2\/media?parent=9282"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.hexacorn.com\/blog\/wp-json\/wp\/v2\/categories?post=9282"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.hexacorn.com\/blog\/wp-json\/wp\/v2\/tags?post=9282"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}