Blob Blame History Raw
/********************************************************/
/*  ntapi: Native API core library                      */
/*  Copyright (C) 2013--2021  SysDeer Technologies, LLC */
/*  Released under GPLv2 and GPLv3; see COPYING.NTAPI.  */
/********************************************************/

#include <psxtypes/psxtypes.h>
#include <ntapi/ntapi.h>
#include "ntapi_impl.h"

/************************************************************/
/* beginning with version 6.0, explicit thread registration */
/* with csrss is no longer required.  the code below should */
/* work with all known versions of NT, however it will only */
/* be used when run on the now-obsolete versions of the OS. */
/************************************************************/

/**
   Nebbett was pretty much right in his interpretation of
   the csrss port message; and as long as one changes
   uint32_t to uintptr_t (especially when it comes to the
   unknown parameters), then the structures behave as
   expected according to his book.

   SysInternals: ProcessExplorer: csrss.exe: the stack shows
   a thread in csrsrv.dll that has CsrUnhandledExceptionFilter
   as its start address, and ntdll!NtReplyWaitReceivePort as
   its next function call.  This suggests that csrss still
   uses LPC (at least to some extent) for communication with
   user processes.

   Given the above, we may deduce that CsrClientCallServer
   contains a call to ZwRequestWaitReplyPort.  Assuming
   the machine code in ntdll is as optimized as possible,
   we may then conclude that on x86 machines, this would be
   an E8 call using relative 32-bit addressing on both NT32
   and NT64.

   On the 32-bit variant of the operating system, the first
   argument is passed on the stack, and is normally expressed
   in terms of an offset from the ds register.

   On the 64-bit variant of the operating system, the first
   argument is passed in the rcx register.  Here, again,
   machine code optimization suggests that the address of
   CsrPortHandle will be provided as a 32-bit relative address,
   or else the code will be larger by several bytes.

   The rest is based on simple logic and straight-forward
   heuristics.  Since we know the addresses of CsrClientCallSertver
   and ZwRequestWaitReplyPort, we first find the call to the latter
   function within the former.  Once we have found that call, we
   start going back to look for the argument-passing
   opcode, and finally do the math to obtain the address of
   CsrPortHandle.
**/


#if defined(__X86_MODEL)
void ** __cdecl __ntapi_tt_get_csr_port_handle_addr_by_logic_i386(void)
{
	#define MAX_BYTES_BETWEEN_ARG1_PUSH_AND_E8_CALL 0x20
	#define MAX_FN_BYTES_TO_TEST			0x800

	typedef struct __attr_aligned__ (1) __attr_packed__ __x86_e8_call_signature {
		unsigned char	__opcode_current_e8;
		unsigned char	__addr_relative[4];
		unsigned char	__opcode_next_any;
	} _x86_e8_call_signature;

	typedef struct __attr_aligned__ (1) __attr_packed__ __x86_push_ds_signature {
		unsigned char	__push;
		unsigned char	__ds;
		unsigned char	__push_ds_arg;
	} _x86_push_ds_signature;

	unsigned char *			ptr_test;
	_x86_e8_call_signature *	ptr_e8_call;
	_x86_push_ds_signature *	ptr_push_ds;
	int32_t				offset;

	/* type-punned tyrants */
	int32_t *			prelative;
	int32_t				relative;
	uintptr_t *			pport_addr;


	/* calling a function within the same library: assume E8 call */
	for (offset = 0; offset < MAX_FN_BYTES_TO_TEST; offset++) {
		ptr_test = (unsigned char *)__ntapi->csr_client_call_server
				+ offset;

		if (*ptr_test == 0xE8) {
			ptr_e8_call = (_x86_e8_call_signature *)ptr_test;

			/* make our type-punned tyrant compiler happy */
			prelative = (int32_t *)&(ptr_e8_call->__addr_relative);
			relative = *prelative;

			/* are we calling ZwRequestWaitReplyPort? */
			if ((uintptr_t)(__ntapi->zw_request_wait_reply_port) ==
					(uintptr_t)&(ptr_e8_call->__opcode_next_any)
					+ relative) {
				/* assume ds relative address for arg1, go back to find it */
				for (offset = 0; offset < MAX_BYTES_BETWEEN_ARG1_PUSH_AND_E8_CALL; offset++) {
					ptr_push_ds = (_x86_push_ds_signature *)((uintptr_t)ptr_e8_call - offset);

					if ((ptr_push_ds->__push == 0xFF) &&
							(ptr_push_ds->__ds == 0x35)) {
						/* bingo */
						/* make our type-punned tyrant compiler happy */
						pport_addr = (uintptr_t *)&(ptr_push_ds->__push_ds_arg);

						/* all done */
						return *(void ***)pport_addr;
					}
				}
			}
		}
	}

	/* CsrPortHandle not found */
	return (void **)0;
}
#endif


