Sign in to follow this  
Followers 0
Psychlo

Moving to x64 - Function Hooking and Code Injection

15 posts in this topic

Hi.

Well, now that Chrome fucked things up setting x64 version as DEFAULT when downloading for those using Windows 64 bits, it is time to move some code to work on these fucking messed up systems half 8086, half x64.

 

I will be posting my progress here so we can fuck back.

 

So far I have just downloaded and installed RAD Studio XE2 and read a little about the trampoline in x64.

 

it seems the most used form is using mov RAX, QWORD_ADDRESS and then jmp RAX

 

Quote
Learned something new today. On x64, it's not possible to jmp directly to a 64 bit address as you might be used to on x86 with a single jmp instruction. Hooking functions on x86 was relatively simple with respect to the trampoline. All you needed was a JMP to the installed hook, totalling 5 bytes. After poring over the IA64 reference and a few Google searches, the best trampoline I could find was

mov rax, 0x4142434445464748
jmp rax

When assembled, the above bit of code amounts to 12 bytes. Using yasm, I got the following machine code.

0000000: 48b8 4847 4645 4443 4241 ffe0            H.HGFEDCBA..

I kept the faith that there was a possibly smaller alternative, however, after checking with EasyHook it seems this is the defacto standard. I'm no .NET coder, but I managed to find my way to its trampoline installation logic in DriverShared/LocalHook/install.c at line 278.

RtlCopyMemory(Hook->OldProc + RelocSize, Jumper_x64, 12);
RtlCopyMemory(Hook->OldProc + RelocSize + 2, &RelAddr, 8);

The definition of Jumper_x64 matches right up with my yasm output.

Jumper_x64[12] = {0x48, 0xb8, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xe0};
Posted 28th October 2008 by Jason

http://maliciousattacker.blogspot.com.br/2008/10/api-hooking-x64-trampolines.html

 

The steps will be try to port the APHEX dll injector code to put an entire dll inside a 64 bit process, of course the dll will be compiled in 64 bit aswell... I am still not sure how much they messed with PE File Format in x64, because I imagine IMAGEBASE, VirtualAddresses and pe header values will be int64 now...

 

so yeah, there is a lot of work ahead of me and I hope to be blogging here my progress, frustrations and hopefully success.

Share this post


Link to post
Share on other sites

Ok, already found an improvement... I downloaded the EasyHook as it is an active project so it will be good to steal the idea and rip the code:

Because yes, I am just a script kiddie, I like when there is code already done and I just have to copy and paste. In this case, copy, paste and translate.

 

	// 50                             push   rax
	// 48 b8 00 00 00 00 00 00 00 00  mov rax, 0x0
	// 48 87 04 24                    xchg   QWORD PTR[rsp], rax
	// c3                             ret

This way we save the old value in RAX.

It is a smart idea, uses a lot of bytes though... who cares... I hope hde is still around some where I guess there was a hde64

Share this post


Link to post
Share on other sites

ok, coded a little..

 

I am lazy so I work slow, as Tesla would say:

Quote

“That is the trouble with many inventors; they lack patience. They lack the willingness to work a thing out slowly and clearly and sharply in their mind, so that they can actually 'feel it work.' They want to try their first idea right off; and the result is they use up lots of money and lots of good material, only to find eventually that they are working in the wrong direction. We all make mistakes, and it is better to make them before we begin.”

(*
 *  Hacker Disassembler Engine 64
 *  Copyright (c) 2006-2009, Veacheslav Patkov
 *  aLL rights reserved.
 *
 *  hde64.pas : pascal header file (Free pascal, Delphi)
 *
 *)

unit hde64;

interface

uses windows;

type
  TDisasm = packed record
    len        : byte;     { length of command                             }
    p_rep      : byte;     { rep/repz (0xf3) & repnz (0xf2) prefix         }
    p_lock     : byte;     { lock prefix: 0xf0                             }
    p_seg      : byte;     { segment prefix: 0x2e,0x36,0x3e,0x26,0x64,0x65 }
    p_66       : byte;     { operand-size override prefix: 0x66            }
    p_67       : byte;     { address-size override prefix: 0x67            }
    rex        : byte;
    rex_w      : byte;
    rex_r      : byte;
    rex_x      : byte;
    rex_b      : byte;
    opcode     : byte;     { opcode                                        }
    opcode2    : byte;     { second opcode (if first opcode is 0x0f)       }
    modrm      : byte;     { ModR/M byte                                   }
    modrm_mod  : byte;     {   mod field of ModR/M                         }
    modrm_reg  : byte;     {   reg field of ModR/M                         }
    modrm_rm   : byte;     {   r/m field of ModR/M                         }
    sib        : byte;     { SIB byte                                      }
    sib_scale  : byte;     {   scale field of SIB                          }
    sib_index  : byte;     {   index field of SIB                          }
    sib_base   : byte;     {   base field of SIB                           }
    imm8       : byte;     { immediate value imm8                          }
    imm16      : word;     { immediate value imm16                         }
    imm32      : dword;    { immediate value imm32                         }
    imm64      : dword64;    { immediate value imm32                       }
    disp8      : byte;     { displacement disp8                            }
    disp16     : word;     { displacement disp16                           }
    disp32     : dword;    { displacement disp32                           }
    flags      : dword;    { displacement disp32                           }
  end;

function hde64_disasm(code: pointer; var hs: TDisasm): dword; cdecl;

implementation

{$LINK 'hde64.obj'}

function hde64_disasm(code: pointer; var hs: TDisasm): dword; cdecl; external;

end.
/*
 * Hacker Disassembler Engine 64 C
 * Copyright (c) 2008-2009, Vyacheslav Patkov.
 * All rights reserved.
 *
 */

#include <stdint.h>
#include <string.h>

#include "../include/hde64.h"
#include "table64.h"

unsigned int hde64_disasm(const void *code, hde64s *hs)
{
    uint8_t x, c, *p = (uint8_t *)code, cflags, opcode, pref = 0;
    uint8_t *ht = hde64_table, m_mod, m_reg, m_rm, disp_size = 0;
    uint8_t op64 = 0;

    memset(hs,0,sizeof(hde64s));

    for (x = 16; x; x--)
        switch (c = *p++) {
            case 0xf3:
                hs->p_rep = c;
                pref |= PRE_F3;
                break;
            case 0xf2:
                hs->p_rep = c;
                pref |= PRE_F2;
                break;
            case 0xf0:
                hs->p_lock = c;
                pref |= PRE_LOCK;
                break;
            case 0x26: case 0x2e: case 0x36:
            case 0x3e: case 0x64: case 0x65:
                hs->p_seg = c;
                pref |= PRE_SEG;
                break;
            case 0x66:
                hs->p_66 = c;
                pref |= PRE_66;
                break;
            case 0x67:
                hs->p_67 = c;
                pref |= PRE_67;
                break;
            default:
                goto pref_done;
        }
  pref_done:

    hs->flags = (uint32_t)pref << 23;

    if (!pref)
        pref |= PRE_NONE;

    if ((c & 0xf0) == 0x40) {
        hs->flags |= F_PREFIX_REX;
        if ((hs->rex_w = (c & 0xf) >> 3) && (*p & 0xf8) == 0xb8)
            op64++;
        hs->rex_r = (c & 7) >> 2;
        hs->rex_x = (c & 3) >> 1;
        hs->rex_b = c & 1;
        if (((c = *p++) & 0xf0) == 0x40) {
            opcode = c;
            goto error_opcode;
        }
    }

    if ((hs->opcode = c) == 0x0f) {
        hs->opcode2 = c = *p++;
        ht += DELTA_OPCODES;
    } else if (c >= 0xa0 && c <= 0xa3) {
        op64++;
        if (pref & PRE_67)
            pref |= PRE_66;
        else
            pref &= ~PRE_66;
    }

    opcode = c;
    cflags = ht[ht[opcode / 4] + (opcode % 4)];

    if (cflags == C_ERROR) {
      error_opcode:
        hs->flags |= F_ERROR | F_ERROR_OPCODE;
        cflags = 0;
        if ((opcode & -3) == 0x24)
            cflags++;
    }

    x = 0;
    if (cflags & C_GROUP) {
        uint16_t t;
        t = *(uint16_t *)(ht + (cflags & 0x7f));
        cflags = (uint8_t)t;
        x = (uint8_t)(t >> 8);
    }

    if (hs->opcode2) {
        ht = hde64_table + DELTA_PREFIXES;
        if (ht[ht[opcode / 4] + (opcode % 4)] & pref)
            hs->flags |= F_ERROR | F_ERROR_OPCODE;
    }

    if (cflags & C_MODRM) {
        hs->flags |= F_MODRM;
        hs->modrm = c = *p++;
        hs->modrm_mod = m_mod = c >> 6;
        hs->modrm_rm = m_rm = c & 7;
        hs->modrm_reg = m_reg = (c & 0x3f) >> 3;

        if (x && ((x << m_reg) & 0x80))
            hs->flags |= F_ERROR | F_ERROR_OPCODE;

        if (!hs->opcode2 && opcode >= 0xd9 && opcode <= 0xdf) {
            uint8_t t = opcode - 0xd9;
            if (m_mod == 3) {
                ht = hde64_table + DELTA_FPU_MODRM + t*8;
                t = ht[m_reg] << m_rm;
            } else {
                ht = hde64_table + DELTA_FPU_REG;
                t = ht[t] << m_reg;
            }
            if (t & 0x80)
                hs->flags |= F_ERROR | F_ERROR_OPCODE;
        }

        if (pref & PRE_LOCK) {
            if (m_mod == 3) {
                hs->flags |= F_ERROR | F_ERROR_LOCK;
            } else {
                uint8_t *table_end, op = opcode;
                if (hs->opcode2) {
                    ht = hde64_table + DELTA_OP2_LOCK_OK;
                    table_end = ht + DELTA_OP_ONLY_MEM - DELTA_OP2_LOCK_OK;
                } else {
                    ht = hde64_table + DELTA_OP_LOCK_OK;
                    table_end = ht + DELTA_OP2_LOCK_OK - DELTA_OP_LOCK_OK;
                    op &= -2;
                }
                for (; ht != table_end; ht++)
                    if (*ht++ == op) {
                        if (!((*ht << m_reg) & 0x80))
                            goto no_lock_error;
                        else
                            break;
                    }
                hs->flags |= F_ERROR | F_ERROR_LOCK;
              no_lock_error:
                ;
            }
        }

        if (hs->opcode2) {
            switch (opcode) {
                case 0x20: case 0x22:
                    m_mod = 3;
                    if (m_reg > 4 || m_reg == 1)
                        goto error_operand;
                    else
                        goto no_error_operand;
                case 0x21: case 0x23:
                    m_mod = 3;
                    if (m_reg == 4 || m_reg == 5)
                        goto error_operand;
                    else
                        goto no_error_operand;
            }
        } else {
            switch (opcode) {
                case 0x8c:
                    if (m_reg > 5)
                        goto error_operand;
                    else
                        goto no_error_operand;
                case 0x8e:
                    if (m_reg == 1 || m_reg > 5)
                        goto error_operand;
                    else
                        goto no_error_operand;
            }
        }

        if (m_mod == 3) {
            uint8_t *table_end;
            if (hs->opcode2) {
                ht = hde64_table + DELTA_OP2_ONLY_MEM;
                table_end = ht + sizeof(hde64_table) - DELTA_OP2_ONLY_MEM;
            } else {
                ht = hde64_table + DELTA_OP_ONLY_MEM;
                table_end = ht + DELTA_OP2_ONLY_MEM - DELTA_OP_ONLY_MEM;
            }
            for (; ht != table_end; ht += 2)
                if (*ht++ == opcode) {
                    if (*ht++ & pref && !((*ht << m_reg) & 0x80))
                        goto error_operand;
                    else
                        break;
                }
            goto no_error_operand;
        } else if (hs->opcode2) {
            switch (opcode) {
                case 0x50: case 0xd7: case 0xf7:
                    if (pref & (PRE_NONE | PRE_66))
                        goto error_operand;
                    break;
                case 0xd6:
                    if (pref & (PRE_F2 | PRE_F3))
                        goto error_operand;
                    break;
                case 0xc5:
                    goto error_operand;
            }
            goto no_error_operand;
        } else
            goto no_error_operand;

      error_operand:
        hs->flags |= F_ERROR | F_ERROR_OPERAND;
      no_error_operand:

        c = *p++;
        if (m_reg <= 1) {
            if (opcode == 0xf6)
                cflags |= C_IMM8;
            else if (opcode == 0xf7)
                cflags |= C_IMM_P66;
        }

        switch (m_mod) {
            case 0:
                if (pref & PRE_67) {
                    if (m_rm == 6)
                        disp_size = 2;
                } else
                    if (m_rm == 5)
                        disp_size = 4;
                break;
            case 1:
                disp_size = 1;
                break;
            case 2:
                disp_size = 2;
                if (!(pref & PRE_67))
                    disp_size <<= 1;
        }

        if (m_mod != 3 && m_rm == 4) {
            hs->flags |= F_SIB;
            p++;
            hs->sib = c;
            hs->sib_scale = c >> 6;
            hs->sib_index = (c & 0x3f) >> 3;
            if ((hs->sib_base = c & 7) == 5 && !(m_mod & 1))
                disp_size = 4;
        }

        p--;
        switch (disp_size) {
            case 1:
                hs->flags |= F_DISP8;
                hs->disp.disp8 = *p;
                break;
            case 2:
                hs->flags |= F_DISP16;
                hs->disp.disp16 = *(uint16_t *)p;
                break;
            case 4:
                hs->flags |= F_DISP32;
                hs->disp.disp32 = *(uint32_t *)p;
        }
        p += disp_size;
    } else if (pref & PRE_LOCK)
        hs->flags |= F_ERROR | F_ERROR_LOCK;

    if (cflags & C_IMM_P66) {
        if (cflags & C_REL32) {
            if (pref & PRE_66) {
                hs->flags |= F_IMM16 | F_RELATIVE;
                hs->imm.imm16 = *(uint16_t *)p;
                p += 2;
                goto disasm_done;
            }
            goto rel32_ok;
        }
        if (op64) {
            hs->flags |= F_IMM64;
            hs->imm.imm64 = *(uint64_t *)p;
            p += 8;
        } else if (!(pref & PRE_66)) {
            hs->flags |= F_IMM32;
            hs->imm.imm32 = *(uint32_t *)p;
            p += 4;
        } else
            goto imm16_ok;
    }


    if (cflags & C_IMM16) {
      imm16_ok:
        hs->flags |= F_IMM16;
        hs->imm.imm16 = *(uint16_t *)p;
        p += 2;
    }
    if (cflags & C_IMM8) {
        hs->flags |= F_IMM8;
        hs->imm.imm8 = *p++;
    }

    if (cflags & C_REL32) {
      rel32_ok:
        hs->flags |= F_IMM32 | F_RELATIVE;
        hs->imm.imm32 = *(uint32_t *)p;
        p += 4;
    } else if (cflags & C_REL8) {
        hs->flags |= F_IMM8 | F_RELATIVE;
        hs->imm.imm8 = *p++;
    }

  disasm_done:

    if ((hs->len = (uint8_t)(p-(uint8_t *)code)) > 15) {
        hs->flags |= F_ERROR | F_ERROR_LENGTH;
        hs->len = 15;
    }

    return (unsigned int)hs->len;
}

