flat assembler
Message board for the users of flat assembler.

Index > Heap > How to Generate and Execute Machine code on the Stack?

Author
Thread Post new topic Reply to topic
Cokine



Joined: 22 Nov 2005
Posts: 4
Cokine
Hi Guys!

I'm a x86 assembly newbie (I began yesterday) and I need help from code gurus!
I try to dynamically create and execute some code on the stack. The code I would like to generate calls a C++ method of a global object that calls printf.

Here is my code which is currently WIN32 only (I would really appreciate if this could work under Linux!!):

Code:
#include "Windows.h"
#include <stdio.h>

class Class {
public:
        void func(int i) {
                printf("out: 0x%08x\n", i); //<--- Crashes here!
        }
};

typedef void (Class::*PointerToClassMember)(int);
typedef void (*DynamicCodeFuncPtr)(void);

const long kDYNCODE_LEN = 1024;
Class g_Class;
DynamicCodeFuncPtr dynCodePtr = NULL;
PointerToClassMember g_f = g_Class.func;

int main (int argc, char* argv[]) {
        DWORD oldProtect;
        int pos = 0;

        dynCodePtr = (DynamicCodeFuncPtr) new BYTE[kDYNCODE_LEN];
        memset(dynCodePtr, 0, kDYNCODE_LEN * sizeof(BYTE));

        ((LPBYTE)dynCodePtr)[pos++] = (BYTE)0x55; // push ebp
        ((LPBYTE)dynCodePtr)[pos++] = (BYTE)0x8b; // mov ebp, esp
        ((LPBYTE)dynCodePtr)[pos++] = (BYTE)0xec;

        ((LPBYTE)dynCodePtr)[pos++] = (BYTE)0x53; // push ebx
        ((LPBYTE)dynCodePtr)[pos++] = (BYTE)0x56; // push esi
        ((LPBYTE)dynCodePtr)[pos++] = (BYTE)0x57; // push edi
        ((LPBYTE)dynCodePtr)[pos++] = (BYTE)0x68; // push 8
        ((LPBYTE)dynCodePtr)[pos++] = (BYTE)0x08;
        ((LPBYTE)dynCodePtr)[pos++] = (BYTE)0x0;
        ((LPBYTE)dynCodePtr)[pos++] = (BYTE)0x0;
        ((LPBYTE)dynCodePtr)[pos++] = (BYTE)0x0;

        ((LPBYTE)dynCodePtr)[pos++] = (BYTE)0x2e; // CS prefix
        ((LPBYTE)dynCodePtr)[pos++] = (BYTE)0xff; // call ptr[ptr]
        ((LPBYTE)dynCodePtr)[pos++] = (BYTE)0x15; 

        PointerToClassMember* ptr = &g_f; 
        memcpy( &((LPBYTE)dynCodePtr)[pos], &ptr, sizeof(DWORD));
        pos += 4;

        ((LPBYTE)dynCodePtr)[pos++] = (BYTE)0x5f; // pop edi
        ((LPBYTE)dynCodePtr)[pos++] = (BYTE)0x5e; // pop esi
        ((LPBYTE)dynCodePtr)[pos++] = (BYTE)0x5b; // pop ebx
        ((LPBYTE)dynCodePtr)[pos++] = (BYTE)0x5d; // pop ebp
        ((LPBYTE)dynCodePtr)[pos++] = (BYTE)0xc3; // ret


        VirtualProtect(dynCodePtr, kDYNCODE_LEN, PAGE_EXECUTE, &oldProtect);
        BOOL bRes = FlushInstructionCache(GetCurrentProcess(), NULL, 0);

        if (bRes) {
                dynCodePtr();
        }

        VirtualProtect(dynCodePtr, kDYNCODE_LEN, oldProtect, &oldProtect);

        delete (LPBYTE)dynCodePtr;

        return 0;

}
    



My main problem is that my code crashes in the printf func which is called by the Class::func(int) method.
What am I doing wrong?

An other question: how could I do not to have to use the temporary PointerToMember* ptr variable? I can't figure out what 'call' I have to use not to have this double dereferencing occur.

Thanks!!

Cokine.
Post 22 Nov 2005, 16:47
View user's profile Send private message Reply with quote
roticv



Joined: 19 Jun 2003
Posts: 374
Location: Singapore
roticv
Did you know that XP does not allow you to run code from the stack with the latest SP?
Post 23 Nov 2005, 06:38
View user's profile Send private message Visit poster's website MSN Messenger Reply with quote
Cokine



Joined: 22 Nov 2005
Posts: 4
Cokine
roticv wrote:
Did you know that XP does not allow you to run code from the stack with the latest SP?

