flat assembler
Message board for the users of flat assembler.

Index > Windows > Windows version of chastehex

Author
Thread Post new topic Reply to topic
chastitywhiterose



Joined: 13 Oct 2025
Posts: 76
chastitywhiterose 06 Nov 2025, 10:57
I managed to learn enough Windows API to rewrite my chastehex program for Windows. Fortunately, most of my functions in chastelib worked without any modification from the Linux version because they used 32 bit registers and didn't call any OS specific calls. After I changed putstring to use the WriteFile call for Windows, I was able to get the output I needed to write and debug the rest of the program while I learned how to open, seek, read, and write a file with the Windows API.

It works the same as the Linux and DOS versions that I posted on this forum in their sections. I was blown away with how much faster it executed compared to the C prototype I built first.

main.asm

Code:
format PE console
entry main

include 'win32a.inc'        ;includes standard Windows 32-bit definitions and macros
include 'chastelib-w32.asm' ;include standard library functions by Chastity
include 'getarg-w32.asm'    ;include Chastity's getarg function for command line arguments

main:

mov [radix],16 ; Choose radix for integer output.
mov [int_width],1

call getarg ;this first call will get the command string
cmp eax,0   ;did the getarg function return 0?
jz help     ;if eax was zero, there are no args so we end the program safely after help message

;optionally display the command string
;call putstring
;call putline

call getarg ;get next arg as file name
cmp eax,0   ;did the getarg function return 0?
jz help     ;if eax was zero, there are no args so we end the program safely after help message

;print the filename
call putstring
call putline

mov [filename], eax ;save the filename to a permanent address

jmp open_sesame

help:

mov eax,help_message
call putstring

jmp main_end

open_sesame:

;open a file with the CreateFileA function
;https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea

;call CreateFileA with Windows 32-bit calling convention

push 0                  ;hTemplateFile = NULL: We are not using a template file
push 0x80               ;dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL
push 3                  ;dwCreationDisposition = OPEN_EXISTING
push 0                  ;lpSecurityAttributes = NULL: No security attributes
push 1                  ;dwShareMode = FILE_SHARE_READ (other programs can read this file)
push 0x10000000         ;dwDesiredAccess = GENERIC_ALL access mode (Read+Write+Execute)
push [filename]         ;lpFileName
call [CreateFileA]


;check eax for file handle or error code
;call putint
cmp eax,-1
jnz file_ok

mov eax,file_error_message
call putstring
call [GetLastError]
call putint
jmp main_end ;end program if the file was not opened

;this label is jumped to when the file is opened correctly
file_ok:

mov [fd],eax

;before we proceed, we also check for more arguments.

;get next arg (first one after name of program)
call getarg
cmp eax,0  ;did the getarg function return 0?
jz hexdump ;proceed to normal hex dump if no more args

;otherwise interpret the arg as a hex address to seek to

call strint
mov [offset],eax

;seek to 32-bit address of file with SetFilePointer function
;https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-setfilepointer


push 0           ;seek from beginning of file (SEEK_SET)
push 0           ;NULL: We are not using a 64 bit address
push [offset]    ;where we are seeking to
push [fd]        ;seek within this file
call [SetFilePointer]


;check for more args after the address argument
call getarg
cmp eax,0
jz read_one_byte ;proceed to read one byte mode

;otherwise, write the rest of the arguments as bytes to the file!
write_bytes:
call strint
mov [buf],al

;Windows 32-bit WriteFile system call
;https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-writefile


push 0               ;lpOverlapped = NULL
push 0               ;lpNumberOfBytesWritten = NULL
push 1               ;nNumberOfBytesToWrite = 1
push buf             ;lpBuffer = address of data to write
push [fd]            ;hFile = [fd] (copied there earlier)
call [WriteFile]


mov eax,[offset]
inc [offset]
mov [int_width],8
call putint_and_space

mov eax,0
mov al,[buf]
mov [int_width],2
call putint_and_line

;check for more args
call getarg ;get next arg as potential bytes to write
cmp eax,0   ;did the getarg function return 0?
jnz write_bytes
;continue write if the args still exist
;otherwise end program
jmp main_end

read_one_byte:

;read only 1 byte using Win32 ReadFile system call.


push 0              ;Optional Overlapped Structure 
push count     ;Store Number of Bytes Read from this call
push 1              ;Number of bytes to read
push buf     ;address to store bytes
push [fd]  ;handle of the open file
call [ReadFile]

cmp [count],1 
jz print_byte ;if less than one bytes read, there is an error

mov eax,[offset]
mov [int_width],8
call putint_and_space
mov eax,end_of_file
call putstr_and_line

jmp main_end

print_byte:
mov eax,[offset]
mov [int_width],8
call putint_and_space

