Submind YouTube summaries
Thumbnail for Malware Analysis - Hooking V8 JavaScript bytecode

Malware Analysis - Hooking V8 JavaScript bytecode

Watch on YouTube

Video summary

The video focuses on analyzing malware that utilizes Node.js, Electron, and V8 JavaScript bytecode as its primary execution mechanism, often employing these technologies to obfuscate the code from reverse engineers. The presenter begins by examining a sample that appears to be a Minecraft mod but actually functions as a downloader for subsequent malicious stages. By inspecting the file structure and manifest, the analyst identifies the entry points and uses tools like Recaf and 7-Zip to unpack archives containing NSIS scripts and Electron applications. A key aspect of this analysis is understanding how ByteNode compiles JavaScript into bytecode, which serves as a protective layer that makes traditional decompilation difficult and time-consuming compared to standard Java or C++ binaries. To bypass the need for full decompilation and quickly extract indicators of compromise (IOCs), the presenter demonstrates a dynamic hooking technique using custom JavaScript scripts. The core strategy involves intercepting the `require` function within the Node.js environment to log every module loaded by the malware, thereby revealing its dependencies such as cryptography libraries, file path handlers, and network clients like Axios. This process allows the analyst to infer the malware's behavior—for instance, detecting if it attempts to kill specific processes or steal data from SQLite databases—without needing to understand the entire source code. The presenter also addresses common challenges where malware authors hook standard console logging functions to suppress output, providing solutions that save original function references to ensure logs are still captured even when the environment is tampered with. The tutorial further explores advanced manipulation of hooked functions to alter the malware's behavior during analysis, such as preventing it from killing critical system processes like Chrome.exe or blocking network calls to command-and-control servers. By creating a reusable script that hooks various Axios methods and other API calls, the analyst can trace communication patterns and identify specific targets, such as Discord stealer functionalities or cryptocurrency wallet theft operations. The video concludes by suggesting that while full decompilation using tools like V8 is possible for detailed reporting, the hooking method presented offers a highly efficient workflow for rapid triage and IOC extraction when time is limited, empowering analysts to flexibly adapt their scripts to investigate specific malicious behaviors without needing deep expertise in JavaScript programming.
Read the full video transcript
Welcome to ME analysis for hedgehogs. A lot of JavaScript based Melvinware nowadays is shipped via Node.js, electron framework and similar. And uh what we also very often see is a form of protection where this code is compiled to byte code for JavaScript. So V8 byte code usually. Um yeah, and this is this isn't the default. So this is actually form of protection that uh the developer of the malware has to implement. We will look at a specific malware sample and how I usually deal with those without spending too much time on decompilation. Maybe decompilation will be part of a future video. I'm not sure yet if I will do this. No promises. So yeah, have fun. And as usual, you will find the samples in sampleedia.cc. I will put a link in the video description below. If you haven't already, also check our discord. And if you want to learn meta analysis, consider taking one of my courses at me analysis forhedgehogs.com. So as you can see I have here prepared two files. Um you will see soon why but generally when the samples are a bit older the next stage might not be available anymore if it's reliant on the infrastructure of the threat actor. So you can download the files as usual from sample pedia CC. So if you want to follow along, please do that. And then you can repeat everything I do here as well. Now let's take a look at this stage here. So let me do a strings listing and uh the strings show me that this is a jar archive. So and why? Because we have here firstly the typical folder structure which is used to represent a package and then we have the uh class file in here but the most telltale sign for me is a meter in manifest mf. So this file will tell Java where the entry point of the Java archive is if there is an entry point. Not all Java archives have one. Um, but yeah, these ones. So, I expect to see if I open this that it starts with PK because that would be the magic byes for a for an zip archive. And indeed, that's the case here. Can see PK. We can um unpack this with sevenzip. However, we will be using recaf and recaf can read it just fine and you don't need to unpack it. So, let's use recaf here. Launch it. And recap is a Java decompiler and you can put a jar archive in there. So let's open file and we say we want stage one and open the workspace. This is the file here and the classes. So what do we see? This is easy right here because we don't have that much going on. So there's just this onecl class file I guess and your mod. So two class files. However, in the manifest, it should tell you where everything starts. But I'm not seeing much in here. Let me check the mecod info. Okay, nothing special. I'm a bit confused that the manifest doesn't contain more. I think this is supposed to be a Minecraft mod. I guess they don't need to be runnable if you double click on them. So, it's not runnable in that sense, but it will run if you start Minecraft, I guess. Check these out. Now, it's also just named example mod and just has some stuff in here that doesn't do much. Let me check your mod. And that is yeah pretty much not obiscated at all. Uh it runs powershell exe and runs this command here. So we simply grab this command and we will decode it. Let's do the same here. We do uh plug in B 64D decode and that's what we get. Let me turn on word rep. So that is the URL this downloads the next stage from. And this is this is basically all this is doing. So it downloads this and runs it. It's called setup exe. Uh puts it into temp as launcher exe. I guess to make it seem like this is the normal behavior of the mod. So let's take a look at stage two. Do the same thing. Run the strings listing here. And let's take a look. So this looks more like a port of executable file, right? This is a little bit bigger. Let me just check. It says 74 megabytes. So quite a lot of strings. So I'm just roughly scrolling through. But yeah, these all don't seem to be actual strings. They are likely high entropy areas. So let me run pot analyzer on that. Do I have it here? Yeah. And uh yeah, it says it's a NSIS script. So if you look at the visualization, yeah, this is all of that is packed uh stuff in the NSIS application. So that's why we didn't see much here. Uh this small little part, that's the actual code of the NSIS stuff. And this huge part here is what's packed inside of this. So yeah, let's simply unpack this with sevenzip, I guess. So what do we see here? We have app uh 64.77z. Um yeah, so this is likely JavaScript that we will find in here. Let's also just extract this. And yeah, looks like it. This is a an electron application. So what's electron? That's a framework which uh for JavaScript which is used to create desktop applications but with a Chromium browser. So will look like a desktop application but underneath it is a browser that just runs your stuff. So um yeah uh so I should probably explain why I clicked on resources. So the thing is in in such an application the the launcher isn't usually the interesting part. It's where the user clicks on. But all that this does or is responsible for is to run the electron environment and eventually run the actual Java script code. And this code is inside an Azar archive. And we can see here these files. Uh sometimes I've I've seen some blog posts mentioning that there is this elevate.exec which is very suspicious probably used for elevation stuff. But this is always shipped with these kinds of applications. It's not unusual. So even in legitimate applications, you will see it in there. So we have this app as unpacked. But that doesn't mean that this is this file unpacked. Okay. So we see here note modules and note modules generally contains some libraries that the application uses. Uh this is a little bit weird. This library has a weird name. Interesting. Uh yeah, but we need to unpack app Azar. And for that, I'm not sure if sevenzip can do this again. Uh you you can have a plugin for Zvenzip. I think that does this. Uh I generally use binary refinery by now or what we can also use is uh the builtin electron azar modules for NodeJS. So let's just do that. So we just tell it to use it and we put it to unpacked. Also, I think I need internet for that. In case it doesn't have that, let's just turn it on. Okay. Seems to be hanging. Let me try something else. Um, it's not supposed to take that long. Okay. Uh, let me use npm We install the electronizer pack package globally and now it added eight packages and now we use it directly. Okay, we say extract app Azar to unpack and that was way better. So turning off the internet. Yeah. Uh so now we get the unpackaged files and we can take a look at this. So this is already very interesting now right from the start. What we see here is app.protected.jsc and an index.js. Let's take a look at the package. JSON what it runs. Oh, why does it have to install? Uh, should have opened it in Notepad because this takes always longer. Um, so this runs index.js as the main close. So, um, index.js is the first thing being executed and yeah, that's what I expected. So, um, this uses byte node. Byte node is a compiler. It compiles the JavaScript code to byte code and this byte code is the only thing that you as a reverse engineer see. So it's a way to protect the code from being reverse engineered because decompiling it isn't as easy as it is for Java for instance. Yeah, that's what the mware authors also use of course so that you can't see their code and have difficult time also to create signatures on that code. Internally, bite node uses some tricks to accomplish this so that the original JavaScript code doesn't have to be shipped with the electron binary. And how that works was a part of the last video that I published before this one um where I explained the details of the V8 engine and of bite node protection. Let's figure out how we can analyze the sample. So in most of the cases in a daily work of a me analyst you actually don't need to decompile this code and this is also not what we will be doing today. We there is a possibility to do that using Vue8 but it takes a lot of time. So we will not do that and instead create a reusable script where you can just run and trace what the application does and then get out all of the values that you need like next stages or some discord hooks or uh well just see what it generally executes. So let's simply create a JavaScript file which we use to hook the application and let's start we just print something so that we see some output hooking malware something like that. Save it as JS or JS. And what we also need to do is to add the passes to the module. Um we need to tell it where all of the required libraries are that the malware ships with. So these uh node modules right here. So we will be using unshift. unshift puts everything to the front of the path uh array or list. Why do we want it at the front? So that we make sure it searches there first. So do this and we also need to add the other ones that we saw. So we have this node modules in the unpacked app aza but we also saw this app a unpacked which contains different node modules. We also need to add these and we should there anything missing? I think there was one in resources. No there isn't. Okay. So this is enough but we also need to replace uh we use normal we replace those with double backslashes. Yeah that should work fine. And what I do at the start or what we should do at the start is we make a hook on the require thing uh require loads libraries modules and I'm not so uh sure about the correct JavaScript terminology in these but for me these are like libraries reusable code like these node module thingies. um require is what loads them. If we hook it, we can see everything that the sample actually uses. Now, how does hooking actually work? Hooking works that we say uh we have an original well we keep an original copy of function XY Z or whatever and then uh modify the original with hook and Then in original copy we wait in original call original copy. Um so that the original function is still executing because we need require to actually run so that the sample still works and that's what we will do here as well. So how does this work? Here we have uh module instructor proto type require. This will get us the original require. And to modify this we say well let's copy paste this is um this anonymous function which has ID as a parameter. So here we assign another function for that and yeah this is the definition of the function. It doesn't have a name here in this part of the code. So, it's this. And we say require and we add here the caller which should be we add the ID when we add the caller the caller is uh this file name. Yeah. Let's test this. So, how do we run this on the mware sample? We use the launcher that this application ships with. So, we need this uh where is it? Here. This launcher. We use this launcher to run our hook code. Oh, I put it in here. Let me just save it to the desktop so I can find it more easily. Yeah. Okay. So, this is what we will run. Uh, and I forgot that we also need to call the sample from here, of course. So the way you do this is we say firstly we say require bite node because this is what we need what the sample needs to run the protected JSC and we require the location of this protected JSC that should be this only in selection. Yeah. Oh my. Okay. So we require our protected JSC and by uh using require it will call this with bite note. So and then we say we are done just so we know when it's finished. Now let's call our hook. It's hanging there in a loop. Nothing seems to happen. That's interesting. Oh, I I know why because we forgot this part. Uh call the original copy, right? So the require doesn't work. Uh because we also need to do this. This will just return the well original function result. So we call the original require with the arguments that it was given. And now I hope we see something. Let's go. and we don't there is a variable that you need to set so that you can run an electron application as nodejs. So this is uh we set it here in PowerShell. We say and electron run as node and we say one and this will make it possible to run this as NodeJS and then we see the output on the terminal. So let me run this again here and we can see that it works. So we see the require and then we see okay this is the file that called this right. So we have here a lot of times require and then node and then we see the caller is some byte node or yeah it's here's it's bite node bite node uh uh this is all library stuff we are not interested in and for the actual malware we don't see any require at all uh which is odd because the malware should use something right Everything that we see here is what we also use with require byte node call right here. Uh but here nothing that's that's really odd. So let me do two things right here. Uh the first thing is that we do not use anything which contains node modules in the path. So when we have node modules in the path this one here I actually don't want to see require output. Why should I? Um so we say we say if not this file name includes node modules right and then and only then we will log that there was a require and I'm assuming if it doesn't contain that it's not a library it's the actual malware that required something and now this is likely empty. Um, this is a bit messed up too. Yeah. So, yeah, almost empty, right? We have our hookjs that's our own sambber and we have the um protected JSC and that's it. So uh from hookjs the most common reason for this is that the malware also uses hooking so that console log doesn't work anymore. That's very common and in general if you write such a script we should probably add some protection for that. How do we do that? So one thing you could do is you could log everything in a file instead. That might work. There are various methods write to file. Maybe some of them aren't hooked by the malware. Uh but what we also can do is we can just get a copy of the original code. Since we are the first ones to run, we can also save the original function of console log. And if they use hooking, we just use the original one that we already saved to do the logging. [snorts] And yeah, let's try that. It's quite simple. Same way we hooked here. We do this uh and save our uh original log as consoli log. And what we do here with the bind is that we tell it. So when when the log is called it needs to know where it came from that it came from console and this way we basically tell it to use console function log here's the message we want to log this will be the function we will be using and all this does is original log message. So it calls that and we replace here require with our log output. And do we have anything else? Maybe we should do this as well here since that's also a sign, right? We didn't see this. We should have seen this, but maybe it ran in a loop. And that might be also a reason that it somehow detects it's running on the VM and then just runs in a loop. uh doesn't do that. Uh doesn't really finish. So, let's let's uh try this again. And now we see more. So, this actually worked. Uh I'm now a little bit So, yeah. Um stop this. Um let's remove this part. Let me think. Yeah, let's just remove the collar. It's not that necessary anymore. So and do that. And now we get a list of all the required modules that it uses. We get an idea of what it might be doing. So uh it it uses do does some cryptography. It uh uses the paths to access files and stuff. Um so SQL Lite 3 is something you often see with stealers as they will access uh browsers and their SQLite databases and yeah some some archiving. Axios is for get and post requests likely and of course child or process is used to to create a process like run commands for instance with exc. So these are quite interesting here. It runs in a loop. Okay, it's already stopped in PH is a bit weird with this. Um maybe easier to use cmd for that. My friend do this. So yeah, I'm in cmd. But does this change anything? I'm not sure. launcher pokejs and yeah cmd is working better. Uh clear screen. Yeah. Okay. Now we are in cmd. Uh if we continue with this approach now we could try and guess what the sample does next based on the require calls. So we had seen there was for instance child process. So maybe it uses exec to call something and we can just see something if we hook this function. So let's do this just to see how this generally works. Uh yeah. So we save child process here under CP we say original exec is sh process exec and uh now say that this is a new function So it has bar ads. We represent these like this. And we now log exec call and print cmd which is the cmd command. And that's executed and we return the original exec. So that it calls the original one. Well, that depends if you actually want to call it, but yeah, let's see what happens here. And uh yeah, that's that's quite interesting. So this execute task call on Chrome exe. Let's try this. I'm going to run Chrome.exe. See if the sample actually does this. Yeah. And it does. So what we can do now is we can adjust this behavior. We could for instance well let's say not return the arguments. What happens if we just say the arguments are null right? Do we still see that it's killing Chrome and this time we see that it exacts task on Chrome but now it hangs here. So, uh, Chrome isn't killed, although it says so because we do not provide the cmd command for the execution. And here it leads to some uh unwanted behavior from the malware because it likely expects some kind of response. Yeah, we could decide to, you know, check cmd if it contains tasker and then just not do it because it seems here with task list it actually needs this to properly work. Experiment a little bit with it. Let's say if uh cmd include this task kill then we don't want this to work. We say return with null and otherwise we run this normally and then it receives the task list results. See? Yeah. So, this now looks like it did before, right? Can now still use Chrome. Maybe next time we have a sample that interferes with our reversing tools. Uh, we could also just rename the tool that would work as well. But here, this now doesn't kill our Chrome anymore. And we have successfully hooked this. Yeah, let's do one more. I think one more that might be interesting and that is uh the uh axios which is used probably to contact some form of C2 server. And we don't exactly know which function it uses, get or post. Let's just put several of the axios functions in here. Or it might use put, right? So this is a list of the functions I want to hook and I say for each we have uh the argument here and we define this function. We access the array like this. So this will access the current function that we are using. Um like if it was get it would be a get or if it was put it would be you know aios put for instance. And now we replace the function with URL function URL and we just say log and might be a good idea to know what kind of function was called. So we just put m in here. Wait, without the quotes, of course, we log this and we log the URL itself. So, and then we return original X applied this arguments. I don't have internet at all here right now. So this will not do anything. Run our hook and we get this. So that's another IOC that you can use to research more about this sample. Before the era of large language models, this is how I did this. I just had a big script which hooked all kinds of functions and then did this because I'm not a JavaScript programmer. So my willingness to learn JavaScript is also pretty low because I only use this for analysis of these samps here that use JavaScript. So it's it's not a language I generally prefer to use or things like that. So I wasn't that willing to know or to to learn how reflection works. But there is of course a way to do the same stuff without having to guess which functions it uses and that is by using reflection. And the only way I um created a script like that is with a large language model. Then you get it back uh may not necessarily understand everything that it wrote there. So that possibility exists too just to say this is how I did it so far. Give you a minimal example for that. Yeah, that looks like it. So this was clanker generated. Uh but what it does is uh you have here at the beginning the config where you put all of the modules inside. So the basically the module path we also have. So let's do the same here. Did you know how this works? So we copy and paste these passes in here. And um yeah, of course the app protected path. That's the one we have here. And uh where the log should be. So this puts everything into a log file and we run this generic hook and that's what we get. So here you can see what what's happening. So it's here hanging because it expects something from this um def x. So it expects to download something from there which isn't happening. So what you could do is run inets sim and then it gets something back and probably responds or we can like uh create a response by hooking this function here. So, but what you also see directly is, oh yeah, uh this is accessing discord uh local state somehow. So, it's probably like a discord stealer. Um we see other functions that are typical for stealing like exodus wallet. So, it's stealing crypto wallets. Um putting them in a zip archive event. Then this is very interesting. So seems to be um galaxy stealer based on this string here and it has some kind of licensing information which it sends to the server here. So uh that's I think maybe related to Roblox I'm not sure. So, and then it builds with path join. It builds paths to cookies and uh some some profile data and then it does some stats sync call. I'm not sure what stats sync does, but yeah, here you see the full process eventually. Um yeah, so it's pretty clear that this is a stealer where it connects to and what it does. And that's just from this um script. I hope you learned something. We'll put both scripts into the video description below. So you can copy paste them or like if anything goes wrong just copy paste them and use them. So but my hopes is just generally you understand how this works this whole hooking process. So you can adjust it for your own purposes and like and then also hook functions and modify them so that the mail does what you want right. So that's the whole purpose of this just copy pasting the script doesn't do stuff for you you know but this then you are flexible and you can do what you want. So this script that we created or that you can well built to get your very own output it's very very useful but of course it doesn't help for all use cases. So when it's useful is when you don't have much time you need to determine if a sample is malicious and you want to extract indicators of compromise. That's enough. Uh if you actually want to write an article though like a blog article and a detailed report how this thing works where everything needs to be very specific and in detail uh then you may actually want to decompile this script and to do that you can use Vue 8. But how to do that that might be a topic for a different video. No promises though.