I used Visual Studio 2015 to compile the .obj file and then used it on RAD Studio XE2.

 

Compiled for 64 bit...

 

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, hde64, uBytesToPascal2;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure Test;
begin
  MessageBox(0, 'Teste', 'Program', MB_OK);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  hs: TDisasm;
  i, c, len, offset: Integer;
  opcodes: String;
begin
  offset := 0;

  for i := 0 to 10 - 1 do
  begin
    len := hde64_disasm(Pointer(UInt64(@Test) + offset), hs);

    opcodes := '';

    for c := 0 to len - 1 do
    begin
      opcodes := opcodes + IntToHex(PByte(UInt64(@Test) + offset + c)^, 2) + ' ';
    end;

    Memo1.Lines.Add(opcodes);

    offset := offset + len;
  end;

  //Memo1.Lines.Add(IntToStr(  ));
end;

end.

Then this is just a sample to get the 10 instructions from a certain address.

 

Just for testing purposes. That will be enough to create our function to capture the necessary instructions out of the original function and copy elsewhere, so we can insert the trampoline and hook the function. after executed the trampoline and our own interception function, we execute the copied instructions and at the end do another trampoline to the original code plus the offset of the overwritten instructions.

 

Share this post


Link to post
Share on other sites
.text:00000000005A0300 sub_5A0300      proc near               ; DATA XREF: sub_5A0340:loc_5A036Eo
.text:00000000005A0300                                         ; sub_5A0340+69o ...
.text:00000000005A0300                 sub     rsp, 28h
.text:00000000005A0304                 xor     rcx, rcx        ; hWnd
.text:00000000005A0307                 lea     rdx, aTeste     ; "Teste"
.text:00000000005A030E                 lea     r8, aProgram    ; "Program"
.text:00000000005A0315                 xor     r9, r9          ; uType
.text:00000000005A0318                 call    MessageBoxW
.text:00000000005A031D                 add     rsp, 28h
.text:00000000005A0321                 retn
.text:00000000005A0321 sub_5A0300      endp

that would be the code in ASM, according to IDA-64

 

I like ollydbg so much, it is a shame it cant work on 64bit files :(

 

When running the sample project for testing it returns this:

 

Quote

48 83 EC 28 
48 33 C9 
48 8D 15 16 00 00 00 
4C 8D 05 1B 00 00 00 
4D 33 C9 
E8 B3 62 E7 FF 
48 83 C4 28 
C3 
00 00 
54 

Just forget the last two lines, the function is until C3.... ret

 

So yes, it works...

Edited by Psychlo

Share this post


Link to post
Share on other sites

What worries me is that if we are going to hook a really short function, we might end up corrupting code that comes after the function...

Share this post


Link to post
Share on other sites

Today just the funciton to get the length necessary for the trampoline:

 

function GetMinLength(iLen: Cardinal; p: Pointer): Integer;
var
  hs: TDisasm;
begin
  Result := 0;

  repeat
    try
      Result := Cardinal(Result) + hde64_disasm(Pointer(UInt64(p) + Result), hs);
    except
      Result := -1;
      Break;
    end;
  until (Cardinal(Result) >= iLen);
end;

 

Share this post


Link to post
Share on other sites

Basic functionality working.

Not saving the original function yet. Have to copy the overwritten bytes to a new Allocated location to be able to use the original function. That is very important when intercepting messages that you don't want to change the functionality of the original program like ssl_write.

It is possible that some programs won't allow to use VirtualAlloc with EXECUTE permissions. That is something new on win8 onwards. Of course that will also make impossible to inject dll directly from memory in code injection style, so maybe we need to create a code cave with empty space on our dll code section.

 

But that is something I will not worry right now.

 

Next step is to implement the original function as result for the InstallHook function.

 

unit uHook64;

interface

uses
  Windows, hde64;

type
  TTrampoline64 = packed record
    asmPushRax: Byte;                     // 50                             push   rax
    asmMovRax: Array [0..1] of Byte;      // 48 b8                          mov rax,
    asmAddress: UInt64;                   // 00 00 00 00 00 00 00 00        0x0
    asmXchgRspRax: Array [0..3] of Byte;  // 48 87 04 24                    xchg   QWORD PTR[rsp], rax
    asmRet: Byte;                         // c3                             ret
  end;
  PTrampoline64 = ^TTrampoline64;

var
  Trampoline64_DefaultValues: TTrampoline64 = (
    asmPushRax:     $50;
    asmMovRax:      ($48, $B8);
    asmAddress:     $0000000000000000;
    asmXchgRspRax:  ($48, $87, $04, $24);
    asmRet:         $C3;
  );

function GetMinHookLength(iLen: Cardinal; p: Pointer): Integer;
function InstallHook64(pHookFunction, pFunction: Pointer): Pointer;

implementation

function GetMinHookLength(iLen: Cardinal; p: Pointer): Integer;
var
  hs: TDisasm;
begin
  Result := 0;

  repeat
    try
      Result := Cardinal(Result) + hde64_disasm(Pointer(UInt64(p) + Result), hs);
    except
      Result := -1;
      Break;
    end;
  until (Cardinal(Result) >= iLen);
end;

function InstallHook64(pHookFunction, pFunction: Pointer): Pointer;
var
  iMinLen: Integer;
  OldProtect: Cardinal;
  Trampoline64_Jump: TTrampoline64;
begin
  Result := nil;

  iMinLen := GetMinHookLength(SizeOf(TTrampoline64), pFunction);

  if (iMinLen > 0) then
  begin
    if (VirtualProtect(pFunction, iMinLen, PAGE_EXECUTE_READWRITE, OldProtect)) then
    begin
      //CopyMemory(@Trampoline64_Jump, @Trampoline64_DefaultValues, SizeOf(TTrampoline64));
      Trampoline64_Jump := Trampoline64_DefaultValues;

      Trampoline64_Jump.asmAddress := UInt64(pHookFunction);

      CopyMemory(pFunction, @Trampoline64_Jump, SizeOf(TTrampoline64));

      VirtualProtect(pFunction, iMinLen, OldProtect, OldProtect);
    end;
  end;
end;

end.
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, hde64, uBytesToPascal2, uHook64;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    Button2: TButton;
    Button3: TButton;
    Button4: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

function Hook_MessageBoxW(p1: Int64; lpText, lpCaption: PWideChar; p4: Cardinal): Integer; stdcall;
begin
  Result := 0;

  MessageBoxA(0, 'Intercepted Message!', 'Hook', MB_OK);
end;

procedure Test;
begin
  MessageBoxW(0, 'Test', 'Program', MB_OK);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  hs: TDisasm;
  i, c, len, offset: Integer;
  opcodes: String;
begin
  offset := 0;

  for i := 0 to 10 - 1 do
  begin
    len := hde64_disasm(Pointer(UInt64(GetProcAddress(LoadLibrary('user32.dll'), 'MessageBoxW')) + offset), hs);

    opcodes := '';

    for c := 0 to len - 1 do
    begin
      opcodes := opcodes + IntToHex(PByte(UInt64(GetProcAddress(LoadLibrary('user32.dll'), 'MessageBoxW')) + offset + c)^, 2) + ' ';
    end;

    Memo1.Lines.Add(opcodes);

    offset := offset + len;
  end;

  //Memo1.Lines.Add(IntToStr(  ));
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  Memo1.Lines.Add(IntToStr(GetMinHookLength(16, @Test)));
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
  Test;
end;

procedure TForm1.Button4Click(Sender: TObject);
begin
  InstallHook64(@Hook_MessageBoxW, GetProcAddress(LoadLibrary('user32.dll'), 'MessageBoxW'));
end;

end.

 

Share this post


Link to post
Share on other sites

I realized there is a fucnking huge problem with hooking in win64...

 

The API functions have execution flow and rip relative addresses right at the beginning... That makes it a lot more difficult to overwrite the original instructions and copy them elsewhere because they dont work elsewhere.. They are dependent of the address where they belong.

 

I downloaded minhook to se how they fiz this problem and apparently they relocate the jcc, rip relative addresses and calls to the new address where they will reside.

 

I didnt fully look into the code but that is what seems they do and that makes sense...

 

So yeah there will be a lot of work ahead to make this to work universally.

 

But I will because that is important to me $$

 

I hope minhook has done all the job so I will only rip the idea ;)

