Zelix KlassMaster - Documentation
 

The open Statement

The ZKM Script open statement opens class files and Java ME JAD files for processing by Zelix KlassMaster. Successive open statements do not have a cumulative effect. It is highly recommended that you open your classes for obfuscation using the same relative directory and/or archive structure that will be used at runtime. If you do this then your obfuscated application can be deployed in exactly the same way as the original, unobfuscated application.

Any non-class files contained in opened archive files will be copied across to the corresponding saved archive files. Some of these files may be updated to reflect obfuscated names. See the File | SaveAll documentation for more detail.

Skip and Unskip

If you open an archive that contains nested JAR, ZIP, WAR or RAR archives then the nested archives will also be opened for obfuscation. This typically happens in the case of JEE EAR or WAR archives. Occassionally this can cause a problem where your EAR contains a nested third party JAR that you don't want to obfuscate. In such cases you can use the -"x.ear!y.war!WEB-INF/lib/z.jar" syntax to tell Zelix KlassMaster™ to not open (i.e. "skip") a specified nested archive file. Note that although "skipped" classes will not be opened for obfuscation they will still be saved to the specified output archive along with the obfuscated classes. They will also be appended to the effective classpath.

You can also use the -"x.jar!com/mycompany/MyClass.class" syntax to tell Zelix KlassMaster™ to not open (i.e. "skip") a specified class or XML file within an archive. You can use the -"com/mycompany/MyClass.class" syntax to tell Zelix KlassMaster™ to not open (i.e. "skip") a specified class or XML file from the file system (i.e. not from within an archive). However, if you "skip" any classes then you must make sure that those classes does not access any of the classes that are to be obfuscated.

If you have specified a broad "skip" clause then you can use a narrower "unskip" to reduce the scope of that "skip" clause. The syntax is +"x.jar!com/mycompany/MyClass.class" The "unskip" must be at the same archive level as the "skip" that it narrows.

Note that if a non-class file contained in an opened archive is specified in a "skip" clause then it will still be copied across to the saved archive. However, it will not be analyzed or updated to reflect obfuscated names as described above.

By contrast, a non-class file opened from the file system (as opposed to contained in an opened archive) which is also specified in a "skip" clause simply will not be opened and therefore will not be saved with the obfuscated output.

Note that it is highly recommended that a "skip" or "unskip" clause that is intended to match files that have been opened from the file system (as opposed to from an archive) should specify its path as fully as possible. Please see the example immediately below.
	 
open "C:\directory1\com\mycompany\*.class"  //Open all classes in C:\directory1\com\mycompany
     -"C:\directory1\com\mycompany\X.class" //Don't open X.class
     -"com\mycompany\Y.class" //Relative path so will match Y.class only if default directory is C:\directory1
     ; 
Also note that "skip" or "unskip" clauses are not supported when they target a top level source path which specifies an entire directory and all of its sub-directories. Again, please see the example immediately below.
	 
open "C:\directory1\*"  //Open all classes in C:\directory1 and its sub-directories.
     -"C:\directory1\com\mycompany\X.class" //Will not be applied against "C:\directory1\*" so will be ignored
     ; 

openNestedArchives

As mentioned above, by default, any archives nested within opened archives will also be opened for processing. You can override this behavior be specifying openNestedArchives=false. (The default of course is openNestedArchives=true.) Specifying openNestedArchives=false will have the same effect as specifying nested archive in "skip" clauses.

File Filtering

You can tell Zelix KlassMaster™ to totally ignore certain files by specifying a file filter. Note that this is fundamentally different from the "skip" and "unskip" syntax that is mentioned above. File filters can specify class or non-class files and filtered files will not be copied to the save destination. Also filtered classes will not be appended to the effective classpath. Note that file filter specifications will be processed before any "skip" clauses.

A file filter specification must immediately follow the open clause (i.e path specification) to which is is to apply. A file in the associated open clause will be processed only if it matches the full file filter specification. A file filter specification can be simple like {"*.class"} which means that only class files should be processed. It can be negative like {!"MANIFEST.MF"} which means don't match any manifest file. Finally, it can be complex like {"*.class" && !"Class0.class" || "*.xml" && !"Junk.xml"} which means match and process all class and XML files but not Class0.class or Junk.xml. Note that the && operator takes precedence over the || operator.

Expected SHA 256 hashes

If you have specified reproducible obfuscate output then you can optionally make use of the expectedInitial_SHA256 and expectedFinal_SHA256 parameters. The expectedInitial_SHA256 parameter allows you to specify the expected SHA-256 hash of a top level file that you are opening for obfuscation. The expectedFinal_SHA256 parameter allows you to specify the expected final SHA-256 hash of a top level file that you are saving after obfuscation.

In either case, if the actual hash does not match then it will result in a fatal error.

Examples

open "C:\directory1\Class1.class" //a single class file
     "C:\directory1\MyJar.jar"    //all the class files in a single jar
     "C:\directory2"              //all the class files in a single directory
     "C:\directory3\*"   //all the class files in a single directory AND its subdirectories
     "C:\directory1\MyJar.jad" //Java ME JAD file will be associated with the JAR it specifies
     "C:\directory4\*.jar" //all the JAR files in directory4
     "C:\directory5\Config.xml" //XML file Config.xml
     "C:\directory6\*.xml" //all the XML files in directory
     ;
	 
