-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Support obfuscation of symbolic information in the precompiler #30524
Comments
…precompiler Obfuscation is controlled by obfuscate flag in Dart_IsolateFlags. Obfuscation of identifiers is performed during script tokenization - when TokenStream is generated from the source. All kIDENT and kINTERPOL_VAR tokens are renamed consistently using a persistent obfuscation map stored in ObjectStore::obfuscation_map. Some identifiers (pseudo-keywords, arithmetic operators, builtin recognized methods and entry-points) are not renamed to keep name based lookups from breaking. All other identifiers are renamed. Constant instances of Symbol-s (both created via literal syntax #ident and using constant constructor const Symbol("ident")) are renamed consistently with corresponding identifiers. Script urls and Library urls and names are also obfuscated. Obfuscation map can be dumped as a JSON array at the end of precompilation using Dart_GetObfuscationMap API. BUG=#30524 R=rmacnak@google.com Review-Url: https://codereview.chromium.org/3003583002 .
7d52317 provides initial implementation of the obfuscation.
Implementation DetailsAll identifiers (except those that are forced to have an identity renaming) are renamed when At the end of precompilation we also collect and rename scripts, libraries and
This means that in the code below class A {
dynamic noSuchMethod(Invocation inv) {
if (inv.memberName == const Symbol('secretMember1')) {
return 'value1';
} else if (inv.memberName == #secretMember2) {
return 'value2';
} else if (inv.memberName == new Symbol('secretMember3')) {
return 'value3';
}
return 'unknown';
}
}
main() {
final obj = new A();
print(A.secretMember1);
print(A.secretMember2);
print(A.secretMember3);
// Unobfuscated AOT build will print: value1, value2, value3
// Obfuscated AOT build will print: value1, value2, unknown
} Known LimitationsObfuscator has a list of identifiers it can't rename for implementation reasons, because compiler, runtime or embedder expect to resolve this identifiers dynamically by unobfuscated name. The following symbols are not renamed:
Full list can be found in the source or extracted from obfuscation map generated by the VM. Future Work
|
@kmillikin I wonder if we can find somebody to look at hooking up obfuscation in the kernel pipeline - it seems @mehmetf is on track hooking it up internally. which means we would need to support it in Dart 2 so that it does not become a blocker for switching to Kernel. Currently it is all done in the tokenizer :-/ Should probably be done in Kernel loader. |
marking it P2 for now - because there are more important things for us to tackle. |
Thanks for the update.
I am assuming and hoping that this implies we will not be losing this functionality with switch to Dart 2. Please clarify. |
If we are to switch today - then yes, obfuscation would stop working. I don't anticipate us switching before we fix this anyway. I will make sure that this is tracked appropriately. |
@jensjoha if you have some cycles, please check out if you can hook up obfuscation in Kernel mode too. |
@mraleph qq about obfuscation and dart entrypoints that Android shell uses. We are experimenting with a solution to allow teams embed Flutter into their existing apps. One way to do this is to create multiple entry points in Dart and call into these from Android. Obfuscation would obviously break this. My question is how does it work today with a single entrypoint (main)? Does obfuscation system know not to mess with main()? Do you have a proposal on how to support this use case? (e.g. don't obfuscate methods that start with a particular prefix?) |
@mehmetf in reality there are already multiple entry points into Flutter program - anything that is looked up by its symbolic name from C++ side is a sort of entry point. Deobfuscation builds on top of the same mechanism that AOT compiler uses and does not obfuscate entry points that are specified by the embedder. In case of Flutter this information comes from a file like this https://github.com/flutter/engine/blob/master/runtime/dart_vm_entry_points.txt that is fed into VM through You can build on top of this. Another alternative is to build on top of void main([args]) {
switch (args.length == 1 ? args[0] : 'default') {
case 'main0': main0(); break;
}
} |
Kernel support landed yesterday (df92e14). Please test and give any feedback. |
I consider this as done, even though there might be still some work to make obfuscator more obfuscaty. Thanks for handling this @jensjoha ! |
IMPORTANT: If you are reading this bug because you tried enabling obfuscation and got the following warning
This warning is there just to remind you that you are now building your Dart application in a mode that behaves differently from a normal execution mode. In this mode some things (e.g. printing string representation of
Type
objects, stacktraces,noSuchMethod
) will behave slightly differently then what is expected from a Dart program running in a default mode - because identifiers are transparently obfuscated.If you understand what you are doing and how obfuscation affects semantics of your program - you should feel free to ignore this warning.
The text was updated successfully, but these errors were encountered: