Thank you everybody for coming to Game Changing Advances in Windows shellcode analysis. So shellcode, it's always out there lurking somewhere, causing problems. And a problem until very, very recently is there just has not been adequate tooling to address shellcode. Well, we have aimed and succeeded in changing that. And we're thrilled to show you how. So a little bit about ourselves. My name is Dr. Bramwell-Brizendine. I am a professor, former director of the Verona Lab. And I've created quite a number of different tools. Joprocket, Shell Wasp, co-creator of Sharem, and also Roprocket, which we'll be presenting on Sunday. I do have a PhD in cyber operations. From Dakota State, which is a highly technical degree. And now we will let one of my former students, Jake, introduce himself. Hey, everyone. I'm Jake. I'm a recent college grad now working as a reverse engineer at the Johns Hopkins University Applied Physics Laboratory. I worked on the Sharem project as a graduate researcher in Dr. Brizendine's Verona Lab. And I'm still an active contributor to the project now. Here's Max. Hello. Hello, everybody. My name is Max Gerson. I go by the nickname of Libra. I work for Trellix in the Advanced Research Center. And I like to write blogs about the research I do. I'm a malware analyst and reverse engineer by trade. I do that both as a personal hobby and for work. And in here I'm representing Trellix and presenting the JITR script that relates to this. So first, let's talk about why shellcode is a topic of interest to us and why we decided to develop the Sharem project in the first place. So we see shellcode quite frequently in the world of malware and exploitation. The common objective of malware is to remain hidden and obfuscated. So shellcode makes a great tool for these objectives. Its inherent obscure nature makes it difficult to statically analyze. An example of such a challenge is Windows API identification. When examining a normal piece of malware, like a PE file executable, you'd expect to see labeled Windows API calls. But this isn't a luxury we have when we're dealing with shellcode. Shellcode has to manually go in and resolve API addresses manually. So what you'll wind up with is something like a call EDI instead of a nice clean call win exec. So this call EDI isn't much for us to go off of. So we have to go in and perform a bunch of static or dynamic analysis. And then this challenge doesn't scale well either. If there's many shellcode APIs called, then this is going to take a ton of time for us to go in and find out. So issues like this and some of the other headaches when dealing with shellcode are why we decided there's a need for more tooling to solve these challenges. Which is where Sharem comes in. Our game-changing solution to shellcode analysis. So what is Sharem? Sharem aims to be the complete all-in-one comprehensive solution to Windows shellcode analysis. It's loaded with a bunch of features that we'll cover in this talk. We have a shellcode emulator that's built on Unicorn. This helps us deal with some of those Windows API identification problems I was mentioning earlier. And then we have a few other unique features such as complete code coverage that helps us unravel unexplored paths of shellcode. We also include a fully accurate disassembler that's labeled and enhanced by our emulation results. Then we have a few other features that are helpful for us unraveling deceptive shellcode encoding and obfuscation. This tool is really useful. It supports for Windows shellcode only, but we include support for both 64-bit and 32-bit shellcodes. And now we're revealing for the first time at this presentation a brand new Ghidra plugin that was developed by Max at Trellix. Alright, so let's talk about Sharem's emulator. Sharem's emulator is built with Unicorn's CPU emulation framework. It's able to hook and log over 20,000 Windows APIs across over 60 DLLs. And it wouldn't be a shellcode emulator unless we also supported syscalls as well. We baked in a support for about 99% of those. Sharem does analysis on raw .bin files as well as the hex byte representation of shellcode. One of the highlights of this tool is its use for high-level indicator of compromised analysis. Running a shellcode in emulation provides a detailed summary of what the shellcode is doing. You'll be able to see things like what Windows APIs were used, what file paths were accessed, or what URLs were reached out to. Also great is that all of the knowledge we gather from emulation is leveraged elsewhere in the Sharem tool. So with the high-level details out of the way, I want to dig a step deeper and talk about some of the implementation details of our emulator. So Unicorn's bare-bones framework that kind of just gives us the essentials needed to emulate code. The shellcode wouldn't get very far into emulation if you just tried to run it with vanilla Unicorn. There's a bunch of stuff we had to do to set up, to make sure our emulation looked and behaved like an actual Windows process. So an example of this is pebwalking. The peb is an internal Windows structure that's built into a shellcode. It is used to resolve DLL address, base addresses. But as the name implies, this technique is heavily reliant on the peb structure. So structures like the peb had to be implemented in high fidelity for our code to, emulated code to get anywhere. So now that our shellcode is able to walk the peb and find a DLL address, the next step is to parse the DLL and loop through the tables until we find the DLL we're looking, API we're looking for. So in our emulation, what's the shellcode actually parsing as a DLL? The answer is actual DLLs. We load the DLL straight into memory from disk and apply a special formula to get them to execution size. As we're loading them into disk, we create references to them in a dictionary. So we'll be able to look them up at runtime once an API is called. Which gets us into API emulation. So now our emulation is able to locate a Windows API. What happens once the API is actually called? Our emulator will log the invoked API along with all the parameters that it needs. Once the API emulation finishes, this information is printed out to the screen for the user to see. This information is very useful as it can give you a really good idea of what the shellcode is doing. In the example on the right here, we have a metasploit shellcode. And it's really obvious to see here that this is a bind shellcode that's opening a bind interface over port 8721. So now what goes on underneath the hood during an emulated API call? For some APIs, it's not enough to just simply log in. You can simply log the call, then return an arbitrary value. Take virtual alloc, for example. The virtual alloc API goes and creates a usable region of memory. But the shellcode's probably expecting to actually do something with that memory. Probably read, write, and or execute. If the emulation doesn't actually go out and create that usable region of memory, the shellcode will probably fail to emulate properly. So for essential functions like this, we wrote out handwritten hooks that will go out and perform whatever steps necessary to keep the high fidelity representation of shellcode emulation alive. So for our virtual alloc example here, our API hook will use Unicorn's memmap to actually go out and create more memory space for us. Currently, we have support for over 600 of these API hooks. Now, doing manual written hooks for this for over 20,000 APIs is a big undertaking. So we've implemented as many as we could for all the security-relevant APIs that shellcode would likely want to play with. We built dictionaries that will look up the required parameters for each API as well as their types. Also determined through this lookup dictionary is an appropriate default return value based on the return type for the API. Default return values can also be specified through config options. So now turning over to Dr. Brizendine for some more information on how we emulate syscalls. Alright, thank you very much, Jake. So Windows syscalls are something that until very recently, had not been supported whatsoever in terms of these types of what we're doing. So Windows syscalls have become very popular, very trendy in the last few years. But up until about 2018, that had not been the case. Previously, virtually the only Windows syscall had, in terms of shellcode, had been egg hunters. Egg hunters were where you can go and search through process memory for something like a tag such as woot woot. Once you find that tag, then you can redirect control to that particular location. So we had assumed naturally that there would be many other Windows syscalls out there being used in shellcode. But the reality was, that was simply not the case. Instead, it was just simply egg hunters. Now as part of this research, we actually received a $300,000 NSA research grant to develop this. And so we wanted to make sure that we were able to successfully emulate Windows syscalls. So since that really hadn't been done, we went and developed our own techniques and tooling to help facilitate that. That was a previous DEF CON talk, which we also expanded, very much so, at Hack in the Box 2023. So how exactly do we emulate these Windows syscalls? Now in terms of something like stack cleanup, when we have Windows APIs, it is necessary to, internally, do the stack cleanup. But Windows syscalls, the opposite is true. So the shellcode author is responsible for that cleanup. So we do not do that ourselves. And for those of you who are familiar with Windows syscalls, the way in which we invoked them is, we will provide a particular value in the EAX register, such as hex 18 for NT allocated virtual memory. And for every different OS build, of Windows, these SSNs will change. And so in order to emulate them, we need to specify the particular Windows OS that we are wanting to do, to make sure we get accurate results. In this example here, we see that we have NT allocate virtual memory. And the particular OS build is identified. With that, we will turn it over to Jake. So in analyzing SHRM's output, we try to make things as easy on the user as possible to understand the various parameters and values. So for example, it may not be immediately obvious what a value of 0x40 means for an FL protect parameter, but page execute read write is a lot more useful. SHRM will also try to parse structures and strings whenever necessary. So here in the bottom screenshot, we have a call to win exec with calc.exe. You'll see that calc.exe is printed instead of the address that points to it. Another thing we do to make parsing the output easier is include some artifacts that are extracted from regular expressions. Under the artifact section of output, you can find IOC information, such as commands run, files and paths references, and URL addresses. Here we have another shellcode example. In this one, you can see that a bunch of commands were run, one to stop Windows Defender, a couple other malicious actions, and then we can see a Chrome updates dot back artifact interacted with as well. So one optional feature we also include is the ability to pull live artifacts from over the web. Suppose the malicious shellcodes reaches out over HTTP to grab some sort of second stage or other external dependency, you may want to actually go and grab this file as well. This option is there to support that. If successful, an MD5 hash will automatically be performed on this download as well. Here's an example of that in action. Here we have a shellcode that pulls down an executable file called some payload dot exe. Sherm will actually issue a get request to pull down that file, then load it into memory if necessary, if it's interactive with later. You can also see that an MD5 hash of the file is included as part of the output. Another nice part about Sherm's emulation output is its ability to apply structure to parameters in APIs. For parameters that pass entire structures instead of just standalone values, Sherm will enumerate through and label all members of the structure. Here's an example of that. Instead of just a pointer value for these two structures, you'll see an expanded list of all the members of the structure included. Not only does it enumerate and label structures, but it also labels nested structures while it's recursive. For this LP time zone information structure, you'll see the standard date and daylight date nested structures as well. There's also instances where APIs will use unions, which is essentially a parameter that shares the same memory space. Sherm is verbose enough to delineate this level of information in its output. The Windows Registry is another area of interest for shellcode, which is why we also developed a Registry Manager for our emulation. The Sherm Registry Manager helps us track and stub out actions for our emulated registry. The Registry Manager helps us track the actions we see on the registry. The Registry Manager maps hex values to actual paths so when registry addresses are printed as output you'll see the full registry path instead of just some random address. We have a bunch of custom hooks written for the registry-related APIs and try to simulate some sort of success action whenever possible. This includes having our Sherm Registry Manager update various things in its faked-out registry that the shellcode can interact with. For output related to a registry, we also have some special parter mechanisms based on the MITRE framework to pick out any sort of registry actions that are security relevant to us. Here we have an example of one that does registry persistence for an executable file. Again, like the rest of our output, it's formatted in such a way that it's easy to parse for the end user. So for all references to H key, instead of just hex values, you'll actually see the full registry keys and paths and data. And all of these registry related things don't just apply to APIs. There's also support included for syscalls as well. This is an example of the NT create syscall for current version slash run to set up persistence. Again, this is immediately obvious due to the way that the output is labeled. So one final part of SHRM's output is timeless debugging information. This is essentially the way that we're going to use the log of every CPU instruction that we emulated along with register values. This is useful if you want to go back and review any of the instructions that were executed and evaluate the registers at that state. There's also an option to view a selected number of bytes from the stack as well. So this is what that timeless debugging log looks like. Here you can see all of the instructions and all of the registry values before and after each execution. So when developing this tool, we conducted a lot of research from public sandboxes and shellcode repositories. We found that 64-bit shellcode is a lot less common than 32-bit. But rest assured, we also have support for 64-bit shellcode emulation as well. So we have different but similar DLLs and internal Windows structures for this. So yeah, anything you can do in 32-bit, you can do in 64-bit for our tool. All right. Now turning back over to Dr. Brizendine for complete code coverage. Thank you for that. So this is one of the most important game changing aspects of all of this. Now some of you may be familiar with the idea of code coverage, but this is a very different twist on that concept. So with complete code coverage, we are going to, to all but guarantee that virtually every single code path is going to be executed. How do we achieve this functionality? Well, we instantiate it at the assembly level, and it works very well. So whenever we encounter an opportunity where we could go left, we could go right, we have a jump table, whatever the case may be, we can capture a particular location, including a snapshot of the stack. And we will maintain a list of all of these locations. Once the shellcode is about to terminate, if there are any unvisited code paths, then the shellcode will simply restart. And because we were able to save the CPU register state alongside a snapshot of the stack, and get the confirmation by doing that, we were able to capture APIs and log them that we otherwise would not have been able to, to do. So to give an example of why that is important. So this could be considered a form of dynamic analysis. So we could do dynamic analysis with a debugger. We possibly could tweak things a little bit to maybe cause it to, Just go on this direction that it went otherwise or we could detonate something in a piece of or detonate a shell code and a C style harness in a sandbox and just see what happens but with complete code coverage We can guarantee that we will see all of the possible code paths not simply those that would have been For that particular instance in the sandbox So to the left there you can see a shell code that was Written by a student of mine, and there are only two api's that are identified now one of these is get computer name a So it's looking for a very specific computer name, and if it's able to to get that computer name then It's going to do reg Set key value and it's Going to establish terminal server so unless we have that particular computer Name we should never be able to see that but with complete code coverage We are able to capture not only the api but all of the Correct parameters that would have been there otherwise So self-modifying code is also an important aspect of shell code so a lot of times our shell code May be encoded and the shell code will simply decode itself in memory I don't have a decoder stub to help facilitate that process and we can actually identify that through the use of fuzzy hashing in particular SS deep and So if we are able to identify that then we will simply take the decoded form and then that decoded form is what we will then Perform our analysis on and then we will also send that to the disassembler So at the bottom there you can see the Serum has identified successfully That this is indeed self-modifying code And this is an example of a shell code that is actually encoded But lo and behold we are seeing the actual api's that are being called we are seeing the parameters so really this is very much a game changer if you're dealing with encoded shellcode. You know, maybe somebody gives you a piece of shellcode and you're not quite sure what that is. Now, your options could be, I'm going to take that, I'm going to put it in a C-style harness, I'm going to debug it. Now, some parts of shellcode may be very repetitive. So, there could be loops that may occur hundreds or even thousands of times. And also, the fact that it's decoding itself in memory, it can be very, very tedious. But with ShareRum, we can instantly see what is going on inside of that shellcode. So, maybe a friend gave you a piece of shellcode, you're going to do that for some particular type of exploitation, but they snuck a little something extra in there, well, you could easily identify that. And so, this is the exact same shellcode in IDA Pro. And it's absolutely correct, but what we're seeing is a series of encrypted bytes, so it's pretty much absolutely meaningless for the human analyst who's trying to go through this and figure out what's going on. Accurate, but just not very helpful, unfortunately. So, the decoder stub is going to be part of the encoded shellcode. It will decode. decode itself in memory. And it can perform one or even multiple or many operations to decode it byte by byte. To the left you can see IDA Pro and to the right you have SHAREM which provides a little bit more information. So at this time we're going to take a brief demo. So we're going to go ahead and ... So we are in SHAREM right now. Let's go ahead and emulate this. So it'll take just a small amount of time to emulate it. Right now it's breaking out of some very long loops. We got some output. We have been able to identify a number of APIs as well as the parameters. And various other artifacts have been identified. SHAREM identifies itself modifying codes. So that's very useful. And then now we've generated disassembly. And so we can see that printed to the screen. So that can be very useful. We have our APIs identified. We have our data down below at the bottom there. And then we're going to take a look at the disk. So what's going to work here is we're going to look at the best-of-the-kind disk. And so what we see is we've got a very nice assembler. We're going to update it a Click here. So you see that the disk is now on the PC now. Now we're going to choose to load the disk. So that's the main thing. We're going to do a little bit of bloom for our disk. So we're like IDA pro or Ghidra they were to put it simply very much inadequate sometimes there'd be as much as 60 or 70 percent only that was correct so meaning 40 percent that was wrong and the root cause of a lot of this was simply misclassification of data is instructions or alternatively a cascading effect of that is some instructions were would start disassembling it in an incorrect offset simple things like strings would be just disassembled is instructions so in order to address this I came up with some static analysis methods to and call that disassembly analysis engine so in x86 you can have instructions and data that are freely intermixed and also with shellcode you can play fast and loose with certain conventions so in order to help deal with this Shario map will actually utilize multiple analysis phases in order to try to achieve more accurate disassembly of shellcode and if we're able to accurately distinguish between instructions deductions in data, then we should get vastly superior disassembly, hypothetically even perfect disassembly. So what SHAREM will do is it will maintain complex metadata about each and every byte of shellcode. Now, SHAREM does work exclusively with shellcode. And it's very much empirical, very much based on experimentation with actual true shellcodes. So I have a large collection of shellcode with where I actually possess the source code. And the process was to try to scrutinize it very closely. And if I noticed at one particular location that something was incorrect, I would try to identify the root cause as to why that was the case. And if I could identify the root cause, then I could try to then remediate it. And I would remediate it not for that particular with that one instance, but for all other similar types. The end result was a very much improved disassembly. Now it could never be perfect, but it was markedly better than what would be produced by ITAPRO or by Ghidra. So some of these can be a little ยด A bit much to discuss. There is a white paper that does discuss them in more detail if you wish, but just very briefly, a few of the things that we do to help with this, if we can find repeating data bytes, then we can label those as data, for instance, long repeating instances of zeros or Fs. We also might check for valid jump destinations. If a shellcode is trying to jump to offset 3,000 and it's only 200 bytes long, well, guess what? Offset 3,000 doesn't exist, so that's incorrect, so we try to then address that. Can locate hidden calls and jumps, and in this particular case, we're looking for the particular opcodes or bytes that produce these, and then we make sure that they are formed, they form the correct disassembly if there is a valid branching destination that exists. Something like strings, we can easily identify those with Unicode or ASCII. So you can see one of the most important aspects of this is we are able to identify functions in our shellcodes. And this is really the only way in which you can do that. So if you were to open this up in IA Pro or Ghidra, you would not see any of these functions identified. You would simply see call EAX, call EDX, and some type of variation to that effect. Now, ShareM, we are able to identify more than 20,000 of these Windows APIs, as well as virtually all Windows syscalls. We also are able to identify disassembly annotations, things such as getPC to self-locate in memory, push-threads, Heaven's Gate, and those can be labeled for us. PAB identification, so walking the process environment block. That's something that each and every shellcode needs to do. It's one of the first steps of dynamically resolving, runtime API addresses. And so we will call out all of those particular PAB features. API tables. So one common thing the shellcode does is it's able to identify a particular location as being a place where a shellcode or a Windows API pointer will be written. So for instance, delete file A, that may always be at that particular offset. So you might have call EDX plus some offset to access that. So let's look at IDA Pro versus ShareM in terms of disassembly. IDA Pro, it cannot determine APIs. How tragic. You pay thousands of dollars and it just doesn't have that information. But we look inside of ShareM and no cost, but you're able to have the APIs identified. Strings, thank you. Now strings are pretty obvious, ASCII, Unicode. We'll identify those and also things like push stack strings. We can have those identified very nicely with our comments. Now one thing that we do do is we utilize emulation data as a way to enhance our disassembly. So now the way in which we do this is very unique. So if we were able to start emulating a shellcode at a particular offset, then we know definitively at this location we have this instruction and it's size two bytes. So we will preserve that information. If we go and produce the disassembly, then to put it simply, that would override what would be determined statically. And for things like data, the data will also be identified with memory reads and writes and so that information can be clearly labeled for us. Now, self-modifying codes, you might say, well, gee, God. Golly, self-modifying code. Each byte is going to be both data and instructions. How can we cope with that? Well, what you actually could do is you could say, OK, we recognize that data, everything is going to be data at least once, so in this case, we will not classify it as data unless it's accessed, read to, or written to more than once. Distinguishing between data and instructions. So at the top there, that's the actual disassembly of instructions. We have call EAX, we're calling virtual alloc, we're creating a region of memory, and the page execute read write is labeled for us. And then down below everything there is data. Now, if this was in Ghidra or IDA Pro, it would just be simply misclassified as instructions, but instead we have our API pointers that are labeled, we have strings that are labeled, and then other D words, which we can surmise maybe things like checksums, which could help us to resolve those APIs. So the way in which we integrate our emulation data, now we take three different forms. We have the starting form, so if something is encoded, we'll take a snapshot of that. And then we do, at a byte by byte basis, every time a particular set of bytes are executed is instructions, we will take a snapshot of that. And then finally, we'll take a snapshot of the final form of the shellcode after it has decoded itself in memory. And then we will merge these together, and it's a very novel way of merging it. So if I was a very clever shellcode author, and I wanted to try to conceal what I was doing, I might have the shellcode re-encode itself after it decoded itself. Now, that doesn't typically happen, but it could. And if somebody were trying to protect the intellectual property of their shellcode by doing that, we would still be able to see what that was, because we are going to prioritize the executed form of the shellcode when we merge these together. Next, we then prioritize the final form, and then finally, we prioritize the starting form. So there is the result of all that madness. We can see a decoded shellcode with the APIs very clearly labeled. The parameters are immediately easy. It's just a C, and it's not just hex values, but the human readable equivalence that we can see, things like, well, in this particular case, we can see that it's downloading an evil.hta, and then doing WinExec on that. In terms of reporting, SHRM has a lot of verbose reporting, of all kinds of very useful information, and it has many different types. Types of outputs that it provides for us, ASCII representation of bytes, raw binary, text format, JSON, so if you want to run SHRM headless, and maybe integrate that into some kind of web server, or web service, then absolutely you could do so. It'll also generate a C-style tester, so if you want to then compile that, and run that in a debugger, it's just very easy to do. All right, so this time we will hand it off to, this is the time right there. All right, let me adjust this. So, since I'm a major user of GDRA in my day-to-day work, during malware analysis, and I also encounter shellcode, I like the SHRM framework. I figured I would combine the best of both worlds. Now it's a, kind of, an open secret I unironically like Java to write in. It's kind of an open secret that the rest of the world kind of hates Java to write in, so I consider this a win-win, where I wrote the script in Java for everybody to use, so you don't have to, but you can still use it. Now, for those unaware, GDRA is a framework for reverse engineering published by the NSA. It's open source. And you can also analyze shellcode with this. Now, you can extend this framework after you've loaded your shellcode, and analyzed it. You can go to the display's script manager, which is the green play button in the top bar, and it will open the script manager for you. Now, you can go to the hamburger menu, and once you open that, you will see the folders where your scripts are located, and you can select any location. Alternatively, your home directory will contain a folder called GDRA scripts. You can put the script in there. And afterwards, you can simply double-click it by running. Now, once you do this, it will call, share them in the background, and it will allow you to see the scripting console, which is located by default in GDRA's bottom. And it will see where comments are placed. You'll see the hexadecimal address afterwards. And given that the disassembly view and decompiler view from GDRA are linked, double-clicking on these addresses will jump towards it, and will highlight that in your overview. Now, the comments in here allow you to have a quick and easy overview as to what was added by the script. And navigate towards interesting parts of the shellcode. Now, it's a brief flowchart as to how does this work. You run the script, which runs share them in the background. Share them can run headless. The script for GDRA itself is written in such a way that it requires no graphical user interface elements, so you can also run the script headless in GDRA, which then runs share them headless. It loads the output once share them is done in its execution, which is the JSON default. It's called this JSON file. And it will load that JSON converted into plain Java objects. It will iterate over all of those, add comments wherever possible based on the output. It will update the disassembly. We just saw the explanation as to why share them is able to catch more than, for example, GDRA. And it will also assign, or reassign, if there was already a datatype. So it's preferring share it and output over GDRA's own analysis, in any type of U bek series, like a four Creams text. along the script's execution. It will log those changes in the script console at the bottom and it will finish the execution. Now, how does that look like? So I have a overview here of a piece of shell code that I received from Bramwell, and this is the default way that Jira shows it to you. Now, if you were to run the script, then you will get information. Now, based on the emulation, you suddenly see that specific offsets or memory locations refer to, in this case, for example, on line 25, six, and seven, you see load library A, but not only do you know this function, and based on MSDN, you know the parameters and the use case offset function, you also know the value of the argument, so not only the type of argument, but the actual value. So in this case, we know that URLmon is loaded by the shell code, and we can see that get proc address is used to get URL download to file A. As for data types, Jira tends to try and guesstimate whatever you have in front of you. In this case, you have unknown bytes. These are, well, they're nothing constructible. However, during emulation, you will see that the API pointers, checksums, and strings reside here. Now, the strings themselves might be found by Jira initially, but maybe the ending null byte is or is not included. Jira generally assumes it is, and that byte might be useful for the next part. Maybe it's alignment or padding afterwards. Kinda depends on what you're looking for. So this gives you an easy overview as to what you're looking for. If you see any checksums or magic values, you might try and find those online to get more information, and if you see references in the code towards that data, you also know the size of the data rather than just being one byte. As for the disassembly, it wasn't really clear in screenshots if I did more than just one instruction, so I manually patched the first line, offset five, pop ex. Once you run the script, this changes to pop edx, which was originally, but this shows that the emulation from Jira and the changes in there are reworked in whatever you see in here. Now, as for a demo, we can see here that we started offset five. Now you can also see it starts at offset five. Otherwise, it's just me. And you can see that it has pop ex. Once we go down, we can see that function calls, for example, are to a memory address. We don't know what we're looking at necessarily, and if we go all the way down, we can see the bytes that are not classified, and we can see the strings that are found by Jira. Opening up the script manager and running it, we'll see in the bottom the log showing us what we can use and what has been added. So this allows us to, in one single overview, see what we need. We can see that the pop ex has changed into pop edx. And once we go down to the function call, we again see the annotation in both the disassembly listing, and in the decompiler view, where we have both the API, but also the value of the arguments. And we can see that in both views. Moving down, we see the checksums located at the bottom. Thank you. This also marks the end of the presentation. You can download and try SHAREM on the address on the top, and you can find the Jira address on the Trilux GitHub located at the bottom. There was an NSA grant used for the research for SHAREM, which is listed at the bottom. Thank you very much for your attention.