Package org.jbls.Warden

Source Code of org.jbls.Warden.WardenModule

/**
* This file is distributed under the GPL
* $Id: WardenModule.java 1747 2009-06-06 16:04:42Z scotta $
*/

package org.jbls.Warden;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.util.zip.Inflater;

import org.jbls.util.BigIntegerEx;
import org.jbls.util.Buffer;
import org.jbls.util.IntFromByteArray;
import org.jbls.util.PadString;

/**
* @author iago
*/
public class WardenModule {
  private int currentLength = 0;
  private int compressedSize = 0;
  private byte[] md5Hash = null;
  private byte[] decryptionSeed = null;
  private String name = null;
  private final File exe;

  public WardenModule(int size, byte[] md5, byte[] decryptor, String game_exe) {
    this.compressedSize = size;
    this.md5Hash = md5;
    this.decryptionSeed = decryptor;
    this.exe = new File(game_exe);
    StringBuffer nametmp = new StringBuffer();
    for (int x = 0; x < 0x10; x++)
      nametmp.append(PadString.padHex(md5[x], 2));
    name = nametmp.toString();
    saveFile(name + ".decr", decryptor);

    if(!exe.exists())
      throw new IllegalStateException();
  }

  public Buffer handleRequest(Buffer in) throws IOException {
    in.removeByte();
    Buffer ret = new Buffer();
    int checksum = 0;
    while (in.size() >= 7) {
      /*int command =*/ in.removeWord();
      int address = in.removeDWord();
      int length = in.removeByte();
//      if(Out.isDebug())
//        Out.debug(getClass(), "Command: 0x" + PadString.padHex(command, 4)
//            + ", " + "Address: 0x" + PadString.padHex(address, 8)
//            + ", " + "Length: " + length);
      ret.addByte((byte) 0);
      ret.addBytes(getFileData(address - 0x400000, length));
      switch (address) {
      case 0x00497FB0:
        checksum += 1;
        break;
      case 0x0049C33D:
        checksum += 2;
        break;
      case 0x004A2FF7:
        checksum += 3;
        break;
      }
      checksum *= 10;
    }
    ret.addDWord(checksum / 10);
    return ret;
  }

  private byte[] getFileData(int i, int length) throws IOException {
    InputStream in = new FileInputStream(exe);
    in.skip(i);
    byte[] ret = new byte[length];
    in.read(ret);
    in.close();
    return ret;
  }

  public void setup() {
    if (fileExists(name + ".mod")) {
      if (md5verify()) {
        try {
          SimpleCrypto crypt = new SimpleCrypto(decryptionSeed);
          byte[] data = crypt.do_crypt(readFile(name + ".mod"));
          saveFile(name + ".tmp1.bin", data);
        } catch (Exception e) {
          System.out.println("Failed to decode .mod file: "
              + e.toString());
        }
      }
    }
    if (fileExists(name + ".tmp1.bin")) {
      if (verifySignature()) {
        try {
          byte[] compressed = readFile(name + ".tmp1.bin");
          Inflater zip = new Inflater();
          zip.setInput(compressed, 4, compressed.length - 0x108);
          int len = ((compressed[0] << 0) & 0x000000FF)
              | ((compressed[1] << 8) & 0x0000FF00)
              | ((compressed[2] << 16) & 0x00FF0000)
              | ((compressed[3] << 24) & 0xFF000000);
          byte[] uncompressed = new byte[len];
          zip.inflate(uncompressed);
          saveFile(name + ".tmp2.bin", uncompressed);
        } catch (Exception e) {
          System.out.println("Failed to inflate .tmp1.bin file: "
              + e.toString());
        }
      }
    }

    if (fileExists(name + ".tmp2.bin")) {
      try {
        byte[] raw = readFile(name + ".tmp2.bin");
        byte[] preped = prepareModule(raw, 0x40000000);
        saveFile(name + ".bin", preped);
      } catch (Exception e) {
        System.out.println("Failed to prepare .tmp2.bin file: "
            + e.toString());
      }
    }
  }

