EvilVM Documentation Site

Global Constants

The core API provides the following words to access elements in the global constant table. These generally govern the state of the compiler, or provide storage for data items important to the IO transport.

glob ( offset – address )

This is an accessor for items in the global offset table. Offsets are in bytes from the base of the table. All globals are one QWORD in size, or 8 bytes. The list of elements can be found in the assembly source for the compiler, in the file agent/table.asm. Some constants are later defined with the AddGlobal or DefGlobal macros, and can be found in other source files.

The standard idiom for accessing a global variable is as follows:

\ get 'here' pointer address to next byte in dictionary
$58 glob @
\ set 'here' pointer to some new value
$28f9d00 $58 glob !

base ( – address )

Get address of the base variable, which represents the numeric base for printing values and reading constants. The system starts in base 10, but can be switched to any reasonable base. Bases higher than base-10 will use alphabetic characters for values higher than 9.

hex ( – )

dec ( – )

Set the numeric base to base-16 or base-10, respectively. These are provided because they are common bases to use. Note that often it is easier to use the $ sigil to submit hexadecimal constants to the compiler, but for longer code sequences that are more sensible in hex, use this word.

A common use case is to print values in hex:

: .hex
  hex . dec

dict ( – address )

Obtain address to the starting byte in the dictionary. A useful situation for this word is to measure space utilization in the dictionary:

\ get bytes used in dictionary
here dict @ - .

bottom ( – address )

Obtain the address of the bottom of the data stack. This word is not the address of the global variable, but will put the actual address of the stack bottom on the stack. So, for example, to see the depth of the stack:

bottom psp - .

Note that this snippet will be inflated by 16 bytes because bottom and psp both put new objects on the stack.

depth ( – depth )

Obtain the depth of the stack. This is in cells, so each item added to the stack increases the depth by one. E.g., the following should show a result of 3:

1 2 3
depth .

This can be useful for adding safety guards on words, to ensure that the stack has the necessary number of values on it before proceeding with some senstive operation. Also, it can be invaluable when writing code that puts variable results on the stack, and depth can be used to confirm the resulting depth.

@key ( – address )

!key ( xt – )

These words can be used to obtain the current execution token (function pointer) for the key word, which reads data from the input stream. If you write code that needs to alter the behavior of byte input (e.g., you want to change to another IO transport), use these words.

here ( – address )

!here ( address – )

Manipulate the here pointer, which points to the next available byte in the dictionary. This example will show the here pointer’s value, store something in the dictionary, and print it again. You’ll see that the here pointer has advanced by one cell in the process:

here .
42 ,
here .

It is not common to change the here pointer directly, but review the contents of samples/local-variables.fth for an example usage. In that file, the here pointer is moved to a separate location to define some temporary words to properly compile references to local variables, and then it is returned to the proper location for compiling.

last ( – address )

!last ( address – )

Points to the header for the most recent completed word definition in the dictionary. Reading this value is not uncommon. This snippet shows the name of the last word that was compiled:

last >name type

this ( – address )

!this ( address – )

Points to the current definition, which may not have been completed. If you are not compiling a word, this should usually be equal to the last pointer. It can be useful in immediate words or when escaping to the interpreter during compilation for various tasks.

This contrived example prints out the name of the word during compilation:

: test
  [ this >name type cr ]
  1 2 + .

@input ( – handle )

!input ( handle – )

Manipulate the current input stream HANDLE. This might be a standard input stream, or a socket, or nothing at all, depending on the transport. You will likely only need to use this if you are writing your own transport that needs post-bootstrap interaction.

stdin ( – handle )

Regardless of the IO transport in use, this word will put the process’s current standard input (the actual STDIN) on the stack.

scratch ( – address )

Put the address of the scratch space on the stack. This region is allocated with size equal to the SCRATCHSZ variable in agent/main.asm. It contains the tib (terminal input buffer), and can also be used as a safe data storage area (as long as you avoid the tib!). Primary use case is for inspection and debugging. Try this to see it in action (assumes you have loaded the default network payload):

scratch .cstring

entrypoint ( – address )

endofshell ( – address )

The address of the entrypoint to the compiler shellcode, and address for the last byte in it, respectively. You can use this to check pointers for being in this range, or as bounds for copying the shellcode somewhere, etc.

This snippet shows the size of the shellcode:

endofshell entrypoint - .

Note that the last byte in the shellcode is a NOP (0x90), which will be the value found at the endofshell address. So technically, the above shows a value one byte too small, as it will not include this last byte if treated as a range.

line ( – address )

Address where the currently line number is stored. It is incremented every time key processes an inbound newline. It is also reset to 1 when the server loads a code file, so it is primarily used to give intelligible error messages. Check the current line as follows:

line @ .

lastword ( – address length )

Get a counted string for the last word that was read by the interpreter or compiler. It is primarily used for error messages, but is available for use. See the last word read in as follows:

lastword type
Last updated on 6 May 2019