I remember IDA (Interactive Disassembler) has a really neat feature of function signatures where you don't have to reverse engineer code found inside standard libraries.
Is there a similar feature for Java byte code, especially for obfuscated code?
There is no similar feature for Java byte code.
When you compile a C program, and statically link it to a standard library, the library code will be present, more or less unmodified, within the binary (except for addresses which will change), but there won't be any hint that a particular function had a particular name before being compiled (unless debugging is enabled while compiling/linking). But, any given function has a more or less fixed pattern of bytes, and by recognizing these bytes, IDA can assign the original name to the function.
In Java bytecode, there is no necessity to do this. Function names, variable names, and similar information is present in the compiled bytecode anyway. The "standard library",
rt.jar, isn't embedded into the bytecode either, so if a class uses an
ArrayList, the class will have a reference to
java.util.ArrayList even after the obfuscation process. So, nothing to do here for a signature analyzer.
If the application chooses to obfuscate the standard library as well, it needs to include that obfuscated library into its own jar files. This will probably raise some licensing issues, but apart from that, as the obfuscator will rename methods and fields, the byte code of that obfuscated library will be too different from the original byte code to be recognizable by a feature like IDA's FLIRT. Also, you can't just rename the method in IDA, because you'd have to modify all the references as well.
However, there is at least one open source project that has a similar problem like yours, and they seem to have solved it quite well. Minecraft is a popular game that includes a server that's written in Java (and obfuscated); the Spigot project decompiles this server, changes some things, adds an API, and distributes the result. Specifically, to avoid licensing issues, they distribute a build system which downloads, decompiles, patches, and recompiles the Minecraft server on the user's machine.
They use the fernflower decompiler, which has an option to rename symbols while decompiling, and include an extensive map of obfuscated-to-readable symbol names. Example:
AttributeInstance a ()LIAttribute; getAttribute AttributeInstance a (D)V setValue AttributeInstance e ()D getValue AttributeMapServer b ()Ljava/util/Set; getAttributes AxisAlignedBB b (DDD)LAxisAlignedBB; grow
The source code, which is generated using these maps, is obviously way more readable than the original one. And, at least for the spigot project, recompiling this source (after adding some patches to create an API) yields a working Minecraft server.
So, maybe, this could be a way for you to proceed - use fernflower to decompile your classes, load them into an editor to find some useful code and assign readable class names, write a mapping file, and repeat this a few times. Then, when you want to do some dynamic analysis, recompile the decompiled Java code and load that into IDA.
Of course, this approach still has some problems:
- you will still have to identify each function manually - but as i said, you probably won't find any standard library functions in your code anyway. And, there are no existing signature libraries for whichever method you use, so there's probably no way around that.
- the decompiled/recompiled code may not work, because of bugs/shortcomings in the Java decompiler itself
- the decompiled/recompiled code may not work because of the obfuscator; for example, the obfuscator might replace all string constants with something like
Obfuscator::decode("some_crypted_stuff"), where the
decodefunction uses the name of the calling class as its decryption key, meaning the decryption fails when the class gets renamed
- the obfuscator might bring its own class loader, which mangles the class name before loading it; for example, it might know to turn a
com.obfuscate.SOME_BASE_64_STRINGclass into the decoded base64 string, so
java.util.ArrayList.something(). This breaks the visible connection between caller and callee (but the name mapping could solve this problem quite well and you could automate a lot)
Even if you can't get a decompiled, recompilable, version of your binary, you can probably write some code that works on the decompiled source to identify functions by their behaviour, using some heuristics, Then, have your code write a mapping table, and write a python plugin to import that mapping table into your code as comments.
It's questionable if these approaches beat manual reverse engineering within IDA, but as IDA doesn't have this feature, and as there are no generic signature libraries (and they wouldn't work anyway, see above), it's the best i have to offer.
Turns out I have a little project where I could use some deobfuscation mapping myself, and checked into the deobfuscation process of the Minecraft decompiling step a bit more.
Turns out they don't even use fernflowers remapping feature. Instead, they have their own set of tools. These are lamentably underdocumented, but there seems to be a GitHub Repository.
The first one, SpecialSource.jar, compares an obfuscated jar with an unobfuscated version, and generates a mapping table. This seems to allow to build new tables quickly when a new version of the obfuscated original comes out. There is, however, no documentation about what's exactly being compared.
The second one, SpecialSource-2.jar, remaps class and method references and names directly in the jar file, without a decompilation/compilation step, so it avoids all the problems that come with that. So once you have a mapping file, you can apply it to the jar to get an output jar, which you can then feed into IDA, or a decompiler of your choice. Usage is
java -jar /path/to/SpecialSource-2.jar map -m mapping.csrg \ -i obfuscated.jar -o readable.jar
with the mappings file looking like this
# This renames a class, given the obfuscated name and the readable one com/example/ab/a MyNiceClassName # This renames a method, given the class, method, signature, and readable name. MyNiceClassName a (D)I doubleToInt
If the control flow graph has not been obfuscated then you could use those to match methods. The biggest hurdle to this is building up the database of library signatures.
Control flow graphs are the structure that the basic blocks make when viewed as a directed graph.  These represent the possible paths of execution in a method. They are relatively easy to parse out and recover from a compiled application. Most Android and Java obfuscation focuses on method names and not on control flow. Also, the Java / Dalvik bytecode can change between compilations if the methods are modified or moved. That's where comparing structure comes in handy, unless serious changes are made, the control flow is likely to remain the same.
I did some work with control flow graph matching in Android applications.  The project's real strength turned out to be malware strain clustering. It would match methods that were of similar structure and you could see that certain applications shared methods with other applications, and how the strains evolved.
If you are looking to create a Java function signature a combination between structural and byte base matching would be very powerful.