Tag: technology

  • Hacking Minecraft Java Edition with chastehex

    The save files on my Linux machine are here:

    /home/chastity/.minecraft/saves/

    I decided to see if my chastehex program would be any use at editing save files of Minecraft.

    The first step is to use chastecmp (another program I wrote) to compare two files of slight differences.

    The first target was the level.dat file. I had created a new world, collected 3 oak logs from a tree, and saved.

    Debian Linux detected that the level.dat file was actually a gzip archive. Therefore, to get the true data, these commands are required. The first copies it so that it has a “.gz” extension. The second decompresses it.

    cp level.dat file0.gz
    gzip -d file0.gz
    

    Now there is a file named file0 which has the raw data uncompressed. The next step is to load up the game, make a small change and then resave the file. I threw away one oak log so that I had 2 instead of 3. Then I saved the game and ran these commands to get the uncompressed data for the second file.

    cp level.dat file1.gz
    gzip -d file1.gz
    

    I compared the two files with chastecmp:

    chastecmp file0 file1

    And the result was:

    file0 Opened OK
    file1 Opened OK
    00000052 48 CF 
    000003E9 48 CF 
    000007FE 48 CF 
    000009F0 03 02 
    000011D2 69 68 
    000011D3 0E 87 
    000011E5 FB 74 
    0000124B 72 81 
    0000124C EE C7 
    0000124D 99 BA 
    file0 has reached EOF
    

    It appears that address 9F0 contains the amount of oak logs in the first item slot in the game.

    I ran this command to change that byte to 20 hex/32 decimal

    chastehex file0 9F0 20

    The next step is to recompress the data into a level.dat file that the game expects to load.

    gzip -k file0
    cp file0.gz level.dat
    

    Amazingly, when I loaded the game, I did in fact have 32 oak logs. Obviously this process requires multiple steps and is painfully slow, however it proves that my command line tools can hack Minecraft because there is compression but no encryption in the files.

    The level.dat file seems to contain the player’s inventory and other important data. I remember this from experiments with it years ago.

    Warning

    The addresses and what they mean can change wildly as new data is added to the file. For example, I found a village and there were 5 iron ingots in a chest. I repeated the steps above to create two uncompressed files.

    The difference between the files in this case were that I threw 3 of the ingots on the ground and so the number had changed from 5 to 2 in the chastecmp file0 file1 output

    00000F5F 05 02

    So then I ran the command to change the count to 64 (40 hex)

    chastehex file0 F5F 40

    Upon recompressing the file and putting it back in the game folder, I did have 64 ingots. I had previously tried numbers higher than 64 but unfortunately the results were not good. I ended up with only one ingot instead. Therefore, it is good to stick within the limits of what the game expects for the cheating to be successful.

    But because the addresses change as new data gets added to the game, cheating by hex editing is painfully slow on Minecraft. This is best done on a brand new world, both because the data is small and also so that you don’t corrupt worlds you have been playing on longer.

    But I did it as a proof of concept just to see if the programs I wrote can be used to hack Minecraft. The answer is yes, with a little help from gzip for decompression and recompression.

    But what if I told you there was an easier way to cheat at Minecraft Java Edition? You see, the secret still lies with the level.dat file. You don’t actually need to use chastehex, chastecmp, or compression. This is because the file contains the player inventory but not the items they have stored in chests in the world! This allows for item duplication.

    So place the items you want to duplicate in your inventory. Then save the game and backup the file.

    cp level.dat backup.dat

    Then place those items in a chest then save again. Restore the file you backed up earlier.

    cp backup.dat level.dat

    When you reload the game, the files will still be in the chest but your player will also be holding them. This means you can infinitely duplicate any item you can obtain in the game normally by just copying save files repeatedly.

    Why then, did I go through the process of showing how to cheat with chastehex? Because the point is not so much about hacking the game, or what the game is, but it is about testing the programs I wrote. I care more about my C and Assembly programming skills than I do the outcome of a game.

    The point is not whether you win or lose the game. The point is that the game was made by humans and can be broken by humans. Sometimes, as in Castle of the Winds or Cave Story, hacking with a hex editor is the most reliable method. In a game like Minecraft, sometimes cheating is easier because of player and world data being in completely separate files.

  • Writing to video RAM

    One of the reasons DOS is the best platform for learning Intel Assembly language in my opinion is that it doesn’t prevent you from writing directly to video memory. People are unaware how much operating systems like Windows, and, to a lesser extent, Linux place limits on what you are allowed to do. The idea is that most people are not smart enough to be trusted with arbitrarily writing to video RAM, devices, etc.

    But that is where DOSBox comes in. Since it runs DOS inside an emulator, there is no danger. The program I am going to show you today is long and complicated but it does something that I can’t even do on Linux, write directly to video memory in multiple colors.

    Here is the source code that made all of that possible! I tested it to assemble with either FASM or NASM. If you assemble it and run it in DOSBox, or even a real DOS system, you will get something that looks like the picture above.

    org 100h
    
    main:
    
    ;set up the extra segment at the beginning of the program
    ;to point to the video memory segment in DOS text mode
    mov ax, 0xB800
    mov es, ax ; Or mov ds, ax
    
    
    ;80 columes times 25 rows is 2000 chars
    ;but since each character is two bytes
    ;4000 is the number of bytes to erase
    ;whatever character we write in this loop will fill the whole screen!
    
    mov bx,0
    screen_clear:
    mov [es:bx],word 0x0403
    add bx,2
    cmp bx,4000
    jnz screen_clear
    
    mov ax,title  ;the string we intend to write to video RAM
    mov ch,0x0F   ;the character attribute
    mov dx,0x0218
    call putstring_vram
    
    mov ax,v_str  ;the string we intend to write to video RAM
    mov ch,0x70   ;the character attribute
    mov dx,0x0401
    call putstring_vram
    
    ;set the starting attribute for characters and location
    mov ch,0x01   ;the character attribute
    mov dx,0x0501 ;x,y position of where text should start on screen
    
    loop_vram:
    cmp ch,0x10
    jz loop_vram_end
    mov ax,v_str  ;the string we intend to write to video RAM
    call putstring_vram
    add dx,0x100
    inc ch
    jmp loop_vram
    loop_vram_end:
    
    mov ax,4C00h
    int 21h
    
    title db 'Chastity Video RAM Demonstration!',0
    v_str db 'Hello World! This string will be written to video RAM using Assembly language!',0
    
    ;Unlike previous functions I wrote that use DOS interrupts to write text to the screen
    ;this one makes use of several registers which are not meant to be preserved
    ;registers ax,cx,and dx must be set before calling this function
    
    ;ax = address of string to write
    ;bx = copied from ax and used to index the string
    ;cx = used for character attribute(ch) and value(cl)
    ;dx = column(x pos) and row(y pos) of where string should be printed
    
    ;For this routine, I chose to copy the dx register to memory locations for clarity
    ;Yes, it wastes some bytes but at least I can read it as I am familiar with x,y coordinates
    ;Most importantly, the dx register is never modified in this function
    ;This is important because the main program may need to modify it in a loop
    ;For writing data in consecutive rows (e.g. integer sequences)
    
    x db 0
    y db 0
    
    putstring_vram:
    
    mov bx,ax             ;copy ax to bx for use as index register
    
    ;get x and y positions from each byte of dx register
    mov [x],dl
    mov [y],dh
    
    mov ax,80  ;set ax to 80 because there are 80 chars per row in text mode
    mul byte [y]    ;multiply with the y value
    mov byte [y],0  ;zero the y byte so we can add a 16 bit x value to ax
    add ax, word [x]
    
    shl ax,1 ;shift left once to account for two bytes per character
    
    mov di,ax ;we will use di as our starting output location
    
    putstring_vram_strlen_start:    ;this loop finds the length of the string as part of the putstring function
    
    cmp [bx],byte 0                 ;compare this byte with 0
    jz putstring_vram_strlen_end    ;if comparison was zero, jump to loop end because we have found the length/end of string
    mov cl,[bx]                     ;mov this character to cl
    mov [es:di],cx                  ;mov character and attribute set in ch(before calling this function) to extra_segment+di
    add di,2                        ;each character contains two bytes (ASCII+Attribute). We must add two here.
    inc bx                          ;increment bx to point to next character
    jmp putstring_vram_strlen_start ;jump to the start of the loop and keep trying until we find a zero
    
    putstring_vram_strlen_end:
    
    ret
    
  • arithmetic by bitwise operators

    I have started a new programming library in C which simulates arithmetic using only bitwise operators. This is purely for computer science purposes and to prove that it can be done. The hardest part was getting the division function working correctly. I can prove that these functions work with another program I wrote, but I really want to record a video sometime to show the process of how these work.

    For now, here is the source of the 4 arithmetic functions and some global variables used in the division function.

    /*
     bitlib.h
    
     This library simulates the four arithmetic functions: ( addition, subtraction, multiplication, and division )
     Using only bitwise operations: ( AND, OR, XOR, SHL, SHR )
     
     Most of the time I would not need to do this, however, there exist applications where this information may prove useful.
     
     - Programming ARM CPUs which don't have a division instruction.
     - Arbitary Precision Arithmetic involving thousands of digits.
    
    */
    
    int add(int di,int si)
    {
     while(si!=0)
     {
      int ax=di;
      di^=si;
      si&=ax;
      si<<=1;
     }
     return di;
    }
    
    
    int sub(int di,int si)
    {
     while(si!=0)
     {
      di^=si;
      si&=di;
      si<<=1;
     }
     return di;
    }
    
    
    
    int mul(int di,int si)
    {
     int ax=0;
     while(si!=0)
     {
      if((si&1)!=0){ax=add(ax,di);}
      di<<=1;
      si>>=1;
     }
     return ax;
    }
    
    /*
    this division function returns the quotient, but also stores the remainder of division in a global variable
    */
    
    int sign_bit=1<<((sizeof(int)<<3)-1); /*used to extract the most significant bit during division function*/
    
    int mod=0; /*to store the modulus/remainder of the division function*/
    
    int bitdiv(int di,int si)
    {
     int ax=0,bx=0,cx=1;
     if(si==0){return 0;} /*division by zero is invalid*/
    
     while(cx!=0)
     {
      ax<<=1;
      bx<<=1;
      if(di&sign_bit){bx|=1;}
      di<<=1;
      
      if(bx>=si)
      {
       bx=sub(bx,si);
       ax|=1;
      }
     
      cx<<=1;
     }
    
     mod=bx;
     return ax;
    }
    
  • Chastity’s Source for ELF 64-bit executable creation

    I did it again. I decoded the 64 bit ELF header format for running an Assembly program on Intel CPUs running Linux. Although I don’t usually do 64 bit programming, I do have references which would allow me to translated my previous programs to use the 64-bit registers. Most of the time this is not needed for what they do.

    ;Chastity's Source for ELF 64-bit executable creation
    ;
    ;All data as defined in this file is based off of the specification of the ELF file format.
    ;I first looked at the type of file created by FASM's "format ELF executable" directive.
    ;It is great that FASM can create an executable file automatically. (Thanks Tomasz Grysztar, you are a true warrior!)
    ;However, I wanted to understand the format for theoretical use in other assemblers like NASM.
    
    ;The Github repository with the spec I used is here.
    ;<https://github.com/xinuos/gabi>
    ;And this is the wikipedia article which linked me to the specification document
    ;<https://en.wikipedia.org/wiki/Executable_and_Linkable_Format>
    
    ;This rest of this file contains a raw binary ELF64 header created using db,dw,dd,dq commands.
    ;After that, it proceeds to assemble a real "Hello World!" program
    
    ;Header for 64 bit ELF executable (with comments based on specification)
    
    db 0x7F,"ELF" ;ELFMAGIC: 4 bytes that identify this as an ELF file. The magic numbers you could say.
    db 2          ;EI_CLASS: 1=32-bit 2=64-bit
    db 1          ;EI_DATA: The endianness of the data. 1=ELFDATA2LSB 2=ELFDATA2MSB For Intel x86 this is always 1 as far as I know.
    db 1          ;EI_VERSION: 1=EV_CURRENT (ELF identity version 1) (which is current at time of specification Version 4.2 I was using)
    db 9 dup 0    ;padding zeros to bring us to address 0x10
    dw 2          ;e_type: 2=ET_EXEC (executable instead of object file)
    dw 0x3E       ;e_machine : 3=EM_386 (Intel 80386) 0x3E (AMD x86-64 architecture)
    dd 1          ;e_version: 1=EV_CURRENT (ELF object file version.)
    
    p_vaddr=0x400000
    e_entry=0x400078 ;we will be reusing this constant later 
    
    dq e_entry    ;e_entry: the virtual address at which the program starts
    dq 0x40       ;e_phoff: where in the file the program header offset is
    db 12 dup 0   ;e_shoff and e_flags are unused in this example,therefore all zeros
    dw 0x40       ;e_ehsize: size of the ELF header
    dw 0x38       ;e_phentsize: size of program header which happens after ELF header
    dw 1          ;e_phnum: How many program headers. Only 1 in this case
    dw 0x40       ;e_shentsize: Size of a section header
    dw 0          ;e_shnum number of section headers
    dw 0          ;e_shstrndx: section header string index (not used here)
    
    ;That is the end of the 0x40 byte (64 bytes decimal) ELF header. Sadly, this is not the end and a program header is also required (what drunk person made this format?)
    
    dd 1           ;p_type: 1=PT_LOAD
    dd 7           ;p_flags: permission flags: 7=4(Read)+2(Write)+1(Execute)
    dq 0           ;p_offset: Base address from file (zero)
    dq p_vaddr     ;p_vaddr: Virtual address in memory where the file will be.
    dq p_vaddr     ;p_paddr: Physical address. Same as previous
    
    image_size=0x1000 ;Chosen size for file and memory size. At minimum this must be as big as the actual binary file (code after header included)
                      ;By choosing a default size of 0x1000, I am assuming all assembly programs I write will be less than 4 kilobytes
    
    dq image_size  ;p_filesz: Size of file image of the segment. Must be equal to the file size or greater
    dq image_size  ;p_memsz: Size of memory image of the segment, which may be equal to or greater than file image.
    
    dq 0           ;p_align; Alignment (none)
    
    ;important FASM directives
    use64          ;tell assembler that 64 bit code is being used
    org e_entry    ;origin of new code begins at the entry point
    
    ;now, the actual hello world program
    mov rax,1   ; invoke SYS_WRITE (kernel opcode 1 on 64 bit systems)
    mov rdi,1   ; write to the STDOUT file
    mov rsi,msg ; pointer/address of string to write
    mov rdx,13  ; number of bytes to write
    syscall
    
    mov rax,0x3C ; invoke SYS_EXIT (kernel opcode 0x3C on 64 bit systems)
    mov rdi,0    ; return 0 status on exit - 'No Errors'
    syscall
    
    msg db 'Hello World!',0Ah
    
    ;This is the makefile I use when assembling and running this program
    
    ;main-fasm:
    ;	fasm ELF-64-hello.asm
    ;	chmod +x ELF-64-hello.bin
    ;	./ELF-64-hello.bin
    
  • Chastity’s Source for ELF 32-bit executable creation

    I wrote an example program using a custom made ELF-32 header using data declaration statements. I wrote many comments which reference the official specification. This was an exercise both in programming and also Technical Reading and Writing. I had to read enough of the specification PDF file to understand what I was doing. I then tried to write descriptive comments that at the very least I would understand when I need to remind myself how this format is created.

    ;Chastity's Source for ELF 32-bit executable creation
    ;
    ;All data as defined in this file is based off of the specification of the ELF file format.
    ;I first looked at the type of file created by FASM's "format ELF executable" directive.
    ;It is great that FASM can create an executable file automatically. (Thanks Tomasz Grysztar, you are a true warrior!)
    ;However, I wanted to understand the format for theoretical use in other assemblers like NASM.
    
    ;The Github repository with the spec I used is here.
    ;<https://github.com/xinuos/gabi>
    ;And this is the wikipedia article which linked me to the specification document
    ;<https://en.wikipedia.org/wiki/Executable_and_Linkable_Format>
    
    ;This file contains a raw binary ELF32 header created using db,dw,dd commands.
    ;After that, it proceeds to assemble a real "Hello World!" program
    
    ;Header for 32 bit ELF executable (with comments based on specification)
    
    db 0x7F,"ELF" ;ELFMAGIC: 4 bytes that identify this as an ELF file. The magic numbers you could say.
    db 1          ;EI_CLASS: 1=32-bit 2=64-bit
    db 1          ;EI_DATA: The endianness of the data. 1=ELFDATA2LSB 2=ELFDATA2MSB For Intel x86 this is always 1 as far as I know.
    db 1          ;EI_VERSION: 1=EV_CURRENT (ELF identity version 1) (which is current at time of specification Version 4.2 I was using)
    db 9 dup 0    ;padding zeros to bring us to address 0x10
    dw 2          ;e_type: 2=ET_EXEC (executable instead of object file)
    dw 3          ;e_machine : 3=EM_386 (Intel 80386)
    dd 1          ;e_version: 1=EV_CURRENT (ELF object file version.)
    
    p_vaddr=0x8048000
    e_entry=0x8048054 ;we will be reusing this constant later 
    
    dd e_entry    ;e_entry: the virtual address at which the program starts
    dd 0x34       ;e_phoff: where in the file the program header offset is
    db 8 dup 0    ;e_shoff and e_flags are unused in this example,therefore all zeros
    dw 0x34       ;e_ehsize: size of the ELF header
    dw 0x20       ;e_phentsize: size of program header which happens after ELF header
    dw 1          ;e_phnum: How many program headers. Only 1 in this case
    dw 0x28       ;e_shentsize: Size of a section header
    dw 0          ;e_shnum number of section headers
    dw 0          ;e_shstrndx: section header string index (not used here)
    
    ;That is the end of the 0x34 byte (52 bytes decimal) ELF header. Sadly, this is not the end and a program header is also required (what drunk person made this format?)
    
    dd 1          ;p_type: 1=PT_LOAD
    dd 0          ;p_offset: Base address from file (zero)
    dd p_vaddr    ;p_vaddr: Virtual address in memory where the file will be.
    dd p_vaddr    ;p_paddr: Physical address. Same as previous
    
    image_size=0x1000 ;Chosen size for file and memory size. At minimum this must be as big as the actual binary file (code after header included)
                      ;By choosing a default size of 0x1000, I am assuming all assembly programs I write will be less than 4 kilobytes
    
    dd image_size  ;p_filesz: Size of file image of the segment. Must be equal to the file size or greater
    dd image_size  ;p_memsz: Size of memory image of the segment, which may be equal to or greater than file image.
    
    dd 7           ;p_flags: permission flags: 7=4(Read)+2(Write)+1(Execute)
    dd 0           ;p_align; Alignment (none)
    
    ;important FASM directives
    use32          ;tell assembler that 32 bit code is being used
    org e_entry    ;origin of new code begins at the entry point
    
    ;now, the actual hello world program
    mov eax,4      ;invoke SYS_WRITE (kernel opcode 4 on 32 bit systems)
    mov ebx,1      ;write to the STDOUT file
    mov ecx,msg    ;pointer/address of string to write
    mov edx,13     ;number of bytes to write
    int 80h
    
    mov eax,1 ;function SYS_EXIT (kernel opcode 1 on 32 bit systems)
    mov ebx,0 ;return 0 status on exit - 'No Errors'
    int 80h   ;call Linux kernel with interrupt
    
    msg db 'Hello World!',0Ah
    
    ;This is the makefile I use when assembling and running this program
    
    ;main-fasm:
    ;	fasm ELF-32-hello.asm
    ;	chmod +x ELF-32-hello.bin
    ;	./ELF-32-hello.bin
    
    

    I made a repository for examples like this. Others may want to understand the header used on Linux systems.

    https://github.com/chastitywhiterose/ELF