mov eax,0
mov al,[buf]
mov [int_width],2
call putint_and_line

jmp main_end

hexdump:

;read bytes using Win32 ReadFile system call.
push 0              ;Optional Overlapped Structure 
push count     ;Store Number of Bytes Read from this call
push 16             ;Number of bytes to read
push buf     ;address to store bytes
push [fd]  ;handle of the open file
call [ReadFile]     ;all the data is in place, do the write thing!

mov eax,[count]
;call putint
;mov eax,buf
;call putstring

cmp eax,0
jnz read_ok ;if more than zero bytes read, proceed to display

jmp eof_end

read_ok:
call print_bytes_row

jmp hexdump

print_EOF:

mov eax,[offset]
mov [int_width],8
call putint_and_space

mov eax,end_of_file
call putstr_and_line

jmp main_end


eof_end:
;before we end the program, let the user know End Of File was reached
mov eax,end_of_file
call putstr_and_line

main_end:

;close the file
push [fd]
call [CloseHandle]

;Exit the process with code 0
push 0
call [ExitProcess]



;variables for displaying messages
file_error_message db 'error: ',0
end_of_file db 'EOF',0
read_error_message db 'Failure during reading of file. Error number: ',0

help_message db 'chastehex by Chastity White Rose',0Ah,0Ah
db 'hexdump a file:',0Ah,0Ah,9,'chastehex file',0Ah,0Ah
db 'read a byte:',0Ah,0Ah,9,'chastehex file address',0Ah,0Ah
db 'write a byte:',0Ah,0Ah,9,'chastehex file address value',0Ah,0Ah
db 'The file must exist',0Ah,0

;this function prints a row of hex bytes
;each row is 16 bytes
print_bytes_row:
mov eax,[offset]
mov [int_width],8
call putint_and_space

mov ebx,buf
mov ecx,[count]
add [offset],ecx
next_byte:
mov eax,0
mov al,[ebx]
mov [int_width],2
call putint_and_space

inc ebx
dec ecx
cmp ecx,0
jnz next_byte

mov ecx,[count]
pad_spaces:
cmp ecx,0x10
jz pad_spaces_end
mov eax,space_three
call putstring
inc ecx
jmp pad_spaces
pad_spaces_end:

;optionally, print chars after hex bytes
call print_bytes_row_text
call putline

ret

space_three db '   ',0

print_bytes_row_text:
mov ebx,buf
mov ecx,[count]
next_char:
mov eax,0
mov al,[ebx]

;if char is below '0' or above '9', it is outside the range of these and is not a digit
cmp al,0x20
jb not_printable
cmp al,0x7E
ja not_printable

printable:
;if char is in printable range,copy as is and proceed to next index
jmp next_index

not_printable:
mov al,'.' ;otherwise replace with placeholder value

next_index:
mov [ebx],al
inc ebx
dec ecx
cmp ecx,0
jnz next_char
mov [ebx],byte 0 ;make sure string is zero terminated

mov eax,buf
call putstring

ret

;variables for managing file IO.
filename dd ?
count dd ? ;how many bytes are read with ReadFile operation
buf db 16 dup ?,0
fd dd ?
offset dd ?

;FASM builds the Import Address Table (IAT) directly in the source file
section '.idata' import data readable writeable

library kernel32, 'KERNEL32.DLL'

import kernel32,\
 GetStdHandle, 'GetStdHandle',\
 WriteFile, 'WriteFile',\
 ExitProcess, 'ExitProcess',\
 GetCommandLineA, 'GetCommandLineA',\
 CreateFileA, 'CreateFileA',\
 GetLastError, 'GetLastError',\
 SetFilePointer, 'SetFilePointer',\
 ReadFile, 'ReadFile',\
 CloseHandle, 'CloseHandle'
    


Also, on my system, I use the following batch file to set the path to the include files.

fasmpath.bat

Code:
set path=C:\fasm
set include=C:\fasm\INCLUDE
    


Description: a library written for handling command line arguments on Windows in a consistent way with my Linux programs.
Download
Filename: getarg-w32.asm
Filesize: 5.76 KB
Downloaded: 39 Time(s)

Description: standard output functions for strings and integers
Download
Filename: chastelib-w32.asm
Filesize: 8.17 KB
Downloaded: 31 Time(s)

Description: main program source for 32-bit editon of chastehex
Download
Filename: main.asm
Filesize: 7.5 KB
Downloaded: 40 Time(s)



Last edited by chastitywhiterose on 18 Oct 2026, 09:23; edited 4 times in total
Post 06 Nov 2025, 10:57
View user's profile Send private message Send e-mail Reply with quote
Mаt Quasar