Share this post


Link to post
Share on other sites

Slowly progressing :)

 

len(4) 48 83 EC 38 
len(3) 45 33 DB 
len(7) 44 39 1D CE 0D 02 00 
Instruction using RIP relative addressing.
len(2) 74 2E 
Direct relative Jcc
len(9) 65 48 8B 04 25 30 00 00 00 
len(4) 4C 8B 50 48 
len(2) 33 C0 
len(9) F0 4C 0F B1 15 A8 26 02 00 
Instruction using RIP relative addressing.
len(7) 4C 8B 15 99 26 02 00 
Instruction using RIP relative addressing.
len(4) 41 8D 43 01 
len(4) 4C 0F 44 D0 
len(7) 4C 89 15 8A 26 02 00 
Instruction using RIP relative addressing.
len(5) 83 4C 24 28 FF 
len(6) 66 44 89 5C 24 20 
len(5) E8 56 00 00 00 
Direct relative CALL
len(4) 48 83 C4 38 
len(1) C3 
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, hde64, uBytesToPascal2, uHook64;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    Button2: TButton;
    Button3: TButton;
    Button4: TButton;
    Button5: TButton;
    Button6: TButton;
    Edit1: TEdit;
    Button7: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button5Click(Sender: TObject);
    procedure Button6Click(Sender: TObject);
    procedure Button7Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  HOriginal_MessageBoxW: function (p1: Int64; lpText, lpCaption: PWideChar; p4: Cardinal): Integer; stdcall = nil;

implementation

{$R *.dfm}

function CreateTrampolineFunction(p: Pointer): Boolean;
var
  hs: TDisasm;
  bFinished: Boolean;

  opcodes: String;
  c: Integer;

  dwDisasmBytes: Cardinal;
  dwTotalDisasmBytes: Cardinal;
begin
  Result := False;
  bFinished := False;

  dwTotalDisasmBytes := 0;

  repeat
    try
      dwDisasmBytes := hde64_disasm(Pointer(UInt64(p) + dwTotalDisasmBytes), hs);

      opcodes := '';
      for c := 0 to dwDisasmBytes - 1 do
      begin
        opcodes := opcodes + IntToHex(PByte(UInt64(p) + dwTotalDisasmBytes + c)^, 2) + ' ';
      end;
      Form1.Memo1.Lines.Add('len(' + IntToStr(hs.len) + ') ' + opcodes);

      dwTotalDisasmBytes := dwTotalDisasmBytes + dwDisasmBytes;

      if ((hs.flags and F_ERROR) <> 0) then
      begin
        Result := False;
        Break;
      end;

      // Instructions using RIP relative addressing. (ModR/M = 00???101B)
      if ((hs.modrm and $C7) = $05) then
      begin
        Form1.Memo1.Lines.Add('Instruction using RIP relative addressing.');
      end

      // Direct relative CALL
      else if (hs.opcode = $E8) then
      begin
        Form1.Memo1.Lines.Add('Direct relative CALL');
      end

      // Direct relative JMP (EB or E9)
      else if ((hs.opcode and $FD) = $E9) then
      begin
        Form1.Memo1.Lines.Add('Direct relative JMP (EB or E9)');
      end

      // Direct relative Jcc
      else if ((hs.opcode and $F0) = $70) or
        ((hs.opcode and $FC) = $E0) or
        ((hs.opcode2 and $F0) = $80) then
      begin
        Form1.Memo1.Lines.Add('Direct relative Jcc');
      end

      else if ((hs.opcode and $FE) = $C2) then
      begin
        bFinished := True;
        Result := True;
      end;

    except
      Result := False;
      Break;
    end;
  until (bFinished);
end;

function Hook_MessageBoxW(p1: Int64; lpText, lpCaption: PWideChar; p4: Cardinal): Integer; stdcall;
var
  strText, strCaption: String;
begin
  Result := 0;

  strText := WideCharToString(lpText);
  strCaption := WideCharToString(lpCaption);

  Form1.Memo1.Lines.Add('Intercepted: ' + strText + ' - ' + strCaption);
  //MessageBoxA(0, PChar(strText), PChar(strCaption), MB_OK);

  //Result := HOriginal_MessageBoxW(p1, lpText, lpCaption, p4);
end;

procedure Test;
begin
  MessageBoxW(0, 'Test', 'Program', MB_OK);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  hs: TDisasm;
  i, c, len, offset: Integer;
  opcodes: String;
begin
  offset := 0;

  for i := 0 to 10 - 1 do
  begin
    len := hde64_disasm(Pointer(UInt64(GetProcAddress(LoadLibrary('user32.dll'), 'MessageBoxW')) + offset), hs);

    opcodes := '';

    for c := 0 to len - 1 do
    begin
      opcodes := opcodes + IntToHex(PByte(UInt64(GetProcAddress(LoadLibrary('user32.dll'), 'MessageBoxW')) + offset + c)^, 2) + ' ';
    end;

    Memo1.Lines.Add(opcodes);

    offset := offset + len;
  end;

  //Memo1.Lines.Add(IntToStr(  ));
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  Memo1.Lines.Add(IntToStr(GetMinHookLength(16, GetProcAddress(LoadLibrary('user32.dll'), 'MessageBoxW'))));
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
  Test;
end;

procedure TForm1.Button4Click(Sender: TObject);
begin
  HOriginal_MessageBoxW := InstallHook64(@Hook_MessageBoxW, GetProcAddress(LoadLibrary('user32.dll'), 'MessageBoxW'));
  if (@HOriginal_MessageBoxW <> nil) then
  begin
    Memo1.Lines.Add('Hook installed successfully! -- pFunction = ' + IntToHex(UInt64(
      GetProcAddress(LoadLibrary('user32.dll'), 'MessageBoxW')), 16));
  end;
end;

procedure TForm1.Button5Click(Sender: TObject);
var
  hs: TDisasm;
  i, c, len, offset: Integer;
  opcodes: String;
begin
  offset := 0;

  for i := 0 to 10 - 1 do
  begin
    len := hde64_disasm(Pointer(UInt64(@HOriginal_MessageBoxW) + offset), hs);

    opcodes := '';

    for c := 0 to len - 1 do
    begin
      opcodes := opcodes + IntToHex(PByte(UInt64(@HOriginal_MessageBoxW) + offset + c)^, 2) + ' ';
    end;

    Memo1.Lines.Add(opcodes);

    offset := offset + len;
  end;

  //Memo1.Lines.Add(IntToStr(  ));
end;

procedure TForm1.Button6Click(Sender: TObject);
var
  hs: TDisasm;
  i, c, len, offset: Integer;
  opcodes: String;
begin
  offset := 0;

  for i := 0 to 10 - 1 do
  begin
    len := hde64_disasm(Pointer(UInt64(StrToInt64(Edit1.Text)) + offset), hs);

    opcodes := '';

    for c := 0 to len - 1 do
    begin
      opcodes := opcodes + IntToHex(PByte(UInt64(StrToInt64(Edit1.Text)) + offset + c)^, 2) + ' ';
    end;

    Memo1.Lines.Add(opcodes);

    offset := offset + len;
  end;

  //Memo1.Lines.Add(IntToStr(  ));
end;

procedure TForm1.Button7Click(Sender: TObject);
begin
  CreateTrampolineFunction(GetProcAddress(LoadLibrary('user32.dll'), 'MessageBoxW'));
end;

end.
(*
 *  Hacker Disassembler Engine 64
 *  Copyright (c) 2006-2009, Veacheslav Patkov
 *  aLL rights reserved.
 *
 *  hde64.pas : pascal header file (Free pascal, Delphi)
 *
 *)

unit hde64;

interface

uses Windows;

const
  F_MODRM         = $00000001;
  F_SIB           = $00000002;
  F_IMM8          = $00000004;
  F_IMM16         = $00000008;
  F_IMM32         = $00000010;
  F_IMM64         = $00000020;
  F_DISP8         = $00000040;
  F_DISP16        = $00000080;
  F_DISP32        = $00000100;
  F_RELATIVE      = $00000200;
  F_ERROR         = $00001000;
  F_ERROR_OPCODE  = $00002000;
  F_ERROR_LENGTH  = $00004000;
  F_ERROR_LOCK    = $00008000;
  F_ERROR_OPERAND = $00010000;
  F_PREFIX_REPNZ  = $01000000;
  F_PREFIX_REPX   = $02000000;
  F_PREFIX_REP    = $03000000;
  F_PREFIX_66     = $04000000;
  F_PREFIX_67     = $08000000;
  F_PREFIX_LOCK   = $10000000;
  F_PREFIX_SEG    = $20000000;
  F_PREFIX_REX    = $40000000;
  F_PREFIX_ANY    = $7f000000;

  PREFIX_SEGMENT_CS   = $2e;
  PREFIX_SEGMENT_SS   = $36;
  PREFIX_SEGMENT_DS   = $3e;
  PREFIX_SEGMENT_ES   = $26;
  PREFIX_SEGMENT_FS   = $64;
  PREFIX_SEGMENT_GS   = $65;
  PREFIX_LOCK         = $f0;
  PREFIX_REPNZ        = $f2;
  PREFIX_REPX         = $f3;
  PREFIX_OPERAND_SIZE = $66;
  PREFIX_ADDRESS_SIZE = $67;

