hasherezade / masm_shc

A helper utility for creating shellcodes. Cleans MASM file generated by MSVC, gives refactoring hints.
MIT License
156 stars 30 forks source link

Issues when compiling PIC to x86 #8

Closed 2Trepidatious closed 2 months ago

2Trepidatious commented 2 months ago

Developing on a windows 10 VM and following the instructions as closely as I am able, I have been unable to successfully run a PIC executable compiled to x86. This includes my own code as well as the example code provided at the end of the documentation. While the example code doesn't run at all for me, I have been able to run my executable, but the send() function fails with error 10045 WSAEOPNOTSUPP. The non-pic version of the executable runs just fine, as well as when I compile the executable to x64 without any changes to the source code. I've taken apart the exe in a disassembler and can see where the path of execution diverges within the send() function as a result of a single value saved to the stack not being 0. Manually setting this value to 0 causes the function to execute as expected. I have yet to trace the stack frames back far enough to find out where this value is coming from, but I was hoping posting here would allow me to get a second pair of eyes on my code and compilation process.

Below is my own simple server code that I am trying to compile to x86. I have tried both WINAPI and PASCAL FAR for the calling conventions of the networking functions.

#include <stdio.h>
#include <winsock2.h>
#include <time.h>
#include <stdint.h>
#include <peb_lookup.h>
typedef struct FunctionStruct_t_s
{
  int (PASCAL FAR * _listen)(SOCKET, int);
  int (PASCAL FAR * _send)(SOCKET, char *, int, int);
  int (PASCAL FAR * _bind)(SOCKET, struct sockaddr *, int);
  int (PASCAL FAR * _closesocket)(SOCKET);
  u_short (PASCAL FAR * _htons)(u_short);
  time_t (WINAPI * _time)(time_t *);
  void (WINAPI * _exit)(int);
  int (PASCAL FAR * _WSAStartup)(WORD, LPWSADATA);
  char * (PASCAL FAR * _ctime)(time_t *);
  SOCKET (PASCAL FAR * _accept)(SOCKET, struct sockaddr *, int *);
  void * (PASCAL FAR * _calloc)(size_t, size_t);
  int (WINAPI * _printf)(char *, ...);
  void (WINAPI * _free)(void *);
  int (PASCAL FAR * _WSACleanup)();
  SOCKET (PASCAL FAR * _socket)(int, int, int);
  size_t (WINAPI * _strlen)(char *);
  DWORD (PASCAL FAR * _WSAGetLastError)();
} FunctionStruct_t;
typedef struct GlobalStruct_t_s
{
  int PORT;
} GlobalStruct_t;
void initializeWinsock(FunctionStruct_t * functions_p, GlobalStruct_t * globals_p)
{
  WSADATA wsaData;
  if (functions_p->_WSAStartup((WORD) (((BYTE) (((DWORD_PTR) 2) & 0xff)) | (((WORD) ((BYTE) (((DWORD_PTR) 2) & 0xff))) << 8)), &wsaData) != 0)
  {
    functions_p->_printf("Failed to initialize Winsock: %d\n", functions_p->_WSAGetLastError());
    functions_p->_exit(1);
  }
}

SOCKET createSocket(FunctionStruct_t * functions_p, GlobalStruct_t * globals_p)
{
  SOCKET serverSocket = functions_p->_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if (serverSocket == INVALID_SOCKET)
  {
    functions_p->_printf("Could not create socket: %d\n", functions_p->_WSAGetLastError());
    functions_p->_WSACleanup();
    functions_p->_exit(1);
  }
  return serverSocket;
}

void bindSocket(FunctionStruct_t * functions_p, GlobalStruct_t * globals_p, SOCKET serverSocket)
{
  struct sockaddr_in server;
  server.sin_family = AF_INET;
  server.sin_addr.s_addr = INADDR_ANY;
  server.sin_port = functions_p->_htons(globals_p->PORT);
  if (functions_p->_bind(serverSocket, (struct sockaddr * ) (&server), sizeof(server)) == SOCKET_ERROR)
  {
    functions_p->_printf("Bind failed with error code: %d\n", functions_p->_WSAGetLastError());
    functions_p->_closesocket(serverSocket);
    functions_p->_WSACleanup();
    functions_p->_exit(1);
  }
}