  private byte[] prepareModule(byte[] original, int base_address) {
    IntFromByteArray ifba = IntFromByteArray.LITTLEENDIAN;
    int counter;

    int length = ifba.getInteger(original, 0);
    byte[] module = new byte[length];

    System.out.println("Allocated " + length + " (0x"
        + PadString.padHex(length, 4) + ") bytes for new module.\n");

    /* Copy 40 bytes from the original module to the new one. */
    System.arraycopy(original, 0, module, 0, 40);

    int source_location = 0x28 + (ifba.getInteger(module, 0x24) * 12);
    int destination_location = ifba.getInteger(original, 0x28);
    int limit = ifba.getInteger(original, 0);

    boolean skip = false;

    System.out.println("Copying code sections to module.");
    while (destination_location < limit) {
      int count = ((original[source_location] & 0x0FF) << 0)
          | ((original[source_location + 1] & 0x0FF) << 8);

      source_location += 2;

      if (!skip) {
        System.arraycopy(original, source_location, module,
            destination_location, count);
        source_location += count;
      }
      skip = !skip;
      destination_location += count;
    }

    System.out.println("Adjusting references to global variables...");
    source_location = ifba.getInteger(original, 8);
    destination_location = 0;

    counter = 0;
    while (counter < ifba.getInteger(module, 0x0c)) {
      if (module[source_location] < 0) {
        /* This code is never used, so I am not 100% sure that it works. */
        destination_location = ((module[source_location + 0] & 0x07F) << 24)
            | ((module[source_location + 1] & 0x0FF) << 16)
            | ((module[source_location + 2] & 0x0FF) << 8)
            | ((module[source_location + 3] & 0x0FF) << 0);
        source_location += 4;
      } else {
        destination_location = destination_location
            + (module[source_location + 1] & 0x0FF)
            + (module[source_location] << 8);
        source_location += 2;
      }
      // System.out.println("Offset 0x" +
      // PadString.padHex(destination_location, 4) +
      // " (was 0x" + PadString.padHex(ifba.getInteger(module,
      // destination_location), 8) + ")");
      ifba.insertInteger(module, destination_location, ifba.getInteger(
          module, destination_location)
          + base_address);
      counter++;
    }

    System.out.println("Updating API library references...");
    counter = 0;
    limit = ifba.getInteger(module, 0x20);
    String library;

    for (counter = 0; counter < limit; counter++) {
      int proc_start = ifba.getInteger(module, 0x1c) + (counter * 8);
      library = getNTString(module, ifba.getInteger(module, proc_start));
      int proc_offset = ifba.getInteger(module, proc_start + 4);

      while (ifba.getInteger(module, proc_offset) != 0) {
        int proc = ifba.getInteger(module, proc_offset);
        int addr = -1 /* Modules.ERROR */;

        if (proc > 0) {
          String strProc = getNTString(module, proc);
          addr = -1; /* Modules.get(library, strProc); */

          if (addr != -1 /* Modules.ERROR */)
            System.out.println("Module " + library + "!" + strProc
                + " found @ 0x" + PadString.padHex(addr, 8));
        } else {
          proc = proc & 0x7FFFFFFF;
          System.out.println("Proc: ord(0x"
              + PadString.padHex(proc, 8) + ")");
        }
        ifba.insertInteger(module, proc_offset, addr); /*
                                 * TODO: Fix
                                 * this.
                                 */
        /*
         * Note: real code increments [ebx+8] here, which is used for
         * unloading the libraries.
         */

        proc_offset += 4;
      }
    }

    return module;
  }

  public void reset() {
    deleteFile(name + ".mod");
    currentLength = 0;
  }

  public boolean md5verify() {
    if (!fileExists(name + ".mod"))
      return false;
    try {
      MessageDigest md5 = MessageDigest.getInstance("MD5");
      byte[] data = readFile(name + ".mod");
      byte[] results = md5.digest(data);
      for (int x = 0; x < 0x10; x++)
        if (results[x] != md5Hash[x])
          return false;
      return true;
    } catch (Exception e) {
      System.out.println("md5Verify failed: " + e.toString());
    }
    return false;
  }