Joined: 29 Jun 2025
Posts: 52
Mаt Quasar 06 Nov 2025, 15:01
A very nice effort! You've targeted DOS, Windows and Linux!

I ran into error when using chastehex.EXE, maybe because of the file attributes when opening file, it can't open itself! (See screenshot, error 20)

Also, when hexdumping the chastehex.ASM, there are no ASCII characters printed.


Description: Screenshot of me running chastehex.EXE in Command Prompt
Filesize: 33.43 KB
Viewed: 4315 Time(s)

chastehex.PNG


Post 06 Nov 2025, 15:01
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 21191
Location: In your JS exploiting you and your system
revolution 06 Nov 2025, 15:06
Windows doesn't allow altering a running exe file.
Code:
push 0x10000000  ;GENERIC_ALL access mode (Read+Write)    
If the access mode is changed to read-only then it probably can open itself, and any other file that can't be written.
Post 06 Nov 2025, 15:06
View user's profile Send private message Visit poster's website Reply with quote
Mаt Quasar



Joined: 29 Jun 2025
Posts: 52
Mаt Quasar 06 Nov 2025, 15:11
Oh, maybe it was designed such a way so that no ASCII characters are printed?


Description: Format-hex cmdlet output of your chastehex.asm
Filesize: 39.12 KB
Viewed: 4312 Time(s)

format-hex.PNG


Post 06 Nov 2025, 15:11
View user's profile Send private message Visit poster's website Reply with quote
Mаt Quasar



Joined: 29 Jun 2025
Posts: 52
Mаt Quasar 06 Nov 2025, 15:13
revolution wrote:
Windows doesn't allow altering a running exe file.
Code:
push 0x10000000  ;GENERIC_ALL access mode (Read+Write)    
If the access mode is changed to read-only then it probably can open itself, and any other file that can't be written.


You're right. I totally forgot chastehex.EXE can write to the opened file. It is a hexedit program for the command-line.
Post 06 Nov 2025, 15:13
View user's profile Send private message Visit poster's website Reply with quote
Mаt Quasar



Joined: 29 Jun 2025
Posts: 52
Mаt Quasar 06 Nov 2025, 15:30
I notice the use of CMP against zero value, can use OR as substitute so that 1 byte is saved:

Code:
cmp ecx,0
jnz next_byte      

6 bytes

Code:
or ecx,ecx
jnz next_byte      

5 bytes

Just being picky. Very Happy
Post 06 Nov 2025, 15:30
View user's profile Send private message Visit poster's website Reply with quote
Mаt Quasar



Joined: 29 Jun 2025
Posts: 52
Mаt Quasar 06 Nov 2025, 15:49
This is the shortest code for a hexdump program in Windows:

Code:
invoke CreateFileA,......
invoke ReadFile,....
invoke CryptBinaryToStringA,....,0x0000000b,..
invoke GetStdHandle,.......
invoke WriteConsoleA,........
invoke ExitProcess,......    
Post 06 Nov 2025, 15:49
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 21191
Location: In your JS exploiting you and your system
revolution 06 Nov 2025, 16:12
Mаt Quasar wrote:
I notice the use of CMP against zero value, can use OR as substitute so that 1 byte is saved:...
Since you raised the point, the entire line can be deleted, because the dec ecx before sets the Z flag.

Or just use loop. Save even more bytes!!!
Post 06 Nov 2025, 16:12
View user's profile Send private message Visit poster's website Reply with quote
Mаt Quasar



Joined: 29 Jun 2025
Posts: 52
Mаt Quasar 08 Nov 2025, 05:21
revolution wrote:
Mаt Quasar wrote:
I notice the use of CMP against zero value, can use OR as substitute so that 1 byte is saved:...
Since you raised the point, the entire line can be deleted, because the dec ecx before sets the Z flag.

Or just use loop. Save even more bytes!!!


I learned something new. Thanks.

Code:
dec ecx
jnz next_byte 
    


4 bytes

Code:
loop next_byte
    


2 bytes

Or 8 bytes (from original code) --> 2 bytes, a substantial 6-byte saving!
Post 08 Nov 2025, 05:21
View user's profile Send private message Visit poster's website Reply with quote
Mаt Quasar



Joined: 29 Jun 2025
Posts: 52
Mаt Quasar 08 Nov 2025, 07:54
This is an hex viewer program for Windows written by another flat assembler forum member:
https://github.com/vlabsc/file_in_hex
Post 08 Nov 2025, 07:54
View user's profile Send private message Visit poster's website Reply with quote
AsmGuru62



Joined: 28 Jan 2004
Posts: 1836
Location: Toronto, Canada
AsmGuru62 08 Nov 2025, 13:31
Can these HEX viewers search for patterns of bytes, like: "B8 00 ** ** ** A4 F5"?
Post 08 Nov 2025, 13:31
View user's profile Send private message Send e-mail Reply with quote
chastitywhiterose



