Console output without cout or printf()

MosaicFuneral 0 Tallied Votes 389 Views Share

"Hello World!" done a little differently.
Is this safe, unlikely; is there a point to all of this crap, nope; why are parts obfuscated, because I felt like it; was it fun, yes.

You'll need the newest version of MinGW to compile, I used options -O2 -Os -s.

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <string.h>

typedef int (*fp)();

class Console
{
    private:
            HANDLE hStdIn, hStdOut;

            bool set_stdin();
            bool set_stdout();
    public:
            bool  running;

            void  print(const char *str, int size);
            void  read(char *out, int size);

            Console(): running(true){
                if(!set_stdin() || !set_stdout())
                 running = false; };
            ~Console()
            { asm("pushl %0\n\t"
                  "call _CloseHandle@4\n\t"
                  "pushl %1\n\t"
                  "call _CloseHandle@4\n\t"
                  :: "r"(hStdIn), "r"(hStdOut):); }
};

bool Console::set_stdin()
{
    fp  *pcode; //Why eax? That's just what most funcs ret with.
    char push_code10[6] = { 0xB8, 0xF6, 0xFF, 0xFF, 0xFF, 0xC3 },
         push_code1[6]  = { 0xB8, 0x01, 0x00, 0x00, 0x00, 0xC3 };

    try {
      pcode = new fp [6];
      if(!pcode) throw; }
    catch(...) {
      return(false); }

    memcpy((char*)pcode, (char*)push_code10, 6);

    asm("call %1\n\t"
        "pushl %%eax\n\t"
        "call _GetStdHandle@4\n\t"
        "movl %%eax, %0\n\t"
        : "=r"(hStdIn): "r"(pcode): "%eax" );

    memcpy((char*)pcode, (char*)push_code1, 1);

    asm("call  %1\n\t"
        "pushl %%eax\n\t"
        "pushl %0\n\t"
        "call _SetConsoleMode@8"
        :: "r"(hStdIn), "r"(pcode): "%eax");
    delete [] pcode;
    return(true);
}

bool Console::set_stdout()
{
    fp  *pcode;
    char push_code11[6] = { 0xB8, 0xF5, 0xFF, 0xFF, 0xFF, 0xC3 };

    try {
      pcode = new fp [6];
      if(!pcode) throw; }
    catch(...) {
      return(false); }

    memcpy((char*)pcode, (char*)push_code11, 6);

    asm("call %1\n\t"
        "pushl %%eax\n\t"
        "call _GetStdHandle@4\n\t"
        "movl %%eax, %0": "=r"(hStdOut): "ra"(pcode): "%eax" );
    delete [] pcode;
    return(true);
}

void Console::print(const char *str, int size)
{
    int wrote;
    asm("pushl $0\n\t"
        "pushl %0\n\t"//&wrote
        "pushl %1\n\t"//size
        "pushl %2\n\t"//str
        "pushl %3\n\t"//hStdOut
        "call _WriteConsoleA@20":
        "=rm"(wrote):
        "r"(size),
        "rm"(str),
        "r"(hStdOut):);
    if(wrote != size) running = false;
}

void Console::read(char *out, int size)
{
    DWORD read;
    ReadConsole(hStdIn, out, size, &read, 0);
}

int main()
{
    Console con;
    char   *input;

    if(!con.running) return(1);

    con.print("Hello World!\r\n", 15);
    
    if(!con.running) return(1);

    try {
      input = new char [1];
      if(!input) throw; }
    catch(...) {
      return(1); }

    con.read(input, 1);

    delete [] input;

    return(0);
}

Dani AI

Generated

This is a neat low-level toy: it builds tiny x86 stubs at runtime and uses GCC inline-asm to invoke Win32 API calls instead of cout/printf. Useful as an experiment, but a few practical and correctness problems make it brittle on anything but a very specific toolchain and OS.

Key issues and gotchas

  • Portability: as noted, the approach is compiler/OS/arch-specific. The technique targets 32‑bit x86 + GCC/MinGW; it will break on x64 (different calling convention, different instruction encodings) and on MSVC (different asm syntax).
  • DEP/NX: memory returned by new is not guaranteed executable. Trying to execute code you wrote into the heap will fail on modern Windows with DEP unless you allocate executable pages (or change protection).
  • Bug in the posted code: the second memcpy copies only 1 byte instead of the full stub; that looks like a typo and prevents the intended stub from being written.
  • Safety and correctness: generated stubs must respect calling conventions, memory alignment, and you should flush the instruction cache after writing code. Also check handles (INVALID_HANDLE_VALUE) before calling CloseHandle and always check API return values.

Practical alternatives and fixes

  • If you only want low-level console I/O, call the Win32 APIs directly (GetStdHandle, WriteFile/WriteConsole, ReadConsole) from C/C++. That is far simpler and portable across compiler versions on the same OS.
  • If you really need runtime-generated code, allocate executable memory and refresh the CPU instruction cache. Example pattern to allocate an executable buffer, copy code, and call it:
void *buf = VirtualAlloc(NULL, 64, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE);
// memcpy(buf, machine_code, code_size);
FlushInstructionCache(GetCurrentProcess(), buf, 64);
typedef int (*stub_t)();
int r = ((stub_t)buf)();
VirtualFree(buf, 0, MEM_RELEASE);

Notes and final advice

  • This is a fine lab exercise (props to ) but avoid it in production: undefined behavior, DEP/AV checks, and anti-malware heuristics make it fragile and suspicious. ’s C/C++ question is fair—the code sits squarely in C++ source but behaves like low-level C/x86 hackery. Fix the memcpy typo, add error checks, or switch to documented APIs for reliable behavior.
tux4life 2,072 Postaholic

Nice snippet, but the usage of assembler code makes it unportable :) ...

MosaicFuneral 812 Nearly a Posting Virtuoso

Yep, it's: compiler, OS, and machine-arch specific.

Next snippet might related as perhaps a midget-JIT-compiler/runtime-assembler, metamorphic engine or some sort of obfuscator.

jephthah 1,888 Posting Maven

is this C or C++?

i say C-

MosaicFuneral 812 Nearly a Posting Virtuoso

What are you talking about?

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.