Thursday 1 May 2008

Java: generating a DOS executable

By incrementally changing instructions in an assembly language, it is possible to build up a picture of how the bytes relate to the assembly instructions (without going to the trouble of actually reading the documentation). Note that, for these trivial examples at least, there is no difference between the binaries generated by NASM and the equivalent MS Debug instructions.

NASM version 0.98.39 compiled on Feb 25 2005
DOS, 16bit, x86

;nasm           ;bytes

org 100h        ;address
mov ax,1234h    ;B8 34 12
mov ax,bx       ;89 D8
int 20h         ;CD 20


;nasm           ;bytes

org 100h        ;address
mov ax,1234h    ;B8 34 12
mov bx,ax       ;89 C3
int 20h         ;CD 20


;nasm           ;bytes

org 100h        ;address
mov ax,1234h    ;B8 34 12
mov ax,cx       ;89 C8
mov ax,bx       ;89 D8
int 20h         ;CD 20


Generating Your Own Binaries

;nasm           ;bytes

org 100h        ;address
mov ax,0200h    ;B8 00 02
mov dx,0041h    ;BA 41 00
int 21h         ;CD 21
int 20h         ;CD 20


The above code emits the character 'A' (explanation of the code). This information can be used as a template for generating a working binary. The Java program GenExecutable (listing below) generates a DOS executable that prints a string.

In the example usage below, it generates an identical binary to the one made by NASM.

C:\test>java -cp .\bin GenExecutable "A"
Writing MADEbyJAVA.COM

C:\test>PrintHex.exe  -h MADEbyJAVA.COM
B8 00 02 BA 41 00 CD 21 CD 20                     ____A__!_

C:\test>MADEbyJAVA.COM
A


Here is Hello, World!
C:\test>java -cp .\bin GenExecutable "Hello, World!"
Writing MADEbyJAVA.COM

C:\test>PrintHex -h MADEbyJAVA.COM
B8 00 02 BA 48 00 CD 21 B8 00 02 BA 65 00 CD 21   ____H__!____e__!
B8 00 02 BA 6C 00 CD 21 B8 00 02 BA 6C 00 CD 21   ____l__!____l__!
B8 00 02 BA 6F 00 CD 21 B8 00 02 BA 2C 00 CD 21   ____o__!____,__!
B8 00 02 BA 20 00 CD 21 B8 00 02 BA 57 00 CD 21   ____ __!____W__!
B8 00 02 BA 6F 00 CD 21 B8 00 02 BA 72 00 CD 21   ____o__!____r__!
B8 00 02 BA 6C 00 CD 21 B8 00 02 BA 64 00 CD 21   ____l__!____d__!
B8 00 02 BA 21 00 CD 21 CD 20                     ____!__!_

C:\test>MADEbyJAVA.COM
Hello, World!




Listing: GenExecutable.java

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

/**
 * Generates a DOS program called "MADEbyJAVA.COM"
 @author McDowell
 */
public class GenExecutable {

  public static void main(String[] args) {
    if(args.length!=1) {
      System.out.println("Usage: java GenExecutable <string>");
      return;
    }
    try {
      writePrintAExecutable(args[0]);
    catch(IOException e) {
      e.printStackTrace();
    }
  }
  
  private static void writePrintAExecutable(String strthrows IOException {
    final String filename = "MADEbyJAVA.COM";
    System.out.println("Writing "+filename);
    OutputStream out = new FileOutputStream(filename);
    try {
      for(int i=0; i<str.length(); i++) {
        char ch = str.charAt(i);
        writePrintCharacter(out, (bytech);
      }
      writeTerminate(out);
    finally {
      out.close();
    }
  }
  
  /**Instruction: write a character to the console*/
  private static void writePrintCharacter(OutputStream out, byte charBytethrows IOException {
    final byte INTERRUPT = (byte0xCD;
    final byte FUNCTION = (byte0x21;
    final byte ZERO = (byte0x00;
    final byte SUB_FUNCTION_2 = (byte0x02;
    setAX(out, SUB_FUNCTION_2, ZERO);
    setDX(out, ZERO, charByte);
    //int 21
    out.write(INTERRUPT);
    out.write(FUNCTION);
  }

  /**Instruction: end program*/
  private static void writeTerminate(OutputStream outthrows IOException {
    final byte INTERRUPT = (byte0xCD;
    final byte FUNCTION_TERMINATE = (byte0x20;
    //int 20
    out.write(INTERRUPT);
    out.write(FUNCTION_TERMINATE);
  }
  
  /**Instruction: set value of register AX*/ 
  private static void setAX(OutputStream out, byte ah, byte althrows IOException {
    final byte MOV_AX = (byte0xB8;
    //mov ax,ahal
    out.write(MOV_AX);
    out.write(al);
    out.write(ah);
  }
  
  /**Instruction: set value of register DX*/
  private static void setDX(OutputStream out, byte dh, byte dlthrows IOException {
    final byte MOV_DX = (byte0xBA;
    //mov dx,dhdl
    out.write(MOV_DX);
    out.write(dl);
    out.write(dh);
  }
  
}

1 comment:

All comments are moderated