-
Notifications
You must be signed in to change notification settings - Fork 80
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rewrite original anonymous to allow new ones #26
Comments
defaultAction is really for extreme cases. it WILL bite you back if you use it, sooner or later. why do you need it? where are you using it? classes by default are add, and you can't change that. this includes nested/inner/anon classes. renaming source classes is a big no for me. it cant be done in a way thats guaranteed to work. you could rename patch classes, but i dont see the point. anon classes dont add much, you can use named inner classes instead. if you have to write too many anon classes in one particular case, you can use a hooking class (DexEdit) that calls into a worker class (DexAdd). the worker class can use anons. accessors are not needed: just make things package private instead of private. if you have to patch an anon class, then yes, you are in trouble. in many cases, anon classes are trivial, so you could just patch the outer class and make it create a named class instance instead, which you completely redefine. otherwise, if you are willing to patch the anon class by its $N number (ugly), you can do a cross-class edit. dxp would need a query language for this: eg: target the anon class(es) of MyClass that implements Runnable and has method xxx(). |
I'm beginning to understand the pain of defaultAction already, I think it's complicating the class/patch I'm working on at the moment. I think it was only needed when access functions are involved, I didn't realise it was possible to have inner classes that didn't result in them being generated (the original apk has a lot of them). I'll try more @DexEdit access modifiers on fields to change their permissions to see if that works in a cleaner fashion. The original project I'm working with uses a lot of large inner anonymous classes, up to 4 deep at times. This project isn't a typical app, it's a heads up display for a car running android behind the scenes where the one large apk is basically the entire single purpose of the device. The global once-off rewrite of all anon seems to be reliable for me, I'll probably keep it in place as it avoids any name clashes. |
the rename might be ok for you, but it can break stuff in the general case. so keep it. dont use defaultAction. define your members more visible instead. if you want to access an existing private field from a new inner class, edit the field to increase its visibility. you can also define your explicit access methods in the outer class. if you want to access an existing private INSTANCE METHOD from a new inner class, you CANT edit the method to increase its visibility: you would be changing a direct method into a virtual method. these method types need different dex code to be invoked, so you would be braking existing code. you'll get a warning or error from dexpatcher if you try. (static methods are all direct, so no issue there.) so you have to define your explicit access methods in the outer class to invoke the private method instead. |
Thanks, I spent a few good hours yesterday writing patches and I think I'm starting to get the hang of it. I do need to pay closer attention to the warnings, ran into a lot of those virtual/direct runtime errors before I figured out it was a private in original code vs package in patch and that I needed my own access type fn to bridge the difference. Thanks again! |
hey, i put out the beta, this release is taking shape now, i hope u subscribed to releases and saw it. i have a detailed grand magnum plan for all these things that will never see the light of that because i simply don't have the time to code all the stuff, but it would beautifully handle obfuscated code and the kind of issues you are up against. but you got me thinking again on a half-baked idea i had before the grand scheme and i sort of got it all sorted out now. it isn't beautiful, it doesn't "help" in patching obfuscated code but it does enable it, and overall it broadens the options and makes a lot of use cases more comfortable. like yours. the good news is that, although it is not beautiful, this can be implemented in a reasonable time frame. so although ill be travelling the following months while i wait for summer to come back to the southern hemisphere, now at least i have a down to earth plan that will very much solve issues like patching anon classes and functions... if not beautifully. just letting you know. how is progress with you HUD? btw, what car is it for? or is it aftermarket? |
That sounds neat, I'll keep a watch for it. Enjoy your travels! The rebuilding of my features in dexpatcher is going great! It's for navdy, which is an aftermarket hud from a now-closed crowd funded company. Thankfully they didn't use any obfuscater on the code so it decompiles relatively ok... though they clearly did use a bit of kotlin which confuses things a bit. I'm basically using just a reference decompilation from jadx to inform what to patch as I add new features into the original apk's (a couple on the hud iteslf, and a separate companion phone app) If you ever want any not-best-practice examples of dexpatcher in action it's all happening at https://gitlab.com/alelec/navdy/alelec_navdy_client/commits/develop_dxp and https://gitlab.com/alelec/navdy/navdy-display-Hud-java/commits/3080_develop fwiw I am still using my renamed anon's and a fair bit of defaultAction=ADD which isn't failing much yet... I look forward to your not-as-nasty method in future! |
you know you can access decompiled code directly in idea right? idea code navigation features work there. |
Yeah but it's built in decompiler (fernflower) struggles a lot more on my apk's than jadx, there's plenty it can't decompile at all. |
is it fern? my idea uses CFR (although it passes to little info to it), i must be using a plugin |
Sorry yeah I think I is CFR, either way I've got far more accurate, configurable and comprehensive results from jadx (and I've tried them all many times, mostly through the tools konloch/bytecode-viewer, luyten and jadx-gui). I've been meaning to figure out how to package up the jadx output into a source-jar and pointing the project to it properly, bit haven't got around to that. I have made a quick script though to regex add most of the needed @DexIgnore (and comment out static assignments) to a jadx file and copy or into my Java tree, which makes it really quick to pull in a file to start editing. |
i definitely have to try jadx then. i always got better results from CFR and i stopped trying others, but its been a long while. i'll look into it at some point. i don't like importSymbols that much and "batch" importing of full classes. and i definitely don't like DexIgnore. read bullet points 3 to 4 here: #14 |
hey @andrewleech, how is your project going? i'm finally having some time to add dex transforms to dexpatcher. for now only transforms for basic but full handling of obfuscated code are in place. but... take a good look at what's coming here... c565348 i made those transforms for you :) they are a bit different from what you are using, because a 2-level nested anonymous class all transforms are done on-the-fly during dex process and output write phases whenever any transformed items are visited. dxp doesn't build or keep transformed models of dex files in memory, so memory usage is generally not impacted by the use of chained transforms. the flipside to this is that diagnostics of transforms come at unexpected times during processing instead of lumped in one place. and --dry-run doesn't even transform the whole way, maybe. because of this, transforms do use memory when logging, to cache the log items as events happen and ensure duplicate events are never output. so extra memory is used in the end, but only if errors occur (or debug logging is enabled). i may add optional thorough-visiting stages to force lumping of transform log items, but these stages will slow down processing for the luxury of a more ordered log. so in the linked commit you'll find source and patch samples and also the output of processing. since you are sort of already using this feature, do you have any comments to this implementation before it gets written in stone? would you like me to prepare an alpha release for you to test? thanks! |
Hey, cheers, yeah I saw some changes come through on main dexpatcher project, thought they looked releated! Yep that inner not allowed the same as outer is quite annoying, it does complicate working with decompiled code. What you've got there does look good to me, what I can follow easily at least. The AnonAnon1 doesn't particularly worry me, it's a valid workaround to keep the compiler happy! I'd be very happy to test an alpha release, I've actually been using DexPatcher a heap lately on a new, second project: https://gitlab.com/alelec/fossil_smartwatches_alelec_android/ One little unrelated thing I've noticed with the android studio gradle plugin, often every second build fails due to the compiler not finding a bunch of things I've changed with DexReplace. I hit build again and it always works though. Have you seen this? Thinking about it now, it might be from cases |
one problem with the project is the small user base. i'm not really dogfooding it because i no longer have time to hack apps myself: either i work on dxp or hack apps, but can't do both. so no, i haven't seen this behavior so far. if you have a project state that reproduces this consistently, post the full project if you can (maybe just tag a commit in your project?) and i'd gladly take a look at it (but possibly not immediately). regarding the small user base: i am now 100% technically sure that i can implement DexPatcher-live for apps!!!... in android 9 :( live is an android system mod to apply dxp patches on the fly without patching the apk files themselves and without invalidating any signatures. it might be possible also for framework, but not in the first release. it is comparable to xposed but safe: patches don't have any rights except over the app they apply on, while xposed modules are always root and could all be trojans. they'd be easy to write instead of the nightmare that is writing xposed modules. would not require reboots to update patches, just an app restart. on the other hand, the first release would patch only code: not resources and not the manifest, which is a drag. now that xposed is kind of dying, this could be an opportunity for the project... if i only had more time. android 10 has changed things drastically with the new APEX packages, so it would be very hard to patch the framework: inside layers of overlapping APEX packages, which contain ext4 partitions, which contain... odexed files! which need to be deodexed, then live-patched. it's hell. but apps on android 10 might still be possible. and if you are willing to deodex your rom (if that can even be done on android 10), then maybe the framework too. now back to the subject... i'll try to get an alpha release ready tonight. i can surely use your testing. thanks!! |
Geez, I hadn't heard of APEX before, yeah that's some more layers of lockdown there. Live patching of apps would be incredible! It'd be great for the fossil app I'm working with at the moment as I'm about to do a first announcement / release.... and the instructions are going to be something like: uninstall the existing app, yes you will lose your settings. If you have Titanium, backup the app first. Then install mine from apk, you'll probably need to agree to security/premissions popup. Then restore data from titanium, be sure to only select data, not apk. profit. On the build issue I get, it's reliable for me with this project, this commit: https://gitlab.com/alelec/fossil_smartwatches_alelec_android/tree/9c8fecfea815dbf33c68e13b5c78498bce3773b8
In this case, jadx decompiles a number of functions like this with a couple more args than the builtin decompilers comes up with. So I've got the jadx copy sitting there completely DexIgnored and that satisfies the compiler (every second time) and function calls I make to the long version seem to work fine in practice. But every other time, the compiler only sees the jar version and complains the signature's wrong. Not the end of the world, I just compile again, but a bit odd nonetheless. Don't rush for a release, chances are I'm going to continue working on this on-and-off for a few weeks at least :-P Cheers! |
thank you!!! your bug is filed here DexPatcher/dexpatcher-gradle#28 for when time permits :) |
your alpha release is ready :) besides the obvious, i'd appreciate comments on usability if you have any:
thanks!! |
Sorry for my ignorance, but is there a way to set the |
yes! |
were you able to test? |
Oh yes, I did try to use it but didn't have much luck, meant to follow up! I think I've got the config wrong...
I've got a decompiled class I'm trying to target called
but I get the errror:
It's currently decompiled into a file of its own, I guess in this format it's less important to have the auto renaming in place as the name's already been bulked out. I might try re-decompiling the top level class with anon's inlined and try it then. Is there a way to get a copy of the source dex/jar after deanon-source has been run to decompile straight into the form that dexpatcher has modified? |
hey,
better if you use
for your case the options should be:
yes. when the the dxp-tool 1.8 is finished, dxp-gradle will be updated with the possibility of 1) importing transformed symbols to the compiler, 2) decompile on-demand the transformed classes, and maybe 3) pre-transform the source to speed up the code-build-test cycle. for now you have to do it manually:
additionally you can define a Gradle task with type DexpatcherTask to do this kind of thing for you. |
Thanks, that's really helped. I think I'm up and running well now on the original unmodified apk.
Thanks for the manual command advice, that has helped. Some of the renaming gets pretty verbose with the repeating Anon's, Eg. When I first enabled
But after a couple of cleans / rebuilds it seemed to come good, looks like it's working well. FWIW when the gradle integration is done it would help if the error messages could have a same sort of "jump to file" links in the android studio build log that the existing warning messages get, eg.
In the (existing) warning messages |
glad it worked!
i want to be able to:
it can get a little weird under some conditions, and so the chosen scheme of repeating anons. i thought of Anon_ (say Anon25_3 instead of AnonAnonAnon25) however, thought experiments seem to break that down under some conditions. it might be fixable, but with so many things of more importance pending on dxp, i think the current solution is enough.
yeah. unfortunately transforms work just in time when the code is processed to save memory and because of other considerations. so context information is not easily obtainable. line numbers are out of the question (too much work for low return) but at least including files might be possible under some circumstances. consider that simply producing diagnostics is complicated. the diagnostics messages happen at any time at all, and the transforms have diagnostics caches that make sure that messages are not repeated. anyway. the most important thing: you notice a performance hit? thanks! |
I haven't noticed a performance issue, if there is one it's not enough to cause me any pain. Certainly the gradle caching deals with most of it anyway. That being said, if you were worried about it the re-write could be done once-off on an apklib similar to what I've previously done to the smali with regex's. |
pre-transforming would only help one-way. reanon and other restoring transforms have to run after the patch anyway, so can't be cached. i want to introduce transforms that allow arbitrary mappings to work with obfuscated code. mappings would be configurable via a file, and analysis workflow would be iterative: decompile (on the fly with IDEA), decide on a good name for an item, add it to mappings, repeat. so map updates would be frequent. writing dex is 10 times slower than online transform, so pre-transforming is only worth it if you'll apply patches 10 times using the same mappings. each time to update maps, you'd be generating new dex files which is a huge performance hit. so it makes sense to do it if you are importing symbols or decompiling based on transformed stuff (which is extra slow anyway because of the dex2jar step), but not because of performance, i think. in dxp-gradle, the visibility of transforms (ie in symbols and decompilation) -and thus pre-transforming- is going to be configurable, but in general you would enable it for reasons other than performance (eg: feeding dex2jar with transformed stuff). i will try to avoid feature creep around pre-transforming support for performance only, especially because performance is very dependent on frequency of mapping changes, but also because you'll probably mostly want to always have visibility of transforms (which require pre-transforming). |
FYI, you now have this build: available in the repo: so you can use that and retire your local copy of dex2jar |
a heads up for you, some changes are coming up and impact you:
really sorry for the changes. being alphas, i don't really struggle for backwards compatibility. sorry and thanks! |
here is a backup of alpha3 for you to use locally, because it will be retired. sorry! |
Thanks but I'm more than happy to figure out what's needed to move the the next beta, I expect alpha level stuff to change and break! Thanks for the heads up! And in future don't event spend a second trying to maintain alpha compatibility, build whatever makes the most sense to you. |
thank you! :) |
there will be a default since you use this, what should be the default plan?
isn't option 1 too verbose? |
Yeah 1 seems unnecessarily long, 2 makes sense to me.
|
i totally agree. thanks! |
well i implemented it but i don't like it. because i can't do this in one pass:
so i don't know what i'm gonna do... |
Nice, yeah I doubt there's many times the full flexibility of setting a custom plan at each step is really needed, I'm personally happy to take a working suggestion/example and go with it! PS. On an unrelated note I've recently had to rebuild my patches on a new official release of an apk, where I rewrote them using more of dexwrap to reduce the amount of proguard obfuscated code I needed to touch. Geez it works well, so quick to use and lower risk of new decompilation bugs! Thanks again! |
plans have to be configurable for the same reason dxp annotation package and code markers are. these options might never ever be used, but they still serve their purpose: they are attack deterrents. app authors (or obfuscation tool authors) could easily attack dexpatcher if they weren't. but knowing that these options can be reconfigured and thus attacks can be easily averted, no attacks will probably exist. a similar thing: --no-reanon-errors was changed to --no-anon-errors when i changed the anonymizer to numeric levels (ie: Anon[] to Anon[_]). this was because a specially crafted dex can be made to make the anonymization level increase whatever type of variable was used to express it be it int or long. so i detected the case and switched the option to --no-anon-errors to account for the fact that deanonymizations now could fail (but only when attacked). i accepted this because the countermeasure to an attack was simply to change the plan. but... thinking of it later: if a patch is published, then the app author could attack the anonymizer in the next app version, forcing the patch author to change the plan. but that requires refactoring the patch codebase to adjust it to the new plan, which is non-trivial work. so yesterday i implemented infinite precision math for the anon level numbers, so no overflow can ever happen. deanonymizations are back to their original 'never failing' status, and the option is back to --no-reanon-errors (and reanons only fail due to patch author errors, never because of attacks).
i never knew your source app was obfuscated! i already implemented another method to deal with obfuscation that is complementary to identifier codes but i haven't published it. i'll push it now and be back... |
that was an easy rewind to a pushable state. i pushed the mapping support and the new (and hopefully final) implementation of the anonymizer (which should have no impact on your end at all). this way of dealing with obfuscation is based on a mapping file (similar to proguard's) such as: where you give meaningful names to all stuff that you need to refer to in your patch. when patching, you --map-source and --unmap-output, so patching is done in your deobfuscated namespace (including all diagnostic messages) but the output is returned to its original obfuscation (to avoid issues with reflection). when a new app version is published, you only need to update the mapping file and not your code. also the map will serve to rev engineer the apk, as IDEA's JIT decompilation will be updated with the meaningful names as you add mappings to it. but this requires a new dxp-gradle, so a preview of the current code would not do that (i only have dxp-tool functionality for now). i'm working now on a 3rd synergistic method for handling obfuscation and when finished. tool's 1.8.0 feature set should be about frozen. then i can publish a beta and switch to dxp-gradle. |
1.8.0 feature set should be around complete. it's pushed but nothing is tested. i suppose its time to close this. thanks! |
I've been porting my old (buggy) fully-decompiled / mod / recompiled apk changes to new concise dpx based ones. A lot of my original code makes heavy use of convenient inline / anonymous classes, which are a bit of a pain to have to split out by hand.
One of the tricks I came up with in my previous re-engineering efforts was re-writing the original apk/jar so that the anonymous functions were a little easier to deal with.
Use apktool to extract, then a python script to update all the smali to turn $1 to $Anon1 (and so on) then rebuild. This renames all the anonymous inner classes and the access() functions so can be referenced in a jar and no longer clash with new anonymous functions created by javac
This trick seems to work exceptionally well with Dexpatcher, I've re-written my original APK in this way and now if I set defaultAction to ADD anonymous inner's seem to be working fine.
For reference, this is my basic python script, my target application's code is all in the package com.navdy.* (I didn't want to rewrite classes from other packages)
I haven't looked into the internals of dexpatcher yet but surely this same trick would be fairly easy to achieve in the dexpatcher tool or the gradle plugin? Is this likely to break something else (other than the general issues to look out for when defaultAction = ADD) ?
The text was updated successfully, but these errors were encountered: