1. Introduction

The code, written in x86_64 assembly using MASM (UASM) syntax, demonstrates a method for dynamically obfuscating API function imports to evade detection and make reverse engineering more difficult. This is especially useful for Windows malware that often employs dynamic import resolution techniques.

2. Key Objectives

  1. Evade memory analysis tools: Tools like Hasherezade’s pe-sieve can recover dynamic Import Address Tables (IATs) easily. Obfuscating these pointers makes it harder for such tools to do their job.

  2. Avoid hash database matching: Standard API hashes often end up in online databases, making it easier for analysts to identify malicious activity. The code avoids this by encoding hashes.

  3. Dynamic Encoding: While this example doesn’t go as far, the possibility exists for implementing dynamic encoding based on runtime-derived values, similar to what Blackmatter ransomware does.

3. Components

Constants and Macros

  • static_rnd: A macro that generates a pseudo-random number.
  • hash_basis, hash_prime: Constants used for FNV32 hash generation, XOR-masked with a random value to obfuscate them.

Data Structures

  • dynimp_entry: Structure for holding an obfuscated function pointer.
  • dynimp: Structure for the dynamic import table, containing an array of dynimp_entry and the table’s length.

Functions

General Functions

  • start: The main entry point for the program.
  • mask_ptr: Encodes or decodes a function address by XOR-masking it.

Dynamic Import Resolution

  • getexp: Resolves a DLL export by its hash.
  • getmod: Resolves a module base address by its hash.
  • gethash: Gets an FNV32 hash of a given buffer.

4. Improvements and Limitations

The approach might benefit from some form of assemble/compile time polymorphism and dynamic encoding/decoding strategies. This example serves as a basis upon which more advanced features can be implemented.

5. C++ Equivalents

The comments in the code also provide C++ macros (DYNIMP, D_TYPE, D_XOR_KEY, D_EXEC) to achieve similar functionality using C++’s decltype and constexpr.

6. The Code

; <main.asm>    -   Import Obfuscation Demonstration                            ;
;                           November 2022                                       ;
;                                                                               ;
; The method of dynamic import resolution in Windows malware is well known.     ;
; Perhaps something less well known, or at least less bothered with are         ;
; the features of memory analysis tools to recover dynamic IATs. After all,     ;
; they look very similar in memory to ordinary IATs.                            ;
;                                                                               ;
; For example, Hasherezade's pe-sieve can easily recover dynamic                ;
; IATs:                                                                         ;
; ---                                                                           ;
; # pe-sieve64.exe /imp 1 /pid xxx                                              ;
; 57038,7ffffd65f0b0,kernel32.GetModuleHandleA #639                             ;
; 57040,7ffffd65aec0,kernel32.GetProcAddress #697                               ;
; 57048,7ffffd6604f0,kernel32.LoadLibraryA #969                                 ;
; 57050,7ffffd6a0ef0,kernel32.CopyFileA #172                                    ;
; 57058,7ffffd65fee0,kernel32.LoadLibraryW #972                                 ;
; ---                                                                           ;
;                                                                               ;
; Evading this is trivial in most cases - all that is necessary is to           ;
; encode pointers at rest. Suddenly, automated tools like pe-sieve don't        ;
; work so well.                                                                 ;
;                                                                               ;
; Another issue is the presence of most ordinary API hashes in online           ;
; databases. We can avoid this by encoding our hashes at rest. Note that this   ;
; will increase the overall entropy of the respective code blocks.              ;
;                                                                               ;
; The developers of Blackmatter have taken this a step further and will         ;
; perform the encoding dynamically based on a runtime derived random value.     ;
; This example doesn't go so far, but it would be easily achievable             ;
; with the macros I've included here.                                           ;
;                                                                               ;
; This program demonstrates how to achieve this in x86_64 masm (uasm) assembly. ;
; Similar results may be achieved with C++'s `decltype` and `constexpr`         ;
; features and by  use of variadic macro args to decode and call function       ;
; pointers with minimal overhead. E.g.;                                         ;
; ---                                                                           ;
; #define DYNIMP( x )      decltype( &x ) x                                     ;
; #define D_TYPE( x )     (decltype( &x ))                                      ;
; #define D_XOR_KEY       RND_XOR(0xffff)   // constexpr function call          ;
; #define D_EXEC( e, ... ) \                                                    ;
;    ( ( decltype(e)((QWORD)(e) ^ D_XOR_KEY) )(__VA_ARGS__) )                   ;
; ---                                                                           ;
; A more fleshed-out C++ example is included after the assembly listing.        ;
;                                                                               ;
; Improvements to the overall approach might include:                           ;
; - Some form of assemble/compile time polymorphism, as facilitated by the      ;
;   macros included at the bottom of this file.                                 ;
; - Dynamic encoding/decoding, e.g. Blackmatter ransomware.                     ;
;                                                                               ;
;       ~ Enjoy!                                                                ;
;                                                                               ;
; -------------------------------------------------------------- yesh --------- ;
option win64:0x08, casemap:none                                                 ;
include win.inc                                                                 ;
; ----------------------------------------------------------------------------- ;
;                           Dynamic Import Macros                               ;
; ----------------------------------------------------------------------------- ;
;                                                                               ;
; Our defined hashes / hashing values. We mask embedded hashes, offsets, and    ;
; primes with our own value to avoid detections on these values.                ;
;                                                                               ;
; We use the `static_rnd` compile time macro, credit to mabdelouahab@masm32.com ;
;                                                                               ;
; Get an assembly-time random byte. This value will stay the same on successive ;
; expansions                                                                    ;
static_rnd macro __mask                                                         ;
    local m                                                                     ;
    m=(@SubStr(%@Time,7,2)) xor (@SubStr(%@Date,7,2))                           ;
    m=(m+@SubStr(%@Time,1,2)+@SubStr(%@Time,4,2))*(@SubStr(%@Time,7,2)+1)       ;
    ifnb <__mask>                                                               ;
        m = m and __mask                                                        ;
    endif                                                                       ;
    exitm % m                                                                   ;
