leibnitz27 / cfr

This is the public repository for the CFR Java decompiler
https://www.benf.org/other/cfr
MIT License
1.94k stars 249 forks source link

Anti-Obfuscation: Integer Inlining #210

Closed Col-E closed 3 years ago

Col-E commented 3 years ago

Inspired by the latest few commits I thought I'd try my hand at further reducing redundant integer patterns.

Examples (Using obfcontrol set to true)

// IXOR
public static int fibonacci(int n) {
    if (n <= (0x67DA ^ 0xFFFFE265 ^ 0x6184 ^ 0xFFFFE43A)) {
        return n;
    }
    return fibonacci(n - (0xBBB ^ 0xFFFFE60B ^ 0x65F2 ^ 0xFFFF8843)) + fibonacci(n - (0x7AD6 ^ 0xFFFFA6F2 ^ 0x66B2 ^ 0xFFFFBA94));
}
// ISUB + IADD
public static int fibonacci(int n) {
    if (n <= 36 - 40 + 26 - 15 - 6) {
        return n;
    }
    return fibonacci(n - (208 - 355 + 353 - 245 + 40)) + fibonacci(n - (223 - 412 + 193 - 2));
}
// I2L + LXOR + L2I
public static int fibonacci(int n) {
    if (n <= (int)((long)2108268340 ^ (long)2108268341)) {
        return n;
    }
    return fibonacci(n - (int)((long)864501408 ^ (long)864501409)) + fibonacci(n - (int)((long)1741113313 ^ (long)1741113315));
}

Cleaned output

public static int fibonacci(int n) {
    if (n <= 1) {
        return n;
    }
     return fibonacci(n - 1) + fibonacci(n - 2);
}

Samples:


cfr_tests: I ran against the Java 8 set comparing bb4a71ac4a81e5a06a4fb8f34b32d32198f19697 to this branch, no diffs detected.


Design: I recognize that I had to add make the constant pool modifiable in order to support inlining of long values. If there is a better approach I'd be happy to improve.

leibnitz27 commented 3 years ago

Hey -

Ah - yeah - I wouldn't do it quite like that. Doing operations at the byte code level (or near to it) is kind of gross - I had to do it for the control flow obfuscation because the exceptions need to be conditionally removed, which is very hard to do at a later stage in the pipeline.

The examples you've got are just constant folding - you can do that much more cleanly later in the pipeline - here's a small patch which achieves what you're doing.

https://github.com/leibnitz27/cfr/compare/constant_folding

I've avoided doing this because I like showing what's going on, rather than trying to transform it too heavily, but I can admit I've opened that box, so not averse to (conditionally on flags) doing more of this - but would definitely want to do it in the way I show in that branch.

Cheers!

leibnitz27 commented 3 years ago

(I've bunged it in LiteralRewriter for simplicity, but I'd put it in a separate transform)

leibnitz27 commented 3 years ago

also - yes, definitely don't want to change constpool - it's shared between multiple passes with different options, so changes would persist.....

leibnitz27 commented 3 years ago

I should mention also - aside from not having to deal with bytecode, further advantages of doing it later in the pipeline is that the code is normalised - swaps, dups, and all that mess has been removed, also we've safely (well, hopefully ;) ) detected exception regimes - so you don't need to worry about constant folding that crosses exception boundaries.

Col-E commented 3 years ago

Ah - yeah - I wouldn't do it quite like that. Doing operations at the byte code level (or near to it) is kind of gross

Figured there was a better way, I've not dived to deep into the src yet so I just based it off the most recent commits.

I've avoided doing this because I like showing what's going on

Right, I can tell from cases like BADBOOL becoming x != 0 instead of just showing true. And of course, this should probably not be default behavior but I (and some others) would be happy to see more of these sorts of changes down the line to counter simple obfuscation. Less mental tracking of things that can easily be automated away.

here's a small patch which achieves what you're doing

Thanks 👍 I'll open a new branch when I mirror the changes further down the pipeline

leibnitz27 commented 3 years ago

Thanks 👍 I'll open a new branch when I mirror the changes further down the pipeline

Cool, go for it. Do you want to close this one off?

leibnitz27 commented 3 years ago

ignoring of course my totally obvious deliberate bug in that branch cough

Col-E commented 3 years ago

Yeah, closing this one

Col-E commented 3 years ago

Is there some sort of way to visit expressions in a tree-visitor pattern? I'm encountering a lot of cases where I have to manually extract expressions from things such as:

And adding specific handling to each case, and finding the appropriate parameters to construct a new one is a bit tedious.

I've pushed my current progress here: https://github.com/leibnitz27/cfr/compare/master...Col-E:inlining

Col-E commented 3 years ago

Oh, found it: expression.applyExpressionRewriter(this, ssaIdentifiers, statementContainer, flags);

leibnitz27 commented 3 years ago

An expression transformer can be applied either at 3rd (graph) stage or 4th (fully structured) stage.

For 3rd, look at NOPSearchingExpressionRewriter For 4th, look at HexLiteralTidier