There is no super easy way to find the bottom of the stack in x86. There's a register "ebp" which points to the base of the stack frame. The address to which "ebp" points, contains the value of the previous "ebp" for the previous stack frame. Its basically a linked list we can traverse until we get to the NULL ebp (which is the bottom frame of the stack).

raw

ULONG findStackBase()
{
    // EBP points to the previous value of ebp on the stack
    PULONG baseptr;
    __asm {
        mov baseptr, ebp
    }

    // Traverse the list until we get to the end.
    while (NULL != *baseptr)
    {
        baseptr = (PULONG)*baseptr;
    }

    return (ULONG)baseptr;
}

This code snippet gets the value of "ebp" and runs down the list of stack base pointers until we find the end.

This really only has a place as a debugging function. Orrr, maybe a way to control your poorly written recursive loop... Here's an example of program which executes a loop, by recursion. Once the loop makes it close to the bottom of the stack, it does a long jump back up to the top of the stack so that it can keep going.

raw

// precomp.h
#pragma once
#include <stdlib.h>
#include <stdio.h>
#include <Windows.h>
#include <setjmp.h>
#include <winnetwk.h>
#include "looper.h"



// looper.h

#pragma once

// Typdef for the function to pass as an argument to the looping function.
typedef VOID(*looperfunc)(ULONG, PVOID);

VOID ExecuteLoop(ULONG count, looperfunc);



// looper.c

#include "precomp.h"
#include <setjmp.h>

#define STACK_RESET_LIMIT 1024 * 800 // 800kb, we should reset

typedef struct _loopcontext {
    ULONG current; // Current count
    ULONG maximum; // Maximum count
    jmp_buf jumpBuffer; // Buffer to store the jump location for longjump
    looperfunc func; // The function to execute 
    ULONG stackStart; // the bottom of the stack
} loopcontext, *Ploopcontext;

ULONG findStackBase()
{
    // EBP points to the previous value of ebp on the stack
    PULONG baseptr;
    __asm {
        mov baseptr, ebp
    }

    // Traverse the list until we get to the end.
    while (NULL != *baseptr)
    {
        baseptr = (PULONG)*baseptr;
    }

    return (ULONG)baseptr;
}

VOID loopHelper(Ploopcontext context)
{
    // Base condition
    if (context->current >= context->maximum)
    {
        return;
    }

    // Get the value of esp
    ULONG currentStackPtr;
    __asm {
        mov currentStackPtr, esp
    };
    // Execute our callback
    context->func(context->current, NULL);

    // Increment the loop counter
    context->current++;

    // Find our distance from the base of the stack (the stack grows downwards)
    ULONG distance = (context->stackStart - currentStackPtr);
    if (distance >= STACK_RESET_LIMIT)
    {
        longjmp((int*)&context->jumpBuffer, 1);
    }

    // Recurse
    loopHelper(context);
}

/// <Summary>
/// Execute a loop, using recursion
/// </Summary>
VOID ExecuteLoop(ULONG count, looperfunc func)
{
    // Initialize the loop context
    loopcontext context;
    context.current = 0;
    context.maximum = count;
    context.func = func;
    context.stackStart = findStackBase();

    setjmp((int*)&context.jumpBuffer);

    // Run the loop
    loopHelper(&context);
}



// main.c

#include "precomp.h"
#include "looper.h"

// This is the func
VOID testfunc(ULONG l, PVOID val)
{
    ULONG buffer[400];
    printf("looping %u \n", l);
}

int main(int arc, char ** argv)
{
    ExecuteLoop(10000, testfunc);
    return 0;
}