endm                                                                            ;
                                                                                ;
random_mask     equ static_rnd(0xffffffff)                                      ;
hash_basis      equ            0xC59D1C81  xor random_mask                      ;
hash_prime      equ            0x01000193  xor random_mask                      ;
                                                                                ;
;                                                                               ;
; Our function hashes will all be double words of high entropy but at least     ;
; they won't be a dead giveaway in a hash database                              ;
;                                                                               ;
hash_ntdll      equ            0x25959F7F xor random_mask                       ;
hash_ntavm      equ            0x6973F2B4 xor random_mask                       ;
                                                                                ;
; Data Structures ------------------------- ;                                   ;
dynimp_entry struct                         ; dynamic import table entry        ;
    address qword ?                         ; our encoded function pointer      ;
dynimp_entry ends                           ;                                   ;
                                            ;                                   ;
dynimp struct                               ; our dynamic import table          ;
    entries qword ?                         ; pointer to entries                ;
    len     dword ?                         ; number of entries                 ;
dynimp ends                                 ;                                   ;
; ----------------------------------------- ;                                   ;
;                                                                               ;
; ----------------------------------------------------------------------------- ;
;                               Executable Code                                 ;
; ----------------------------------------------------------------------------- ;
text segment align(10h) 'code' read execute ;                                   ;
;                                           ;                                   ;
; Program entry point                       ;                                   ;
start proc                                  ;                                   ;
    local   d_ents[10]:dynimp_entry         ; encoded address on the stack      ;
    local   d_table:dynimp                  ; dynamic api table                 ;
    mov     rcx, hash_ntdll                 ; encoded hash of `ntdll.dll`       ;
    call    getmod                          ; get module base of ntdll.dll      ;
    mov     rcx, rax                        ; rcx is the module base            ;
    mov     rdx, hash_ntavm                 ; rdx encoded function hash         ;
    call    getexp                          ; resolve address by hash           ;
    mov     edx, [d_table.len]              ; store the result in the table     ;
    inc     [d_table.len]                   ;                                   ;
    mov     rcx, rax                        ; rcx is the unencoded address      ;
    call    mask_ptr                        ; encode the address                ;
    mov     [d_table.entries + rdx * 8], rax; store the encoded address         ;
    mov     rcx, rax                        ; rcx is the encoded address        ;
    call    mask_ptr                        ; decode the address                ;
    ret                                     ; in this way, you can gather as    ;
    ;                                       ; many function addresses as you    ;
    ;                                       ; like...                           ;
