ocmbytecode

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:

- It has 256 global variables that are addressed using a single byte.
- Entry 252 (0xFC): blob with 8 bytes created from gettimeofday
- Entry 253 (0xFD): blob with 1024 bytes: function pointers for byte codes (patched by interpreter extensions)
- Entry 254 (0xFE): shortint, hiword/loword are major/minor OCM machine version (3.3 for OpenMG 3.4)
- Entry 255 (0xFF): shortint, in OpenMG 3.4: constant 1

- It has a hash table for further global variables that is indexed by a 32 bit number.
- It has a second, alternat stack. This stack is used to save context information for call/return, but also provides operands to certain instructions or can be used as general purpose storage. As it is used for saving the return context, add the end of a code block you must not have anything left on the alternate stack that was not there at the beginning.
- The interpreter can do simple multithreading
- The interpreter has a Stream RNG to decode the instructions (although instruction decoding is initially off)
- The interpreter has a User RNG with a 63 byte state (totally seperate from the stream RNG!)
- It provides a "backdoor" in that you can load native x86 code and execute it.

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 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 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:

- smallints and bigints are encoded as INTEGER (ASN.1 tag 0x02)
- blobs are encoded as OCTET STRING (ASN.1 tag 0x04)
- arrays are encoded as SEQUENCE (ASN.1 tag 0x30), the sequence is always encoded as unknown-length and double-zero-terminated.

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:

- Tag 0x01 (BOOLEAN) → decoded as smallint 0 or 1
- Tag 0x02 (INTEGER) → Values 0..32767 as smallint; other values as bigint
- Tag 0x17, Tag 0x18 (UTCTime, GeneralizedTime) → bigint containing a unix timestamp (seconds since 1.1.1970, 00:00 GMT)
- all other univesal tags with composed object bit clear (0x03-0x16;0x19-0x1F) → decoded as blob
- Tag 0x30, Tag 0x31 (SEQUENCE, SET) → decoded as array
- all context-specific tags (0x80..0xBF) → decoded as blob

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 <m>GF_{2^n}</m> defined by the irreducible polynomial <m>t^n+t^m+1</m>

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 <m>GF_{2^bits}</m>. 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.

dd 0 (0xc2) ; DATA XREF: initproc:loc_4166r dd offset BCX_00_Subblob ; DATA XREF: initproc+42r ; string index length -> substring Marcus: If length == -1, take until end of blob. 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

ocmbytecode.txt · Last modified: 2011/01/18 13:01 by megadiscman