open "C:\directory1\MyEar.ear"     //Open the EAR and all its nested WAR and JAR files
     //Don't open the archive X.jar (in the WEB-INF/lib/ directory) nested within MyWar.war nested within MyEar.ear
     -"MyEar.ear!MyWar.war!WEB-INF/lib/X.jar"
     //Don't open ANY zip file archive (in the WEB-INF/lib/ directory) nested within MyWar.war nested within MyEar.ear
     -"MyEar.ear!MyWar.war!WEB-INF/lib/*.zip"
     //Unskip (i.e. open) special.zip (in the WEB-INF/lib/ directory) nested within MyWar.war nested within MyEar.ear
     +"MyEar.ear!MyWar.war!WEB-INF/lib/special.zip"
     //Don't open any class file within the package com/mycompany within MyJar.jar nested within MyEar.ear
     -"MyEar.ear!MyJar.jar!com/mycompany/*.class"
     //Unskip (i.e. open) the class file com/mycompany/MyClass.class within MyJar.jar nested within MyEar.ear
     +"MyEar.ear!MyJar.jar!com/mycompany/MyClass.class"
     ; 
	 
open openNestedArchives=false     //Don't open any archives nested withing the WAR
     "C:\directory1\MyWar.war"     
     ; 
	 
open "C:\directory1\com\mycompany\*.class"  //Open all classes in C:\directory1\com\mycompany
     "C:\directory1\com\mycompany\*.xml"    //Open all xml files in C:\directory1\com\mycompany
     -"C:\directory1\com\mycompany\X.class" //Don't open the class X.class
     -"C:\directory1\com\mycompany\Y.xml"   //Don't open the XML file Y.xml
     ; 
	 
//File filter examples
open "MyJar0.jar" {"*.class"} //match all class files
     "MyJar1.jar" {!"MANIFEST.MF"} //match all files except for the manifest
     //match all class files except Class0 and match all XML files. "&&" takes precedence.
     "MyJar2.jar" {!"*.class" && !"Class0.class" || "*.xml"} 
     ;
	 
//Expected hash examples
open "MyJar0.jar" {"*.class"} 
         expectedInitial_SHA256="bbf5eaa55d8c875d431f51a898b4ee6d9603f6dbd777d5e89b534db8939b6d22"
         expectedFinal_SHA256="6a59669b8975413c64acd13e334667453442c1f2582643c2ee7daad0e9b4e3bc"  
     "MyJar2.jar" 
     ;

Explanation

The statement's action for each of the sourceName types is
Type Action
class file Open the specified class file
ZIP file Open all class files stored in the ZIP file
JAR file Open all class files stored in the JAR file
EAR file Open all class files stored in the nested WAR, JAR or RAR files
directory Open all class files stored in the directory
directory followed by an "*" Open all class files stored in the directory and all of its subdirectories
Java ME JAD file Open the JAD file and associate it with the opened JAR that it specifies
XML file Open the XML file from the file system. Note that any XML file within an opened archive will be automatically opened.
Properties or Yaml file Open the properties or Yaml from the file system. Note that any properties or Yaml files within an opened archive will be automatically opened.

Syntax

"open" ["openNestedArchives" "=" ("true" | "false")]
       ("\"" sourceName "\"" [fileFilter] [expectedInitial_SHA256] [expectedFinal_SHA256])+
       ("-" "\"" skipName "\"") | ("+" "\"" unSkipName "\""))*
       ";"

fileFilter ::= simpleFileFilter | fileFilterOrList

simpleFileFilter ::= ["!"] "{" "\"" filterString "\"" "}"

fileFilterOrList ::= fileFilterAndList ("||" fileFilterAndList)*

fileFilterAndList ::= simpleFileFilter ("&&" simpleFileFilter)*

expectedInitial_SHA256 ::= "expectedInitial_SHA256" "=" "\"" hashString "\""

expectedFinal_SHA256 ::= "expectedFinal_SHA256" "=" "\"" hashString "\""

where
  • sourceName is the fully qualified pathname of a:
    • class file or
    • JAR file or
    • EAR file or
    • WAR file or
    • RAR file or
    • ZIP file or
    • directory or
    • directory followed by an "*" or
    • Java ME JAD file or
    • properties file or
    • Yaml or
    • XML file.
    Note that a sourceName can contain "*" wildcards in the non-suffix part of an archive, class or XML file name or in the final directory qualifier. For example:
    • directory0\*.class OR
    • directory1\*\*.jar OR
    • directory2\dir*\*.xml
  • skipName is the path to a nested archive, class file or XML file that should NOT be opened. The levels of nesting must be separated by "!" characters. Note that a skipName can contain "*" wildcards in the final file name which may be an archive name. For example:
    • x.ear!y.war!WEB-INF/lib/myJar.jar OR
    • x.ear!y.war!WEB-INF/lib/*.jar OR
    • x.jar!com/mycompany/*.class OR
    • C:\directory1\com\mycompany\*.class
    Also, the skipName can contain "*" wildcards in the path that qualifies the final file name.
    • x.ear!y.war!WEB-INF/*/myJar.jar OR
    • x.ear!y.war!WEB-INF/*/myXml.xml OR
    • x.jar!com/mycompany/*/myClass.class
    • C:\directory1\com\mycompany\*.class
  • unSkipName is the path to a nested archive, class file or XML file that should be removed from a corresponding broader "skip" exclusion and therefore SHOULD be opened. The levels of nesting must be separated by "!" characters. As in the case of a skipName, the unSkipName can contain "*" wildcards in the final file name which may be an archive name or in the path that qualifies the final file name. If there is no corresponding broader skipName ("skip" exclusion) then the unSkipName ("unskip" exclusion) will have no effect.
  • filterString is a file name or the final part of a file path. It may contain the "*" wildcard.
    • *.class OR
    • MANIFEST.MF OR
    • myDirectory/*.xml
  • hashString is a SHA-256 file hash. E.g. "e0391f6ac33ac9b687439b1d0a35a0a9fa02c053fd338c9e17bcb6df74b0d759"
ZKM Script resetIgnoreMissingReferences statement The ZKM Script Language ZKM Script trimExclude statement
Zelix KlassMaster - Java Obfuscator