start endp                                  ;                                   ;
;                                           ;                                   ;
; Address masking operations                ;                                   ;
mask_ptr proc fastcall value:qword          ;                                   ;
    push    rbx                             ;                                   ;
    mov     ebx, random_mask                ; rbx is the mask                   ;
    mov     rax, rcx                        ; rax is the address                ;
    xor     ecx, ecx                        ; rcx is the counter                ;
_loop:                                      ;                                   ;
    cmp     ecx, 8                          ; eight is sufficient               ;
    je      _done                           ;                                   ;
    xor     rax, rbx                        ;                                   ;
    inc     ecx                             ;                                   ;
    shl     rbx, cl                         ; shift the mask over by the rcx    ;
    jmp     _loop                           ; next operation                    ;
_done:                                      ;                                   ;
    pop     rbx                             ;                                   ;
    ret                                     ; return the enc/dec address        ;
mask_ptr endp                               ;                                   ;
; ----------------------------------------- ;                                   ;
; Resolve a DLL export by hash              ;                                   ;
getexp proc fastcall base:qword, hash:qword ;                                   ;
    local   nth:qword                       ; nt headers                        ;
    local   dir:qword                       ; data directory                    ;
    local   exp:qword                       ; export directory                  ;
    local   aof:qword                       ; address of function               ;
    local   aon:qword                       ; address of name                   ;
    local   aoo:qword                       ; address of name ordinal           ;
    push    rbx                             ;                                   ;
    push    rsi                             ;                                   ;
    push    rdi                             ;                                   ;
    push    r10                             ;                                   ;
    xor     eax, eax                        ; eax is offset holder              ;
    mov     rsi, rcx                        ; rsi is the module base            ;
    mov     r10, rsi                        ; r10 is a backup of the mod base   ;
    mov     rdi, rdx                        ; rdi is the target hash            ;
    mov     eax, [rsi].dos_hdr.e_lfanew     ; eax is nth offset                 ;
    add     rsi, rax                        ; rsi is the nt header va           ;
    lea     rsi, [rsi].nt_hdr.opt.d_dir     ; rsi is the rva of the data dir    ;
    mov     dir, rsi                        ; store the address                 ;
    mov     ebx, [rsi].img_data_dir.va      ; rbx is the va of the export dir   ;
    add     rbx, r10                        ; rbx is the va of export dir       ;
    mov     exp, rbx                        ; store the va of the export dir    ;
    mov     eax, [rbx].exp_dir.aon          ; resolve AddressOfNames            ;
    add     rax, r10                        ;                                   ;
    mov     aon, rax                        ;                                   ;
    mov     eax, [rbx].exp_dir.aof          ; resolve AddressOfFunctions        ;
    add     rax, r10                        ;                                   ;
    mov     aof, rax                        ;                                   ;
    mov     eax, [rbx].exp_dir.aoo          ; resolve ordinals                  ;
    mov     rbx, [exp]                      ;                                   ;
    xor     esi, esi                        ; esi is the counter                ;
_loop:                                      ; iterate over the exports          ;
    cmp     esi, [rbx].exp_dir.n_names      ;                                   ;
    jge     _done                           ;                                   ;
    mov     rcx, [aon]                      ; aon                               ;
    mov     ecx, [rcx+rsi*4]                ; next offset                       ;
    add     rcx, r10                        ; next va                           ;
    mov     rbx, rcx                        ; rcd is va of string               ;
    call    strlen                          ; calculate its length              ;
    mov     rcx, rbx                        ;                                   ;
    mov     edx, eax                        ;                                   ;
    call    gethash                         ; calculate its hash                ;
    inc     esi                             ; next ordinal                      ;
    cmp     rax, rdi                        ; hashes match?                     ;
    je      _match                          ; resolve the function address      ;
    jmp     _loop                           ; next function                     ;
_match:                                     ;                                   ;
    xor     eax, eax                        ; resolve the function address      ;
    mov     rcx, aoo                        ; get current ordinal               ;
    movzx   eax, word ptr [rcx+rsi*2]       ;                                   ;
    mov     rcx, aof                        ;                                   ;
    mov     eax, [rcx+rsi*4]                ; get current function rva          ;
    add     rax, r10                        ; get current function va           ;
    jmp     _done                           ; all done here                     ;