type
  TDisasm = packed record
    len        : byte;     { length of command                             }
    p_rep      : byte;     { rep/repz (0xf3) & repnz (0xf2) prefix         }
    p_lock     : byte;     { lock prefix: 0xf0                             }
    p_seg      : byte;     { segment prefix: 0x2e,0x36,0x3e,0x26,0x64,0x65 }
    p_66       : byte;     { operand-size override prefix: 0x66            }
    p_67       : byte;     { address-size override prefix: 0x67            }
    rex        : byte;
    rex_w      : byte;
    rex_r      : byte;
    rex_x      : byte;
    rex_b      : byte;
    opcode     : byte;     { opcode                                        }
    opcode2    : byte;     { second opcode (if first opcode is 0x0f)       }
    modrm      : byte;     { ModR/M byte                                   }
    modrm_mod  : byte;     {   mod field of ModR/M                         }
    modrm_reg  : byte;     {   reg field of ModR/M                         }
    modrm_rm   : byte;     {   r/m field of ModR/M                         }
    sib        : byte;     { SIB byte                                      }
    sib_scale  : byte;     {   scale field of SIB                          }
    sib_index  : byte;     {   index field of SIB                          }
    sib_base   : byte;     {   base field of SIB                           }
    imm8       : byte;     { immediate value imm8                          }
    imm16      : word;     { immediate value imm16                         }
    imm32      : dword;    { immediate value imm32                         }
    imm64      : dword64;    { immediate value imm32                       }
    disp8      : byte;     { displacement disp8                            }
    disp16     : word;     { displacement disp16                           }
    disp32     : dword;    { displacement disp32                           }
    flags      : dword;    { displacement disp32                           }
  end;

function hde64_disasm(code: pointer; var hs: TDisasm): dword; cdecl;

implementation

{$LINK 'hde64.obj'}

function hde64_disasm(code: pointer; var hs: TDisasm): dword; cdecl; external;

end.

 

Share this post


Link to post
Share on other sites
.text:0000000078C91360 ; =============== S U B R O U T I N E =======================================
.text:0000000078C91360
.text:0000000078C91360
.text:0000000078C91360                 public MessageBoxW
.text:0000000078C91360 MessageBoxW     proc near               ; CODE XREF: sub_78C703F4+57p
.text:0000000078C91360                                         ; DATA XREF: .rdata:off_78CA33A8o
.text:0000000078C91360
.text:0000000078C91360 var_18          = word ptr -18h
.text:0000000078C91360 var_10          = dword ptr -10h
.text:0000000078C91360
.text:0000000078C91360                 sub     rsp, 38h						len(4) 48 83 EC 38 
.text:0000000078C91364                 xor     r11d, r11d					len(3) 45 33 DB 
.text:0000000078C91367                 cmp     cs:dword_78CB213C, r11d		len(7) 44 39 1D CE 0D 02 00 (Instruction using RIP relative addressing.)
.text:0000000078C9136E                 jz      short loc_78C9139E			len(2) 74 2E (Direct relative Jcc) 
.text:0000000078C91370                 mov     rax, gs:30h					len(9) 65 48 8B 04 25 30 00 00 00 
.text:0000000078C91379                 mov     r10, [rax+48h]				len(4) 4C 8B 50 48 
.text:0000000078C9137D                 xor     eax, eax						len(2) 33 C0 
.text:0000000078C9137F                 lock cmpxchg cs:qword_78CB3A30, r10	len(9) F0 4C 0F B1 15 A8 26 02 00 (Instruction using RIP relative addressing.)
.text:0000000078C91388                 mov     r10, cs:qword_78CB3A28		len(7) 4C 8B 15 99 26 02 00 (Instruction using RIP relative addressing.) 
.text:0000000078C9138F                 lea     eax, [r11+1]					len(4) 41 8D 43 01 
.text:0000000078C91393                 cmovz   r10, rax						len(4) 4C 0F 44 D0 
.text:0000000078C91397                 mov     cs:qword_78CB3A28, r10		len(7) 4C 89 15 8A 26 02 00 (Instruction using RIP relative addressing.)
.text:0000000078C9139E
.text:0000000078C9139E loc_78C9139E:                           ; CODE XREF: MessageBoxW+Ej
.text:0000000078C9139E                 or      [rsp+38h+var_10], 0FFFFFFFFh	len(5) 83 4C 24 28 FF 
.text:0000000078C913A3                 mov     [rsp+38h+var_18], r11w		len(6) 66 44 89 5C 24 20
.text:0000000078C913A9                 call    MessageBoxTimeoutW			len(5) E8 56 00 00 00 (Direct relative CALL.)
.text:0000000078C913AE                 add     rsp, 38h						48 83 C4 38 
.text:0000000078C913B2                 retn									len(1) C3 
.text:0000000078C913B2 ; ---------------------------------------------------------------------------

 

Share this post


Link to post
Share on other sites

of course that for this to work it is only necessary to relocate the addresses that are in the range occupied by the trampoline to the interception function.. that is something like 16 or 14 bytes depending on what method is used. it is possible to make a JMP [RIP+6] followed by the absolute address. This only consumes 14 bytes, so I will probably be using this...

 

Besides that I am thinking about creating a "emulation" function just to map the entire function... with its branches and stuff. Not following the calls but following the jumps and stopping on ret.

 

That would be interesting and maybe it would be possible to relocate entire functions out of its original location. That is just an idea.

1 person likes this

Share this post


Link to post
Share on other sites

Important to fix a big mistake I made at converting the hde64 structure.

I fuck.ed things up translating unions to Delphi... :$

 

(*
 *  Hacker Disassembler Engine 64
 *  Copyright (c) 2006-2009, Veacheslav Patkov
 *  aLL rights reserved.
 *
 *  hde64.pas : pascal header file (Free pascal, Delphi)
 *
 *)

unit hde64;

interface

const
  F_MODRM         = $00000001;
  F_SIB           = $00000002;
  F_IMM8          = $00000004;
  F_IMM16         = $00000008;
  F_IMM32         = $00000010;
  F_IMM64         = $00000020;
  F_DISP8         = $00000040;
  F_DISP16        = $00000080;
  F_DISP32        = $00000100;
  F_RELATIVE      = $00000200;
  F_ERROR         = $00001000;
  F_ERROR_OPCODE  = $00002000;
  F_ERROR_LENGTH  = $00004000;
  F_ERROR_LOCK    = $00008000;
  F_ERROR_OPERAND = $00010000;
  F_PREFIX_REPNZ  = $01000000;
  F_PREFIX_REPX   = $02000000;
  F_PREFIX_REP    = $03000000;
  F_PREFIX_66     = $04000000;
  F_PREFIX_67     = $08000000;
  F_PREFIX_LOCK   = $10000000;
  F_PREFIX_SEG    = $20000000;
  F_PREFIX_REX    = $40000000;
  F_PREFIX_ANY    = $7f000000;

  PREFIX_SEGMENT_CS   = $2e;
  PREFIX_SEGMENT_SS   = $36;
  PREFIX_SEGMENT_DS   = $3e;
  PREFIX_SEGMENT_ES   = $26;
  PREFIX_SEGMENT_FS   = $64;
  PREFIX_SEGMENT_GS   = $65;
  PREFIX_LOCK         = $f0;
  PREFIX_REPNZ        = $f2;
  PREFIX_REPX         = $f3;
  PREFIX_OPERAND_SIZE = $66;
  PREFIX_ADDRESS_SIZE = $67;

type
  TImm = packed record
    case integer of
      0: (imm8       : byte;);     { immediate value imm8                          }
      1: (imm16      : word;);     { immediate value imm16                         }
      2: (imm32      : cardinal;); { immediate value imm32                         }
      3: (imm64      : uint64;);   { immediate value imm64                         }
  end;
  TDisp = packed record
    case integer of
      0: (disp8      : byte;);     { displacement disp8                            }
      1: (disp16     : word;);     { displacement disp16                           }
      2: (disp32     : cardinal;); { displacement disp32                           }
  end;

  TDisasm = packed record
    len        : byte;     { length of command                             }
    p_rep      : byte;     { rep/repz (0xf3) & repnz (0xf2) prefix         }
    p_lock     : byte;     { lock prefix: 0xf0                             }
    p_seg      : byte;     { segment prefix: 0x2e,0x36,0x3e,0x26,0x64,0x65 }
    p_66       : byte;     { operand-size override prefix: 0x66            }
    p_67       : byte;     { address-size override prefix: 0x67            }
    rex        : byte;
    rex_w      : byte;
    rex_r      : byte;
    rex_x      : byte;
    rex_b      : byte;
    opcode     : byte;     { opcode                                        }
    opcode2    : byte;     { second opcode (if first opcode is 0x0f)       }
    modrm      : byte;     { ModR/M byte                                   }
    modrm_mod  : byte;     {   mod field of ModR/M                         }
    modrm_reg  : byte;     {   reg field of ModR/M                         }
    modrm_rm   : byte;     {   r/m field of ModR/M                         }
    sib        : byte;     { SIB byte                                      }
    sib_scale  : byte;     {   scale field of SIB                          }
    sib_index  : byte;     {   index field of SIB                          }
    sib_base   : byte;     {   base field of SIB                           }
    imm        : TImm;
    disp       : TDisp;
    flags      : cardinal; { flags                                         }
  end;

function hde64_disasm(code: pointer; var hs: TDisasm): cardinal; cdecl;

implementation

{$LINK 'hde64.obj'}

function hde64_disasm(code: pointer; var hs: TDisasm): cardinal; cdecl; external;

end.

 

$0000000077421360 len(4) 48 83 EC 38 
$0000000077421364 len(3) 45 33 DB 
$0000000077421367 len(7) 44 39 1D CE 0D 02 00 
Instruction using RIP relative addressing ($00020DCE).
Flags = $00000000
disp32 = $00020DCE
Address = $000000007744213C
$000000007742136E len(2) 74 2E 
Direct relative Jcc.
$0000000077421370 len(9) 65 48 8B 04 25 30 00 00 00 
$0000000077421379 len(4) 4C 8B 50 48 
$000000007742137D len(2) 33 C0 
$000000007742137F len(9) F0 4C 0F B1 15 A8 26 02 00 
Instruction using RIP relative addressing ($000226A8).
Flags = $00000000
disp32 = $000226A8
Address = $0000000077443A30
$0000000077421388 len(7) 4C 8B 15 99 26 02 00 
Instruction using RIP relative addressing ($00022699).
Flags = $00000000
disp32 = $00022699
Address = $0000000077443A28
$000000007742138F len(4) 41 8D 43 01 
$0000000077421393 len(4) 4C 0F 44 D0 
$0000000077421397 len(7) 4C 89 15 8A 26 02 00 
Instruction using RIP relative addressing ($0002268A).
Flags = $00000000
disp32 = $0002268A
Address = $0000000077443A28
$000000007742139E len(5) 83 4C 24 28 FF 
$00000000774213A3 len(6) 66 44 89 5C 24 20 
$00000000774213A9 len(5) E8 56 00 00 00 
Direct relative CALL.
$00000000774213AE len(4) 48 83 C4 38 
$00000000774213B2 len(1) C3 
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, hde64, uBytesToPascal2, uHook64;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    Button2: TButton;
    Button3: TButton;
    Button4: TButton;
    Button5: TButton;
    Button6: TButton;
    Edit1: TEdit;
    Button7: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button5Click(Sender: TObject);
    procedure Button6Click(Sender: TObject);
    procedure Button7Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  HOriginal_MessageBoxW: function (p1: Int64; lpText, lpCaption: PWideChar; p4: Cardinal): Integer; stdcall = nil;

implementation

{$R *.dfm}

function CreateTrampolineFunction(p: Pointer): Boolean;
var
  hs: TDisasm;
  bFinished: Boolean;

  opcodes: String;
  c: Integer;

  dwDisasmBytes: Cardinal;
  dwTotalDisasmBytes: Cardinal;
  pCurrentInstructionAddr: Pointer;
begin
  Result := False;
  bFinished := False;

  dwTotalDisasmBytes := 0;

  repeat
    try
      pCurrentInstructionAddr := Pointer(UInt64(p) + dwTotalDisasmBytes);
      dwDisasmBytes := hde64_disasm(pCurrentInstructionAddr, hs);

      dwTotalDisasmBytes := dwTotalDisasmBytes + dwDisasmBytes;

      opcodes := '';
      for c := 0 to dwDisasmBytes - 1 do
      begin
        opcodes := opcodes + IntToHex(PByte(UInt64(pCurrentInstructionAddr) + c)^, 2) + ' ';
      end;
      Form1.Memo1.Lines.Add('$' + IntToHex(UInt64(pCurrentInstructionAddr), 16) +
        ' len(' + IntToStr(hs.len) + ') ' + opcodes);

      if ((hs.flags and F_ERROR) <> 0) then
      begin
        Result := False;
        Break;
      end;

      // Instructions using RIP relative addressing. (ModR/M = 00???101B)
      if ((hs.modrm and $C7) = $05) then
      begin
        // Relative address is stored at (instruction length - immediate value length - 4).

        Form1.Memo1.Lines.Add('Instruction using RIP relative addressing ($' +
          IntToHex(PCardinal( UInt64(pCurrentInstructionAddr) + hs.len - ((hs.flags and $3C) shr 2) - 4 )^, 8) +
          ').');

        Form1.Memo1.Lines.Add('Flags = $' + IntToHex(((hs.flags and $3C) shr 2), 8));
          //hs.len - ((hs.flags and $3C) shr 2)

        Form1.Memo1.Lines.Add('disp32 = $' + IntToHex(hs.disp.disp32, 8));
        Form1.Memo1.Lines.Add('Address = $' + IntToHex(UInt64(pCurrentInstructionAddr) + hs.len + hs.disp.disp32, 16));
      end

      // Direct relative CALL
      else if (hs.opcode = $E8) then
      begin
        Form1.Memo1.Lines.Add('Direct relative CALL.');
      end

      // Direct relative JMP (EB or E9)
      else if ((hs.opcode and $FD) = $E9) then
      begin
        Form1.Memo1.Lines.Add('Direct relative JMP (EB or E9).');
      end

      // Direct relative Jcc
      else if ((hs.opcode and $F0) = $70) or
        ((hs.opcode and $FC) = $E0) or
        ((hs.opcode2 and $F0) = $80) then
      begin
        Form1.Memo1.Lines.Add('Direct relative Jcc.');
      end

      // RET (C2 or C3)
      else if ((hs.opcode and $FE) = $C2) then
      begin
        bFinished := True;
        Result := True;
      end;

    except
      Result := False;
      Break;
    end;
  until (bFinished);
end;

function Hook_MessageBoxW(p1: Int64; lpText, lpCaption: PWideChar; p4: Cardinal): Integer; stdcall;
var
  strText, strCaption: String;
begin
  Result := 0;

  strText := WideCharToString(lpText);
  strCaption := WideCharToString(lpCaption);

  Form1.Memo1.Lines.Add('Intercepted: ' + strText + ' - ' + strCaption);
  //MessageBoxA(0, PChar(strText), PChar(strCaption), MB_OK);

  //Result := HOriginal_MessageBoxW(p1, lpText, lpCaption, p4);
end;

procedure Test;
begin
  MessageBoxW(0, 'Test', 'Program', MB_OK);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  hs: TDisasm;
  i, c, len, offset: Integer;
  opcodes: String;
begin
  offset := 0;

  for i := 0 to 10 - 1 do
  begin
    len := hde64_disasm(Pointer(UInt64(GetProcAddress(LoadLibrary('user32.dll'), 'MessageBoxW')) + offset), hs);

    opcodes := '';

    for c := 0 to len - 1 do
    begin
      opcodes := opcodes + IntToHex(PByte(UInt64(GetProcAddress(LoadLibrary('user32.dll'), 'MessageBoxW')) + offset + c)^, 2) + ' ';
    end;

    Memo1.Lines.Add(opcodes);

    offset := offset + len;
  end;

  //Memo1.Lines.Add(IntToStr(  ));
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  Memo1.Lines.Add(IntToStr(GetMinHookLength(16, GetProcAddress(LoadLibrary('user32.dll'), 'MessageBoxW'))));
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
  Test;
end;

procedure TForm1.Button4Click(Sender: TObject);
begin
  HOriginal_MessageBoxW := InstallHook64(@Hook_MessageBoxW, GetProcAddress(LoadLibrary('user32.dll'), 'MessageBoxW'));
  if (@HOriginal_MessageBoxW <> nil) then
  begin
    Memo1.Lines.Add('Hook installed successfully! -- pFunction = ' + IntToHex(UInt64(
      GetProcAddress(LoadLibrary('user32.dll'), 'MessageBoxW')), 16));
  end;
end;

procedure TForm1.Button5Click(Sender: TObject);
var
  hs: TDisasm;
  i, c, len, offset: Integer;
  opcodes: String;
begin
  offset := 0;

  for i := 0 to 10 - 1 do
  begin
    len := hde64_disasm(Pointer(UInt64(@HOriginal_MessageBoxW) + offset), hs);

    opcodes := '';

    for c := 0 to len - 1 do
    begin
      opcodes := opcodes + IntToHex(PByte(UInt64(@HOriginal_MessageBoxW) + offset + c)^, 2) + ' ';
    end;

    Memo1.Lines.Add(opcodes);

    offset := offset + len;
  end;

  //Memo1.Lines.Add(IntToStr(  ));
end;

procedure TForm1.Button6Click(Sender: TObject);
var
  hs: TDisasm;
  i, c, len, offset: Integer;
  opcodes: String;
begin
  offset := 0;

  for i := 0 to 10 - 1 do
  begin
    len := hde64_disasm(Pointer(UInt64(StrToInt64(Edit1.Text)) + offset), hs);

    opcodes := '';

    for c := 0 to len - 1 do
    begin
      opcodes := opcodes + IntToHex(PByte(UInt64(StrToInt64(Edit1.Text)) + offset + c)^, 2) + ' ';
    end;

    Memo1.Lines.Add(opcodes);

    offset := offset + len;
  end;

  //Memo1.Lines.Add(IntToStr(  ));
end;

procedure TForm1.Button7Click(Sender: TObject);
begin
  CreateTrampolineFunction(GetProcAddress(LoadLibrary('user32.dll'), 'MessageBoxW'));
end;

end.

So now we know where the RIP relative address is at the instruction, and to where it points.

When copying to our own trampoline function we will relocate it based on our new location, that should be the (instructionAddress + instructionLength + disp32) - (New_instructionAddress + instructionLength)

Share this post


Link to post
Share on other sites

Nice and cozy, RIP relative addressing working.

 

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, hde64, uBytesToPascal2, uHook64;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    Button2: TButton;
    Button3: TButton;
    Button4: TButton;
    Button5: TButton;
    Button6: TButton;
    Edit1: TEdit;
    Button7: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button5Click(Sender: TObject);
    procedure Button6Click(Sender: TObject);
    procedure Button7Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  HOriginal_MessageBoxW: function (p1: Int64; lpText, lpCaption: PWideChar; p4: Cardinal): Integer; stdcall = nil;

implementation

{$R *.dfm}

function CreateTrampolineFunction(p, pTrampoline: Pointer): Boolean;
var
  hs: TDisasm;
  bFinished: Boolean;

  opcodes: String;
  c: Integer;

  dwDisasmBytes: Cardinal;
  dwTotalDisasmBytes: Cardinal;
  pCurrentInstructionAddr: Pointer;

  pCopyAddress: Pointer;
  pCopySrc: Pointer;
  dwCopySize: NativeUInt;

  arrBuffer: Array [0..15] of Byte;
  pRelAddress: PCardinal;

  Trampoline64_ReturnOriginal: TTrampoline64;
