 She's going to give you a presentation about riddles. Well, close enough, I'm talking about runtime riddles. And I'm also standing on a platform, so I hope everybody's able to see me and I hope I don't fall off. But today I'm gonna be talking to you about abusing manipulation points in the Android 13 runtime for anti-reverse engineering. So here we am, I'm Lori Kirk. I'm a reverse engineer at Microsoft. I specialize in cross-platform malware analysis with a focus on mobile threats. I also run a YouTube channel under my alias Lori Wired and I'm also on GitHub and Twitter if you want to connect with me. And today I will be representing myself as a security researcher, not representing Microsoft. And if you'd like to follow along with this presentation at all, feel free to go ahead and scan the sketchy QR code right there. I promise it's not malicious, but I've also added the URL to my presentation if you don't want to trust me, which is fine. So let's get right into it. Now imagine you're a seasoned security analyst and you're analyzing an Android application and you've checked every single method inside of this application and it's all looking clean and you're not finding any malicious indicators inside of this application. So you're about to call it and say that this application is indeed clean. And then you get to this part of the code. And you might think this looks entirely benign. We have a log.e method, which is an official method inside of the Android framework used to log an error to the console. And it looks like it's setting a button to the Android application screen that the user can interact with. You might think this code is perfectly safe. This code has actually been dynamically modified and hooked during runtime of the application so that every time the official log.e method inside of the Android framework is invoked, it's actually executing an underlying info stealer inside of custom code. So this actually just stole your data, which brings me into my agenda for today. We're going to manipulate the Android 13 runtime and we're actually going to replace the official Android APIs while our application is executing to execute our own custom code. This also works on Android 14 in the upside down cake release, the beta version. And I'm also going to provide a new open source tool to the community that I just released about an hour ago. And this is going to let you import this as a library into your own Android applications to provide additional obfuscation for your Android APKs. And this will work for any device that you're trying to protect your Android applications against. And it's specifically designed before Android 13 and 14. Now for a little bit of background before we really get into it. So the primary goals for obfuscation inside of Android for dynamic obfuscation is to load additional code into the Android runtime. So basically this is trying to defeat static analysis of Android applications. Basically there's an additional class or method or even executable that is defined on disk that Android malicious or benign developers will try and include inside of their Android applications to prevent static analysis of their code on disk. So if they load and execute this code while the application is actually running and a reverse engineer is taking a look at this code they actually won't know unless they dynamically analyze their code that this additional code is actually executing. There's a few methods that are really commonly used for dynamic code loading. These can be found inside of the Android framework. You might see dex class loader, path class loader or even some different Java reflective calls. And basically they will take an additional Dalvik executable on disk and load that into the Android runtime or they can even work on different methods, classes or variables. So these are all effective at actually altering the dynamic flow of the application. The only problem is, is that these are really easily searchable inside of the code. So I took a malicious application and I threw this into my favorite Android decompiler, JDEX. And all I need to do is go search text and then start typing in my favorite Android APIs and I can find all of these dynamic code loading calls in plain text. So this is really simple and at that point I'm able to fully understand what this is doing and be able to potentially analyze those additional code loading calls. Furthermore, there's a lot of default frameworks inside of Android. You might be familiar with Frida that's actually able to hook methods for a lot of different platforms, not just Android while the application is running or there are hooking frameworks that are designed specifically for Android applications. The popular one might be Medusa, which actually by default hooks a lot of these different Android APIs that are specifically targeted for code loading and will dump the executable for you so that you can take a look really easily and actually statically analyze that additional binary. So when I started working on this presentation, I wanted to think, is there a way that I could actually bypass these common API calls so I could try to hide the dynamic modification of my Android application? Which brings me into my first point, which is going to be manipulating the Android runtime directly. First of all, a little background if you're not familiar, the Android runtime or also known as ART is responsible for taking in binaries that are defined on disk. So these are gonna be your Android applications, your APK files and actually parsing out all of the code from inside of those, bringing those into the actual Android runtime environment, mapping those into memory, making the relevant portions executable and actually executing the entry point of the particular executable binary. So basically, you have your Android runtime responsible for taking your code on disk and bringing it into the runtime environment and importing any necessary data inside of your application. The high level of it looks like this. You have your method defined on disk and then you have ART, which is gonna parse out all of that data that I mentioned, bring that inside of our process memory and actually execute those underlying instructions. So from here, I had an idea. What if I could actually modify those methods that have already been mapped inside of memory or even modify an entire executable that has already been brought into the runtime environment instead of actually just messing with it on disk? This means that we would avoid any of those plain text API calls that I mentioned earlier that dex class loader, path class loader or in memory dex class loader that are so commonly used and such common targets for Android reverse engineers already. This would also mean that we would not have any standard methods to hook with a lot of those default Android frameworks that will commonly just dump the dynamic binaries that we're trying to execute and hide. Now the only problem with this is that Android has a lot of abstraction layers that we have to keep in mind if we're trying to actually hook and modify these methods. So it's not just as easy as hooking the Java method, altering the behavior and just having the execution. If we're actually trying to swap out entire methods or entire executables and fields inside of our memory of our application, we actually need to modify the underlying native structure of those Android methods. Now what you might be familiar with is that the Android framework is actually written in Java. So this means that Android developers write in Java or Kotlin and they'll import those Java defined official APIs into their code. So the developer is only interfacing with the Java portion of these APIs. But what you might not be familiar with is that the underlying implementation is actually defined in native C++ code. So the Java basically works as a wrapper around a lot of underlying C++ implementation. The same thing goes for the official Android APIs as well as in our own user binaries. We often have Java invoking the native C++ which actually contains a lot of the implementation. The high level view looks something like this. You have your developer on top interfacing with the Java portion of our application. And then we have this special thing called the Java native interface or JNI that actually lets the developer and the Java code communicate with the C++ code underneath and vice versa. So if you're trying to think of the JNI, this is important, so we're gonna use it later. Think of it as the bridge between our managed Java code and our native C++ code. And remember, we want to modify the underlying C++ in our process memory instead of just looking at the Java portion of this. So we're gonna use the JNI to do that. This means that our new methodology is gonna be something like this. We're gonna locate our Java target on top. We're gonna intercept those specific targets using that Java native interface bridge between managed code and native code. And then we're actually going to dereference a pointer that we're able to get with the JNI to modify and overwrite that underlying native data inside of our process memory. So let's get on to our first point. We're gonna talk about finding entry points and targets within the Android source code inside of the Android framework. This means that I'm gonna first dive into the actual source code of the Android framework. One thing that I really like about Android is that it's all available for you online. You can read any of the source code. There's even really helpful developer comments inside a lot of the code for you to understand. So we're gonna look into that top point right there and take a look at the Android platform and see if there's any Java methods of interest that we could potentially use to actually swap different relevant data inside of our application. Now the first target I looked at was trying to swap an entire DEX file. A DEX file is a Dolvik executable file and it contains all of the actual Java byte code or executable code that's inside of your application that's actually gonna be run on the underlying processor. And if you take a look inside of the Android framework, we have this DEX file.java class that's defined that we're able to locate. And this contains all of the relevant data for a DEX file or Dolvik executable file that's being mapped from disk into memory. Specifically, if you look at that M cookie field there on the left hand side, this is containing a reference to that open Dolvik executable file. So my first target was, what if we took that M cookie field and we actually swapped that with another field during execution so that we're executing an entirely separate executable than what we would expect and what a reverse engineer or anybody taking a look at our code would expect. This didn't work super well for a few different reasons. The main problem is kind of a chicken and egg scenario. So your main Dolvik executable file is already initially kicked off as soon as ART gets the input from the user to actually run this application. It's going to immediately call that DEX file that's already been mapped into memory. Now, how would you actually invoke your code that's going to do the hook and swap if that main DEX file hasn't already been mapped inside? And if you're trying to map an additional DEX file into memory, you have to use those pesky API calls like DEX class loader or path class loader that we were trying to avoid that are so commonly hooked or else what's the point of doing any of this? So this does not really seem like a viable option for us. So I moved on to trying to modify individual members inside of a DEX file. Specifically, I found a very interesting portion of code inside of the Android platform. So Android is actually built on top of Java and you can see a lot of the original Java definitions and references are defined inside of our platform. This is a field object. So every single field inside of an Android application is going to be instantiated and get its own version of this when the application is actually running. And this contains all of the relevant runtime data for every single field inside of an Android application. You can see we have our access flags, declaring class and all of the different runtime data that we're actually gonna need to use this field. So if we keep on looking around here and try to find an interesting place to actually modify and manipulate, I found this executable.java file and this actually contains an ART method variable inside of it, which is sounding really interesting. And if you take a closer look at the ART method variable that I have highlighted right here from our Android source code, this is gonna contain the actual ART method structure for every single method that is defined inside of an Android Dolvik executable file. So every method that's defined on disk gets a corresponding instantiation inside of the runtime as defined by this ART method structure in native code. So I had an idea here. How about we take this ART method field and we try and swap two methods while the application is actually running? So that's gonna be my plan. We're gonna have two methods and we're going to try and overwrite one behavior with the behavior of another method to see if we can actually do some manipulation. So I have my benign method, my fake malicious method, and as soon as the application starts running, we're gonna hook both instantiations of these methods and try to overwrite that ART method field so that we can see if we can get benign method to be called but malicious method code actually executed. So now we need to move on to intercepting methods via the Java native interface. Remember that was that really important bridge between native code and managed code. So what we need to do is we actually need to call the JNI from our own native application in Android. We need to use that to retrieve a reference or handle to both of those objects and then we actually need to load the corresponding ART method field for both of those. And then once we've actually done that, we can do our overwriting. And this is gonna be the code that's actually going to be able to perform that. On the top there, you can see we're trying to find the executable class that was defined inside of our Android platform. And then from there, we're able to find that ART method field that is inside of that class defined in our Android platform. And we also need to get this for malicious method and benign method so that we actually have the instantiations of both of these since every single method is gonna get its own. Now there's one problem you probably didn't notice or maybe you didn't. That ART method field is actually private. So this means that only instances of that executable class or its children can actually access this ART method field which is kind of a problem because we're trying to access that from code that has nothing to do with the Android platform. We're just using it inside of our own user code. So this actually is a really big problem because the ART method object is completely hidden from us or is it? So actually the Java native interface does not respect access modifiers. So if we just ignore the warning like any good hacker would, we can just run this code and it's going to run and execute this just fine even though Android Studio is telling me that this is wrong and I should not be doing this. That's okay because it still runs. So now the first thing that I wanted to do is I wanted to do something kind of simple. I basically just took the handles to both of those objects that we were targeting and just set the nine method equal to malicious method and let's just see if it's actually going to work. So let's go to test time. So I'm gonna pull up my first video here and let's take a look at this code actually running and if you're able to see here, I have two methods defined inside of my Android application. I have benign method which is gonna return the string. I am benign. I have malicious method which is gonna return the string lol and we're just printing the return value of benign method to the Android screen inside of our Android application. Now this is my native code that we saw a little bit earlier, we're getting that executable class with the respective ART method field. We're getting the objects for both of those benign and malicious methods and then if you can see highlighted there, we're doing something really silly just setting benign method equal to malicious method but hopefully this actually works and if it does we're gonna see lol printed to the Android application instead of the text I am benign. So let's actually let that run and see if this is gonna work. So real quick. Show of hands, who thinks this is gonna work? Show of hands, who thinks this isn't gonna work? Show of hands, anybody asleep, drunk, I don't know. Okay, that didn't work. That's too bad. So let's go back to our slides and we finished our actual test which was kind of a dumb test just setting one value equal to another, we didn't actually do any effort to understand what was happening underneath. So all that means is we just need to dive deeper and we need to actually understand that underlying native structure inside of our Android application inside of our Android framework. So let's go through and let's understand that native structure. First of all, if we kept on going and we started spending a lot of time or too much time inside of the Android source code then we would find out that that ART method field is actually a pointer to an underlying native ART method structure. So this ART method actually defines the whole representation for a native ART method object that's mapped into process memory. So this is the native representation of an ART method inside of our Android runtime. And we can actually find the declaration of this which is put inside of artmethod.h and this contains all of the relevant data for any method that's gonna be running inside of our Android application. It's gonna contain the access modifiers, the actual executable code or any data that is needed for this method to fully execute and run. And if we keep on going, we can find the individual fields defined inside of this artmethod.h. And one field of interest, if you take a look, you can see entry point from quick compiled code. And as I said, really helpfully, the Android source code provides a ton of comments defining what it's actually doing. So we can figure out exactly what this variable stands for and what it is actually doing. And this is going to be the actual native instructions compiled from this method that are going to run directly on your underlying processor. So that sounds really interesting. What if we actually changed the instructions that are pointed to by this entry point from quick compiled code? And I kept causing repeated crashes. So what I was doing is I was working in a 64-bit operating system. So I just tried to take eight byte increments and actually just keep overriding those bytes bit by bit and seeing if I could find where that entry point from quick compiled code actually lived inside of the process memory since we had a handle to the beginning of that artmethod field. But we don't actually know where everything else is inside of that artmethod field in memory. So my new plan was, okay, I'm gonna test every two bytes, keep going forward and forward and forward with eight byte increments and actually see if I could accidentally overwrite this pointer in memory and then cause the malicious method to actually execute. But that was totally not working and it was getting really frustrating because I was like, I don't even know if I'm doing the right thing. I don't even know if I'm starting out correctly right here because what if it's actually not the native structure? What if I'm looking at something completely different and wrong? So I eventually just gave up on that and then I had a new bright idea. I thought, okay, I'm just gonna take 64 random bytes from malicious method and overwrite those with 64 bytes of benign method because I'm working in a 64-bit operating system and there were like roughly eight variables inside of my artmethod class. So I was trying to account for those sizes roughly. I mean, you can shout it out if you disagree and think it should be another side but we went with 64 bytes here. So let's go to a second test and let's actually see if this is gonna work. All right, so this should look familiar for you. We have our executable class that we're trying to get a reference to. We found our respective artmethod field. We have our malicious and benign method and then we have that nice, pretty silly line there of memcopy, completely unsafe. Let's just copy over 64 bytes from malicious method to benign method and let's see if that's gonna work. So what we want to see is lol written to the screen instead of I am benign. So show of hands now. Who thinks that this is gonna work? Who thinks that this isn't gonna work? Okay, I think that's the majority right now. Well, let's see. So I'm gonna start my application. Here we go. And it worked. So I appreciate the vote of confidence from everybody who thought it wasn't going to work but it did. So I kept on going after I figured out that it was that easy and you could just make it work. And I wanted to see what are the actual specifications and what can we do with this? So after a lot of testing and a lot of breaking stuff, I found out that the methods have to have the same signature meaning they have to have the same parameters and they have to return the same type. They must be static. They can be declared in separate classes or wherever you want inside of your Android application. They can have completely different functionality and they will work inside of the application context meaning that if you're in your app you actually can't modify the methods of another app but you can do whatever you want inside of your own application which is good enough for us and for obfuscation. So that's cool and overriding random 64 bytes in memory does work but let's actually try to increase our accuracy a little bit. So I'm gonna do some byte math now and we're gonna calculate all of the different offsets for this ART method object and see where everything actually is in the process memory of our application. So the Android source code, I was taking a look at it and it itself is actually finding these offsets in memory using the actual size of the variables defined in our ART method.h that defined all of our method components. So this is actually used by the Android, the official Android runtime for getting and setting these values. So I thought I could either count the sizes of all of these different variables inside of here or I could let it calculate itself which is exactly what I did. So I created a dummy ART method class. I stole all of the source code that I thought was relevant for the ART method class, put it in my own application and just added the necessary member variables for that and then I let the program calculate itself and print its own offsets to all of those in memory. And this is what it looked like. It worked really well. I found the corresponding variables defined inside of our ART method.h and I wrote those down so that it would print out the offsets for every single one based off of the defined sizes that the actual application, that dummy application that I was running actually found. So those are all the correct offsets in memory for every single variable as defined inside of our native C++ ART method structure. So now it's time for a little bit of byte math. If we keep going and use those actual offsets that we found, we can take the actual bytes for two objects or two methods that we were trying to calculate in memory and associate those particular bytes with their actual variables and types. And we can see we have our declaring class, our access flags, that really important entry point from quick compiled code there on the bottom right hand side. And I was working in a 64-bit architecture, so those are eight bytes long because those are actually pointers to different locations in memory that are actually defining the implementation and data for our ART method object. So now that looks cool, but I wanted to know did it actually work? Are these offsets correct? Because I'm saying they are and it looks kind of okay, but did it actually define the specific locations in memory and do those bytes point to anything? So I wanted to look specifically at that entry point from quick compiled code because this should just be arbitrary native code that's going to execute on your underlying processor. So this is actually going to be assembly instructions if you disassemble them. And I look at it, I mean that looks like disassembly to me, looks like a function prologue that's gonna be setting up the actual code for our Android method. And we have all of that, all of those relevant native instructions that are going to be setting up this native method. So I think that worked and I think we actually have our correct offsets and we know exactly where everything stands in memory and we could overwrite it if we wish. Now we have the exact sizes that we need to do if we're trying to overwrite an entire method or affect any of the individual variables inside of that method. And if you want, you can look closely, I have all the sizes written there but I won't go into the details too much. Feel free to do that byte math on your own. And additionally you'll have to account for if you're running in a 32-bit or 64-bit operating system but you should still have the same offsets for ARM or x86. So our calculations are actually complete at this point and now we have successfully finished our byte math and we fully understand the native and Java structure of our ART method objects. And we can pretty much do whatever we want from here. So I thought, okay, let's have some fun. How far can we push this and what can we do with this? So I had another bright idea. What if we took all of the official Android APIs and we tried to replace them with our own custom code so that the developer or a reverse engineer is going to think that we're calling those official Android APIs but we're actually executing something entirely different. So my target now was the official log.e method which again is just logging a string error to the console. If you look at it in the Android logcat you're able to see these messages. And this is used by many different Android applications both malicious and benign. It's a very common method. Reverse engineers will pretty much completely gloss over this method if they actually see it because it's entirely benign. So what if we took this benign log.e method and we actually had it steal all of the data on the device and replaced the log.e functionality with a malicious info stealer? So what we would have to do is I had to create a static method that had a matching signature. So remember it's going to have to have the same parameters and return value but the actual implementation can be entirely different. So this is going to contain the code that's actually going to get executed every time that log.e official Android framework method is invoked. So it's going to invoke my code instead of theirs. So let's do a test and let's see if that's actually successful and see if it will work. So I'm gonna open up my video inside of here. Looks like it's already running. And let's just start looking familiar to you. We have the job native interface which is hooking both instances. One is gonna be the instance of the log.e method and then one is going to be the instance of our new custom method that's actually going to get executed. And then we're going to overwrite 32 bytes in memory since we upgraded from our random 64 bytes to actually account for the correct size so that we can actually completely overwrite this and execute our custom code. So now I'm going to be running this and I'm going to pull up my application and I'm actually invoking that log.e method repeatedly. So every time that log.e method is invoked it's then going to sleep for two seconds and instead it's going to be executing our fake info stealing functionality. And on the right hand side after it's sleeping for two seconds you can see it's going found installed application found installed application enumerating through all of the applications in your device to actually steal that data from those even though this has actually never been invoked if you take a look at the method on the device statically. So let's go back to our slides. So now it's your turn to try your hand at manipulating the Android runtime. I have just released an open source tool about an hour ago. This is called Artful and it's built for manipulating the Android 13 runtime but I did just do a check on Android 14 upside down cake and it was perfectly successful at manipulating that as well. This should work on many different API versions of Android although I have just tested on the latest versions since those were my primary targets. So what is this? This is a library that you can include inside of your Android applications for dynamically swapping the behavior of methods from those that you would expect on disk. So it's doing all of the complicated calculations on the back end so that you don't have to worry about calling the Java native interface directly or actually doing the calculations of those underlying bytes and native code. This works for your user methods. I have default methods that will take either an object or the signature of two methods that are defined inside of your application and you simply call the Artful library that you very easily included into your app and then you can swap the behavior of any of those two methods. Additionally, I have some default Android framework official APIs that this will replace and you can call those and simply pass in the object that you would like that has your new custom code that's going to actually be run every time that Android framework method is called or you can call the first method and actually swap out different Android APIs if those are not implemented. Additionally, I included the dummy ART method class that's defined inside of the Android source code so that you could print those offsets if you wanted to do additional calculations and if you want to actually try this and extend it to support a different API version, all you need to do is take those variables that are defined inside of the ART method object of the specific API that you're trying to support and it will calculate the updated offsets for you. Let's go through and let's summarize some of our results. Primarily the main challenges for this are understanding the code inside of the Android open source project. It might be open source but there's a lot of overhead that you need to understand to be able to actually follow the flow of control inside of the Android source code. You need to be able to follow from Java land over to native C++ land and understand where you're going. Additionally, you need to be able to know which Java target you have and be able to calculate the sizes of the underlying C++ objects that you're trying to manipulate. Also, you have to do that complicated pointer math if you're trying to find the offset to a particular variable inside of the Android source code and you also have to account for the differences in Android versions since with every single code change inside of every single Android API release or update, these calculations could be entirely different if they added additional functionality or if they removed different variables from the source code. Now the benefits of this if you're able to do that is you're going to be able to avoid all of those standard class loader API calls from inside of your code and you're going to be able to completely avoid that dex class loader, path class loader or any of those Java reflective calls. And this means that you're going to be able to execute unexpected code while the application is running. So if you have your two methods in memory, you're going to be able to define your entire custom code and if a reverse engineer is taking a look at your application on disk, they won't realize that your actual target method is being invoked and they'll only see invocations of the benign methods that appear on disk. So this effectively thwarts a lot of different reverse engineering and any static analysis of your binary so that you can further obfuscate and protect your applications without use of the standard API calls. So here's an example of an application that I created using my artful library. And I had one call that's going to replace a benign method with a new method that had custom behavior inside of it. And I threw this again into my favorite Android decompiler JDEX and I just hit the cross references button and there's actually no references to this method. So if a reverse engineer is taking a look top down from the application, they might entirely avoid actually looking at this method and never see that it's referenced and see that it's completely hidden. Additionally, this has no calls to those standard class loaders for dex class loader or path class loader or the standard Java reflective calls. So that means if you remember those frameworks that automatically hook a lot of relevant methods that reverse engineers will try to use to dump the dynamic code that's getting executed on the system, all of those checks are going to actually fail and they're not going to be able to find these methods that are getting invoked. Now they could try to hook the different Java native interface calls that are actually doing the manipulation of these methods but those JNI calls actually look entirely benign since a lot of applications will use the Java native interface to communicate back and forth between their managed Java code and their native C++ code. So how would anybody know that this is actually an obfuscation technique? Now go modify the Android runtime and you should be able to solve this. So thank you so much everyone. So I do have time for questions if anybody wants to ask them. Otherwise I'll step down upfront if you would like to ask me directly. And this again is another sketchy QR code but it links to the artful tool that I was talking about. So if you're feeling brave, go ahead and scan it. So the question was if you're having reflective calls inside of Java code and you're having reflective calls inside of native code how does this benefit if you're having those reflective calls inside of native code instead? So the way that a lot of the hooking frameworks actually work is they'll hook these API calls no matter what if they're invoked from Java code or native code it actually doesn't matter. So if you're using these JNI calls yes they could be hooked and analyzed by a reverse engineer but there's nothing to say that they'll know exactly what's happening. They'll probably just be very confused and not realize that this is doing anything interesting since it's actually not commonly used as a dynamic code loading thing it's basically just hooking the actual ART method object so there's not any dynamic code loading happening since this method has already been loaded into the runtime so it basically just looks benign. Any other questions? All right, thanks everybody.