#if defined(__X86_64_MODEL)
void ** __ntapi_tt_get_csr_port_handle_addr_by_logic_x86_64(void)
{
	#define MAX_BYTES_BETWEEN_ARG1_PUSH_AND_E8_CALL 0x20
	#define MAX_FN_BYTES_TO_TEST			0x800

	typedef struct __attr_aligned__ (1) __attr_packed__ __x86_e8_call_signature {
		unsigned char	__opcode_current_e8;
		unsigned char	__addr_relative[4];
		unsigned char	__opcode_next_any;
	} _x86_e8_call_signature;

	typedef struct __attr_aligned__ (1) __attr_packed__ __x86_move_rcx_rel_signature {
		unsigned char	__move;
		unsigned char	__rcx;
		unsigned char	__relative;
		unsigned char	__arg_32_relative[4];
		unsigned char	__opcode_next_any;
	} _x86_move_rcx_rel_signature;

	unsigned char *			ptr_test;
	_x86_e8_call_signature *	ptr_e8_call;
	_x86_move_rcx_rel_signature *	ptr_move_rcx_rel;
	int32_t				offset;
	int32_t				relative;
	int32_t *			prelative;
	int				bias;


	/* calling a function within the same library: assume E8 call and 32-bit relative addressing */
	for (bias = 0; bias < MAX_FN_BYTES_TO_TEST; bias++) {
		ptr_test  = (unsigned char *)__ntapi->csr_client_call_server;
		ptr_test += bias;

		if (*ptr_test == 0xE8) {
			ptr_e8_call = (_x86_e8_call_signature *)ptr_test;

			/* please our type-punned tyrant compiler */
			prelative = (int32_t *)&(ptr_e8_call->__addr_relative);
			relative = *prelative;

			/* are we calling ZwRequestWaitReplyPort? */
			/* comparing, not writing; ignore type-punned msgs. */
			if ((uintptr_t)(__ntapi->zw_request_wait_reply_port) ==
					(uintptr_t)&(ptr_e8_call->__opcode_next_any)
					+ relative) {
				/* arg1 must be passed in rcx, so go back to find it */
				for (offset = 0; offset < MAX_BYTES_BETWEEN_ARG1_PUSH_AND_E8_CALL; offset++) {
					ptr_move_rcx_rel = (_x86_move_rcx_rel_signature *)((uintptr_t)ptr_e8_call - offset);

					if ((ptr_move_rcx_rel->__move == 0x48) &&
							(ptr_move_rcx_rel->__rcx == 0x8b) &&
							(ptr_move_rcx_rel->__relative == 0x0d)) {
						/* bingo */
						/* make our type-punned tyrant compiler happy */
						prelative = (int32_t *)&(ptr_move_rcx_rel->__arg_32_relative);
						relative  = *prelative;

						/* all done */
						return (void **)(
							(uintptr_t)&ptr_move_rcx_rel->__opcode_next_any
							+ relative);
					}
				}
			}
		}
	}

	/* CsrPortHandle not found */
	return 0;
}
#endif