Joined: 13 Oct 2025
Posts: 76
chastitywhiterose 09 Nov 2025, 01:43
revolution wrote:
Mаt Quasar wrote:
I notice the use of CMP against zero value, can use OR as substitute so that 1 byte is saved:...
Since you raised the point, the entire line can be deleted, because the dec ecx before sets the Z flag.

Or just use loop. Save even more bytes!!!


My understanding was that inc and dec don't modify any flags. Also, everyone is funny when they explain how to save a few bytes but that is one of the reasons I love Assembly. Obviously I could write an optimized version later but I was just thrilled that I got it working and that it did exactly what I want!
Post 09 Nov 2025, 01:43
View user's profile Send private message Send e-mail Reply with quote
chastitywhiterose



Joined: 13 Oct 2025
Posts: 76
chastitywhiterose 09 Nov 2025, 01:55
Mаt Quasar wrote:
revolution wrote:
Windows doesn't allow altering a running exe file.
Code:
push 0x10000000  ;GENERIC_ALL access mode (Read+Write)    
If the access mode is changed to read-only then it probably can open itself, and any other file that can't be written.


You're right. I totally forgot chastehex.EXE can write to the opened file. It is a hexedit program for the command-line.


Yes, the idea was that not only reading was possibly but also writing. Depending on the arguments you give it, you can read either the whole thing by only providing the file name, or the filename and the seek address to read that byte. If you add any other arguments after that, then they become bytes to write starting at that address.

I had something specific in mind when I made it. I was thinking of learning how to make my own headers for executable files using chastehex so that I could generate them from scripts rather than relying on the defaults FASM uses. Perhaps I could even use NASM for some development and then use chastehex to make the headers since it can't do it on its own without a linker like FASM can.

But also, chastehex can be used to cheat at some video games where save files have fixed addresses for data.

I also have a file comparison tool that I wrote in C but I have not written it in Assembly yet. That might be something I do but for now I can use the C version just fine on any platform to find where the bytes differ.

And yes, it doesn't currently display ASCII characters like some hex dumpers do. It was intended to be used on binary files which would not contain much text data, like Cave Story save files!
Post 09 Nov 2025, 01:55
View user's profile Send private message Send e-mail Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 21191
Location: In your JS exploiting you and your system
revolution 09 Nov 2025, 02:19
The x86 docs have this to say for DEC flags:
Quote:
The CF flag is not affected. The OF, SF, ZF, AF, and PF flags are set according to the result.
This was designed to allow the carry flag to be propagated around a loop for a long chain of arithmetic operations.
Post 09 Nov 2025, 02:19
View user's profile Send private message Visit poster's website Reply with quote
chastitywhiterose



Joined: 13 Oct 2025
Posts: 76
chastitywhiterose 09 Nov 2025, 02:22
revolution wrote:
The x86 docs have this to say for DEC flags:
Quote:
The CF flag is not affected. The OF, SF, ZF, AF, and PF flags are set according to the result.
This was designed to allow the carry flag to be propagated around a loop for a long chain of arithmetic operations.


I will look more into this because this could allow removing cmp statements in some places. However, I tend to have them in my code even when not needed just for clarity because I am planning to put a lot of my source code into a book.
Post 09 Nov 2025, 02:22
View user's profile Send private message Send e-mail Reply with quote
chastitywhiterose



Joined: 13 Oct 2025
Posts: 76
chastitywhiterose 18 Oct 2026, 09:34
I have an update. I learned enough of the 64-bit Windows calling convention to get chastehex working using the modern calling convention. It works the same way as the 32-bit edition which was in the original post of this threat.

The original post has also been updated with new attachments which should have corrected comments and my new library for handling command line arguments.

I have also been writing a book on my discoveries of Assembly programming on Windows. It has a lot of code samples for 32 and 64 bit calling conventions.

https://leanpub.com/assemblyarithmeticalgorithms-windows

As usual, all my programs are command line tools that started out on Linux and then I worked hard to figure out the Windows API of doing the same tricks. Good thing there is only a tiny subset of the API functions I need to do my work.


Description: my command line argument handling function for Windows 64-bit
Download
Filename: getarg-w64.asm
Filesize: 5.89 KB
Downloaded: 32 Time(s)

Description: 64 bit Windows library of standard output functions
Download
Filename: chastelib-w64.asm
Filesize: 8 KB
Downloaded: 39 Time(s)

Description: main part of program
Download
Filename: main.asm
Filesize: 8.57 KB
Downloaded: 33 Time(s)

Post 18 Oct 2026, 09:34
View user's profile Send private message Send e-mail Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2026, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.