void listenForConnections(FunctionStruct_t * functions_p, GlobalStruct_t * globals_p, SOCKET serverSocket)
{
  if (functions_p->_listen(serverSocket, 3) == SOCKET_ERROR)
  {
    functions_p->_printf("Listen failed with error code: %d\n", functions_p->_WSAGetLastError());
    functions_p->_closesocket(serverSocket);
    functions_p->_WSACleanup();
    functions_p->_exit(1);
  }
  functions_p->_printf("Server is listening on port %d...\n", globals_p->PORT);
}

void acceptConnection(FunctionStruct_t * functions_p, GlobalStruct_t * globals_p, SOCKET serverSocket)
{
  struct sockaddr_in client;
  int c = sizeof(struct sockaddr_in);
  SOCKET clientSocket = functions_p->_accept(serverSocket, (struct sockaddr * ) (&client), &c);
  if (clientSocket == INVALID_SOCKET)
  {
    functions_p->_printf("Accept failed with error code: %d\n", functions_p->_WSAGetLastError());
    functions_p->_closesocket(serverSocket);
    functions_p->_WSACleanup();
    functions_p->_exit(1);
  }
  functions_p->_printf("Connection accepted.\n");
  time_t currentTime;
  functions_p->_time(&currentTime);
  char * timeString = functions_p->_ctime(&currentTime);
  functions_p->_printf("Time is %s", timeString);
  int32_t ret = functions_p->_send(clientSocket, timeString, functions_p->_strlen(timeString), 0);
  if ((-1) == ret)
  {
    int error = functions_p->_WSAGetLastError();
    functions_p->_printf("Send Failed with error: %d.\n", error);
  }
  else
  {
    functions_p->_printf("Sent %d bytes.\n", ret);
  }
  functions_p->_closesocket(clientSocket);
}

GlobalStruct_t *  init_GlobalStruct(FunctionStruct_t * functions_p)
{
  GlobalStruct_t * globals_p = (GlobalStruct_t * ) functions_p->_calloc(1, sizeof(GlobalStruct_t));
  if (NULL == globals_p)
  {
    goto G_CLEANUP;
  }
  globals_p->PORT = 12345;
  G_CLEANUP:
  return globals_p;

}