begin
  Result := False;
  bFinished := False;

  dwTotalDisasmBytes := 0;

  pCopyAddress := pTrampoline;
  pCopySrc := nil;
  dwCopySize := 0;

  repeat
    try
      pCurrentInstructionAddr := Pointer(UInt64(p) + dwTotalDisasmBytes);
      dwDisasmBytes := hde64_disasm(pCurrentInstructionAddr, hs);

      dwTotalDisasmBytes := dwTotalDisasmBytes + dwDisasmBytes;

      opcodes := '';
      for c := 0 to dwDisasmBytes - 1 do
      begin
        opcodes := opcodes + IntToHex(PByte(UInt64(pCurrentInstructionAddr) + c)^, 2) + ' ';
      end;
      Form1.Memo1.Lines.Add('$' + IntToHex(UInt64(pCurrentInstructionAddr), 16) +
        ' len(' + IntToStr(hs.len) + ') ' + opcodes);

      if ((hs.flags and F_ERROR) <> 0) then
      begin
        Result := False;
        Break;
      end;

      pCopySrc := pCurrentInstructionAddr;
      dwCopySize := hs.len;

      if (dwTotalDisasmBytes >= SizeOf(TTrampoline64)) then
      begin
        Form1.Memo1.Lines.Add('Necessary space reached, copying jump instructions.');

        Trampoline64_ReturnOriginal := Trampoline64_DefaultValues;
        Trampoline64_ReturnOriginal.asmAddress := UInt64(p) + dwTotalDisasmBytes;

        pCopySrc := @Trampoline64_ReturnOriginal;
        dwCopySize := SizeOf(TTrampoline64);

        bFinished := True;
      end

      // Instructions using RIP relative addressing. (ModR/M = 00???101B)
      else if ((hs.modrm and $C7) = $05) then
      begin
        // Relative address is stored at (instruction length - immediate value length - 4).
        CopyMemory(@arrBuffer[0], pCurrentInstructionAddr, hs.len);
        pRelAddress := Pointer(UInt64(@arrBuffer[0]) + hs.len - ((hs.flags and $3C) shr 2) - 4);
        pRelAddress^ := Cardinal(
          (UInt64(pCurrentInstructionAddr) + hs.len + hs.disp.disp32) -
          (UInt64(pCopyAddress) + hs.len)
        );

        pCopySrc := @arrBuffer[0];

        Form1.Memo1.Lines.Add('Instruction using RIP relative addressing ($' +
          IntToHex(PCardinal( UInt64(pCurrentInstructionAddr) + hs.len - ((hs.flags and $3C) shr 2) - 4 )^, 8) +
          ').');

        Form1.Memo1.Lines.Add('Flags = $' + IntToHex(((hs.flags and $3C) shr 2), 8));
          //hs.len - ((hs.flags and $3C) shr 2)

        Form1.Memo1.Lines.Add('disp32 = $' + IntToHex(hs.disp.disp32, 8));
        Form1.Memo1.Lines.Add('Address = $' + IntToHex(UInt64(pCurrentInstructionAddr) + hs.len + hs.disp.disp32, 16));
      end

      // Direct relative CALL
      else if (hs.opcode = $E8) then
      begin
        Form1.Memo1.Lines.Add('Direct relative CALL.');
      end

      // Direct relative JMP (EB or E9)
      else if ((hs.opcode and $FD) = $E9) then
      begin
        Form1.Memo1.Lines.Add('Direct relative JMP (EB or E9).');
      end

      // Direct relative Jcc
      else if ((hs.opcode and $F0) = $70) or
        ((hs.opcode and $FC) = $E0) or
        ((hs.opcode2 and $F0) = $80) then
      begin
        Form1.Memo1.Lines.Add('Direct relative Jcc.');
      end

      // RET (C2 or C3)
      else if ((hs.opcode and $FE) = $C2) then
      begin
        bFinished := True;
        Result := True;
      end;

      if ((pCopySrc <> nil) and (dwCopySize > 0)) then
      begin
        CopyMemory(pCopyAddress, pCopySrc, dwCopySize);
        pCopyAddress := Pointer(UInt64(pCopyAddress) + dwCopySize);
      end;

    except
      Result := False;
      Break;
    end;
  until (bFinished);
end;

function Hook_MessageBoxW(p1: Int64; lpText, lpCaption: PWideChar; p4: Cardinal): Integer; stdcall;
var
  strText, strCaption: String;
begin
  //Result := 0;

  strText := WideCharToString(lpText);
  strCaption := WideCharToString(lpCaption);

  Form1.Memo1.Lines.Add('Intercepted: ' + strText + ' - ' + strCaption);
  //MessageBoxA(0, PChar(strText), PChar(strCaption), MB_OK);

  Result := HOriginal_MessageBoxW(p1, lpText, lpCaption, p4);
end;

procedure Test;
begin
  MessageBoxW(0, 'Test', 'Program', MB_OK);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  hs: TDisasm;
  i, c, len, offset: Integer;
  opcodes: String;
begin
  offset := 0;

  for i := 0 to 10 - 1 do
  begin
    len := hde64_disasm(Pointer(UInt64(GetProcAddress(LoadLibrary('user32.dll'), 'MessageBoxW')) + offset), hs);

    opcodes := '';

    for c := 0 to len - 1 do
    begin
      opcodes := opcodes + IntToHex(PByte(UInt64(GetProcAddress(LoadLibrary('user32.dll'), 'MessageBoxW')) + offset + c)^, 2) + ' ';
    end;

    Memo1.Lines.Add(opcodes);

    offset := offset + len;
  end;

  //Memo1.Lines.Add(IntToStr(  ));
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  Memo1.Lines.Add(IntToStr(GetMinHookLength(16, GetProcAddress(LoadLibrary('user32.dll'), 'MessageBoxW'))));
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
  Test;
end;

procedure TForm1.Button4Click(Sender: TObject);
begin
  HOriginal_MessageBoxW := InstallHook64(@Hook_MessageBoxW, GetProcAddress(LoadLibrary('user32.dll'), 'MessageBoxW'));
  if (@HOriginal_MessageBoxW <> nil) then
  begin
    Memo1.Lines.Add('Hook installed successfully! -- pFunction = ' + IntToHex(UInt64(
      GetProcAddress(LoadLibrary('user32.dll'), 'MessageBoxW')), 16));
  end;
end;

procedure TForm1.Button5Click(Sender: TObject);
var
  hs: TDisasm;
  i, c, len, offset: Integer;
  opcodes: String;
begin
  offset := 0;

  for i := 0 to 10 - 1 do
  begin
    len := hde64_disasm(Pointer(UInt64(@HOriginal_MessageBoxW) + offset), hs);

    opcodes := '';

    for c := 0 to len - 1 do
    begin
      opcodes := opcodes + IntToHex(PByte(UInt64(@HOriginal_MessageBoxW) + offset + c)^, 2) + ' ';
    end;

    Memo1.Lines.Add(opcodes);

    offset := offset + len;
  end;

  //Memo1.Lines.Add(IntToStr(  ));
end;

procedure TForm1.Button6Click(Sender: TObject);
var
  hs: TDisasm;
  i, c, len, offset: Integer;
  opcodes: String;
begin
  offset := 0;

  for i := 0 to 10 - 1 do
  begin
    len := hde64_disasm(Pointer(UInt64(StrToInt64(Edit1.Text)) + offset), hs);

    opcodes := '';

    for c := 0 to len - 1 do
    begin
      opcodes := opcodes + IntToHex(PByte(UInt64(StrToInt64(Edit1.Text)) + offset + c)^, 2) + ' ';
    end;

    Memo1.Lines.Add(opcodes);

    offset := offset + len;
  end;

  //Memo1.Lines.Add(IntToStr(  ));
end;

procedure TForm1.Button7Click(Sender: TObject);
var
  pTrampoline: Pointer;
begin
  pTrampoline := VirtualAlloc(nil, $1000, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE);
  if (pTrampoline <> nil) then
  begin
    CreateTrampolineFunction(GetProcAddress(LoadLibrary('user32.dll'), 'MessageBoxW'), pTrampoline);

    HOriginal_MessageBoxW := pTrampoline;
    if (@HOriginal_MessageBoxW <> nil) then
    begin
      InstallHook64(@Hook_MessageBoxW, GetProcAddress(LoadLibrary('user32.dll'), 'MessageBoxW'));
      Memo1.Lines.Add('Hook installed successfully! -- pFunction = ' + IntToHex(UInt64(
        GetProcAddress(LoadLibrary('user32.dll'), 'MessageBoxW')), 16));
    end;
  end;
end;

end.

Tested only with MessageBoxW because jumps and calls are not implemented yet.

 

Using the JMP [RIP+6] followed by the absolute address and method to jump, only needs 14 bytes.

type
  TTrampoline64 = packed record
    asmJmpRip: Array [0..1] of Byte;      // FF 25                          JMP [RIP+6]
    asmZero32: Cardinal;                  // 00 00 00 00                    00000000
    asmAddress: UInt64;
  end;
  (*
  TTrampoline64 = packed record
    asmPushRax: Byte;                     // 50                             push   rax
    asmMovRax: Array [0..1] of Byte;      // 48 b8                          mov rax,
    asmAddress: UInt64;                   // 00 00 00 00 00 00 00 00        0x0
    asmXchgRspRax: Array [0..3] of Byte;  // 48 87 04 24                    xchg   QWORD PTR[rsp], rax
    asmRet: Byte;                         // c3                             ret
  end;
  *)
  (*
        0xFF, 0x25, 0x00000000, // FF25 00000000: JMP [RIP+6]
        0x0000000000000000ULL   // Absolute destination address
  *)
  PTrampoline64 = ^TTrampoline64;

var
  (*
  Trampoline64_DefaultValues: TTrampoline64 = (
    asmPushRax:     $50;
    asmMovRax:      ($48, $B8);
    asmAddress:     $0000000000000000;
    asmXchgRspRax:  ($48, $87, $04, $24);
    asmRet:         $C3;
  );
  *)
  Trampoline64_DefaultValues: TTrampoline64 = (
    asmJmpRip:      ($FF, $25);
    asmZero32:      $00000000;
    asmAddress:     $0000000000000000;
  );

Next I will finish the implementation of the other address relative instructions (jmp, jcc, call) and I will implement the same Allocation method used by MinHook on buffer.c.

 

hook is going well and it seems to be a solved issue, just a matter of time until I have everything ported to delphi.

Share this post


Link to post
Share on other sites

oops, small fix:

 

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, hde64, uBytesToPascal2, uHook64;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    Button2: TButton;
    Button3: TButton;
    Button4: TButton;
    Button5: TButton;
    Button6: TButton;
    Edit1: TEdit;
    Button7: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button5Click(Sender: TObject);
    procedure Button6Click(Sender: TObject);
    procedure Button7Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  HOriginal_MessageBoxW: function (p1: Int64; lpText, lpCaption: PWideChar; p4: Cardinal): Integer; stdcall = nil;

implementation

{$R *.dfm}

function CreateTrampolineFunction(p, pTrampoline: Pointer): Boolean;
var
  hs: TDisasm;
  bFinished: Boolean;

  opcodes: String;
  c: Integer;

  dwDisasmBytes: Cardinal;
  dwTotalDisasmBytes: Cardinal;
  pCurrentInstructionAddr: Pointer;

  pCopyAddress: Pointer;
  pCopySrc: Pointer;
  dwCopySize: NativeUInt;

  arrBuffer: Array [0..15] of Byte;
  pRelAddress: PCardinal;

  Trampoline64_ReturnOriginal: TTrampoline64;