_done:                                      ;                                   ;
    pop     r10                             ;                                   ;
    pop     rdi                             ;                                   ;
    pop     rsi                             ;                                   ;
    pop     rbx                             ;                                   ;
    retn                                    ;                                   ;
getexp endp                                 ;                                   ;
; ----------------------------------------- ;                                   ;
; Resolve a module base address by hash     ;                                   ;
getmod proc fastcall hash:qword             ;                                   ;
    local   modname[256*2]:byte             ; stack space for module name buf   ;
    local   first:qword                     ; first module entry                ;
    local   curr:qword                      ; current module entry              ;
    push    rbx                             ;                                   ;
    push    rsi                             ;                                   ;
    push    rdi                             ;                                   ;
    mov     rdi, rcx                        ;                                   ;
    mov     rsi, [gs:0x60]                  ; get PEB                           ;
    mov     rsi, [rsi].peb.ldr              ; rsi -> PEB_LDR_DATA entry         ;
    mov     rsi, [rsi].pld.moml.fw-10h      ; rsi points to LDR_MODULE link     ;
    mov     first, rsi                      ;                                   ;
    mov     rbx, [rsi].ldte.moml.fw-10h     ; each LDR_MODULE links to others   ;
    mov     curr, rbx                       ; save current module               ;
_loop:                                      ; loop over modules                 ;
    lea     rcx, modname                    ;                                   ;
    xor     edx, edx                        ;                                   ;
    mov     r8d, 256                        ;                                   ;
    call    memset                          ; clear the name buffer             ;
    lea     rcx, modname                    ;                                   ;
    lea     rdx, [rbx].ldte.basename.buffer ;                                   ;
    call    wstrcpy                         ; copy the UNICODE_STRING buffer    ;
    lea     rcx, modname                    ; convert it to lowercase           ;
    call    wstrtolower                     ; returns the length as well        ;
    lea     rcx, modname                    ;  (in bytes)                       ;
    mov     rdx, rax                        ;                                   ;
    imul    rdx, 2                          ; since unicode_strings are wide    ;
    call    gethash                         ; get module name hash              ;
    cmp     rax, rdi                        ; match target?                     ;
    je      _match                          ;                                   ;
    mov     rbx, curr                       ; while current != first            ;
    cmp     rbx, first                      ;                                   ;
    je      _done                           ;                                   ;
    jmp     _loop                           ;                                   ;
_match:                                     ;                                   ;
    mov     rax, [rbx].ldte.dllbase         ; get dll base address              ;
_done:                                      ;                                   ;
    pop     rdi                             ;                                   ;
    pop     rsi                             ;                                   ;
    pop     rbx                             ;                                   ;
    retn                                    ;                                   ;
getmod endp                                 ;                                   ;
; ----------------------------------------- ;                                   ;
; Get a FNV32 hash of a buffer              ;                                   ;
gethash proc fastcall src:qword, len:dword  ;                                   ;
    push    rbx                             ;                                   ;
    push    rsi                             ;                                   ;
    push    rdi                             ;                                   ;
    xor     rbx, rbx                        ;                                   ;
    mov     rsi, rcx                        ; rsi is the source buffer          ;
    xor     ecx, ecx                        ; ecx is the counter                ;
    mov     eax, hash_basis                 ; eax is the hash basis             ;
    xor     eax, random_mask                ; decode the basis                  ;
_loop:                                      ; loop over src bytes               ;
    cmp     ecx, edx                        ;                                   ;
    je      _done                           ;                                   ;
    xor     ebx, ebx                        ;                                   ;
    mov     bl, [rsi+rcx]                   ; bl is the current byte            ;
    xor     eax, ebx                        ; hash = hash ^ src[i]              ;
    mov     edi, hash_prime                 ;                                   ;
    xor     edi, random_mask                ; decode the prime                  ;
    imul    eax, edi                        ; hash = hash * prime               ;
    inc     ecx                             ; next byte                         ;
    jmp     _loop                           ;                                   ;
_done:                                      ;                                   ;
    xor     eax, random_mask                ; mask the hash                     ;
    pop     rdi                             ;                                   ;
    pop     rsi                             ;                                   ;
    pop     rbx                             ;                                   ;
    retn                                    ;                                   ;
