R0 CREW

SEH based VM Engine by Yattering

Простейший движок ВМ для виртуализации x86 кода, который позволяет заменять некоторые машинные инструкции собсвенными подобными инструкциями, поэтому защищенный код не может выполняться без внешнего движка ВМ.

Пример использования движка в FASM`е(исходник)+дизасм листинг
; SEH based VM Engine by Yattering, 2016
; e-mail: yattering (at) sigaint (d0t) org
; jabber: yattering (at) xmpp (d0t) jp

format MS COFF

include 'sehvm.inc'

extrn '__imp__MessageBoxA@16' as MessageBoxA:dword

extrn '_exception_handler' as _exception_handler

public _main

section '.text' code readable executable
_main:
        ; Register exception handler
        lea     eax, [_exception_handler]
        xor     ecx, ecx
        push    eax
        push    DWord [fs:ecx]
        mov     DWord [fs:ecx], esp
        ; Run unprotected code
        call _unprotected_code_function
        ; Run protecred code
        SEHVM_CALL_REL_C32 _protected_code_function
        ; Unregister exception handler
        xor     ecx, ecx
        pop     DWord [fs:ecx]
        pop     eax
        ret

_protected_code_function:
    SEHVM_PUSH_C32 0
        SEHVM_PUSH_C32 _caption
        SEHVM_PUSH_C32 _messageP
        SEHVM_PUSH_C32 0
        call DWord [MessageBoxA]
        SEHVM_RET_C8 0

_unprotected_code_function:
    push 0
        push _caption
        push _messageUP
        push 0
        call DWord [MessageBoxA]
    ret

section '.data' data readable writeable

 _caption   db 'Win32 assembly',0
 _messageUP db 'MessageBox from unprotected code',0
 _messageP  db 'MessageBox from protected code',0

Листинг дизассемлера этого примера в OllyDbg

00401000 >/$ 8D05 60104000  LEA EAX,DWORD PTR DS:[401060]
00401006  |. 31C9           XOR ECX,ECX
00401008  |. 50             PUSH EAX
00401009  |. 64:FF31        PUSH DWORD PTR FS:[ECX]
0040100C  |. 64:8921        MOV DWORD PTR FS:[ECX],ESP
0040100F  |. E8 2E000000    CALL demo_fas.00401042
00401014  |. CC             INT3
00401015  |. 05 0D000000    ADD EAX,0D
0040101A  |. 31C9           XOR ECX,ECX
0040101C  |. 64:8F01        POP DWORD PTR FS:[ECX]
0040101F  |. 58             POP EAX
00401020  \. C3             RETN
00401021     CC             INT3
00401022     01             DB 01
00401023     00             DB 00
00401024     00             DB 00
00401025     00             DB 00
00401026     00             DB 00
00401027     CC             INT3
00401028     01             DB 01
00401029     00304000       DD demo_fas.00403000                     ;  ASCII "Win32 assembly"
0040102D     CC             INT3
0040102E     01             DB 01
0040102F     30304000       DD demo_fas.00403030                     ;  ASCII "MessageBox from protected code"
00401033     CC             INT3
00401034     01             DB 01
00401035     00             DB 00
00401036     00             DB 00
00401037     00             DB 00
00401038     00             DB 00
00401039     FF             DB FF
0040103A     15             DB 15
0040103B     80304000       DD <&USER32.MessageBoxA>
0040103F     CC             INT3
00401040     00             DB 00
00401041     00             DB 00
00401042  /$ 6A 00          PUSH 0                                   ; /Style = MB_OK|MB_APPLMODAL
00401044  |. 68 00304000    PUSH demo_fas.00403000                   ; |Title = "Win32 assembly"
00401049  |. 68 0F304000    PUSH demo_fas.0040300F                   ; |Text = "MessageBox from unprotected code"
0040104E  |. 6A 00          PUSH 0                                   ; |hOwner = NULL
00401050  |. FF15 80304000  CALL DWORD PTR DS:[<&USER32.MessageBoxA>>; \MessageBoxA
00401056  \. C3             RETN

Список команд ВМ:

  • RET_C8
  • PUSH_R32
  • PUSH_C32
  • POP_R32
  • JMP_REL_C32
  • CALL_REL_C32
  • NOP

Все команды ВМ начинаються с INT3 инструкции х86 процессора, благодаря чему управление передаеться на SEH обработчик, который и являеться движком ВМ. Движок написан на чистых сях.

Исходник ВМ на сях
/*SEH based VM Engine by Yattering, 2016
 *e-mail: yattering (at) sigaint (d0t) org
 *jabber: yattering (at) xmpp (d0t) jp
 */
#define WIN32_LEAN_AND_MEAN
#ifdef SELF_TEST
#define __DEBUG__
#endif

#include <windows.h>
#include <stdint.h>
#include "debug_log.h"
#include "sehvm.h"

#ifdef SELF_TEST
#pragma comment(linker, "/ENTRY:main")
#pragma comment(linker, "/SUBSYSTEM:WINDOWS")
#endif

EXCEPTION_DISPOSITION __cdecl exception_handler(struct _EXCEPTION_RECORD *except, void * establisher_frame, struct _CONTEXT *context, void *dispatcher_context);

__DEBUG_GLOBAL_VARIABLES()

EXCEPTION_DISPOSITION __cdecl exception_handler(struct _EXCEPTION_RECORD *except, void * establisher_frame, struct _CONTEXT *context, void *dispatcher_context) {
  uint8_t reg;
  uint8_t *p_nnm = except->ExceptionAddress;
  __DEBUG_PUTS("\r\nEXCEPTION HANDLER\r\n");
  if (*p_nnm == SEHVM_INT3_OPCODE) {
    p_nnm++;
    switch (*p_nnm) {
      case SEHVM_TYPE_RET_C8:
        __DEBUG_PRINTF("] NANOMITE TYPE <RET_C8>\r\n]] RET 0x%X\r\n", p_nnm[1]);
        uint32_t ret_addr = *(uint32_t *)(context->Esp);
        context->Esp += 4 + p_nnm[1];
        context->Eip = ret_addr;
        __DEBUG_PAUSE();
        break;
      case SEHVM_TYPE_PUSH_C32: {
        __DEBUG_PRINTF("] NANOMITE TYPE <PUSH_C32>\r\n]] PUSH 0x%X\r\n", (*(uint32_t *)&p_nnm[1]));
        context->Esp-=4;
        *(uint32_t *) context->Esp = *(uint32_t *) &p_nnm[1];
        context->Eip += 0x6;
        __DEBUG_PAUSE();
        break;
      }
      case SEHVM_TYPE_PUSH_R32: {
        reg = p_nnm[1] & 0x7;
#ifdef __DEBUG__
        char *reg_name = \
         ((reg == SEHVM_ARG_EAX)?("EAX"):((reg == SEHVM_ARG_EBX)?("EBX"):((reg == SEHVM_ARG_ECX)?("ECX"):\
         ((reg == SEHVM_ARG_EDX)?("EDX"):((reg == SEHVM_ARG_EDI)?("EDI"):((reg == SEHVM_ARG_ESI)?("ESI"):\
         ((reg == SEHVM_ARG_EBP)?("EBP"):"INVALID_REGISTER")))))));
        __DEBUG_PRINTF("] NANOMITE TYPE <PUSH_R32>\r\n]] PUSH %s\r\n", reg_name);
        uint32_t reg_val =
         ((reg == SEHVM_ARG_EAX)?(context->Eax):\
         ((reg == SEHVM_ARG_EBX)?(context->Ebx):\
         ((reg == SEHVM_ARG_ECX)?(context->Ecx):\
         ((reg == SEHVM_ARG_EDX)?(context->Edx):\
         ((reg == SEHVM_ARG_EDI)?(context->Edi):\
         ((reg == SEHVM_ARG_ESI)?(context->Esi):\
         ((reg == SEHVM_ARG_EBP)?(context->Ebp):\
         0xDEADBEEF)))))));
        __DEBUG_PRINTF("]]] %s = 0x%X\r\n", reg_name, reg_val);
#endif
        context->Esp-=4;
        *(uint32_t *)(context->Esp) = \
         ((reg == SEHVM_ARG_EAX)?(context->Eax):\
         ((reg == SEHVM_ARG_EBX)?(context->Ebx):\
         ((reg == SEHVM_ARG_ECX)?(context->Ecx):\
         ((reg == SEHVM_ARG_EDX)?(context->Edx):\
         ((reg == SEHVM_ARG_EDI)?(context->Edi):\
         ((reg == SEHVM_ARG_ESI)?(context->Esi):\
         ((reg == SEHVM_ARG_EBP)?(context->Ebp):\
           0xDEADBEEF)))))));
        context->Eip += 0x3;
        __DEBUG_PAUSE();
        break;
      }
      case SEHVM_TYPE_POP_R32: {
        reg = p_nnm[1] & 0x7;
#ifdef __DEBUG__
        char *reg_name = \
         ((reg == SEHVM_ARG_EAX)?("EAX"):((reg == SEHVM_ARG_EBX)?("EBX"):((reg == SEHVM_ARG_ECX)?("ECX"):\
         ((reg == SEHVM_ARG_EDX)?("EDX"):((reg == SEHVM_ARG_EDI)?("EDI"):((reg == SEHVM_ARG_ESI)?("ESI"):\
         ((reg == SEHVM_ARG_EBP)?("EBP"):"INVALID_REGISTER")))))));
        if (reg == SEHVM_ARG_INVALID_REGISTER) {
          __DEBUG_PRINTF("] NANOMITE TYPE <NOP1>\r\n");
        } else {
          __DEBUG_PRINTF("] NANOMITE TYPE <POP_R32>\r\n]] POP %s\r\n", reg_name);
          uint32_t reg_val =
           ((reg == SEHVM_ARG_EAX)?(context->Eax):\
           ((reg == SEHVM_ARG_EBX)?(context->Ebx):\
           ((reg == SEHVM_ARG_ECX)?(context->Ecx):\
           ((reg == SEHVM_ARG_EDX)?(context->Edx):\
           ((reg == SEHVM_ARG_EDI)?(context->Edi):\
           ((reg == SEHVM_ARG_ESI)?(context->Esi):\
           (context->Ebp)))))));
          __DEBUG_PRINTF("]]] BEFORE: %s = 0x%X ", reg_name, reg_val);
        }
#endif
        if (reg != SEHVM_ARG_INVALID_REGISTER) {
          *((reg == SEHVM_ARG_EAX)?(&context->Eax):\
           ((reg == SEHVM_ARG_EBX)?(&context->Ebx):\
           ((reg == SEHVM_ARG_ECX)?(&context->Ecx):\
           ((reg == SEHVM_ARG_EDX)?(&context->Edx):\
           ((reg == SEHVM_ARG_EDI)?(&context->Edi):\
           ((reg == SEHVM_ARG_ESI)?(&context->Esi):\
           (&context->Ebp))))))) = *(uint32_t *)(context->Esp);
           context->Esp += 0x4;
        }
#ifdef __DEBUG__
        if (reg != SEHVM_ARG_INVALID_REGISTER) {
          uint32_t reg_val = \
           ((reg == SEHVM_ARG_EAX)?(context->Eax):\
           ((reg == SEHVM_ARG_EBX)?(context->Ebx):\
           ((reg == SEHVM_ARG_ECX)?(context->Ecx):\
           ((reg == SEHVM_ARG_EDX)?(context->Edx):\
           ((reg == SEHVM_ARG_EDI)?(context->Edi):\
           ((reg == SEHVM_ARG_ESI)?(context->Esi):\
           (context->Ebp)))))));
          __DEBUG_PRINTF("AFTER: %s = 0x%X \r\n", reg_name, reg_val);
        }
#endif
        context->Eip += 0x3;
        __DEBUG_PAUSE();
        break;
      }
      case SEHVM_TYPE_JMP_REL_C32:
        __DEBUG_PUTS("] NANOMITE TYPE <JMP_REL_C32>\r\n");
        __DEBUG_PRINTF("]] JMP_REL_C32 0x%X\r\n", *(uint32_t *)&p_nnm[1]);
        __DEBUG_PRINTF("]]] BEFORE: EIP = 0x%X\r\n", context->Eip);
        context->Eip += *(uint32_t *)&p_nnm[1];
        __DEBUG_PRINTF("AFTER: EIP = 0x%X\r\n", context->Eip);
        __DEBUG_PAUSE();
        break;
      case SEHVM_TYPE_CALL_REL_C32:
        __DEBUG_PUTS("] NANOMITE TYPE <CALL_REL_C32>\r\n");
        __DEBUG_PRINTF("]] JMP_CALL_C32 0x%X\r\n", *(uint32_t *)&p_nnm[1]);
        __DEBUG_PRINTF("]]] BEFORE: EIP = 0x%X\r\n", context->Eip);
        context->Esp -= 4;
        *(uint32_t *)(context->Esp) = context->Eip + 0x6;
        context->Eip += *(uint32_t *)(&p_nnm[1]);
        __DEBUG_PRINTF("AFTER: EIP = 0x%X\r\n", context->Eip);
        __DEBUG_PAUSE();
        break;
    };
  };
  return(ExceptionContinueExecution);
}
#ifndef SELF_TEST
/*
void __declspec(naked) exception_handler_end(void) {
  __asm _emit(0xCA) __asm _emit(0xFE) __asm _emit(0xDE) __asm _emit(0xAD);
} */
#endif

#ifdef SELF_TEST

int main(void) {
  __DEBUG_INIT();
  __asm {
    xor     ecx, ecx
    push    offset exception_handler
    push    DWord Ptr fs:[ecx]
    mov     DWord Ptr fs:[ecx], esp
  }

__DEBUG_PUTS(" [[[ START SEH BASED VM ENGINE TESTING ]]] \r\n\r\n");

__DEBUG_PUTS("TESTED COMMAND <PUSH_R32 EAX>\r\n");
  SEHVM_PUSH_R32(SEHVM_ARG_EAX);
  __asm pop eax
__DEBUG_PUTS("TESTED COMMAND <PUSH_R32 EBX>\r\n");
  SEHVM_PUSH_R32(SEHVM_ARG_EBX)
  __asm pop eax
__DEBUG_PUTS("TESTED COMMAND <PUSH_R32 ECX>\r\n");
  SEHVM_PUSH_R32(SEHVM_ARG_ECX)
  __asm pop eax
__DEBUG_PUTS("TESTED COMMAND <PUSH_R32 EDX>\r\n");
  SEHVM_PUSH_R32(SEHVM_ARG_EDX)
  __asm pop eax
__DEBUG_PUTS("TESTED COMMAND <PUSH_R32 EDI>\r\n");
  SEHVM_PUSH_R32(SEHVM_ARG_EDI)
  __asm pop eax
__DEBUG_PUTS("TESTED COMMAND <PUSH_R32 ESI>\r\n");
  SEHVM_PUSH_R32(SEHVM_ARG_ESI)
  __asm pop eax
__DEBUG_PUTS("TESTED COMMAND <PUSH_R32 EBP>\r\n");
  SEHVM_PUSH_R32(SEHVM_ARG_EBP)
  __asm pop eax
__DEBUG_PUTS("TESTED COMMAND <PUSH_R32 INVALID_REGISTER>\r\n");
  SEHVM_PUSH_R32(SEHVM_ARG_INVALID_REGISTER)
  __asm pop eax
  __DEBUG_PUTS("TESTED COMMAND <POP_R32 EAX>\r\nTOS VALUE = 0xDEADBEEF\r\n");
  __asm push eax
  __asm push 0xDEADBEEF
  SEHVM_POP_R32(SEHVM_ARG_EAX)
  __asm pop eax
  __DEBUG_PUTS("TESTED COMMAND <POP_R32 EBX>\r\nTOS VALUE = 0xDEADCAFE\r\n");
  __asm push ebx
  __asm push 0xDEADCAFE
  SEHVM_POP_R32(SEHVM_ARG_EBX)
  __asm pop ebx
  __DEBUG_PUTS("TESTED COMMAND <POP_R32 ECX>\r\nTOS VALUE = 0xDEADCAFE\r\n");
  __asm push ecx
  __asm push 0xDEADCAFE
  SEHVM_POP_R32(SEHVM_ARG_ECX)
  __asm pop ecx
  __DEBUG_PUTS("TESTED COMMAND <POP_R32 EDX>\r\nTOS VALUE = 0xDEADCAFE\r\n");
  __asm push edx
  __asm push 0xDEADCAFE
  SEHVM_POP_R32(SEHVM_ARG_EDX)
  __asm pop edx
  __DEBUG_PUTS("TESTED COMMAND <POP_R32 ESI>\r\nTOS VALUE = 0xDEADCAFE\r\n");
  __asm push esi
  __asm push 0xDEADCAFE
  SEHVM_POP_R32(SEHVM_ARG_ESI)
  __asm pop esi
  __DEBUG_PUTS("TESTED COMMAND <POP_R32 EDI>\r\nTOS VALUE = 0xDEADCAFE\r\n");
  __asm push edi
  __asm push 0xDEADCAFE
  SEHVM_POP_R32(SEHVM_ARG_EDI)
  __asm pop edi
  __DEBUG_PUTS("TESTED COMMAND <POP_R32 EBP>\r\nTOS VALUE = 0xDEADCAFE\r\n");
  __asm push ebp
  __asm push 0xDEADCAFE
  SEHVM_POP_R32(SEHVM_ARG_EBP)
  __asm pop ebp
  __DEBUG_PUTS("TESTED COMMAND <NOP>\r\n");
  SEHVM_NOP();
  __DEBUG_PUTS("TESTED COMMAND <SEHVM_JMP_REL_C32>\r\n");
  SEHVM_JMP_REL_C32(0x6);
  __DEBUG_PUTS("\r\n\r\n [[[ END SEH BASED VM ENGINE TESTING ]]] \r\n\r\n");
  __DEBUG_PAUSE();
  __asm {
    pop     DWord Ptr fs:[0]
    pop     eax
  }
  return(0x0);
};
#endif

Репозиторий проекта с наиболее полной докуменацией находиться здесь

Если чесно то как-то слабовато, код PoC’a написан максимально непонятно. Плюсом наномитов было то, что инструкция int 3 заменявшая условный\безусловный переход имела размер 1 байт, все данные для декодирования были ассоциативно привязанны по адресу инструкции. Т.е. втиснуть оригинальную инструкцию в 1 байт не сломав код было бы сложновато без плясок с VEH\SEH. В вашем случае эта фитча увы не сохранена

Хотя судя по инфе из инета не все реализации наномитов сдвигают код вокруг вырезанной инструкции до 1 байта, что весьма странно)