begin
  Result := False;
  bFinished := False;

  dwTotalDisasmBytes := 0;

  pCopyAddress := pTrampoline;
  pCopySrc := nil;
  dwCopySize := 0;

  repeat
    try
      pCurrentInstructionAddr := Pointer(UInt64(p) + dwTotalDisasmBytes);
      dwDisasmBytes := hde64_disasm(pCurrentInstructionAddr, hs);

      opcodes := '';
      for c := 0 to dwDisasmBytes - 1 do
      begin
        opcodes := opcodes + IntToHex(PByte(UInt64(pCurrentInstructionAddr) + c)^, 2) + ' ';
      end;
      Form1.Memo1.Lines.Add('$' + IntToHex(UInt64(pCurrentInstructionAddr), 16) +
        ' len(' + IntToStr(hs.len) + ') ' + opcodes);

      if ((hs.flags and F_ERROR) <> 0) then
      begin
        Result := False;
        Break;
      end;

      pCopySrc := pCurrentInstructionAddr;
      dwCopySize := hs.len;

      if (dwTotalDisasmBytes >= SizeOf(TTrampoline64)) then
      begin
        Form1.Memo1.Lines.Add('Necessary space reached, copying jump instructions.');

        Trampoline64_ReturnOriginal := Trampoline64_DefaultValues;
        Trampoline64_ReturnOriginal.asmAddress := UInt64(p) + dwTotalDisasmBytes;

        pCopySrc := @Trampoline64_ReturnOriginal;
        dwCopySize := SizeOf(TTrampoline64);

        bFinished := True;
        Result := True;
      end

      // Instructions using RIP relative addressing. (ModR/M = 00???101B)
      else if ((hs.modrm and $C7) = $05) then
      begin
        // Relative address is stored at (instruction length - immediate value length - 4).
        CopyMemory(@arrBuffer[0], pCurrentInstructionAddr, hs.len);
        pRelAddress := Pointer(UInt64(@arrBuffer[0]) + hs.len - ((hs.flags and $3C) shr 2) - 4);
        pRelAddress^ := Cardinal(
          (UInt64(pCurrentInstructionAddr) - UInt64(pCopyAddress) + hs.disp.disp32)
        );

        pCopySrc := @arrBuffer[0];

        Form1.Memo1.Lines.Add('Instruction using RIP relative addressing ($' +
          IntToHex(PCardinal( UInt64(pCurrentInstructionAddr) + hs.len - ((hs.flags and $3C) shr 2) - 4 )^, 8) +
          ').');

        Form1.Memo1.Lines.Add('Flags = $' + IntToHex(((hs.flags and $3C) shr 2), 8));
          //hs.len - ((hs.flags and $3C) shr 2)

        Form1.Memo1.Lines.Add('disp32 = $' + IntToHex(hs.disp.disp32, 8));
        Form1.Memo1.Lines.Add('Address = $' + IntToHex(UInt64(pCurrentInstructionAddr) + hs.len + hs.disp.disp32, 16));
      end

      // Direct relative CALL
      else if (hs.opcode = $E8) then
      begin
        Form1.Memo1.Lines.Add('Direct relative CALL.');
      end

      // Direct relative JMP (EB or E9)
      else if ((hs.opcode and $FD) = $E9) then
      begin
        Form1.Memo1.Lines.Add('Direct relative JMP (EB or E9).');
      end

      // Direct relative Jcc
      else if ((hs.opcode and $F0) = $70) or
        ((hs.opcode and $FC) = $E0) or
        ((hs.opcode2 and $F0) = $80) then
      begin
        Form1.Memo1.Lines.Add('Direct relative Jcc.');
      end

      // RET (C2 or C3)
      else if ((hs.opcode and $FE) = $C2) then
      begin
        bFinished := True;
        Result := True;
      end;

      if ((pCopySrc <> nil) and (dwCopySize > 0)) then
      begin
        CopyMemory(pCopyAddress, pCopySrc, dwCopySize);
        pCopyAddress := Pointer(UInt64(pCopyAddress) + dwCopySize);
      end;

      dwTotalDisasmBytes := dwTotalDisasmBytes + dwDisasmBytes;

    except
      Result := False;
      Break;
    end;
  until (bFinished);
end;

function Hook_MessageBoxW(p1: Int64; lpText, lpCaption: PWideChar; p4: Cardinal): Integer; stdcall;
var
  strText, strCaption: String;
begin
  //Result := 0;

  strText := WideCharToString(lpText);
  strCaption := WideCharToString(lpCaption);

  Form1.Memo1.Lines.Add('Intercepted: ' + strText + ' - ' + strCaption);
  //MessageBoxA(0, PChar(strText), PChar(strCaption), MB_OK);

  Result := HOriginal_MessageBoxW(p1, lpText, lpCaption, p4);
end;

procedure Test;
begin
  MessageBoxW(0, 'Test', 'Program', MB_OK);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  hs: TDisasm;
  i, c, len, offset: Integer;
  opcodes: String;
begin
  offset := 0;

  for i := 0 to 10 - 1 do
  begin
    len := hde64_disasm(Pointer(UInt64(GetProcAddress(LoadLibrary('user32.dll'), 'MessageBoxW')) + offset), hs);

    opcodes := '';

    for c := 0 to len - 1 do
    begin
      opcodes := opcodes + IntToHex(PByte(UInt64(GetProcAddress(LoadLibrary('user32.dll'), 'MessageBoxW')) + offset + c)^, 2) + ' ';
    end;

    Memo1.Lines.Add(opcodes);

    offset := offset + len;
  end;

  //Memo1.Lines.Add(IntToStr(  ));
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  Memo1.Lines.Add(IntToStr(GetMinHookLength(16, GetProcAddress(LoadLibrary('user32.dll'), 'MessageBoxW'))));
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
  Test;
end;

procedure TForm1.Button4Click(Sender: TObject);
begin
  HOriginal_MessageBoxW := InstallHook64(@Hook_MessageBoxW, GetProcAddress(LoadLibrary('user32.dll'), 'MessageBoxW'));
  if (@HOriginal_MessageBoxW <> nil) then
  begin
    Memo1.Lines.Add('Hook installed successfully! -- pFunction = ' + IntToHex(UInt64(
      GetProcAddress(LoadLibrary('user32.dll'), 'MessageBoxW')), 16));
  end;
end;

procedure TForm1.Button5Click(Sender: TObject);
var
  hs: TDisasm;
  i, c, len, offset: Integer;
  opcodes: String;
begin
  offset := 0;

  for i := 0 to 10 - 1 do
  begin
    len := hde64_disasm(Pointer(UInt64(@HOriginal_MessageBoxW) + offset), hs);

    opcodes := '';

    for c := 0 to len - 1 do
    begin
      opcodes := opcodes + IntToHex(PByte(UInt64(@HOriginal_MessageBoxW) + offset + c)^, 2) + ' ';
    end;

    Memo1.Lines.Add(opcodes);

    offset := offset + len;
  end;

  //Memo1.Lines.Add(IntToStr(  ));
end;

procedure TForm1.Button6Click(Sender: TObject);
var
  hs: TDisasm;
  i, c, len, offset: Integer;
  opcodes: String;
begin
  offset := 0;

  for i := 0 to 10 - 1 do
  begin
    len := hde64_disasm(Pointer(UInt64(StrToInt64(Edit1.Text)) + offset), hs);

    opcodes := '';

    for c := 0 to len - 1 do
    begin
      opcodes := opcodes + IntToHex(PByte(UInt64(StrToInt64(Edit1.Text)) + offset + c)^, 2) + ' ';
    end;

    Memo1.Lines.Add(opcodes);

    offset := offset + len;
  end;

  //Memo1.Lines.Add(IntToStr(  ));
end;

procedure TForm1.Button7Click(Sender: TObject);
var
  pTrampoline: Pointer;
begin
  pTrampoline := VirtualAlloc(nil, $1000, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE);
  if (pTrampoline <> nil) then
  begin
    CreateTrampolineFunction(GetProcAddress(LoadLibrary('user32.dll'), 'MessageBoxW'), pTrampoline);

    HOriginal_MessageBoxW := pTrampoline;
    if (@HOriginal_MessageBoxW <> nil) then
    begin
      InstallHook64(@Hook_MessageBoxW, GetProcAddress(LoadLibrary('user32.dll'), 'MessageBoxW'));
      Memo1.Lines.Add('Hook installed successfully! -- pFunction = ' + IntToHex(UInt64(
        GetProcAddress(LoadLibrary('user32.dll'), 'MessageBoxW')), 16));
    end;
  end;
end;

end.

 

Share this post


Link to post
Share on other sites