gethash endp                                ;                                   ;
; ----------------------------------------- ;                                   ;
; Generic memset                            ;                                   ;
memset proc fastcall dst:qword, val:byte, len:dword                             ;
    push    rbx                             ;                                   ;
    xor     eax, eax                        ;                                   ;
_loop:                                      ;                                   ;
    cmp     r8d, eax                        ;                                   ;
    jge     _done                           ;                                   ;
    mov     [rcx+rax], dl                   ;                                   ;
    inc     eax                             ;                                   ;
    jmp     _loop                           ;                                   ;
_done:                                      ;                                   ;
    pop     rbx                             ;                                   ;
    retn                                    ;                                   ;
memset endp                                 ;                                   ;
; ----------------------------------------- ;                                   ;
; Copy a wide string                        ;                                   ;
wstrcpy proc fastcall dst:qword, src:qword  ;                                   ;
    push    rbx                             ;                                   ;
    xor     eax, eax                        ;                                   ;
_loop:                                      ;                                   ;
    mov     bx, [rdx+rax*2]                 ;                                   ;
    test    bx, bx                          ;                                   ;
    jz      _done                           ;                                   ;
    mov     [rcx+rax*2], bx                 ;                                   ;
    inc     eax                             ;                                   ;
    jmp     _loop                           ;                                   ;
_done:                                      ;                                   ;
    pop     rbx                             ;                                   ;
    retn                                    ;                                   ;
wstrcpy endp                                ;                                   ;
; ----------------------------------------- ;                                   ;
; Convert a wide string to lowercase        ;                                   ;
wstrtolower proc fastcall src:qword         ;                                   ;
    push    rbx                             ;                                   ;
    xor     eax, eax                        ;                                   ;
_loop:                                      ;                                   ;
    mov     bx, [rcx+rax*2]                 ;                                   ;
    test    bx, bx                          ;                                   ;
    jz      _done                           ;                                   ;
    cmp     bx, 'A'                         ;                                   ;
    jl      _next                           ;                                   ;
    cmp     bx, 'Z'                         ;                                   ;
    jg      _next                           ;                                   ;
    add     bx, 0x20                        ;                                   ;
    mov     [rcx+rax*2], bx                 ;                                   ;
_next:                                      ;                                   ;
    add     eax, 2                          ;                                   ;
    jmp     _loop                           ;                                   ;
_done:                                      ;                                   ;
    imul    eax, 2                          ;                                   ;
    inc     eax                             ;                                   ;
    pop     rbx                             ;                                   ;
    retn                                    ;                                   ;
wstrtolower endp                            ;                                   ;
; ----------------------------------------- ;                                   ;
; Calculate length of a string              ;                                   ;
strlen proc fastcall src:qword              ;                                   ;
    push    rbx                             ;                                   ;
    xor     eax, eax                        ;                                   ;
_loop:                                      ;                                   ;
    mov     bl, [rcx+rax]                   ;                                   ;
    test    bl, bl                          ;                                   ;
    jz      _done                           ;                                   ;
    inc     eax                             ;                                   ;
    jmp     _loop                           ;                                   ;
_done:                                      ;                                   ;
    pop     rbx                             ;                                   ;
    retn                                    ;                                   ;