  public boolean verifySignature() {
    try {
      byte[] data = readFile(name + ".tmp1.bin");
      if (data == null)
        return false;

      if (data[data.length - 0x104] != 'N'
          || data[data.length - 0x103] != 'G'
          || data[data.length - 0x102] != 'I'
          || data[data.length - 0x101] != 'S')
        return false;

      byte[] signature = new byte[0x100];
      byte[] module = new byte[data.length - 0x104];
      System.arraycopy(data, data.length - 0x100, signature, 0, 0x100);
      System.arraycopy(data, 0, module, 0, data.length - 0x104);

      BigIntegerEx power = new BigIntegerEx(BigIntegerEx.LITTLE_ENDIAN,
          new byte[] { 0x01, 0x00, 0x01, 0x00 });
      BigIntegerEx mod = new BigIntegerEx(BigIntegerEx.LITTLE_ENDIAN,
          new byte[] { (byte) 0x6B, (byte) 0xCE, (byte) 0xF5,
              (byte) 0x2D, (byte) 0x2A, (byte) 0x7D, (byte) 0x7A,
              (byte) 0x67, (byte) 0x21, (byte) 0x21, (byte) 0x84,
              (byte) 0xC9, (byte) 0xBC, (byte) 0x25, (byte) 0xC7,
              (byte) 0xBC, (byte) 0xDF, (byte) 0x3D, (byte) 0x8F,
              (byte) 0xD9, (byte) 0x47, (byte) 0xBC, (byte) 0x45,
              (byte) 0x48, (byte) 0x8B, (byte) 0x22, (byte) 0x85,
              (byte) 0x3B, (byte) 0xC5, (byte) 0xC1, (byte) 0xF4,
              (byte) 0xF5, (byte) 0x3C, (byte) 0x0C, (byte) 0x49,
              (byte) 0xBB, (byte) 0x56, (byte) 0xE0, (byte) 0x3D,
              (byte) 0xBC, (byte) 0xA2, (byte) 0xD2, (byte) 0x35,
              (byte) 0xC1, (byte) 0xF0, (byte) 0x74, (byte) 0x2E,
              (byte) 0x15, (byte) 0x5A, (byte) 0x06, (byte) 0x8A,
              (byte) 0x68, (byte) 0x01, (byte) 0x9E, (byte) 0x60,
              (byte) 0x17, (byte) 0x70, (byte) 0x8B, (byte) 0xBD,
              (byte) 0xF8, (byte) 0xD5, (byte) 0xF9, (byte) 0x3A,
              (byte) 0xD3, (byte) 0x25, (byte) 0xB2, (byte) 0x66,
              (byte) 0x92, (byte) 0xBA, (byte) 0x43, (byte) 0x8A,
              (byte) 0x81, (byte) 0x52, (byte) 0x0F, (byte) 0x64,
              (byte) 0x98, (byte) 0xFF, (byte) 0x60, (byte) 0x37,
              (byte) 0xAF, (byte) 0xB4, (byte) 0x11, (byte) 0x8C,
              (byte) 0xF9, (byte) 0x2E, (byte) 0xC5, (byte) 0xEE,
              (byte) 0xCA, (byte) 0xB4, (byte) 0x41, (byte) 0x60,
              (byte) 0x3C, (byte) 0x7D, (byte) 0x02, (byte) 0xAF,
              (byte) 0xA1, (byte) 0x2B, (byte) 0x9B, (byte) 0x22,
              (byte) 0x4B, (byte) 0x3B, (byte) 0xFC, (byte) 0xD2,
              (byte) 0x5D, (byte) 0x73, (byte) 0xE9, (byte) 0x29,
              (byte) 0x34, (byte) 0x91, (byte) 0x85, (byte) 0x93,
              (byte) 0x4C, (byte) 0xBE, (byte) 0xBE, (byte) 0x73,
              (byte) 0xA9, (byte) 0xD2, (byte) 0x3B, (byte) 0x27,
              (byte) 0x7A, (byte) 0x47, (byte) 0x76, (byte) 0xEC,
              (byte) 0xB0, (byte) 0x28, (byte) 0xC9, (byte) 0xC1,
              (byte) 0xDA, (byte) 0xEE, (byte) 0xAA, (byte) 0xB3,
              (byte) 0x96, (byte) 0x9C, (byte) 0x1E, (byte) 0xF5,
              (byte) 0x6B, (byte) 0xF6, (byte) 0x64, (byte) 0xD8,
              (byte) 0x94, (byte) 0x2E, (byte) 0xF1, (byte) 0xF7,
              (byte) 0x14, (byte) 0x5F, (byte) 0xA0, (byte) 0xF1,
              (byte) 0xA3, (byte) 0xB9, (byte) 0xB1, (byte) 0xAA,
              (byte) 0x58, (byte) 0x97, (byte) 0xDC, (byte) 0x09,
              (byte) 0x17, (byte) 0x0C, (byte) 0x04, (byte) 0xD3,
              (byte) 0x8E, (byte) 0x02, (byte) 0x2C, (byte) 0x83,
              (byte) 0x8A, (byte) 0xD6, (byte) 0xAF, (byte) 0x7C,
              (byte) 0xFE, (byte) 0x83, (byte) 0x33, (byte) 0xC6,
              (byte) 0xA8, (byte) 0xC3, (byte) 0x84, (byte) 0xEF,
              (byte) 0x29, (byte) 0x06, (byte) 0xA9, (byte) 0xB7,
              (byte) 0x2D, (byte) 0x06, (byte) 0x0B, (byte) 0x0D,
              (byte) 0x6F, (byte) 0x70, (byte) 0x9E, (byte) 0x34,
              (byte) 0xA6, (byte) 0xC7, (byte) 0x31, (byte) 0xBE,
              (byte) 0x56, (byte) 0xDE, (byte) 0xDD, (byte) 0x02,
              (byte) 0x92, (byte) 0xF8, (byte) 0xA0, (byte) 0x58,
              (byte) 0x0B, (byte) 0xFC, (byte) 0xFA, (byte) 0xBA,
              (byte) 0x49, (byte) 0xB4, (byte) 0x48, (byte) 0xDB,
              (byte) 0xEC, (byte) 0x25, (byte) 0xF3, (byte) 0x18,
              (byte) 0x8F, (byte) 0x2D, (byte) 0xB3, (byte) 0xC0,
              (byte) 0xB8, (byte) 0xDD, (byte) 0xBC, (byte) 0xD6,
              (byte) 0xAA, (byte) 0xA6, (byte) 0xDB, (byte) 0x6F,
              (byte) 0x7D, (byte) 0x7D, (byte) 0x25, (byte) 0xA6,
              (byte) 0xCD, (byte) 0x39, (byte) 0x6D, (byte) 0xDA,
              (byte) 0x76, (byte) 0x0C, (byte) 0x79, (byte) 0xBF,
              (byte) 0x48, (byte) 0x25, (byte) 0xFC, (byte) 0x2D,
              (byte) 0xC5, (byte) 0xFA, (byte) 0x53, (byte) 0x9B,
              (byte) 0x4D, (byte) 0x60, (byte) 0xF4, (byte) 0xEF,
              (byte) 0xC7, (byte) 0xEA, (byte) 0xAC, (byte) 0xA1,
              (byte) 0x7B, (byte) 0x03, (byte) 0xF4, (byte) 0xAF,
              (byte) 0xC7 });

      byte[] result = new BigIntegerEx(BigIntegerEx.LITTLE_ENDIAN,
          signature).modPow(power, mod).toByteArray();

      byte[] digest;
      byte[] properResult = new byte[0x100];

      /* Fill the proper result with 0xBB */
      for (int i = 0; i < properResult.length; i++)
        properResult[i] = (byte) 0xBB;

      /* Do a SHA1 of the data and the string (for some reason). */
      MessageDigest md = MessageDigest.getInstance("SHA1");
      md.update(module);
      md.update("MAIEV.MOD".getBytes());
      digest = md.digest();

      /* Copy the digest over the proper result. */
      System.arraycopy(digest, 0, properResult, 0, digest.length);

      /* Finally, check the array against the signature. */
      for (int i = 0; i < result.length; i++)
        if (result[i] != properResult[i])
          return false;

      return true;
    } catch (Exception e) {
      System.out.println("Failed to verify signature: " + e.toString());
    }
    return false;
  }