What is the latest SP?
I wrote and tested this under Windows XP SP2 which executes the code without complain (but it crahes in the middle of execution Crying or Very sad).

Cokine.
Post 23 Nov 2005, 06:42
View user's profile Send private message Reply with quote
roticv



Joined: 19 Jun 2003
Posts: 374
Location: Singapore
roticv
Post 23 Nov 2005, 15:29
View user's profile Send private message Visit poster's website MSN Messenger Reply with quote
JMGK



Joined: 26 Aug 2005
Posts: 27
JMGK
Cokine,

put a __asm int 3; after the if (bRes) { line, and run it with a debugger... i guess this is the easiest way to figure out whats happening

jmgk

ps: the push 8 can be encoded as 0x6a 0x08 - 2 bytes instead of 5

pps: maybe is the CS: CALL thingie thats causing the problem? you´re storing there (after the opcode) the address of a variable that have the function address you want?
Post 24 Nov 2005, 03:27
View user's profile Send private message Reply with quote
Cokine



Joined: 22 Nov 2005
Posts: 4
Cokine
Thank you JMGK,

I went into the assembly code using the debugger and didn't noticed any difference between the code I generate end the code generated by the Compiler: I wrote a fucntion that calls g_Class.func(Cool and the disassembly of this function looks like my handwritten code.

The crash occurs in some allocation code of CRT. If I replace printf by an sprintf, no crash occurs.

I updated my code to correctly call the C++ method using the thiscall calling convention (eax must contain this)

Here is the updated source code:
Code:
#include "Windows.h"
#include <stdio.h>

class Class {
public:
        int m_i;
        char m_buf[256];
        void func(int i) {
                m_i = i;
                sprintf(m_buf, "out: 0x%08x\n", i);
                printf("out: 0x%08x\n", i);     // <-- now crahses in here.
                                                // If this line is commented, seems to work.
        }
};

typedef void (Class::*PointerToClassMember)(int);
typedef void (_stdcall*DynamicCodeFuncPtr)(void);

const long kDYNCODE_LEN = 1024;
Class g_Class;
DynamicCodeFuncPtr dynCodePtr = NULL;
PointerToClassMember g_f = g_Class.func;

static void _stdcall function() {
        g_Class.func(6);
}


int _cdecl main (int argc, char* argv[]) {
        DWORD oldProtect;
        int pos = 0;

        BYTE* bytes = new BYTE[kDYNCODE_LEN];
        memset(bytes, 0, kDYNCODE_LEN * sizeof(BYTE));

        const kOffset = 32;

        for (int offset=0; offset<kOffset; offset++) {
                ((LPBYTE)bytes)[pos++] = (BYTE)0x00;
        }

        ((LPBYTE)bytes)[pos++] = (BYTE)0x55; // push ebp
        ((LPBYTE)bytes)[pos++] = (BYTE)0x8b; // mov ebp, esp
        ((LPBYTE)bytes)[pos++] = (BYTE)0xec;

        ((LPBYTE)bytes)[pos++] = (BYTE)0x53; // push ebx
        ((LPBYTE)bytes)[pos++] = (BYTE)0x56; // push esi
        ((LPBYTE)bytes)[pos++] = (BYTE)0x57; // push edi

        ((LPBYTE)bytes)[pos++] = (BYTE)0x8b; // mov esi, esp
        ((LPBYTE)bytes)[pos++] = (BYTE)0xf4;

        ((LPBYTE)bytes)[pos++] = (BYTE)0x6a; // push 8
        ((LPBYTE)bytes)[pos++] = (BYTE)0xff;

        ((LPBYTE)bytes)[pos++] = (BYTE)0xb9; // mov ecx, g_Class::func
        {
                void* ptr = &g_Class; 
                memcpy( &((LPBYTE)bytes)[pos], &ptr, sizeof(DWORD));
                pos += 4;
        }


        // near call, absolute indirect
        ((LPBYTE)bytes)[pos++] = (BYTE)0xff; // call ptr[ptr]
        ((LPBYTE)bytes)[pos++] = (BYTE)0x15;
        {
                PointerToClassMember* ptr = &g_f; 
                memcpy( &((LPBYTE)bytes)[pos], &ptr, sizeof(DWORD));
                pos += 4;
        }

        ((LPBYTE)bytes)[pos++] = (BYTE)0x5f; // pop edi
        ((LPBYTE)bytes)[pos++] = (BYTE)0x5e; // pop esi
        ((LPBYTE)bytes)[pos++] = (BYTE)0x5b; // pop ebx


        ((LPBYTE)bytes)[pos++] = (BYTE)0x5d; // pop ebp
        ((LPBYTE)bytes)[pos++] = (BYTE)0xc3; // ret

//      function(); // <-- if uncommented, the printf call works...

        VirtualProtect(bytes, kDYNCODE_LEN, PAGE_EXECUTE_READ, &oldProtect);
        BOOL bRes = FlushInstructionCache(GetCurrentProcess(), NULL, 0);

        dynCodePtr = (DynamicCodeFuncPtr) (bytes + kOffset);

        if (bRes) {
                dynCodePtr();
        }

        DWORD oldOldProtect;
        VirtualProtect(bytes, kDYNCODE_LEN, PAGE_READWRITE, &oldOldProtect);
        bRes = FlushInstructionCache(GetCurrentProcess(), NULL, 0);

        delete []bytes;

        printf("%s", g_Class.m_buf);

        return 0;
}
    


Thanks again,

Cokine.
Post 24 Nov 2005, 10:35
View user's profile Send private message Reply with quote
JMGK



Joined: 26 Aug 2005
Posts: 27
JMGK
hi again,

i guess that:
Code:
((LPBYTE)bytes)[pos++] = (BYTE)0x6a; // push 8 
((LPBYTE)bytes)[pos++] = (BYTE)0xff;
    


should be:

Code:
((LPBYTE)bytes)[pos++] = (BYTE)0x6a; // push 8 
((LPBYTE)bytes)[pos++] = (BYTE)0x08;
    


the first one do a push -1, instead of a push 8

jmgk
Post 24 Nov 2005, 12:04
View user's profile Send private message Reply with quote
vid
Verbosity in development


Joined: 05 Sep 2003
Posts: 7105
Location: Slovakia
vid
wow, there is a extend_and_push_byte instruction? good to know, i will use it in some boot code optimizations.
Post 24 Nov 2005, 18:26
View user's profile Send private message Visit poster's website AIM Address MSN Messenger ICQ Number Reply with quote
JMGK



Joined: 26 Aug 2005
Posts: 27
JMGK
vid,

the 0x6a push opcode is a signed push... it only work for values between 127 and -128

for all others values, you should use 0x68 push opcode, followed by a dword

jmgk
Post 25 Nov 2005, 02:17
View user's profile Send private message Reply with quote
Cokine



Joined: 22 Nov 2005
Posts: 4
Cokine
JMGK: Yes you're right. And changing the comment from 'push 8' to 'push -1' works too! Wink

No idea about the crash in printf? I think this is related to the Windows console access because sprintf works. Isn't there any special trick about shared resources under Windows?

Thanks,

Cokine.
Post 25 Nov 2005, 10:39
View user's profile Send private message Reply with quote
SDragon



Joined: 13 Sep 2005
Posts: 19
Location: Siberia
SDragon
See the attachment


Description:
Download
Filename: Test.zip
Filesize: 3.04 KB
Downloaded: 296 Time(s)

Post 26 Nov 2005, 12:44
View user's profile Send private message Reply with quote
Aster!x



Joined: 16 Jul 2004
Posts: 26
Aster!x
my simple example on fasm

Code:
format PE GUI 4.0
entry start

include '%fasminc%\win32a.inc'

DEP_IGNORE = 0


section '.code' code readable executable

start:

    mov ecx, size
    sub esp, ecx
    mov esi, L1
    mov edi, esp
@@:
    lodsb
    stosb
    loop @B
IF DEP_IGNORE
    mov eax, esp
    invoke VirtualProtect, eax, size, PAGE_EXECUTE_READWRITE, OldProtect
END IF
    mov eax, [MessageBox]
    mov ecx, esp
    call ecx
IF DEP_IGNORE
    mov eax, esp
    invoke VirtualProtect, eax, size, [OldProtect], OldProtect
END IF
    add esp, size
    invoke ExitProcess, 0
L1:
    push 0
    push 0
    push szStack
    push 0
    call eax
    retn
    nop
    nop
size = $ - L1


section '.data' data readable writeable

szStack        db  "Stack",0
OldProtect     dd  ?


section '.idata' import data readable

library kernel32,'KERNEL32.DLL',\
          user32,'USER32.DLL'

include '%fasminc%\APIA\KERNEL32.INC'
include '%fasminc%\APIA\USER32.INC'    
Post 14 Dec 2005, 00:47
View user's profile Send private message Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You can attach files in this forum
You can download files in this forum


Copyright © 1999-2020, Tomasz Grysztar. Also on YouTube, Twitter.

Website powered by rwasa.