LauncherBuildTask введение новой системы сборки лаунчера

Часть 1
This commit is contained in:
Gravit 2019-01-05 22:15:19 +07:00
parent 2590c97061
commit 4e1f268d4f
No known key found for this signature in database
GPG key ID: 061981E1E85D3216
13 changed files with 360 additions and 223 deletions

View file

@ -8,16 +8,17 @@
import java.util.zip.ZipInputStream; import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
import ru.gravit.launchserver.binary.tasks.MainBuildTask;
import ru.gravit.utils.helper.IOHelper; import ru.gravit.utils.helper.IOHelper;
public class BuildContext { public class BuildContext {
public final ZipOutputStream output; public final ZipOutputStream output;
public final JAConfigurator config; public final JAConfigurator config;
public final JARLauncherBinary data; public final MainBuildTask data;
public final HashSet<String> fileList; public final HashSet<String> fileList;
public BuildContext(ZipOutputStream output, JAConfigurator config, JARLauncherBinary data) { public BuildContext(ZipOutputStream output, JAConfigurator config, MainBuildTask data) {
this.output = output; this.output = output;
this.config = config; this.config = config;
this.data = data; this.data = data;

View file

@ -41,12 +41,13 @@ public void clear() {
public EXEL4JLauncherBinary(LaunchServer server) { public EXEL4JLauncherBinary(LaunchServer server) {
super(server, server.dir.resolve(server.config.binaryName + ".exe")); super(server, server.dir.resolve(server.config.binaryName + ".exe"));
faviconFile = server.dir.resolve("favicon.ico"); faviconFile = server.dir.resolve("favicon.ico");
setConfig(); //setConfig();
} }
@Override @Override
public void build() throws IOException { public void build() throws IOException {
LogHelper.info("Building launcher EXE binary file (Using Launch4J)"); LogHelper.info("Building launcher EXE binary file (Using Launch4J)");
setConfig();
// Set favicon path // Set favicon path
Config config = ConfigPersister.getInstance().getConfig(); Config config = ConfigPersister.getInstance().getConfig();
@ -99,14 +100,14 @@ private void setConfig() {
info.setTxtFileVersion(formatVars(server.config.launch4j.txtFileVersion)); info.setTxtFileVersion(formatVars(server.config.launch4j.txtFileVersion));
info.setTxtProductVersion(formatVars(server.config.launch4j.txtProductVersion)); info.setTxtProductVersion(formatVars(server.config.launch4j.txtProductVersion));
// Prepare version info (misc) // Prepare version info (misc)
info.setOriginalFilename(binaryFile.getFileName().toString()); info.setOriginalFilename(syncBinaryFile.getFileName().toString());
info.setLanguage(LanguageID.RUSSIAN); info.setLanguage(LanguageID.RUSSIAN);
config.setVersionInfo(info); config.setVersionInfo(info);
// Set JAR wrapping options // Set JAR wrapping options
config.setDontWrapJar(false); config.setDontWrapJar(false);
config.setJar(server.launcherBinary.syncBinaryFile.toFile()); config.setJar(server.launcherBinary.syncBinaryFile.toFile());
config.setOutfile(binaryFile.toFile()); config.setOutfile(syncBinaryFile.toFile());
// Return prepared config // Return prepared config
ConfigPersister.getInstance().setAntConfig(config, null); ConfigPersister.getInstance().setAntConfig(config, null);

View file

@ -15,9 +15,9 @@ public EXELauncherBinary(LaunchServer server) {
@Override @Override
public void build() throws IOException { public void build() throws IOException {
if (IOHelper.isFile(binaryFile)) { if (IOHelper.isFile(syncBinaryFile)) {
LogHelper.subWarning("Deleting obsolete launcher EXE binary file"); LogHelper.subWarning("Deleting obsolete launcher EXE binary file");
Files.delete(binaryFile); Files.delete(syncBinaryFile);
} }
} }

View file

@ -9,6 +9,7 @@
import javassist.CtMethod; import javassist.CtMethod;
import javassist.NotFoundException; import javassist.NotFoundException;
import ru.gravit.launcher.LauncherConfig; import ru.gravit.launcher.LauncherConfig;
import ru.gravit.launchserver.binary.tasks.MainBuildTask;
public class JAConfigurator implements AutoCloseable { public class JAConfigurator implements AutoCloseable {
public ClassPool pool; public ClassPool pool;
@ -20,7 +21,7 @@ public class JAConfigurator implements AutoCloseable {
StringBuilder moduleBody; StringBuilder moduleBody;
int autoincrement; int autoincrement;
public JAConfigurator(String configclass, JARLauncherBinary jarLauncherBinary) throws NotFoundException { public JAConfigurator(String configclass, MainBuildTask jarLauncherBinary) throws NotFoundException {
pool = new ClassPool(false); pool = new ClassPool(false);
pool.insertClassPath(jarLauncherBinary.cleanJar.toFile().getAbsolutePath()); pool.insertClassPath(jarLauncherBinary.cleanJar.toFile().getAbsolutePath());
pool.appendSystemPath(); pool.appendSystemPath();

View file

@ -32,6 +32,10 @@
import ru.gravit.launcher.serialize.HOutput; import ru.gravit.launcher.serialize.HOutput;
import ru.gravit.launchserver.LaunchServer; import ru.gravit.launchserver.LaunchServer;
import ru.gravit.launchserver.asm.ClassMetadataReader; import ru.gravit.launchserver.asm.ClassMetadataReader;
import ru.gravit.launchserver.binary.tasks.LauncherBuildTask;
import ru.gravit.launchserver.binary.tasks.MainBuildTask;
import ru.gravit.launchserver.binary.tasks.ProGuardBuildTask;
import ru.gravit.launchserver.binary.tasks.UnpackBuildTask;
import ru.gravit.launchserver.manangers.hook.BuildHookManager.ZipBuildHook; import ru.gravit.launchserver.manangers.hook.BuildHookManager.ZipBuildHook;
import ru.gravit.utils.helper.CommonHelper; import ru.gravit.utils.helper.CommonHelper;
import ru.gravit.utils.helper.IOHelper; import ru.gravit.utils.helper.IOHelper;
@ -41,89 +45,15 @@
import ru.gravit.utils.helper.UnpackHelper; import ru.gravit.utils.helper.UnpackHelper;
public final class JARLauncherBinary extends LauncherBinary { public final class JARLauncherBinary extends LauncherBinary {
//public ClassMetadataReader reader;
private final class RuntimeDirVisitor extends SimpleFileVisitor<Path> { public ArrayList<LauncherBuildTask> tasks;
private final ZipOutputStream output;
private final Map<String, byte[]> runtime;
private RuntimeDirVisitor(ZipOutputStream output, Map<String, byte[]> runtime) {
this.output = output;
this.runtime = runtime;
}
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
String dirName = IOHelper.toString(runtimeDir.relativize(dir));
output.putNextEntry(newEntry(dirName + '/'));
return super.preVisitDirectory(dir, attrs);
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
String fileName = IOHelper.toString(runtimeDir.relativize(file));
runtime.put(fileName, SecurityHelper.digest(DigestAlgorithm.MD5, file));
// Create zip entry and transfer contents
output.putNextEntry(newEntry(fileName));
IOHelper.transfer(file, output);
// Return result
return super.visitFile(file, attrs);
}
}
// TODO: new native security wrapper and library...
@SuppressWarnings("unused")
private final class GuardDirVisitor extends SimpleFileVisitor<Path> {
private final ZipOutputStream output;
private final Map<String, byte[]> guard;
private GuardDirVisitor(ZipOutputStream output, Map<String, byte[]> guard) {
this.output = output;
this.guard = guard;
}
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
String dirName = IOHelper.toString(guardDir.relativize(dir));
output.putNextEntry(newGuardEntry(dirName + '/'));
return super.preVisitDirectory(dir, attrs);
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
String fileName = IOHelper.toString(guardDir.relativize(file));
guard.put(fileName, SecurityHelper.digest(DigestAlgorithm.MD5, file));
// Create zip entry and transfer contents
output.putNextEntry(newGuardEntry(fileName));
IOHelper.transfer(file, output);
// Return result
return super.visitFile(file, attrs);
}
}
private static ZipEntry newEntry(String fileName) {
return newZipEntry(Launcher.RUNTIME_DIR + IOHelper.CROSS_SEPARATOR + fileName);
}
private static ZipEntry newGuardEntry(String fileName) {
return newZipEntry(Launcher.GUARD_DIR + IOHelper.CROSS_SEPARATOR + fileName);
}
public final Path cleanJar;
public final Path runtimeDir;
public final Path guardDir;
public final Path initScriptFile;
public final Path obfJar;
public final Path obfOutJar;
public ClassMetadataReader reader;
public JARLauncherBinary(LaunchServer server) throws IOException { public JARLauncherBinary(LaunchServer server) throws IOException {
super(server, server.dir.resolve(server.config.binaryName + "-nonObf.jar"), super(server);
server.dir.resolve(server.config.binaryName + ".jar")); tasks = new ArrayList<>();
runtimeDir = server.dir.resolve(Launcher.RUNTIME_DIR); tasks.add(new UnpackBuildTask());
tasks.add(new MainBuildTask());
tasks.add(new ProGuardBuildTask());
/*runtimeDir = server.dir.resolve(Launcher.RUNTIME_DIR);
guardDir = server.dir.resolve(Launcher.GUARD_DIR); guardDir = server.dir.resolve(Launcher.GUARD_DIR);
initScriptFile = runtimeDir.resolve(Launcher.INIT_SCRIPT_FILE); initScriptFile = runtimeDir.resolve(Launcher.INIT_SCRIPT_FILE);
obfJar = server.dir.resolve(server.config.binaryName + "-obfPre.jar"); obfJar = server.dir.resolve(server.config.binaryName + "-obfPre.jar");
@ -132,30 +62,26 @@ public JARLauncherBinary(LaunchServer server) throws IOException {
cleanJar = server.dir.resolve(server.config.binaryName + "-clean.jar"); cleanJar = server.dir.resolve(server.config.binaryName + "-clean.jar");
reader = new ClassMetadataReader(); reader = new ClassMetadataReader();
UnpackHelper.unpack(IOHelper.getResourceURL("Launcher.jar"), cleanJar); UnpackHelper.unpack(IOHelper.getResourceURL("Launcher.jar"), cleanJar);
reader.getCp().add(new JarFile(cleanJar.toFile())); reader.getCp().add(new JarFile(cleanJar.toFile()));*/
tryUnpack();
} }
@Override @Override
public void build() throws IOException { public void build() throws IOException {
tryUnpack();
// Build launcher binary // Build launcher binary
LogHelper.info("Building launcher binary file"); LogHelper.info("Building launcher binary file");
stdBuild(); Path thisPath = null;
for(LauncherBuildTask task : tasks)
{
LogHelper.subInfo("Task %s",task.getName());
thisPath = task.process(thisPath);
LogHelper.subInfo("Task %s processed",task.getName());
}
syncBinaryFile = thisPath;
LogHelper.info("Build successful");
// ProGuard // ProGuard
Configuration proguard_cfg = new Configuration();
ConfigurationParser parser = new ConfigurationParser(server.proguardConf.confStrs.toArray(new String[0]), /*for (Runnable r : server.buildHookManager.getPostProguardRunHooks())
server.proguardConf.proguard.toFile(), System.getProperties());
try {
parser.parse(proguard_cfg);
ProGuard proGuard = new ProGuard(proguard_cfg);
proGuard.execute();
} catch (ParseException e1) {
e1.printStackTrace();
}
for (Runnable r : server.buildHookManager.getPostProguardRunHooks())
r.run(); r.run();
try (ZipInputStream input = new ZipInputStream(IOHelper.newInput(obfJar)); try (ZipInputStream input = new ZipInputStream(IOHelper.newInput(obfJar));
ZipOutputStream output = new ZipOutputStream(IOHelper.newOutput(obfOutJar))) { ZipOutputStream output = new ZipOutputStream(IOHelper.newOutput(obfOutJar))) {
@ -170,7 +96,7 @@ public void build() throws IOException {
IOHelper.transfer(input, outputStream); IOHelper.transfer(input, outputStream);
bytes = outputStream.toByteArray(); bytes = outputStream.toByteArray();
} }
bytes = server.buildHookManager.proGuardClassTransform(bytes, classname, this); //bytes = server.buildHookManager.proGuardClassTransform(bytes, classname, this);
try (ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes)) { try (ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes)) {
IOHelper.transfer(inputStream, output); IOHelper.transfer(inputStream, output);
} }
@ -182,11 +108,11 @@ public void build() throws IOException {
h.build(output); h.build(output);
} }
if (server.config.buildPostTransform.enabled) if (server.config.buildPostTransform.enabled)
transformedBuild(); transformedBuild();*/
} }
private void transformedBuild() throws IOException { private void transformedBuild() throws IOException {
List<String> cmd = new ArrayList<>(1); /*List<String> cmd = new ArrayList<>(1);
for (String v : server.config.buildPostTransform.script) for (String v : server.config.buildPostTransform.script)
cmd.add(CommonHelper.replace(v, "launcher-output", IOHelper.toAbsPathString(syncBinaryFile), "launcher-obf", cmd.add(CommonHelper.replace(v, "launcher-output", IOHelper.toAbsPathString(syncBinaryFile), "launcher-obf",
IOHelper.toAbsPathString(obfJar), "launcher-nonObf", IOHelper.toAbsPathString(binaryFile))); IOHelper.toAbsPathString(obfJar), "launcher-nonObf", IOHelper.toAbsPathString(binaryFile)));
@ -199,98 +125,6 @@ private void transformedBuild() throws IOException {
LogHelper.debug("Transformer process return code: " + proc.waitFor()); LogHelper.debug("Transformer process return code: " + proc.waitFor());
} catch (InterruptedException e) { } catch (InterruptedException e) {
LogHelper.error(e); LogHelper.error(e);
} }*/
}
private void stdBuild() throws IOException {
try (ZipOutputStream output = new ZipOutputStream(IOHelper.newOutput(binaryFile));
JAConfigurator jaConfigurator = new JAConfigurator(AutogenConfig.class.getName(), this)) {
jaConfigurator.pool.insertClassPath(cleanJar.toFile().getAbsolutePath());
BuildContext context = new BuildContext(output, jaConfigurator, this);
server.buildHookManager.preHook(context);
jaConfigurator.setAddress(server.config.getAddress());
jaConfigurator.setPort(server.config.port);
jaConfigurator.setProjectName(server.config.projectName);
jaConfigurator.setSecretKey(SecurityHelper.randomStringAESKey());
jaConfigurator.setClientPort(32148 + SecurityHelper.newRandom().nextInt(512));
jaConfigurator.setUsingWrapper(server.config.isUsingWrapper);
jaConfigurator.setDownloadJava(server.config.isDownloadJava);
jaConfigurator.setEnv(server.config.env);
server.buildHookManager.registerAllClientModuleClass(jaConfigurator);
try (ZipInputStream input = new ZipInputStream(IOHelper.newInput(cleanJar))) {
ZipEntry e = input.getNextEntry();
while (e != null) {
String filename = e.getName();
if (server.buildHookManager.isContainsBlacklist(filename)) {
e = input.getNextEntry();
continue;
}
try {
output.putNextEntry(IOHelper.newZipEntry(e));
} catch (ZipException ex) {
LogHelper.error(ex);
e = input.getNextEntry();
continue;
}
if (filename.endsWith(".class")) {
String classname = filename.replace('/', '.').substring(0,
filename.length() - ".class".length());
byte[] bytes;
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream(2048)) {
IOHelper.transfer(input, outputStream);
bytes = outputStream.toByteArray();
}
bytes = server.buildHookManager.classTransform(bytes, classname, this);
try (ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes)) {
IOHelper.transfer(inputStream, output);
}
} else
IOHelper.transfer(input, output);
context.fileList.add(filename);
e = input.getNextEntry();
}
}
// write additional classes
for (Entry<String, byte[]> ent : server.buildHookManager.getIncludeClass().entrySet()) {
output.putNextEntry(newZipEntry(ent.getKey().replace('.', '/').concat(".class")));
output.write(server.buildHookManager.classTransform(ent.getValue(), ent.getKey(), this));
}
// map for guard
Map<String, byte[]> runtime = new HashMap<>(256);
if (server.buildHookManager.buildRuntime()) {
// Verify has init script file
if (!IOHelper.isFile(initScriptFile))
throw new IOException(String.format("Missing init script file ('%s')", Launcher.INIT_SCRIPT_FILE));
// Write launcher guard dir
IOHelper.walk(runtimeDir, new RuntimeDirVisitor(output, runtime), false);
// IOHelper.walk(guardDir, new GuardDirVisitor(output, runtime), false);
}
// Create launcher config file
byte[] launcherConfigBytes;
try (ByteArrayOutputStream configArray = IOHelper.newByteArrayOutput()) {
try (HOutput configOutput = new HOutput(configArray)) {
new LauncherConfig(server.config.getAddress(), server.config.port, server.publicKey, runtime)
.write(configOutput);
}
launcherConfigBytes = configArray.toByteArray();
}
// Write launcher config file
output.putNextEntry(newZipEntry(Launcher.CONFIG_FILE));
output.write(launcherConfigBytes);
ZipEntry e = newZipEntry(jaConfigurator.getZipEntryPath());
output.putNextEntry(e);
jaConfigurator.compile();
output.write(jaConfigurator.getBytecode());
server.buildHookManager.postHook(context);
} catch (CannotCompileException | NotFoundException e) {
LogHelper.error(e);
}
}
public void tryUnpack() throws IOException {
LogHelper.info("Unpacking launcher native guard files and runtime");
UnpackHelper.unpackZipNoCheck("guard.zip", guardDir);
UnpackHelper.unpackZipNoCheck("runtime.zip", runtimeDir);
} }
} }

View file

@ -12,23 +12,19 @@ public abstract class LauncherBinary {
public final LaunchServer server; public final LaunchServer server;
public final Path binaryFile; public Path syncBinaryFile;
public final Path syncBinaryFile;
private volatile DigestBytesHolder binary; private volatile DigestBytesHolder binary;
private volatile byte[] sign; private volatile byte[] sign;
protected LauncherBinary(LaunchServer server, Path binaryFile) { protected LauncherBinary(LaunchServer server, Path binaryFile) {
this.server = server; this.server = server;
this.binaryFile = binaryFile;
syncBinaryFile = binaryFile; syncBinaryFile = binaryFile;
} }
protected LauncherBinary(LaunchServer server, Path binaryFile, Path syncBinaryFile) { protected LauncherBinary(LaunchServer server) {
this.server = server; this.server = server;
this.binaryFile = binaryFile;
this.syncBinaryFile = syncBinaryFile;
} }
@ -36,7 +32,7 @@ protected LauncherBinary(LaunchServer server, Path binaryFile, Path syncBinaryFi
public final boolean exists() { public final boolean exists() {
return IOHelper.isFile(syncBinaryFile); return syncBinaryFile != null && IOHelper.isFile(syncBinaryFile);
} }

View file

@ -30,22 +30,29 @@ private static String generateString(SecureRandom rand, int il) {
public final Path config; public final Path config;
public final Path mappings; public final Path mappings;
public final Path words; public final Path words;
public final Path outputJar;
public final ArrayList<String> confStrs; public final ArrayList<String> confStrs;
public transient final LaunchServer srv;
public ProguardConf(LaunchServer srv) { public ProguardConf(LaunchServer srv) {
proguard = srv.dir.resolve("proguard"); proguard = srv.dir.resolve("proguard");
config = proguard.resolve("proguard.config"); config = proguard.resolve("proguard.config");
mappings = proguard.resolve("mappings.pro"); mappings = proguard.resolve("mappings.pro");
words = proguard.resolve("random.pro"); words = proguard.resolve("random.pro");
outputJar = srv.dir.resolve(srv.config.binaryName + "-obfPre.jar");
confStrs = new ArrayList<>(); confStrs = new ArrayList<>();
this.srv = srv;
}
public void buildConfig(Path inputJar)
{
confStrs.clear();
prepare(false); prepare(false);
if (srv.config.genMappings) confStrs.add("-printmapping \'" + mappings.toFile().getName() + "\'"); if (srv.config.genMappings) confStrs.add("-printmapping \'" + mappings.toFile().getName() + "\'");
confStrs.add("-obfuscationdictionary \'" + words.toFile().getName() + "\'"); confStrs.add("-obfuscationdictionary \'" + words.toFile().getName() + "\'");
confStrs.add("-injar \'" + srv.dir.toAbsolutePath() + IOHelper.PLATFORM_SEPARATOR + srv.config.binaryName + "-nonObf.jar\'"); confStrs.add("-injar \'" + inputJar.toAbsolutePath() + "\'");
confStrs.add("-outjar \'" + srv.dir.toAbsolutePath() + IOHelper.PLATFORM_SEPARATOR + srv.config.binaryName + "-obfPre.jar\'"); confStrs.add("-outjar \'" + outputJar.toAbsolutePath() + "\'");
confStrs.add("-classobfuscationdictionary \'" + words.toFile().getName() + "\'"); confStrs.add("-classobfuscationdictionary \'" + words.toFile().getName() + "\'");
confStrs.add(readConf()); confStrs.add(readConf());
} }
private void genConfig(boolean force) throws IOException { private void genConfig(boolean force) throws IOException {

View file

@ -0,0 +1,10 @@
package ru.gravit.launchserver.binary.tasks;
import java.io.IOException;
import java.nio.file.Path;
public interface LauncherBuildTask {
String getName();
int priority();
Path process(Path inputFile) throws IOException;
}

View file

@ -0,0 +1,215 @@
package ru.gravit.launchserver.binary.tasks;
import javassist.CannotCompileException;
import javassist.NotFoundException;
import ru.gravit.launcher.AutogenConfig;
import ru.gravit.launcher.Launcher;
import ru.gravit.launcher.LauncherConfig;
import ru.gravit.launcher.serialize.HOutput;
import ru.gravit.launchserver.LaunchServer;
import ru.gravit.launchserver.binary.BuildContext;
import ru.gravit.launchserver.binary.JAConfigurator;
import ru.gravit.launchserver.binary.JARLauncherBinary;
import ru.gravit.utils.helper.IOHelper;
import ru.gravit.utils.helper.LogHelper;
import ru.gravit.utils.helper.SecurityHelper;
import ru.gravit.utils.helper.UnpackHelper;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import static ru.gravit.utils.helper.IOHelper.newZipEntry;
public class MainBuildTask implements LauncherBuildTask {
public static LaunchServer server = LaunchServer.server;
public final Path runtimeDir;
public final Path guardDir;
public final Path binaryFile;
public Path cleanJar;
private final class RuntimeDirVisitor extends SimpleFileVisitor<Path> {
private final ZipOutputStream output;
private final Map<String, byte[]> runtime;
private RuntimeDirVisitor(ZipOutputStream output, Map<String, byte[]> runtime) {
this.output = output;
this.runtime = runtime;
}
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
String dirName = IOHelper.toString(runtimeDir.relativize(dir));
output.putNextEntry(newEntry(dirName + '/'));
return super.preVisitDirectory(dir, attrs);
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
String fileName = IOHelper.toString(runtimeDir.relativize(file));
runtime.put(fileName, SecurityHelper.digest(SecurityHelper.DigestAlgorithm.MD5, file));
// Create zip entry and transfer contents
output.putNextEntry(newEntry(fileName));
IOHelper.transfer(file, output);
// Return result
return super.visitFile(file, attrs);
}
}
// TODO: new native security wrapper and library...
@SuppressWarnings("unused")
private final class GuardDirVisitor extends SimpleFileVisitor<Path> {
private final ZipOutputStream output;
private final Map<String, byte[]> guard;
private GuardDirVisitor(ZipOutputStream output, Map<String, byte[]> guard) {
this.output = output;
this.guard = guard;
}
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
String dirName = IOHelper.toString(guardDir.relativize(dir));
output.putNextEntry(newGuardEntry(dirName + '/'));
return super.preVisitDirectory(dir, attrs);
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
String fileName = IOHelper.toString(guardDir.relativize(file));
guard.put(fileName, SecurityHelper.digest(SecurityHelper.DigestAlgorithm.MD5, file));
// Create zip entry and transfer contents
output.putNextEntry(newGuardEntry(fileName));
IOHelper.transfer(file, output);
// Return result
return super.visitFile(file, attrs);
}
}
private static ZipEntry newEntry(String fileName) {
return newZipEntry(Launcher.RUNTIME_DIR + IOHelper.CROSS_SEPARATOR + fileName);
}
private static ZipEntry newGuardEntry(String fileName) {
return newZipEntry(Launcher.GUARD_DIR + IOHelper.CROSS_SEPARATOR + fileName);
}
public MainBuildTask() {
runtimeDir = server.dir.resolve(Launcher.RUNTIME_DIR);
guardDir = server.dir.resolve(Launcher.GUARD_DIR);
binaryFile = server.dir.resolve(server.config.binaryName + "-main_task.jar");
}
@Override
public String getName() {
return "main";
}
@Override
public int priority() {
return 0;
}
@Override
public Path process(Path cleanJar) throws IOException {
this.cleanJar = cleanJar;
try (ZipOutputStream output = new ZipOutputStream(IOHelper.newOutput(binaryFile));
JAConfigurator jaConfigurator = new JAConfigurator(AutogenConfig.class.getName(), this)) {
jaConfigurator.pool.insertClassPath(cleanJar.toFile().getAbsolutePath());
BuildContext context = new BuildContext(output, jaConfigurator, this);
server.buildHookManager.preHook(context);
jaConfigurator.setAddress(server.config.getAddress());
jaConfigurator.setPort(server.config.port);
jaConfigurator.setProjectName(server.config.projectName);
jaConfigurator.setSecretKey(SecurityHelper.randomStringAESKey());
jaConfigurator.setClientPort(32148 + SecurityHelper.newRandom().nextInt(512));
jaConfigurator.setUsingWrapper(server.config.isUsingWrapper);
jaConfigurator.setDownloadJava(server.config.isDownloadJava);
jaConfigurator.setEnv(server.config.env);
server.buildHookManager.registerAllClientModuleClass(jaConfigurator);
try (ZipInputStream input = new ZipInputStream(IOHelper.newInput(cleanJar))) {
ZipEntry e = input.getNextEntry();
while (e != null) {
String filename = e.getName();
if (server.buildHookManager.isContainsBlacklist(filename)) {
e = input.getNextEntry();
continue;
}
try {
output.putNextEntry(IOHelper.newZipEntry(e));
} catch (ZipException ex) {
LogHelper.error(ex);
e = input.getNextEntry();
continue;
}
/*if (filename.endsWith(".class")) {
String classname = filename.replace('/', '.').substring(0,
filename.length() - ".class".length());
byte[] bytes;
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream(2048)) {
IOHelper.transfer(input, outputStream);
bytes = outputStream.toByteArray();
}
bytes = server.buildHookManager.classTransform(bytes, classname, this);
try (ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes)) {
IOHelper.transfer(inputStream, output);
}
} else */
IOHelper.transfer(input, output);
context.fileList.add(filename);
e = input.getNextEntry();
}
}
// write additional classes
for (Map.Entry<String, byte[]> ent : server.buildHookManager.getIncludeClass().entrySet()) {
output.putNextEntry(newZipEntry(ent.getKey().replace('.', '/').concat(".class")));
output.write(server.buildHookManager.classTransform(ent.getValue(), ent.getKey(), this));
}
// map for guard
Map<String, byte[]> runtime = new HashMap<>(256);
if (server.buildHookManager.buildRuntime()) {
// Write launcher guard dir
IOHelper.walk(runtimeDir, new RuntimeDirVisitor(output, runtime), false);
// IOHelper.walk(guardDir, new GuardDirVisitor(output, runtime), false);
}
// Create launcher config file
byte[] launcherConfigBytes;
try (ByteArrayOutputStream configArray = IOHelper.newByteArrayOutput()) {
try (HOutput configOutput = new HOutput(configArray)) {
new LauncherConfig(server.config.getAddress(), server.config.port, server.publicKey, runtime)
.write(configOutput);
}
launcherConfigBytes = configArray.toByteArray();
}
// Write launcher config file
output.putNextEntry(newZipEntry(Launcher.CONFIG_FILE));
output.write(launcherConfigBytes);
ZipEntry e = newZipEntry(jaConfigurator.getZipEntryPath());
output.putNextEntry(e);
jaConfigurator.compile();
output.write(jaConfigurator.getBytecode());
server.buildHookManager.postHook(context);
} catch (CannotCompileException | NotFoundException e) {
LogHelper.error(e);
}
return binaryFile;
}
public void tryUnpack() throws IOException {
LogHelper.info("Unpacking launcher native guard files and runtime");
UnpackHelper.unpackZipNoCheck("guard.zip", guardDir);
UnpackHelper.unpackZipNoCheck("runtime.zip", runtimeDir);
}
}

View file

@ -0,0 +1,39 @@
package ru.gravit.launchserver.binary.tasks;
import proguard.Configuration;
import proguard.ConfigurationParser;
import proguard.ParseException;
import proguard.ProGuard;
import ru.gravit.launchserver.LaunchServer;
import java.io.IOException;
import java.nio.file.Path;
public class ProGuardBuildTask implements LauncherBuildTask {
public static LaunchServer server = LaunchServer.server;
@Override
public String getName() {
return "proguard";
}
@Override
public int priority() {
return 1;
}
@Override
public Path process(Path inputFile) throws IOException {
Configuration proguard_cfg = new Configuration();
server.proguardConf.buildConfig(inputFile);
ConfigurationParser parser = new ConfigurationParser(server.proguardConf.confStrs.toArray(new String[0]),
server.proguardConf.proguard.toFile(), System.getProperties());
try {
parser.parse(proguard_cfg);
ProGuard proGuard = new ProGuard(proguard_cfg);
proGuard.execute();
} catch (ParseException e1) {
e1.printStackTrace();
}
return server.proguardConf.outputJar;
}
}

View file

@ -0,0 +1,30 @@
package ru.gravit.launchserver.binary.tasks;
import ru.gravit.launchserver.LaunchServer;
import ru.gravit.utils.helper.IOHelper;
import ru.gravit.utils.helper.UnpackHelper;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Path;
public class UnpackBuildTask implements LauncherBuildTask {
public static LaunchServer server = LaunchServer.server;
@Override
public String getName() {
return "unpack clean";
}
@Override
public int priority() {
return -1;
}
@Override
public Path process(Path inputFile) throws IOException {
Path result = server.dir.resolve(server.config.binaryName + "-clean.jar");
URL url = IOHelper.getResourceURL("Launcher.jar");
UnpackHelper.unpack(url, result);
return result;
}
}

View file

@ -10,6 +10,7 @@
import ru.gravit.launcher.LauncherConfig; import ru.gravit.launcher.LauncherConfig;
import ru.gravit.launchserver.asm.SafeClassWriter; import ru.gravit.launchserver.asm.SafeClassWriter;
import ru.gravit.launchserver.binary.JARLauncherBinary; import ru.gravit.launchserver.binary.JARLauncherBinary;
import ru.gravit.launchserver.binary.tasks.MainBuildTask;
import ru.gravit.launchserver.manangers.hook.BuildHookManager.Transformer; import ru.gravit.launchserver.manangers.hook.BuildHookManager.Transformer;
public class NodeTransformer implements Transformer { public class NodeTransformer implements Transformer {
@ -29,13 +30,14 @@ public NodeTransformer() {
} }
@Override @Override
public byte[] transform(byte[] input, String classname, JARLauncherBinary data) { public byte[] transform(byte[] input, String classname, MainBuildTask data) {
ClassReader cr = new ClassReader(input); //ClassReader cr = new ClassReader(input);
ClassNode cn = new ClassNode(); //ClassNode cn = new ClassNode();
cr.accept(cn, data.server.config.env.equals(LauncherConfig.LauncherEnvironment.PROD) || data.server.config.env.equals(LauncherConfig.LauncherEnvironment.STD) ? ClassReader.SKIP_DEBUG : 0); //cr.accept(cn, data.server.config.env.equals(LauncherConfig.LauncherEnvironment.PROD) || data.server.config.env.equals(LauncherConfig.LauncherEnvironment.STD) ? ClassReader.SKIP_DEBUG : 0);
for (ClassNodeTransformer tr : transLst) tr.transform(cn, classname, data); //for (ClassNodeTransformer tr : transLst) tr.transform(cn, classname, data);
ClassWriter cw = new SafeClassWriter(data.reader, ClassWriter.COMPUTE_MAXS); //ClassWriter cw = new SafeClassWriter(data.reader, ClassWriter.COMPUTE_MAXS);
cn.accept(cw); //cn.accept(cw);
return cw.toByteArray(); //return cw.toByteArray();
return null;
} }
} }

View file

@ -11,6 +11,7 @@
import ru.gravit.launchserver.binary.BuildContext; import ru.gravit.launchserver.binary.BuildContext;
import ru.gravit.launchserver.binary.JAConfigurator; import ru.gravit.launchserver.binary.JAConfigurator;
import ru.gravit.launchserver.binary.JARLauncherBinary; import ru.gravit.launchserver.binary.JARLauncherBinary;
import ru.gravit.launchserver.binary.tasks.MainBuildTask;
import ru.gravit.launchserver.manangers.NodeTransformer; import ru.gravit.launchserver.manangers.NodeTransformer;
public class BuildHookManager { public class BuildHookManager {
@ -26,7 +27,7 @@ public interface BuildHook {
@FunctionalInterface @FunctionalInterface
public interface Transformer { public interface Transformer {
byte[] transform(byte[] input, String classname, JARLauncherBinary data); byte[] transform(byte[] input, String classname, MainBuildTask data);
} }
private boolean BUILDRUNTIME; private boolean BUILDRUNTIME;
@ -96,13 +97,13 @@ public boolean buildRuntime() {
return BUILDRUNTIME; return BUILDRUNTIME;
} }
public byte[] classTransform(byte[] clazz, String classname, JARLauncherBinary reader) { public byte[] classTransform(byte[] clazz, String classname, MainBuildTask reader) {
byte[] result = clazz; byte[] result = clazz;
for (Transformer transformer : CLASS_TRANSFORMER) result = transformer.transform(result, classname, reader); for (Transformer transformer : CLASS_TRANSFORMER) result = transformer.transform(result, classname, reader);
return result; return result;
} }
public byte[] proGuardClassTransform(byte[] clazz, String classname, JARLauncherBinary reader) { public byte[] proGuardClassTransform(byte[] clazz, String classname, MainBuildTask reader) {
byte[] result = clazz; byte[] result = clazz;
for (Transformer transformer : POST_PROGUARD_HOOKS) result = transformer.transform(result, classname, reader); for (Transformer transformer : POST_PROGUARD_HOOKS) result = transformer.transform(result, classname, reader);
return result; return result;