The OCM bytecode machine is an interpreter for a stack based language. That means that the operands are pushed on a stack first, than the operation is encoded that takes the operands from the stack and puts the result back on the stack. Some typical stack-based languages include RPN calculators, PostScript, Forth and the Java VM. These are specific properties of the OCM machine:
Small numbers are a value type, whereas all other types are reference types, i.e. the values are merely references to reference counted objects. The term object means the thing the reference points to. Reference counting can be disabled on individual objects, making the permanent.
Small numbers are stored directly in the stack. Other objects are accessed through an intermediate data structure (handle) that points to the object. The value on the stack then is a pointer to the handle, shifted by two bits to the right. The highest two bits contain the type.
struct stack_value { union { unsigned int value : 30; void *handle_div_4; }; unsigned int type : 2; }; struct bigint_handle { uint8_t refcount; uint8_t is_negative; uint16_t len_in_32bitwords; uint32_t payload[0]; // Variable-length-array }; struct blob_handle { uint8_t refcount; uint8_t heap_allocated; uint8_t unknown; uint8_t zero; // Location of data pointer for 0 byte blocks. uint32_t length; void *data; }; struct array_handle { uint8_t refcount; uint8_t unused; uint16_t nr_elements; stack_value data[0]; // Variable length array, entries are handles/smallints };
Serialization format: Length followed by two's complement little endian representation. The length is according to ASN.1 lengths: Length is directly encoded as one byte for lengths 0..127. A length encoding starting with 128 is invalid (would mean unspecified). If the first byte of the length is above 128, subtract 128 and take that many bytes. They make up a big-endian unsigned integer.
Serialization format: Length followed by the bytes. Length is encoded as for the integers.
Serialization format: ASN.1 heterogenous sequence
does nothing
immediate data: 1 byte (plaintext, even if OCM code encryption is enabled)
effect: pushes the unsigned 8 bit value of the immediate operand to the stack as small number
immediate data: 1 16-bit-word (plaintext, even if OCM code encryption is enabled)
effect: pushes the signed 16 bit value of the immediate operand to the stack as small number
note: This is used to set up a new decryption key before a 0x73 (Set initial bytecode decryption key in the interpreter) instruction.
immediate data: 1 big int (plaintext, even if OCM code encryption is enabled)
effect: pushes the big int onto stack (after deserialization)
immediate data: 1 blob (plaintext, even if OCM code encryption is enabled)
effect: pushes the blob onto stack (after deserialization)
stack input: one value of any type
effect: leaves the input on the stack and pushes a (shallow) copy
stack input: one value of any type
effect: duplicates the value like opcode 05, unless it is zero stored as a small int. In that case: no effect
stack input: one value of any type
effect: the top-of-stack value is deleted. If the stack would get empty, a small int zero is pushed.
stack input: one value of any type
effect: Like Pop, but instead of just dropping the reference count for references, the object gets deleted at once. Mostly useful for permanent objects.
effect: exchanges top of stack with next-to-top-of-stack
effect: pushes a copy of the next-to-top value.
PostScript equivalent: "1 index"
stack input: an index into the stack (call it n); must be small integer and > 0
effect: pushes a copy of the value at n below the input operand. So "Immediate 1;FetchStack" is the same as "Duplicate" and "Immediate 2;FetchStack" is the same as "NextToTop".
PostScript equivalent: "1 sub index"
effect: The value at Top-2 gets moved to top of stack, top of stack becomes top-1 and top-1 is moved to top-2
PostScript equivalent: "3 -1 roll"
stack input: in index into the stack (call it n); must be small integer and > 1
effect: moves the n-th operand below the input operand n to top of stack, shifting everything inbetween downwards. "Immediate 2;MoveToTop" is the same as "Exchange".
PostScript equivalent: "-1 roll"
stack input: value (any type, next-to-top); Key (blob of 4 bytes at top of stack)
effect: Adds/Updates an entry in the user dictionary.
stack input: value (any type, next-to-top); Key (blob of 4 bytes OR small int at top of stack)
effect: If top of stack is a small int, the value is stored in the system dictionary at the given index. Must be below 0x100. If top of stack is a blob, like SetInUserDict.
stack input: dictionary key (either nonnegative smallint < 0x100 or 4 byte blob)
effect: Looks up the given key in the system dictionary (if a small int) or the user dictionary otherwise. OCM exception gets thrown if the key is not found
stack output: the result of the dictionary lookup
stack input: smallint a (next to top); smallint b (top of stack)
stack output: smallint containing a+b
stack input: smallint a (next to top); smallint b (top of stack)
stack output: smallint containing a-b
stack input: smallint a (next to top); smallint b (top of stack)
stack output: smallint containing lowest 16 bits of a*b
stack input: smallint a (next to top); smallint b (top of stack)
stack output: smallint containing a/b
stack input: smallint a (next to top); smallint b (top of stack)
stack output: smallint containing a/b (next to top); smallint containing a%b (top of stack)
stack input: smallint a (next to top); smallint b (top of stack)
stack output: smallint containing a%b
stack input: smallint a (next to top); smallint b (top of stack)
stack output: smallint containing a ^ b
stack input: smallint a (next to top); smallint b (top of stack)
stack output: smallint containing a & b
stack input: smallint a (next to top); smallint b (top of stack)
stack output: smallint containing a | b
stack input: smallint a
stack output: smallint containing ~a
stack input: smallint a (next to top); smallint count (top of stack)
stack output: smallint containing a « count
stack input: smallint a (next to top); smallint count (top of stack)
stack output: smallint containing ( (unsigned)a) » count
stack input: smallint a (next to top); smallint count (top of stack)
stack output: smallint containing a » count
stack input: smallint a (next to top); smallint b (top of stack)
stack output: smallint containing 1 if a > b, 0 otherwise
stack input: smallint a (next to top); smallint b (top of stack)
stack output: smallint containing 1 if a < b, 0 otherwise
stack input: smallint a (next to top); smallint b (top of stack)
stack output: smallint containing 1 if a == b, 0 otherwise
stack input: object of any type (maybe only smallint intended)
stack output: 1 if object has type smallint and is zero
stack input: small/big int a (next to top); small/big int b (top of stack)
stack output: 32 bit bigint containing a+b
note: DWORD instructions only use the 32 least significant bits in bigint input operands, and samllint operands are treated as unsigned for unknown reasons. All arithmetic is unsigned. As bigints are in sign/magnitude representation, the input sign is ignored for bigints, but not for small ints that are two's complement. But do not use the function with negative inputs unless you wrote the OCM bytecode interpreter, because the sign of the output is undefined in that case (unless you know the internals of the interpreter, that is)
stack input: small/big int a (next to top); small/big int b (top of stack)
stack output: 32 bit bigint containing a-b
note: See Opcode 22 for important notes on DWORD instructions.
stack input: small/big int a (next to top); small/big int b (top of stack)
stack output: 32 bit bigint containing a*b (low order 32 bits)
note: See Opcode 22 for important notes on DWORD instructions.
stack input: small/big int a (next to top); small/big int b (top of stack)
stack output: 32 bit bigint containing a/b
note: See Opcode 22 for important notes on DWORD instructions.
stack input: small/big int a (next to top); small/big int b (top of stack)
stack output: 32 bit bigint containing a/b (next-to-top); 32 bit bigint containing a%b (top of stack)
note: See Opcode 22 for important notes on DWORD instructions.
stack input: small/big int a (next to top); small/big int b (top of stack)
stack output: 32 bit bigint containing a%b
note: See Opcode 22 for important notes on DWORD instructions.
stack input: small/big int a (next to top); small/big int b (top of stack)
stack output: 32 bit bigint containing a ^ b
note: See Opcode 22 for important notes on DWORD instructions.
stack input: small/big int a (next to top); small/big int b (top of stack)
stack output: 32 bit bigint containing a & b
note: See Opcode 22 for important notes on DWORD instructions.
stack input: small/big int a (next to top); small/big int b (top of stack)
stack output: 32 bit bigint containing a | b
note: See Opcode 22 for important notes on DWORD instructions.
stack input: small/big int a (top of stack)
stack output: 32 bit bigint containing ~a
note: See Opcode 22 for important notes on DWORD instructions.
stack input: small/big int a (next to top); small/big int count (top of stack)
stack output: 32 bit bigint containing a « count
note: See Opcode 0x22 for important notes on DWORD instructions.
stack input: small/big int a (next to top); small/big int count (top of stack)
stack output: 32 bit bigint containing a » count
note: See Opcode 22 for important notes on DWORD instructions.
stack input: small/big int a (next to top); small/big int b (top of stack)
stack output: smallint containing 1 if a < b; 0 otherwise
note: See Opcode 22 for important notes on DWORD instructions.
stack input: small/big int a (next to top); small/big int b (top of stack)
stack output: smallint containing 1 if a > b; 0 otherwise
note: See Opcode 22 for important notes on DWORD instructions.
stack input: small/big int a (next to top); small/big int b (top of stack)
stack output: smallint containing 1 if a == b; 0 otherwise
note: See Opcode 22 for important notes on DWORD instructions.
stack input: binary blob containing bytecode
effect: executes the code from the binary blob, continuing after the Execute instruction after the end of the block has been reached.
note: The current execution context is saved on the alternate stack. The blob must not leave something behind on the alternate stack or bad things will happen. The blob also shouldn't try to access the context information on the alternate stack, as it might contain arbitrary 32 bit data that can not be validly interpreted as OCM language objects. Of course the standard stack might be altered by the code in the subblock.
PostScript equivalent: "exec"
stack input: selector (smallint at second to top of stack), blob for the true case (next to top), blob for the false case (top of stack)
effect: if the selector is not zero, the true blob is executed (as in Opcode 31), otherwise the false blob is executed.
stack input: condition flag (smallint)
alternate stack input: blob to execute (not removed by this instruction)
effect: if the condition flag is not zero, the blob at the top of the alternate stack is executed. After execution, the while instruction is restarted, examining the value now at the top of the stack.
note: The blob reference on the alternate stack is not removed by this instruction, just the condition flag on the standard stack.
stack input: value of any type
alternate stack output: that value of any type
effect: The value is moved from the standard stack to the alternate stack
alternate stack input: value of any type
stack output: that value
effect: The value is moved from the alternate stack to the standard stack
alternate stack input: value of any type (not removed by this instruction)
stack output: that value
effect: The value is copied from the alternate stack to the standard stack
stack output: smallint containing 0
stack output: smallint containing 1
stack output: smallint containing 2
stack output: smallint containing 3
stack output: smallint containing 4
stack output: smallint containing 5
stack output: smallint containing 6
stack output: smallint containing 7
stack output: smallint containing 8
stack output: smallint containing 9
stack output: smallint containing -1
stack output: smallint containing -2
stack output: smallint containing -3
stack input: smallint a
stack ouput: smallint 1 if a > 0, smallint 0 else
stack input: smallint a
stack ouput: smallint 1 if a < 0, smallint 0 else
stack input: smallint a
stack ouput: smallint 1 if a == 0, smallint 0 else
note: in contrast to Opcode 21 "Test for Small Int Zero", this opcode has undefined behaviour of the operand is not a small integer.
effect: pushes a copy of the second-to-top (the third value if you call top-of-stack the first one) to the standard stack.
PostScript equivalent: "2 index"
effect: pushes a copy of the third-to-top (the fourth value if you call top-of-stack the first one) to the standard stack.
PostScript equivalent: "3 index"
effect: pushes a copy of the fourth-to-top (the 5th value if you call top-of-stack the first one) to the standard stack.
PostScript equivalent: "4 index"
effect: pushes a copy of the 5th-to-top (the 6th value if you call top-of-stack the first one) to the standard stack.
PostScript equivalent: "5 index"
effect: The value at Top-3 gets moved to top of stack, top of stack becomes top-1 and so on.
PostScript equivalent: "4 -1 roll"
effect: The value at Top-4 gets moved to top of stack, top of stack becomes top-1 and so on.
PostScript equivalent: "5 -1 roll"
effect: The value at Top-5 gets moved to top of stack, top of stack becomes top-1 and so on.
PostScript equivalent: "6 -1 roll"
stack input: smallint a
stack ouput: smallint (a+1)
stack input: smallint a
stack output: smallint (a-1)
stack input: value a that is not an array
stack output: a if a is numeric (smallint or bigint). If a is a blob, is is read as a two's complement signed big-endian number and returned in a bigint.
stack input: value a that is not an array
stack output: a if a is numeric (smallint or bigint). If a is a blob, is is read as an unsigned big-endian number and returned in a bigint.
note: Negative smallints or bigints are unchanged by this operation. Compare opcode 53 "Make operand unsigned".
stack input: value a that is not an array
stack output: If the input is a blob, it remains unchanged. Otherwise, it is returned as two's complement big-endian number in a blob.
stack input: numeric value a
stack output: A numeric value that results from interpreting the bits in a as unsigned number. I.e. smallints 0..32767 are returned unchanged. smallints -1..-32768 are converted to bigints 65536..32768 respsctively. Negative big-ints are sign-extended to whole bytes and the resulting value is treated as unsigned value
stack input: numeric value a
stack output: smallints stay unchanged. The low 16 bits of the absolute value are returned for bigints, so unless you know what you are doing, only use this instruction for the range 0..32767
stack input: n value of any type; smallint n (at top of stack)
stack output: (reference to) a newly created array containing the n elements from the stack below the count itself
stack input: an array of n elements
stack output: n values of any type (the array element); smallint n (count of elements; at top of stack)
stack input: an array with n elements
stack output: smallint n
stack input: entry (any type, second to top); an array (next to top); index (small or big int at top of stack)
stack output: A (reference to a) copy of the array with the new element entry inserted before the index'th element (moving all elements after the inserted one position to the end of the array)
stack input: an array (next to top); index (small or big int at top of stack)
stack output: A (reference to a) copy of the array with the element at position index removed (and all further elements moved one position to the front)
stack input: (reference to) an array (next to top); smallint k (top of stack)
stack output: The k'th element of the array.
stack input: value x of any type (second to top); (reference to) an array (next to top); smallint k (at top)
stack output: The k'th element of the array before replacing it.
effect: The k'th element of the array is replaced by x.
note: This operation does not copy the array, so the element changes globally.
stack input: n (smallint at top of stack)
stack output: A newly allocated array of n elements. All elements are initialized with smallint zero.
stack input: value to encode (any type)
stack output: A blob that contains the ASN.1 representation of the value.
note: Encoding uses universal tags as follows:
stack input: n blobs (should contain ASN.1 encoded data, first element pushed first, last element next-to-top); n (smallint at top of stack)
stack output: a blob containing an ASN.1 sequence of the n encoded objects.
note: The output is just 0x30 (SEQUENCE); 0x80 (unknown length); catenation of all blobs; 0x00; 0x00
stack input: a blob containing an ASN.1 encoded object.
stack ouput: the decoded object.
note: The following objects are recognized:
stack input: A smallint
stack output: A blob containing the little-endian (host byte order) representation of the smallint
Note: the blob that gets returned is non-refcounted and lives until explicit destruction (for example by opcode 08)
stack input: A blob that encloses a small integer, created by opcode 60
stack output: The enclosed small integer
stack input: Object of any type
stack ouput: Significant bit in that object. For smallints and bigints its the minimal number of bits to represent the absolute value of that number, for blobs the number of bytes multiplied by 8 and for arrays the sum of the number of significant bits of all entries.
immediate data: 1 byte (encrypted if OCM code encryption is enabled)
effect: pushes the unsigned 8 bit value of the immediate operand to the stack as small number
note: Is often used to refer to the dictionary index where the data is stored.
immediate data: 1 16-bit-word (encrypted if OCM code encryption is enabled)
effect: pushes the signed 16 bit value of the immediate operand to the stack as small number
immediate data: 1 big int (encrypted if OCM code encryption is enabled)
effect: pushes the big int onto stack (after deserialization)
note: If the OCM module uses the DWORD-based decryptor (it doesn't start with 03 01 in that case, we did not encounter such a module yet), it is important to know that the immediate arg is decrypted in three parts: (1) initial length byte, (2) extra length bytes, optional, (3) data bytes.
immediate data: 1 blob (encrypted if OCM code encryption is enabled)
effect: pushes the blob onto stack (after deserialization)
note: If the OCM module uses the DWORD-based decryptor (it doesn't start with 03 01 in that case, we did not encounter such a module yet), it is important to know that the immediate arg is decrypted in three parts: (1) initial length byte, (2) extra length bytes, optional, (3) data bytes.
stack input: common thread arg (any type); n blobs containing byte code; smallint n (at top of stack)
effect: Starts pseudo-parallel execution of all n blobs. Execution of the main code (that invoked this opcode) stops until all threads are finished or the threaded evaluation mode is cancelled. All threads have their own stack, each containing just the common arg. Also every thread has its private alternate stack.
stack input: smallint or bigint len
effect: Sets the time slice length for this subthread to len. That is the number of bytecode instructions executed for this thread until execution continues with another thread. The current timeslice is unmodified, it just affects the following slices.
effect: Aborts executing of all subthreads and immediately continues the main thread past its start threads opcode.
stack input: a value of any type
effect: Moves that value to the stack of the currently suspended main thread.
note: This opcode does only make sense in subthreads.
effect: Pops the top value from the stack of the suspended main thread
stack output: the value removed from the suspended main thread's stack.
note: This opcode does only make sense in subthreads.
effect: Copies the top value from the stack of the suspended main thread
stack output: the value at the top of the suspended main thread's stack.
note: This opcode does only make sense in subthreads.
stack output: seconds since midnight (tv_sec) as bigint (next to top); microseconds within current second (tv_usec) as bigint (top of stack)
stack input: 6 smallint or bigint values in the following order: seconds, minutes, hours, day (1-based), month (0-based), year (top of stack)
stack output: the corresponding unix timestamp
stack input: a small/big int containing a unix timestamp
stack output: 6 smallints in this order: seconds, minutes, hours, day (1-based), month (0-based), year (top of stack)
stack input: object of any type
stack output: The type tag of that object (see top of the page)
stack input: smallint n of values to skip
stack output: The number generated by the user RNG after skipping n values.
stack input: smallint n
effect: Enables autoskipping, i.e. advancing the user RNG by n for every opcode executed. n==0 disables it.
stack input: smallint n as 16 bit seed value.
effect: Turn on the Stream RNG that generates a key stream to decode all following instruction.
note: This will indicate that the following bytes are encrypted.
stack input: A reference to a block of bytes. The size must be a power of 2
effect: Replaces the standard table in the Stream PRNG by the one given as parameter
stack input: A blob containing a (possibly compressed) module of native code
stack ouput: smallint containing the module handle. (a one-byte number)
effect: The contents of that blob gets loaded into the virtual machine. The first 0x60 bytes of the blob are a header for the module that, amongst other things, contains the name of the module which is used to identify it. The module is decompressed, relocated and the exported names are put into the salwrap global exports table (unless the module is named "bc", in that case, exports are not yet loaded)
stack input: A module handle (as smallint)
stack ouput: 1 if the module had on-demand-exports, 0 otherwise (probably wrong description)
effect: Unloads the given native module. If it is not named "bc", the exports are removed from the global symbol table before unloading the module.
stack input: args for the native module (type(s) module specific); module handle (smallint at top)
stack output: result from the native module (type(s) module specific)
effect: Runs the native code associated with the given module handle. It removes the module handle from the stack before running that module. The module itself can do to the stack whatever it wants.
stack input: possible module args; a blob containing a non-compressed module (at top; like for Opcode 75)
stack output: possible module results
effect: Executes the native code from the blob given as parameter. The blob is relocated and exports are loaded (unless it is a "bc" named blob). As the blob is non-permanent, there better are no exports. The entrypoint is finally called (Like Opcode 77 does for a loaded module)
stack input: module handle (as smallint)
stack ouput: blob containing name of that module
stack input: blob containing of module name
stack ouput: smallint containing module handle
stack input: module name (blob next to top); module contents (blob at top)
stack output: smallint containing module handle
Creates an entry in the module list that references the address range of the blob.
stack input: module contents (blob); module name (blob at top, optional)
stack output: smallint containing module handle
Copies the blob contents into the interpreter as bytecode module. If top of stack is ⇐ 8 chars, it is treated as a name. Otherwise the standard name "bc" is used instead.
stack input: smallint containing module handle.
effect: the module handle must refer to a module not containing native code, but some byte code. This bytecode is executed (as a subroutine)
note: The ocm module is stored in the global dictionary. This instruction is used to "import" the device.sal library declarations to a programs environment. The ocm module data and the global dictionary reference to it are loaded using the salwrap function setDeviceSal.
stack input: smallint; blob (at top of stack)
effect: uses the smallint and the blob to seed the User PRNG.
effect: none
stack input: codenum (smallint)
effect: if codenum is zero, calls a hookable function in salwrap (export 0x68), otherwise does nothing
stack input: value of any type
effect: removes the value from the stack without adjusting its reference count.
note: is the same as 84
effect: none
effect: none
stack input: value of any type
effect: removes the value from the stack without adjusting its reference count.
note: is the same as 81
stack input: dictionary key (smallint for system dict, 32 bit blob for user dictionary)
effect: Gets the blob at the given dictionary index, as opcode 10 would do too, and then invokes it like opcode 31 would do, but this call opcode is the only possibility to do clean inter-module calls, at only it uses the context (Stream PRNG, OCM header valid for blob) stored in the dict alongside the value.
stack output: a 64 bit bigint containing the result of rdtsc
complicated calling instruction, probably including stack swapping. Might by something like try/catch
Contains long number arithmetic (arbitrary precision). small/bigint indicates parameters that are auto-promoted to bigints if given as smallint. The usual signed interpretation of smallints is used (opposed to dword arithmetic instructions)
A datatype "fuzzed integer" is used in some opcodes. Technically, its just a big int, but semantically, it has been transformed to not have the number directly in core. This transformation is dependent on the modulus in modular artihmetics, so "fuzzed integers" are specific to one modules
stack input: small/big int a; small/big int b
stack output: bigint a + b
stack input: small/big int a; small/big int b
stack output: bigint a - b
stack input: small/big int a; small/big int b
stack output: bigint a * b
stack input: small/big int a; small/big int b
stack output: bigint a / b
stack input: small/big int a; small/big int b
stack output: bigint a % b; a / b (careful, different order than DivMod for smallints and dwords. Somerthing wrong here?)
stack input: small/big int a; small/big int b
stack output: bigint a % b
stack input: small/big int a; small/big int b
stack output: smallint 1 if a>b, smallint 0 otherwise
stack input: small/big int a; small/big int b
stack output: smallint 1 if a<b, smallint 0 otherwise
stack input: small/big int a; small/big int b
stack output: smallint 1 if a==b, smallint 0 otherwise
stack input: small/big int a; small/big int b
stack output: bigint a & b
stack input: small/big int a; small/big int b
stack output: bigint a | b
stack input: small/big int a; small/big int b
stack output: bigint a ^ b
stack input: small/big int a; DWORD b
stack output: bigint a « b
note: DWORDs means that smallints are treated as unsigned 16 bit values in that case. See Instruction 0x22 for details.
stack input: small/big int a; DWORD b
stack output: bigint a » b
note: DWORDs means that smallints are treated as unsigned 16 bit values in that case. See Instruction 0x22 for details.
stack input: small/big int a; small/big int b; small/big int m
stack output: bigint a + b (mod m)
stack input: small/big int a; small/big int b; small/big int m
stack output: bigint a - b (mod m)
stack input: small/big int a; small/big int b; small/big int m
stack output: bigint a * b (mod m)
stack input: small/big int a; small/big int m
stack output: bigint -a (mod m)
stack input: small/big int a; small/big int m
stack output: bigint (1 / a) (mod m)
stack input: small/big int a; small/big int e; small/big int m
stack output: bigint (a to the e'th power (mod m) )
stack input: small/big int a1; small/big int e1; small/big int a2; small/big int e2; small/big int m
stack output: bigint (a1 to the e1'th power * a2 to the e2'th power (mod m) )
note: This is more efficient than using 0x9A twice and then multiply the result with 0x97, but yields the same result.
stack input: small/big int a; small/big int m
stack output bigint r such that r*r == a (mod m), or r == 0 if no value satisfies that equation.
stack input: small/big int m
stack ouptut: fuzzing context object for modulus m
stack input: small/big int a; fuzzing context
stack output: fuzzed value of a
stack input: fuzzed int f; fuzzing context
stack ouput: unfuzzed value of f
stack input: fuzzed int f; fuzzed int g; fuzzing context
stack ouput: fuzzed int f+g (mod m from fuzzing context)
stack input: fuzzed int f; fuzzed int g; fuzzing context
stack ouput: fuzzed int f-g (mod m from fuzzing context)
stack input: fuzzed int f; fuzzed int g; fuzzing context
stack ouput: fuzzed int f*g (mod m from fuzzing context)
stack input: fuzzed int f; fuzzing context
stack ouput: fuzzed int -f (mod m from fuzzing context)
stack input: fuzzed int f; fuzzing context
stack ouput: fuzzed int 1 / f (mod m from fuzzing context)
stack input: fuzzed int f; (unfuzzed) small/big int e; fuzzing context
stack ouput: fuzzed int f to the e'th power (mod m from fuzzing context)
stack input: fuzzed int f1; (unfuzzed) small/big int e1; fuzzed int a2; (unfuzzed) small/big int e2; fuzzing context
stack output: fuzzed int (f1 to the e1'th power * f2 to the e2'th power (mod m from fuzzing context) )
note: This is more efficient than using 0xA5 twice and then multiply the result with 0xA2, but yields the same result.
stack input: fuzzed int base; DWORD bits; DWORD parts; fuzzing context
stack output: power context for the given parameters.
note: A power context contains helper data to calculate different powers of the same base in with the same modulus. The power context gets optimized for exponents with approximately bits significant bits. The underlying algorithm is a divide-and-conquer algorithm. The number of parts the exponent will be divided to is given in parts, with an upper bound of 16.
stack input: small/big int exp; power context
stack output: unfuzzed(!) result of base (from the context) to the exp'th power.
stack input: small/big int exp1; power context ctx1; small/big int exp2; power context ctx2
stack output: bigint containing base1 to the exp1'th power multiplied by base2 to the exp2'th power (bases taken from contexts)
note: Does only work if the modulus in ctx1 und ctx2 is the same. Otherwise strange things will happen.
stack input: small/big int p; small/big int a; small/big int b
stack output: elliptic curve context describing the elliptic curve with the parameters a and b in the finite field of modular arithmetics to the base p, which should be prime.
stack input: small/big int x1; small/big int y1; small/big int x2; small/big int y2; elliptic curve context
stack output: bigint xr; bigint yr (such that (xr,yr) = (x1,y1)+(x2,y2))
note: This is about addition within an elliptic curve. This is not standard vector addition, it is just called addition, as it has a mathematical similar structure. If you want to understand what is really going on, read an introduction into elliptic curve arithmetics.
stack input: small/big int x; small/big int y; elliptic curve context
stack output: bigint x; bigint -y (mod p from context)
stack input: small/big int n; small/big int x; small/big int y; elliptic curve context
stack output: bigint xres; bigint yres, satisfying (xres,yres) = n*(x,y)
note: This is multiplication with a scalar within an elliptic curve. It is the same as adding up (x,y) n times by elliptic curve addition. It is not standard vector multiplication. Especially, the output does not resemble (n*x, n*y) in any way.
stack input: small/big int n1; small/big int x1; small/big int y1; small/big int n2; small/big int x2; small/big int y2; elliptic curve context
stack output: bigint xres; bigint yres, satisfying (xres,yres) = n1*(x1,y1) + n2*(x2,y2)
note: This is faster than doing two multiplications with 0xAD and adding with 0xAB, but yields the same result. Notes of that opcodes apply here, too.
stack input: small/big int x; small/big int y; DWORD bits; DWORD parts; elliptic curve context
stack output: elliptic curve multiplication context to calculate multiples of (x,y) on the given elliptic curve.
note: An elliptic curve multiplication context contains helper data to calculate different multiples of the same point in with the same modulus. The multiplication context gets optimized for scalar factors with approximately bits significant bits. The underlying algorithm is a divide-and-conquer algorithm. The number of parts the scalar factor will be divided to is given in parts, with an upper bound of 16.
stack input: small/big int n; elliptic curve multiplication context
stack output: bigint x; bigint y: satifying (x,y) = n*(x0,y0) (from the context)
stack input: small/big int n1; elliptic curve multiplication context ctx1; small/big int n2; elliptic curve multiplication context ctx2
stack output: bigint x; bigint y: satifying (x,y) = n1*(x1,y1)+n2*(x2,y2) (x1, y1, x2, y2 from the contexts)
note: Does only work if the modulus and the a and b parameters in ctx1 und ctx2 is the same. Otherwise strange things will happen.
stack input: DWORD n; DWORD m; small/big int a; small/big int b; (n > m; m > 0)
stack output: GF elliptic curve context describing the elliptic curve with the parameters a and b in the finite field defined by the irreducible polynomial
stack input: small/big int x1; small/big int y1; small/big int x2; small/big int y2; GF elliptic curve context
stack output: bigint xr; bigint yr (such that (xr,yr) = (x1,y1)+(x2,y2))
note: This is about addition within an elliptic curve. This is not standard vector addition, it is just called addition, as it has a mathematical similar structure. If you want to understand what is really going on, read an introduction into elliptic curve arithmetics.
stack input: small/big int x; small/big int y; GF elliptic curve context
stack output: bigint xr; bigint yr, such that (xr,yr) + (x,y) results in the point at infinity.
stack input: small/big int n; small/big int x; small/big int y; GF elliptic curve context
stack output: bigint xres; bigint yres, satisfying (xres,yres) = n*(x,y)
note: This is multiplication with a scalar within an elliptic curve. It is the same as adding up (x,y) n times by elliptic curve addition. It is not standard vector multiplication. Especially, the output does not resemble (n*x, n*y) in any way.
stack input: small/big int n1; small/big int x1; small/big int y1; small/big int n2; small/big int x2; small/big int y2; GF elliptic curve context
stack output: bigint xres; bigint yres, satisfying (xres,yres) = n1*(x1,y1) + n2*(x2,y2)
note: This is faster than doing two multiplications with 0xB5 and adding with 0xB3, but yields the same result. Notes of that opcodes apply here, too.
stack input: small/big int x; small/big int y; DWORD bits; DWORD parts; GF elliptic curve context
stack output: GF elliptic curve multiplication context to calculate multiples of (x,y) on the given elliptic curve.
note: An elliptic curve multiplication context contains helper data to calculate different multiples of the same point in with the same modulus. The multiplication context gets optimized for scalar factors with approximately bits significant bits. The underlying algorithm is a divide-and-conquer algorithm. The number of parts the scalar factor will be divided to is given in parts, with an upper bound of 16.
stack input: small/big int n; GF elliptic curve multiplication context
stack output: bigint x; bigint y: satifying (x,y) = n*(x0,y0) (from the context)
stack input: small/big int n1; GF elliptic curve multiplication context ctx1; small/big int n2; GF elliptic curve multiplication context ctx2
stack output: bigint x; bigint y: satifying (x,y) = n1*(x1,y1)+n2*(x2,y2) (x1, y1, x2, y2 from the contexts)
note: Does only work if the polynomial and the a and b parameters in ctx1 und ctx2 is the same. Otherwise strange things will happen.
stack input: DWORD bits; small/big int a; small/big int b;
stack output: GF elliptic curve context describing the elliptic curve with the parameters a and b in the finite field . An representation of that Galois field is chosen in which squaring is very efficient. More info about that representation to come later.
stack input: small/big int x1; small/big int y1; small/big int x2; small/big int y2; GFES elliptic curve context
stack output: bigint xr; bigint yr (such that (xr,yr) = (x1,y1)+(x2,y2))
note: This is about addition within an elliptic curve. This is not standard vector addition, it is just called addition, as it has a mathematical similar structure. If you want to understand what is really going on, read an introduction into elliptic curve arithmetics.
stack input: small/big int x; small/big int y; GFES elliptic curve context
stack output: bigint xr; bigint yr, such that (xr,yr) + (x,y) results in the point at infinity.
stack input: small/big int n; small/big int x; small/big int y; GFES elliptic curve context
stack output: bigint xres; bigint yres, satisfying (xres,yres) = n*(x,y)
note: This is multiplication with a scalar within an elliptic curve. It is the same as adding up (x,y) n times by elliptic curve addition. It is not standard vector multiplication. Especially, the output does not resemble (n*x, n*y) in any way.
stack input: small/big int n1; small/big int x1; small/big int y1; small/big int n2; small/big int x2; small/big int y2; GFES elliptic curve context
stack output: bigint xres; bigint yres, satisfying (xres,yres) = n1*(x1,y1) + n2*(x2,y2)
note: This is faster than doing two multiplications with 0xBD and adding with 0xBB, but yields the same result. Notes of that opcodes apply here, too.
stack input: small/big int x; small/big int y; DWORD bits; DWORD parts; GFES elliptic curve context
stack output: GFES elliptic curve multiplication context to calculate multiples of (x,y) on the given elliptic curve.
note: An elliptic curve multiplication context contains helper data to calculate different multiples of the same point in with the same modulus. The multiplication context gets optimized for scalar factors with approximately bits significant bits. The underlying algorithm is a divide-and-conquer algorithm. The number of parts the scalar factor will be divided to is given in parts, with an upper bound of 16.
stack input: small/big int n; GFES elliptic curve multiplication context
stack output: bigint x; bigint y: satifying (x,y) = n*(x0,y0) (from the context)
stack input: small/big int n1; GFES elliptic curve multiplication context ctx1; small/big int n2; GFES elliptic curve multiplication context ctx2
stack output: bigint x; bigint y: satifying (x,y) = n1*(x1,y1)+n2*(x2,y2) (x1, y1, x2, y2 from the contexts)
note: Does only work if the bit count and the a and b parameters in ctx1 und ctx2 is the same. Otherwise strange things will happen.
This list shows an overview over the opcodes C2..E0 (from compat, a module inside init.ocm). The numbers are relative opcode numbers, i.e. the opcode minus the base opcode of C2.
If the length is -1, the subblob is taken until the end of the blob.
stack input: string; index; length
stack output: substring
dd 1 (0xc3) dd offset BCX_01_Concat ; firstpart secondpart --> concatenated dd 2 (0xc4) dd offset BCX_02_CompareBlob ; blob1 blob2 --> cmpresult ; (lexicographically) dd 3 (0xc5) dd offset BCX_03_CompareN ; blob1 blob2 count --> cmpresult ; (lexicographically, but trimm blob1 and blob2 to count before) dd 4 (0xc6) dd offset BCX_04_IndexBlob ; blob idx --> char_at_idx dd 5 (0xc7) dd offset BCX_05_BlobLen ; blob --> length (16 bit OR bigint) dd 6 (0xc8) dd offset BCX_06_XorBlobs ; blob1 blob2 --> blob1^blob2 ; extends shorter blob with zero bytes dd 7 (0xc9) dd offset BCX_07_XorBlobsRepeating ; blob1 blob2 --> blob1 ^ blob2 ; the shorter blobs gets repeated to reach the length of the longer blob. ; If the length of the longer blob is not evenly divisible by the length of the shorter blob ; --> runtime error dd 8 (0xca) dd offset BCX_08_RepeatNUL ; count --> blob dd 9 (0xcb) dd offset BCX_09_ReplaceSubblob ; bigblob offset newdata --> patchedbigblob dd 0Ah (0xcc) dd offset BCX_0A_FloatAdd ; num1 num2 --> num1+num2 dd 0Bh (0xcd) dd offset BCX_0B_FloatSub ; num1 num2 --> num1-num2 dd 0Ch (0xce) dd offset BCX_0C_FloatMul ; num1 num2 --> num1*num2 dd 0Dh (0xcf) dd offset BCX_0D_FloatDiv ; num1 num2 --> num1/num2 dd 0Eh (0xd0) dd offset BCX_0E_FloatEq ; flt1 flt2 --> flt1==flt2 dd 0Fh (0xd1) dd offset BCX_0F_FloatLt ; flt1 flt2 --> flt2 < flt1 dd 10h (0xd2) dd offset BCX_10_FloatGt ; flt1 flt2 --> flt2 > flt1 dd 11h (0xd3) dd offset BCX_11_FloatToInt ; flt --> int ; Does not handle ints > 32 bits dd 12h (0xd4) dd offset BCX_12_atof ; ascii-blob --> float dd 13h (0xd5) dd offset BCX_13_nop dd 14h (0xd6) dd offset BCX_14_DESSetKey ; keyblob --> scheduleblob dd 15h (0xd7) dd offset BCX_15_DESEncrypt1 ; data keyschedule --> encrypted data dd 16h (0xd8) dd offset BCX_16_DESDecrypt1 ; data keyschedule --> decrypted data dd 17h (0xd9) dd offset BCX_17_DES_ECB_Encrypt ; data keyschedule --> encrypted data dd 18h (0xda) dd offset BCX_18_DES_ECB_Decrypt ; data keyschedule --> decrypted data dd 19h (0xdb) dd offset BCX_19_InitSHA1 ; --> SHA1 state object dd 1Ah (0xdc) dd offset BCX_1A_SHA1AddData ; data length state --> newstate Marcus: I have seen LENGTH being 0, which I can only suppose means that the whole blob DATA is added. dd 1Bh (0xdd) dd offset BCX_1B_SHA1Finish ; state --> hash dd 1Ch (0xde) dd offset BCX_1C_GetPRNGBytes ; count --> blob dd 1Dh (0xdf) dd offset DES_CBC_Encrypt ; data iv keyschedule magic -> data' ; magic must be DES_ECB_Encrypt or DES_ECB_Decrypt opcode as blob dd 1Eh (0xe0) dd offset DES_CBC_Decrypt ; data iv keyschedule magic -> data' ; magic must be DES_ECB_Encrypt or DES_ECB_Decrypt opcode as blob