FunctionStruct_t *  init_FunctionStruct()
{
  FunctionStruct_t * functions_p = NULL;
  LPVOID kernel32_handle = get_module_by_name((const LPWSTR)L"kernel32.dll");
  if (NULL == kernel32_handle)
  {
    goto F_CLEANUP;
  }
  LPVOID load_lib = get_func_by_name((HMODULE)kernel32_handle, (LPSTR) "LoadLibraryA");
  if (NULL == load_lib)
  {
    goto F_CLEANUP;
  }
  LPVOID get_proc = get_func_by_name((HMODULE)kernel32_handle, (LPSTR) "GetProcAddress");
  if (NULL == get_proc)
  {
    goto F_CLEANUP;
  }
  HMODULE (WINAPI * _LoadLibraryA)(LPCSTR) = (HMODULE (WINAPI * )(LPCSTR)) load_lib;
  FARPROC (WINAPI * _GetProcAddress)(HMODULE, LPCSTR) = (FARPROC (WINAPI * )(HMODULE, LPCSTR)) get_proc;
  HMODULE ws2_32_handle = _LoadLibraryA("ws2_32.dll");
  if (NULL == ws2_32_handle)
  {
    goto F_CLEANUP;
  }
  int (PASCAL FAR * _listen)(SOCKET, int) = (int (PASCAL FAR * )(SOCKET, int)) _GetProcAddress(ws2_32_handle, "listen");
  if (NULL == _listen)
  {
    goto F_CLEANUP;
  }
  int (PASCAL FAR * _send)(SOCKET, char *, int, int) = (int (PASCAL FAR * )(SOCKET, char *, int, int)) _GetProcAddress(ws2_32_handle, "send");
  if (NULL == _send)
  {
    goto F_CLEANUP;
  }
  int (PASCAL FAR * _bind)(SOCKET, struct sockaddr *, int) = (int (PASCAL FAR * )(SOCKET, struct sockaddr *, int)) _GetProcAddress(ws2_32_handle, "bind");
  if (NULL == _bind)
  {
    goto F_CLEANUP;
  }
  int (PASCAL FAR * _closesocket)(SOCKET) = (int (PASCAL FAR * )(SOCKET)) _GetProcAddress(ws2_32_handle, "closesocket");
  if (NULL == _closesocket)
  {
    goto F_CLEANUP;
  }
  u_short (PASCAL FAR * _htons)(u_short) = (u_short (PASCAL FAR * )(u_short)) _GetProcAddress(ws2_32_handle, "htons");
  if (NULL == _htons)
  {
    goto F_CLEANUP;
  }
  HMODULE msvcrt_handle = _LoadLibraryA("msvcrt.dll");
  if (NULL == msvcrt_handle)
  {
    goto F_CLEANUP;
  }
  time_t (WINAPI * _time)(time_t *) = (time_t (WINAPI * )(time_t *)) _GetProcAddress(msvcrt_handle, "time");
  if (NULL == _time)
  {
    goto F_CLEANUP;
  }
  HMODULE ucrtbase_handle = _LoadLibraryA("ucrtbase.dll");
  if (NULL == ucrtbase_handle)
  {
    goto F_CLEANUP;
  }
  void (WINAPI * _exit)(int) = (void (WINAPI * )(int)) _GetProcAddress(ucrtbase_handle, "exit");
  if (NULL == _exit)
  {
    goto F_CLEANUP;
  }
  int (PASCAL FAR * _WSAStartup)(WORD, LPWSADATA) = (int (PASCAL FAR * )(WORD, LPWSADATA)) _GetProcAddress(ws2_32_handle, "WSAStartup");
  if (NULL == _WSAStartup)
  {
    goto F_CLEANUP;
  }
  char * (WINAPI * _ctime)(time_t *) = (char * (WINAPI * )(time_t *)) _GetProcAddress(msvcrt_handle, "ctime");
  if (NULL == _ctime)
  {
    goto F_CLEANUP;
  }
  SOCKET (PASCAL FAR * _accept)(SOCKET, struct sockaddr *, int *) = (SOCKET (PASCAL FAR * )(SOCKET, struct sockaddr *, int *)) _GetProcAddress(ws2_32_handle, "accept");
  if (NULL == _accept)
  {
    goto F_CLEANUP;
  }
  void * (WINAPI * _calloc)(size_t, size_t) = (void * (WINAPI * )(size_t, size_t)) _GetProcAddress(ucrtbase_handle, "calloc");
  if (NULL == _calloc)
  {
    goto F_CLEANUP;
  }
  int (WINAPI * _printf)(char *, ...) = (int (WINAPI * )(char *, ...)) _GetProcAddress(msvcrt_handle, "printf");
  if (NULL == _printf)
  {
    goto F_CLEANUP;
  }
  void (WINAPI * _free)(void *) = (void (WINAPI * )(void *)) _GetProcAddress(msvcrt_handle, "free");
  if (NULL == _free)
  {
    goto F_CLEANUP;
  }
  int (PASCAL FAR * _WSACleanup)() = (int (PASCAL FAR * )()) _GetProcAddress(ws2_32_handle, "WSACleanup");
  if (NULL == _WSACleanup)
  {
    goto F_CLEANUP;
  }
  SOCKET (PASCAL FAR * _socket)(int, int, int) = (SOCKET (PASCAL FAR * )(int, int, int)) _GetProcAddress(ws2_32_handle, "socket");
  if (NULL == _socket)
  {
    goto F_CLEANUP;
  }
  size_t (WINAPI * _strlen)(char *) = (size_t (WINAPI * )(char *)) _GetProcAddress(msvcrt_handle, "strlen");
  if (NULL == _strlen)
  {
    goto F_CLEANUP;
  }
  DWORD (PASCAL FAR * _WSAGetLastError)() = (DWORD (PASCAL FAR * )()) _GetProcAddress(ws2_32_handle, "WSAGetLastError");
  if (NULL == _WSAGetLastError)
  {
    goto F_CLEANUP;
  }
  functions_p = (FunctionStruct_t * ) _calloc(1, sizeof(FunctionStruct_t));
  if (NULL == functions_p)
  {
    goto F_CLEANUP;
  }
  functions_p->_listen = _listen;
  functions_p->_send = _send;
  functions_p->_bind = _bind;
  functions_p->_closesocket = _closesocket;
  functions_p->_htons = _htons;
  functions_p->_time = _time;
  functions_p->_exit = _exit;
  functions_p->_WSAStartup = _WSAStartup;
  functions_p->_ctime = _ctime;
  functions_p->_accept = _accept;
  functions_p->_calloc = _calloc;
  functions_p->_printf = _printf;
  functions_p->_free = _free;
  functions_p->_WSACleanup = _WSACleanup;
  functions_p->_socket = _socket;
  functions_p->_strlen = _strlen;
  functions_p->_WSAGetLastError = _WSAGetLastError;
  F_CLEANUP:
  return functions_p;

}