  public void savePart(byte[] data, int length) {
    try {
      checkWardenFolder();
      FileOutputStream out = new FileOutputStream("warden/" + name + ".mod",
          (currentLength == 0 ? false : true));
      out.write(data);
      out.close();
    } catch (Exception e) {
      System.out
          .println("Failed to save module segment: " + e.toString());
    }
    currentLength += length;
  }

  private void checkWardenFolder() {
    File folder = new File("warden");
    if(!folder.exists())
      folder.mkdir();
    if(!folder.isDirectory())
      throw new IllegalStateException("Warden must be a folder");
  }

  public boolean downloadComplete() {
    return currentLength == compressedSize;
  }

  public boolean alreadyExists() {
    if (!md5verify())
      return false;
    return true;
  }

  private boolean fileExists(String file) {
    file = "warden/" + file;
    return (new File(file)).exists();
  }

  private void deleteFile(String file) {
    try {
      file = "warden/" + file;
      File f = new File(file);
      if (f.exists())
        f.delete();
    } catch (Exception e) {
      System.out.println("Failed to delete file: " + file + ": "
          + e.toString());
    }
  }

  public byte[] readFile(String path) throws IOException {
    path = "warden/" + path;
    File file = new File(path);
    if (!file.exists())
      return null;
    byte[] ret = new byte[(int) file.length()];
    InputStream in = new FileInputStream(file);
    in.read(ret);
    in.close();
    return ret;
  }

  public void saveFile(String file, byte[] data) {
    try {
      checkWardenFolder();
      file = "warden/" + file;
      FileOutputStream out = new FileOutputStream(file);
      out.write(data);
      out.close();
    } catch (Exception e) {
      System.out.println("Failed to save file: " + file + ": "
          + e.toString());
    }
  }

  private String getNTString(byte[] data, int offset) {
    StringBuffer s = new StringBuffer();
    while (data[offset] != (byte) 0x00) {
      s.append(data[offset]);
      offset++;
    }

    return s.toString();
  }

  public int getSize() {
    return compressedSize;
  }

  public String getName() {
    return name;
  }

  public String getSeed() {
    StringBuffer s = new StringBuffer();

    for(byte x : decryptionSeed)
      s.append(PadString.padHex(x, 2));

    return s.toString();
  }
}
TOP

Related Classes of org.jbls.Warden.WardenModule

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.