strlen endp                                 ;                                   ;
; ----------------------------------------- ;                                   ;
text ends                                   ;                                   ;
end                                         ;                                   ;
;                                                                               ;
; ----------------------------------------------------------------------------- ;
;                  C++ - Obfuscated Dynamic Imports (Example)                   ;
; -----------------------------------c----------------------------------------- ;
; #define DYNIMP( x )      decltype( &x ) x
; #define D_TYPE( x )     (decltype( &x ))
; #define D_XOR_KEY       RND_XOR(0xffff)   // constexpr function call
;
; #define D_EXEC( e, ... ) \
;    ( ( decltype(e)((QWORD)(e) ^ D_XOR_KEY) )(__VA_ARGS__) )
;
; typedef struct _DYNIMP_NTDLL
; {
;       DYNIMP(NtAllocateVirtualMemory);
;       ...
; } DYNIMP_NTDLL, * PDYNIMP_NTDLL;
; #define DYNIMP_NTDLL_LEN ( sizeof(DYNIMP_NTDLL) / sizeof(QWORD) )
;
; typedef struct _DYNIMP 
; {
;       union { 
;           DYNIMP_NTDLL  Apis
;           PVOID       Entries[DYNIMP_NTDLL_LEN];
;       } Ntdll;
; } DYNIMP, * PDYNIMP;
;
; VOID ResolveDapi(PDYNIMP Api)
; {
;       constexpr NtdllHash = ...;
;       constexpr FnvNtAllocateVirtualMemory = D_TYPE(NtAllocateVirtualMemory) 
;           (FNV_NTALLOCATEVIRTUALMEMORY ^ D_XOR_KEY);
;       for (... resolve the module and pointers to functions)
;       {
;           Api->Ntdll.Entries[idx] = (PVOID)((QWORD)Ptr ^ D_XOR_KEY);
;       };       
; }
;
; // now, just call the pointers like the normal function
; 
; D_EXEC(Api->Ntdll.Entries[idx], args...);
;
; ----------------------------------------------------------------------------- ;
;                       Future Work - Obfuscation Macros                        ;
; ----------------------------------------------------------------------------- ;
; ; x86 registers                                                               ;
; _eax            equ 0                                                         ;
; _ecx            equ 1                                                         ;
; _edx            equ 2                                                         ;
; _ebx            equ 3                                                         ;
; _esp            equ 4                                                         ;
; _ebp            equ 5                                                         ;
; _esi            equ 6                                                         ;
; _edi            equ 7                                                         ;
;                                                                               ;
; ; x64 extended registers                                                      ;
; _r8             equ 0                                                         ;
; _r9             equ 1                                                         ;
; _r10            equ 2                                                         ;
; _r11            equ 3                                                         ;
; _r12            equ 4                                                         ;
; _r13            equ 5                                                         ;
; _r14            equ 6                                                         ;
; _r15            equ 7                                                         ;
;                                                                               ;
; ; MODRM                                                                       ;
; S_mod_ri        equ 00000000b             ; 0x00 MODRM register indirect      ;
; s_mod_ra        equ 11000000b             ; 0xC0 MODRM register addressing    ;
; s_mod_1sbdsp    equ 01000000b             ; 0x40 MODRM one byte signed disp   ;
; s_mod_4sbdsp    equ 10000000b             ; 0x80 MODRM four byte signed disp  ;
;                                                                               ;
; ; Prefixes                                                                    ;
; s_pfx_o16       equ 01100110b             ; 0x66 16/32 bit operand override   ;
; s_pfx_a16       equ 01100111b             ; 0x67 16/32 bit address override   ;
; s_pfx_o8        equ 10001000b             ; 0x88 8 bit operand override       ;
; s_rex           equ 01000000b             ; 0x40 REX (access new 8 bit regы)  ;
; s_rex_8         equ 01000001b             ; 0x41 REX reg imm mode             ;
; s_pfx_rexw      equ 01001000b             ; 0x48 REX.W (64 bit operand)       ;
; s_pfx_rexwb     equ 01001001b             ; 0x49 REX.WB                       ;
;                                                                               ;
; ; Opcodes                                                                     ;
; s_op_pushr      equ 0x50                  ; push rax. OR with reg encodings.  ;
; s_op_popr       equ 0x58                  ; pop rax. OR with reg encodings    ;
;                                                                               ;
; ; Conditional Jumps                                                           ;
; s_jb_rel8       equ 0x72                  ; jb/jnae/jc                        ;
; s_jae_rel8      equ 0x73                  ; jnb/jae/jnc                       ;
; s_je_rel8       equ 0x74                  ; jz/ne                             ;
; s_jne_rel8      equ 0x75                  ; jnz/jne                           ;
; s_jna_rel8      equ 0x76                  ; jbe/jna                           ;
; s_ja_rel8       equ 0x77                  ; jnbe/ja                           ;
; s_jnge_rel8     equ 0x7c                  ; jl/jnge                           ;
; s_jd_rel8       equ 0x7d                  ; jnl/jge                           ;
; s_jle_rel8      equ 0x7e                  ; jle/jng                           ;
; s_jf_rel8       equ 0x7f                  ; jnle/jg                           ;
;                                                                               ;
; ; Relative Jumps                                                              ;
; s_jmp_rel8      equ 0xeb                  ; jmp rel8                          ;
;                                                                               ;
; ; Basic Register Operations                                                   ;
; s_add_rall      equ 0x03                  ; add r/16/32/64                    ;
; s_or_rall       equ 0x0b                  ; or  r/16/32/64                    ;
; s_and_rall      equ 0x23                  ; and r/16/32/64                    ;
; s_sub_rall      equ 0x2b                  ; sub r/16/32/64                    ;
; s_xor_rall      equ 0x33                  ; xor r/16/32/64                    ;
; s_cmp_rall      equ 0x3b                  ; cmp r/16/32/64                    ;
; s_mov_rall      equ 0x8b                  ; mov r/16/32/64                    ;
; s_mov_r8_imm8   equ 0xb0                  ; mov r8 imm8                       ;
; s_mov_r8        equ 0x8a                  ; mov r8 r/m8                       ;
; s_mov_r8_imm    equ 0xb8                  ; mov r8 imm8                       ;
; s_mov_r64_imm64 equ 0xC7                  ; mov r64 imm64                     ;
; s_shl           equ 0xe0c1                ; shl                               ;
;                                                                               ;
; ; Get an assembly-time random value of a specific size. Maximum 32 bits.      ;
; ; This value will change on successive expansions                             ;
; rnd macro __mask                                                              ;
;     local m                                                                   ;
;     m=(@SubStr(%@Time,7,2)+@Line)*(@SubStr(%@Date,1,2)                        ;
;     m=m+@SubStr(%@Date,4,2)*100+@SubStr(%@Date,7,2))* (-1001)                 ;
;     m=(m+@SubStr(%@Time,1,2)+@SubStr(%@Time,4,2))*(@SubStr(%@Time,7,2)+1)     ;
;     ifnb <__mask>                                                             ;
;         m = m and __mask                                                      ;
;     endif                                                                     ;
;     exitm % m                                                                 ;
; endm                                                                          ;
;                                                                               ;
; ; Emit some junk bytes that look vaguely like real code                       ;
; emit_junk macro                                                               ;
;     local v1, v2, r0, r1, r2, r3, r4, b                                       ;
;     count = 0                                                                 ;
;     ...                                                                       ;
; endm                                                                          ;
;                                                                               ;
; ; Emit a junk operation of the given type (example/incomplete)                ;
; emit_junk_op macro v1, opc, r1, r2                                            ;
;     if v1 eq 0                                                                ;
;         db s_pfx_rexw                                                         ;
;         db opc                                                                ;
;         b = r2                                                                ;
;         b = (b shl 3) or (r1)                                                 ;
;         db b                                                                  ;
;     elseif v1 eq 1                                                            ;
;         db opc                                                                ;
;         b = r1                                                                ;
;         b = (b shl 3) or (r2)                                                 ;
;         db b                                                                  ;
;     elseif v1 eq 2                                                            ;
;         db opc                                                                ;
;         b = r2                                                                ;
;         b = (b shl 3) or (r1)                                                 ;
;         db b                                                                  ;
;     endif                                                                     ;
; endm                                                                          ;
;                                                                               ;
; ; Emit a junk conditional comparison                                          ;
; emit_junk_jcnd macro v, dist                                                  ;
;     if v eq 0                                                                 ;
;         db s_ja_rel8                                                          ;
;         db dist                                                               ;
;     elseif v eq 1                                                             ;
;         db s_jle_rel8                                                         ;
;         db dist                                                               ;
;     elseif v eq 2                                                             ;
;         db s_jne_rel8                                                         ;
;         db dist                                                               ;
;     endif                                                                     ;
; endm                                                                          ;
;                                                                               ;
; ; Emit the bytes of a string                                                  ;
; emit_bytes macro string                                                       ;
;     for value, <string>                                                       ;
;         db value                                                              ;
;     endm                                                                      ;
; endm                                                                          ;
;                                                                               ;
; ; Emit a relative 8 jump                                                      ;
; emit_jmp_rel8 macro dist                                                      ;
;     db s_jmp_rel8                                                             ;
;     db dist                                                                   ;
; endm                                                                          ;
; ------------------------------------------------------------------------------;