Chronos VM
This virtual machine (VM) is custom-designed for a specific purpose. It includes standard instructions for data movement and arithmetic operations, along with unique jump instructions that extend the capabilities of typical jumping behavior. The VM also has a limited set of flags that can be expanded based on specific needs.
It operates using 32-bit opcodes and registers.
This VM is created as a part of a challenge for an International CTF, bi0s CTF 2024.
In that challenge, the VM performs bit manipulation on the user input which demands reversing the logic of the bit operations and retrieve back the original bytes.
The source code of the VM can be found here.
Registers
There are in total 8 General Purpose registers and 4 Special registers
General Registers
- 700: S0
- 701: S1
- 702: S2
- 703: S3
- 704: S4
- 705: S5
- 706: S6
- 707: S7
Special Registers
- 708: MP - Memory pointer
- PC - Program counter
- SP - Stack pointer
- BP - Base pointer
Flags
- EF - Exit Flag
- ZF - Zero Flag
Instruction Set
| Opcodes | Mnemonic | Description | Use Case of Opcodes |
|---|---|---|---|
| 1200 (10) | MOVR | “Moves” a value from one register to another (It does not copy the value but actually moves it) | MOVR S0, S1 S1 –> S0 |
| 1201 (8) | COPI | Typical ‘mov’ operation from x86 with an immediate value | COPI S0, 4 4 –> S0 |
| 1202 (10) | COPR | Typical mov operation from x86 | COPR S0, S1 |
| 1203 (8) | ADDI | Add the values of the register and the immediate value and store it in first operand | ADDI S0, 4 S0 += 4 |
| 1204 (10) | ADDR | Add the values of the 2 register and save the result in the first register | ADDR S0, S1 S0 = S0 + S1 |
| 1205 (8) | SUBI | Subtract the values of the register and the immediate value and store it in first operand | SUBI S0, 4 S0 -= 4 |
| 1206 (10) | SUBR | Subtract the values of the 2 register and save the result in the first register | SUBR S0, S1 S0 = S0 - S1 |
| 1207 (8) | XORI | Xor the values of the register and the immediate value and store it in first operand | XORI S0, 4 S0 ^= 4 |
| 1208 (10) | XORR | Xor the values of the 2 register and save the result in the first register | XORR S0, S1 S0 = S0 ^ S1 |
| 1209 (8) | DIVI | Divide the value of the register by the immediate value and store it in first operand | DIVI S0, 4 S0 /= 4 |
| 1210 (10) | DIVR | Divide the value of the 1st register by 2nd register and save the result in the first register | DIVR S0, S1 S0 = S0 / S1 |
| 1211 (8) | MULI | Multiply the values of the register and the immediate value and store it in first operand | MULI S0, 4 S0 *= 4 |
| 1212 (10) | MULR | Multiply the values of the 2 register and save the result in the first register | MULR S0, S1 S0 = S0 * S1 |
| 1213 (7) | POPA | Pops the top value of the stack and stores it in the given register. SP –> SP + 1 | POPA S0 [SP] –> S0 |
| 1214 (7) | PUSH | The value in the register is pushed on the top of the stack. SP –> SP - 1 | PUSH S0 S0 –> [SP-1] |
| 1215 (8) | LOAD | Load the data from the memory to register. The offset of the MP is given as the 2nd argument | LOAD S0, 1 [MP+1] –>S0 |
| 1216 (7) | STOR | Store the data from register to memory. MP –> MP + 1 |
STOR S0 S0 –> [MP] |
| 1217 (~) | CALL | Calls the function given, the length of the function name and then the function name is provided | CALL 5 func0 |
| 1218 (7) | INCR | Increments the value of the register by 1 and store it in the same register | INCR S0 S0 += 1 |
| 1219 (7) | DECR | Decrements the value of the register by 1 and store it in the same register | DECR S0 S0 -= 1 |
| 1220 (8) | LD_D | Another version of Load, where the value from the memory is deleted when it’s loaded into the register | LD_D S1, 1 [MP+1] –>S1 |
| 1221 (8) | SHRI | Shift Right the value of the register by the given value | SHRI S0, 1 S0 = S0 >>1 |
| 1222 (10) | SHRR | Shift Right the value of the register by the given value within a register | SHRR S0, S1 S0 = S0 >>S1 |
| 1223 (8) | SHLI | Shift Left the value of the register by the given value | SHLI S0, 1 S0 = S0 << 1 |
| 1224 (10) | SHLR | Shift Left the value of the register by the given value within a register | SHLR S0, S1 S0 = S0 << S1 |
| 1225 (~) | PRIM | Print the message, the length of the message and the message is given | PRIM 5 Hello |
| 1226 (7) | PRIR | Prints the value of the register | PRIR S0 |
| 1227 (8) | COPS | Copies the value from the stack to the register given | COPS S0,4 [SP+4] –> S0 |
| 1228 (~) | JZD | Increment the PC by the value given if the ZF is set to 1 | JZD 4 PC += 4 |
| 1229 (~) | JNZD | Increment the PC by the value given if the ZF is set to 0 | JNZD 4 PC += 4 |
| 1230 (~) | JMPD | Increment the PC by the value given | JMPD 4 PC += 4 |
| 1231 (~) | SCAN | Takes input in 3 forms, integer, character and string The type of data needs to be specified as ‘i’ for integer, ‘c’ for character and ‘s’ for string Integer input is stored in the register specified Character input is stored directly in the memory String input is converted to it’s base64 form and then stored in the memory |
SCAN i S0 input –> S0 |
| 1232 (8) | CMPI | Compares the value in the register with the immediate value and sets the zero flag accordingly | CMPI S0, 3 |
| 1233 (10) | CMPR | Compares the values in the 2 registers and sets the zero flag accordingly | CMPI S0, S1 |
| 1234 (4) | EXIT | Sets the Exit flag as 1 | EXIT |
| 1235 (8) | ANDI | Bitwise And operation is performed on the value of the register and the immediate value and is stored it in the first operand | ANDI S0, 4 S0 &= 4 |
| 1236 (10) | ANDR | Bitwise And operation is performed on the values of the 2 registers and result is stored in the first operand | ANDR S0, S1 S0 = S0 & S1 |
| 1237 (8) | ORRI | Bitwise Or operation is performed on the value of the register and the immediate value and is stored it in the first operand | ANDI S0, 4 S0 |= 4 |
| 1238 (10) | ORRR | Bitwise Or operation is performed on the values of the 2 registers and result is stored in the first operand | ORRR S0, S1 S0 = S0 | S1 |
| 1239 (~) | JZU | Decrement the PC by the value given if the ZF is set to 1 | JZU 4 PC -= 4 |
| 1240 (~) | JNZU | Decrement the PC by the value given if the ZF is set to 0 | JNZU 4 PC -= 4 |
| 1241 (~) | JMPU | Decrement the PC by the value given | JMPD 4 PC -= 4 |
| 1242 (~) | JNZ | Place the PC at the offset mentioned if the zero flag is set to 0 | JNZ 4 PC = 4 |
| 1243 (~) | JZ | Place the PC at the offset mentioned if the zero flag is set to 1 | JZ 4 PC = 4 |
Application
This VM is created as a part of a challenge for an International CTF, bi0s CTF 2024
In that challenge, the VM performs bit manipulation on the user input which demands reversing the logic of the bit operations and retrieve back the original bytes.
You can download the source code of the VM here.
Control Flow
- Prompt for input
- Take input and format it
- Store the modified input in memory
- Store the same in the stack
- Convert into binary and store it in stack and remove the input from memory
- Classified array is computed and stored in memory
Disassembly
1 | ;S0 has the modified input length |
Ciao !!
- Title: Chronos VM
- Author: Sejal Koshta
- Created at : 2024-03-03 21:28:07
- Updated at : 2024-08-07 22:42:45
- Link: https://k1n0r4.github.io/2024/03/03/Chronos-VM/
- License: This work is licensed under CC BY-NC-SA 4.0.