Didn`t forget the project haha

 

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, hde64, uBytesToPascal2, uHook64;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    Button2: TButton;
    Button3: TButton;
    Button4: TButton;
    Button5: TButton;
    Button6: TButton;
    Edit1: TEdit;
    Button7: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button5Click(Sender: TObject);
    procedure Button6Click(Sender: TObject);
    procedure Button7Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  HOriginal_MessageBoxW: function (p1: Int64; lpText, lpCaption: PWideChar; p4: Cardinal): Integer; stdcall = nil;

implementation

{$R *.dfm}

function CreateTrampolineFunction(p, pTrampoline: Pointer): Boolean;
var
  hs: TDisasm;
  bFinished: Boolean;

  opcodes: String;
  c: Integer;

  dwDisasmBytes: Cardinal;
  dwTotalDisasmBytes: Cardinal;
  pCurrentInstructionAddr: Pointer;

  pCopyAddress: Pointer;
  pCopySrc: Pointer;
  dwCopySize: NativeUInt;

  arrBuffer: Array [0..15] of Byte;
  pRelAddress: PCardinal;

  Trampoline64_ReturnOriginal: TTrampoline64;
  Trampoline64Call_Redirect: TTrampolineCall64;
begin
  Result := False;
  bFinished := False;

  dwTotalDisasmBytes := 0;

  pCopyAddress := pTrampoline;
  pCopySrc := nil;
  dwCopySize := 0;

  repeat
    try
      pCurrentInstructionAddr := Pointer(UInt64(p) + dwTotalDisasmBytes);
      dwDisasmBytes := hde64_disasm(pCurrentInstructionAddr, hs);

      opcodes := '';
      for c := 0 to dwDisasmBytes - 1 do
      begin
        opcodes := opcodes + IntToHex(PByte(UInt64(pCurrentInstructionAddr) + c)^, 2) + ' ';
      end;
      Form1.Memo1.Lines.Add('$' + IntToHex(UInt64(pCurrentInstructionAddr), 16) +
        ' len(' + IntToStr(hs.len) + ') ' + opcodes);

      if ((hs.flags and F_ERROR) <> 0) then
      begin
        Result := False;
        Break;
      end;

      pCopySrc := pCurrentInstructionAddr;
      dwCopySize := hs.len;

      //if (dwTotalDisasmBytes >= SizeOf(TTrampoline64)) then
      if (dwTotalDisasmBytes >= 83) then
      begin
        Form1.Memo1.Lines.Add('Necessary space reached, copying jump instructions.');

        Trampoline64_ReturnOriginal := Trampoline64_DefaultValues;
        Trampoline64_ReturnOriginal.asmAddress := UInt64(p) + dwTotalDisasmBytes;

        pCopySrc := @Trampoline64_ReturnOriginal;
        dwCopySize := SizeOf(TTrampoline64);

        bFinished := True;
        Result := True;
      end

      // Instructions using RIP relative addressing. (ModR/M = 00???101B)
      else if ((hs.modrm and $C7) = $05) then
      begin
        // Relative address is stored at (instruction length - immediate value length - 4).
        CopyMemory(@arrBuffer[0], pCurrentInstructionAddr, hs.len);
        pRelAddress := Pointer(UInt64(@arrBuffer[0]) + hs.len - ((hs.flags and $3C) shr 2) - 4);
        pRelAddress^ := Cardinal(
          (UInt64(pCurrentInstructionAddr) - UInt64(pCopyAddress) + hs.disp.disp32)
        );

        pCopySrc := @arrBuffer[0];

        Form1.Memo1.Lines.Add('Instruction using RIP relative addressing ($' +
          IntToHex(PCardinal( UInt64(pCurrentInstructionAddr) + hs.len - ((hs.flags and $3C) shr 2) - 4 )^, 8) +
          ').');

        Form1.Memo1.Lines.Add('Flags = $' + IntToHex(((hs.flags and $3C) shr 2), 8));
          //hs.len - ((hs.flags and $3C) shr 2)

        Form1.Memo1.Lines.Add('disp32 = $' + IntToHex(hs.disp.disp32, 8));
        Form1.Memo1.Lines.Add('Address = $' + IntToHex(UInt64(pCurrentInstructionAddr) + hs.len + hs.disp.disp32, 16));
      end

      // Direct relative CALL
      else if (hs.opcode = $E8) then
      begin
        Form1.Memo1.Lines.Add('Direct relative CALL.');

        Form1.Memo1.Lines.Add('hs.imm.imm32 = $' + IntToHex(hs.imm.imm32, 8));

        Form1.Memo1.Lines.Add('RELATIVE CALL ($' +
          IntToHex(Cardinal( UInt64(pCurrentInstructionAddr) + hs.len + Integer(hs.imm.imm32) ), 8) +
          ').');

        Trampoline64Call_Redirect := TrampolineCall64_DefaultValues;
        Trampoline64Call_Redirect.asmAddress := UInt64(
          UInt64(pCurrentInstructionAddr) + hs.len + Integer(hs.imm.imm32)
        );

        pCopySrc := @Trampoline64Call_Redirect;
        dwCopySize := SizeOf(TTrampolineCall64);
      end

      // Direct relative JMP (EB or E9)
      else if ((hs.opcode and $FD) = $E9) then
      begin
        Form1.Memo1.Lines.Add('Direct relative JMP (EB or E9).');
      end

      // Direct relative Jcc
      else if ((hs.opcode and $F0) = $70) or
        ((hs.opcode and $FC) = $E0) or
        ((hs.opcode2 and $F0) = $80) then
      begin
        Form1.Memo1.Lines.Add('Direct relative Jcc.');
      end

      // RET (C2 or C3)
      else if ((hs.opcode and $FE) = $C2) then
      begin
        bFinished := True;
        Result := True;
      end;

      if ((pCopySrc <> nil) and (dwCopySize > 0)) then
      begin
        CopyMemory(pCopyAddress, pCopySrc, dwCopySize);
        pCopyAddress := Pointer(UInt64(pCopyAddress) + dwCopySize);
      end;

      dwTotalDisasmBytes := dwTotalDisasmBytes + dwDisasmBytes;

    except
      Result := False;
      Break;
    end;
  until (bFinished);
end;

function Hook_MessageBoxW(p1: Int64; lpText, lpCaption: PWideChar; p4: Cardinal): Integer; stdcall;
var
  strText, strCaption: String;
begin
  //Result := 0;

  strText := WideCharToString(lpText);
  strCaption := WideCharToString(lpCaption);

  Form1.Memo1.Lines.Add('Intercepted: ' + strText + ' - ' + strCaption);
  //MessageBoxA(0, PChar(strText), PChar(strCaption), MB_OK);

  Result := HOriginal_MessageBoxW(p1, lpText, lpCaption, p4);
end;

procedure Test;
begin
  MessageBoxW(0, 'Test', 'Program', MB_OK);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  hs: TDisasm;
  i, c, len, offset: Integer;
  opcodes: String;
begin
  offset := 0;

  for i := 0 to 10 - 1 do
  begin
    len := hde64_disasm(Pointer(UInt64(GetProcAddress(LoadLibrary('user32.dll'), 'MessageBoxW')) + offset), hs);

    opcodes := '';

    for c := 0 to len - 1 do
    begin
      opcodes := opcodes + IntToHex(PByte(UInt64(GetProcAddress(LoadLibrary('user32.dll'), 'MessageBoxW')) + offset + c)^, 2) + ' ';
    end;

    Memo1.Lines.Add(opcodes);

    offset := offset + len;
  end;

  //Memo1.Lines.Add(IntToStr(  ));
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  Memo1.Lines.Add(IntToStr(GetMinHookLength(16, GetProcAddress(LoadLibrary('user32.dll'), 'MessageBoxW'))));
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
  Test;
end;

procedure TForm1.Button4Click(Sender: TObject);
begin
  HOriginal_MessageBoxW := InstallHook64(@Hook_MessageBoxW, GetProcAddress(LoadLibrary('user32.dll'), 'MessageBoxW'));
  if (@HOriginal_MessageBoxW <> nil) then
  begin
    Memo1.Lines.Add('Hook installed successfully! -- pFunction = ' + IntToHex(UInt64(
      GetProcAddress(LoadLibrary('user32.dll'), 'MessageBoxW')), 16));
  end;
end;

procedure TForm1.Button5Click(Sender: TObject);
var
  hs: TDisasm;
  i, c, len, offset: Integer;
  opcodes: String;
begin
  offset := 0;

  for i := 0 to 10 - 1 do
  begin
    len := hde64_disasm(Pointer(UInt64(@HOriginal_MessageBoxW) + offset), hs);

    opcodes := '';

    for c := 0 to len - 1 do
    begin
      opcodes := opcodes + IntToHex(PByte(UInt64(@HOriginal_MessageBoxW) + offset + c)^, 2) + ' ';
    end;

    Memo1.Lines.Add(opcodes);

    offset := offset + len;
  end;

  //Memo1.Lines.Add(IntToStr(  ));
end;

procedure TForm1.Button6Click(Sender: TObject);
var
  hs: TDisasm;
  i, c, len, offset: Integer;
  opcodes: String;
begin
  offset := 0;

  for i := 0 to 10 - 1 do
  begin
    len := hde64_disasm(Pointer(UInt64(StrToInt64(Edit1.Text)) + offset), hs);

    opcodes := '';

    for c := 0 to len - 1 do
    begin
      opcodes := opcodes + IntToHex(PByte(UInt64(StrToInt64(Edit1.Text)) + offset + c)^, 2) + ' ';
    end;

    Memo1.Lines.Add(opcodes);

    offset := offset + len;
  end;

  //Memo1.Lines.Add(IntToStr(  ));
end;

procedure TForm1.Button7Click(Sender: TObject);
var
  pTrampoline: Pointer;
begin
  pTrampoline := VirtualAlloc(nil, $1000, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE);
  if (pTrampoline <> nil) then
  begin
    CreateTrampolineFunction(GetProcAddress(LoadLibrary('user32.dll'), 'MessageBoxW'), pTrampoline);

    HOriginal_MessageBoxW := pTrampoline;
    if (@HOriginal_MessageBoxW <> nil) then
    begin
      InstallHook64(@Hook_MessageBoxW, GetProcAddress(LoadLibrary('user32.dll'), 'MessageBoxW'));
      Memo1.Lines.Add('Hook installed successfully! -- pFunction = ' + IntToHex(UInt64(
        GetProcAddress(LoadLibrary('user32.dll'), 'MessageBoxW')), 16));
    end;
  end;
end;

end.
unit uHook64;

interface

uses
  Windows, hde64;

type
  TTrampoline64 = packed record
    asmJmpRip: Array [0..1] of Byte;      // FF 25                          JMP [RIP+6]
    asmZero32: Cardinal;                  // 00 00 00 00                    00000000
    asmAddress: UInt64;
  end;
  (*
  TTrampoline64 = packed record
    asmPushRax: Byte;                     // 50                             push   rax
    asmMovRax: Array [0..1] of Byte;      // 48 b8                          mov rax,
    asmAddress: UInt64;                   // 00 00 00 00 00 00 00 00        0x0
    asmXchgRspRax: Array [0..3] of Byte;  // 48 87 04 24                    xchg   QWORD PTR[rsp], rax
    asmRet: Byte;                         // c3                             ret
  end;
  *)
  (*
        0xFF, 0x25, 0x00000000, // FF25 00000000: JMP [RIP+6]
        0x0000000000000000ULL   // Absolute destination address
  *)
  PTrampoline64 = ^TTrampoline64;

  TTrampolineCall64 = packed record
    asmCallRip: Array [0..1] of Byte;      // FF 15                          CALL [RIP+8]
    asmRipOffset: Cardinal;                // 00 00 00 02                    00000002
    asmJmp10: Array [0..1] of Byte;        // EB 08                          JMP +10
    asmAddress: UInt64;
  end;
  PTrampolineCall64 = ^TTrampolineCall64;

var
  (*
  Trampoline64_DefaultValues: TTrampoline64 = (
    asmPushRax:     $50;
    asmMovRax:      ($48, $B8);
    asmAddress:     $0000000000000000;
    asmXchgRspRax:  ($48, $87, $04, $24);
    asmRet:         $C3;
  );
  *)
  Trampoline64_DefaultValues: TTrampoline64 = (
    asmJmpRip:      ($FF, $25);
    asmZero32:      $00000000;
    asmAddress:     $0000000000000000;
  );

  TrampolineCall64_DefaultValues: TTrampolineCall64 = (
    asmCallRip:      ($FF, $15);
    asmRipOffset:    $00000002;
    asmJmp10:        ($EB, $08);
    asmAddress:      $0000000000000000;
  );

function GetMinHookLength(iLen: Cardinal; p: Pointer): Integer;
function InstallHook64(pHookFunction, pFunction: Pointer): Pointer;

implementation

function GetMinHookLength(iLen: Cardinal; p: Pointer): Integer;
var
  hs: TDisasm;
begin
  Result := 0;

  repeat
    try
      Result := Cardinal(Result) + hde64_disasm(Pointer(UInt64(p) + Result), hs);
    except
      Result := -1;
      Break;
    end;
  until (Cardinal(Result) >= iLen);
end;

function InstallHook64(pHookFunction, pFunction: Pointer): Pointer;
var
  iMinLen: Integer;
  OldProtect: Cardinal;
  Trampoline64_Jump, Trampoline64_ReturnOriginal: TTrampoline64;
begin
  Result := nil;

  iMinLen := GetMinHookLength(SizeOf(TTrampoline64), pFunction);

  if (iMinLen > 0) then
  begin
    if (VirtualProtect(pFunction, iMinLen, PAGE_EXECUTE_READWRITE, OldProtect)) then
    begin
      Trampoline64_Jump := Trampoline64_DefaultValues;

      Trampoline64_Jump.asmAddress := UInt64(pHookFunction);

      // copy original instructions before overwriting it
      Result := VirtualAlloc(nil, iMinLen + SizeOf(TTrampoline64), MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE);
      if (Result <> nil) then
      begin
        Trampoline64_ReturnOriginal := Trampoline64_DefaultValues;
        Trampoline64_ReturnOriginal.asmAddress := UInt64(pFunction) + iMinLen;

        CopyMemory(Result, pFunction, iMinLen);
        CopyMemory(Pointer(UInt64(Result) + iMinLen), @Trampoline64_ReturnOriginal, SizeOf(TTrampoline64));
      end;

      CopyMemory(pFunction, @Trampoline64_Jump, SizeOf(TTrampoline64));

      VirtualProtect(pFunction, iMinLen, OldProtect, OldProtect);
    end;
  end;
end;

end.

Added the RELATIVE DIRECT CALL assembly "re-coding".

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0