int main()
{
  FunctionStruct_t * functions_p = NULL;
  functions_p = init_FunctionStruct();
  if (NULL == functions_p)
  {
    goto F_CLEANUP;
  }
  GlobalStruct_t * globals_p = NULL;
  globals_p = init_GlobalStruct(functions_p);
  if (NULL == globals_p)
  {
    goto G_CLEANUP;
  }
  initializeWinsock(functions_p, globals_p);
  SOCKET serverSocket = createSocket(functions_p, globals_p);
  bindSocket(functions_p, globals_p, serverSocket);
  listenForConnections(functions_p, globals_p, serverSocket);
  while (1)
  {
    acceptConnection(functions_p, globals_p, serverSocket);
  }

  functions_p->_closesocket(serverSocket);
  functions_p->_WSACleanup();
  functions_p->_free(globals_p);
  G_CLEANUP:

  functions_p->_free(functions_p);
  F_CLEANUP:

  return 0;
}

I compile the code as below, using the x86 version of the VCVars bat file loaded into my environment:

$> cl /c /FA /GS- .\output\server\server.c /I .\src
$> .\build\masm_shc\Debug\masm_shc.exe server.asm pic_server.asm
$> ml /c pic_server.asm
$> link pic_server.obj /ENTRY:main

The output from my code being executed is below:

Server is listening on port 12345...
Connection accepted.
Time is Fri Jun 07 23:08:01 2024
Send Failed with error: 10045.

I'm building masm_shc as so:

cmake -G "Visual Studio 17 2022" -A Win32 .
cmake --build .
hasherezade commented 2 months ago

hi @2Trepidatious ! Thank you for sharing, I will look into it as soon as I get some free time.

hasherezade commented 2 months ago

I have been unable to successfully run a PIC executable compiled to x86. This includes my own code as well as the example code provided at the end of the documentation.

@2Trepidatious - I just checked quickly my example code, and TBH I didn't have any problems running it. My system is Windows 10 Enterprise (10.0.19045). The resulting shellcode (plus the executable) that I've got is attached below:

knock32.zip

And this is how it looks in action. The Python script knocks to the ports in a particular order, and the shellcode does exactly what it is supposed to, sending the expected response, and exiting upon the sequence completion:

results

As I understood you have problems with this particular demo, right? So please check it out and let me know if it works for you. I will check your code soon, and let you know.

hasherezade commented 2 months ago

@2Trepidatious - ok. so I refactored your code a bit - mostly the initialization of the functions (I tried not to interfere in it more than necessarily). Here is the snippet: https://gist.github.com/hasherezade/26f3ae0316337cf97e04bd0b578cea50 Check it out, it should work now. I tested it and it worked for me.

test

Attaching the shellcode: issue32_A.zip

2Trepidatious commented 2 months ago

Thank you. That works.