We could use LocalVariableTable in theory, but it's more complicated as
we'd need to do live variable analysis and type inference. It's much
easier to leave that for the decompiler.
Signed-off-by: Graham <gpe@openrs2.dev>
The next commit will introduce an ArgRef class which uses a MemberRef
directly, rather than a MemberRef partition.
Signed-off-by: Graham <gpe@openrs2.dev>
This allows two different classes in different modules to be refactored
to the same name - for example, Node in client and unpackclass.
Under the hood, it is implemented by prefixing each class name with the
library name and an exclamation mark (which is highly unlikely to appear
in a class name, as it is invalid Java syntax).
At first, prefixing class names with the library name feels like a bit
of a hack. However, it is much simpler than trying to track libraries
throughout the existing code. Furthermore, it allows us to avoid
forking ASM classes like Remapper.
The Fernflower driver was also changed to deobfuscate each library in
its own context, rather than trying to decompile them all in one go - by
the time classes reach Fernflower, the prefixes have already been
removed and Fernflower can't deal with duplicate class names either.
Signed-off-by: Graham <gpe@openrs2.dev>
This commit adds support for mapping:
* class names
* field names
* field owners
* method names
* method owners
It does not yet support argument names, local variable names or
re-ordering method arguments. Local variable names are blocked on
Fernflower patches and method argument re-ordering is blocked on the IR.
Signed-off-by: Graham <gpe@openrs2.dev>
volatile is pointless if a field is final - even on arrays/objects, as
it only applies to the reference, not the contents of the target.
Signed-off-by: Graham <gpe@openrs2.dev>
If we don't do this then we incorrectly mark some fields as final, due
to writes present inside if blocks removed by ConstantArgTransformer.
Signed-off-by: Graham <gpe@openrs2.dev>
This fixes a bug where fields could never become static if a class had a
mixture of static and non-static fields and constructors.
Signed-off-by: Graham <gpe@openrs2.dev>
The new entry set functionality allows us to distinguish fields NEVER
written from fields we haven't explored yet. This allows us to find more
EXACTLY_ONCE fields in constructors with loops.
Signed-off-by: Graham <gpe@openrs2.dev>
Propagating them looks a bit silly, because the arguments are unused. I
think this problem mainly occurs in constructors, which the dummy
argument obfuscator doesn't touch.
Signed-off-by: Graham <gpe@openrs2.dev>
This change paves the way to feed the NameMap into the TypedRemapper,
such that refactored names (and static member movements) are preserved
across deobfuscator runs.
Signed-off-by: Graham <gpe@openrs2.dev>
This reverts commit b6bba95435.
Although this code is more complicated, it allows us to control the
destination class for fields individually (rather than an entire set at
a time). This is a requirement for name mapping.
The new system will make it easier to port the deobfuscator to different
revisions.
There are two main changes:
- The addition of a Profile class, which contains a list of excluded
classes, methods and fields, and the maximum obfuscated name length.
It is passed to Transformers that require it with dependency
injection.
- New ClassFilter and MemberFilter infrastructure. The MemberFilter
class adds support for filtering fields and methods based on the
owner and descriptor, in addition to the name. This makes the filters
more precise than the previous system. It also supports globs, which
makes it easy to filter whole groups of classes, fields and methods
in one go.
The Profile class uses a ClassFilter and MemberFilters to
represent the list of excluded classes, methods and fields.
A separate benefit is the addition of a separate entry points filter
to the Profile class. Prior to this commit, many Transformers re-used
the excluded method filter to find entry points, which is less precise
(many of the excluded methods in 550 are not entry points).
Support for filtering methods by owner and descriptor in addition to
name allows the DEFAULT_PUBLIC_CTOR_CLASSES Set in VisibilityTransformer to
be combined with the entry points filter.
In the future it might be desirable to split the excluded method set
into three separate sets:
- One to represent methods that can't be renamed.
- One to represent methods whose signature can't be changed.
- One to represent methods that can't be removed.
Signed-off-by: Graham <gpe@openrs2.dev>
The new transformer uses a different approach to the old one. It starts
exploring the call graph from the entry points, recursively analysing
method calls. Methods are only re-analysed if their possible argument
values change, with the Unknown value being used if we can't identify a
single integer constant at a call site. This prevents us from recursing
infinitely if the client code does.
While this first pass does simplify branches in order to ignore dummy
method calls that are never evaluated at runtime, it operates on a copy
of the method (as we initially ignore more calls while the argument
value sets are smaller, ignoring fewer calls as they build up).
A separate second pass simplifies branches on the original method and
inlines singleton constants, paving the way for the UnusedArgTransformer
to actually remove the newly unused arguments.
This new approach has several benefits:
- It is much faster than the old approach, as we only re-analyse methods
as required by argument value changes, rather than re-analysing every
method during every pass.
- It doesn't require special cases for dealing with mutually recursive
dummy calls. The old approach hard-coded special cases for mutually
recursive calls involving groups of 1 and 2 methods. The code for this
wasn't clean. Furthermore, while it was just about good enough for the
HD client, the SD client contains a mutually recursive group of 3
methods. The new approach is capable of dealing with mutually
recursive groups of any size.
Finally, the new transformer has a much cleaner implementation.
Signed-off-by: Graham <gpe@openrs2.dev>
We only need to check for the ACC_FINAL flag once at the start of
transformCode(), given the insn.owner == clazz.name condition. This
change also removes the redundant getNode() call - we already have a
reference to the ClassNode.
Signed-off-by: Graham <gpe@openrs2.dev>
As I'm splitting it up into smaller modules (e.g. compress and crypto) I
think util is a more appropriate name for the remainder.
Signed-off-by: Graham <gpe@openrs2.dev>
The non-HD client appears to have been compiled with javac 1.1 or 1.2,
which compiles synchronized blocks differently to modern versions of
Java. For example, it uses a single try/catch block (modern versions use
two, the latter of which is recursive), subroutines (modern versions do
not) and the range of the try block is narrower.
Fernflower doesn't understand this old style of bytecode, and produces
empty synchronized blocks that do not cover the correct range.
The MONITORENTER sequence is also slightly different: it uses
ASTORE ALOAD MONITORENTER after pushing the monitor variable, rather
than DUP ASTORE MONITORENTER. This doesn't break Fernflower, but does
make it introduce a pointless assignment to a variable only used in the
synchronized() statement, rather than inlining the expression.
This commit introduces a transformer which fixes up Java 1.1-style
synchronized blocks just enough for Fernflower to be able to decompile
them cleanly. (It doesn't add a recursive try/catch, as Fernflower
ignores them.)
Signed-off-by: Graham <gpe@openrs2.dev>