Blame src/system/ntapi_tt_get_csr_port_handle_addr_by_logic.c

dd89bb
/********************************************************/
dd89bb
/*  ntapi: Native API core library                      */
59d585
/*  Copyright (C) 2013--2021  Z. Gilboa                 */
dd89bb
/*  Released under GPLv2 and GPLv3; see COPYING.NTAPI.  */
dd89bb
/********************************************************/
dd89bb
dd89bb
#include <psxtypes/psxtypes.h>
dd89bb
#include <ntapi/ntapi.h>
dd89bb
#include "ntapi_impl.h"
dd89bb
dd89bb
/************************************************************/
dd89bb
/* beginning with version 6.0, explicit thread registration */
dd89bb
/* with csrss is no longer required.  the code below should */
dd89bb
/* work with all known versions of NT, however it will only */
dd89bb
/* be used when run on the now-obsolete versions of the OS. */
dd89bb
/************************************************************/
dd89bb
dd89bb
/**
dd89bb
   Nebbett was pretty much right in his interpretation of
dd89bb
   the csrss port message; and as long as one changes
dd89bb
   uint32_t to uintptr_t (especially when it comes to the
dd89bb
   unknown parameters), then the structures behave as
dd89bb
   expected according to his book.
dd89bb
dd89bb
   SysInternals: ProcessExplorer: csrss.exe: the stack shows
dd89bb
   a thread in csrsrv.dll that has CsrUnhandledExceptionFilter
dd89bb
   as its start address, and ntdll!NtReplyWaitReceivePort as
dd89bb
   its next function call.  This suggests that csrss still
dd89bb
   uses LPC (at least to some extent) for communication with
dd89bb
   user processes.
dd89bb
dd89bb
   Given the above, we may deduce that CsrClientCallServer
dd89bb
   contains a call to ZwRequestWaitReplyPort.  Assuming
dd89bb
   the machine code in ntdll is as optimized as possible,
dd89bb
   we may then conclude that on x86 machines, this would be
dd89bb
   an E8 call using relative 32-bit addressing on both NT32
dd89bb
   and NT64.
dd89bb
dd89bb
   On the 32-bit variant of the operating system, the first
dd89bb
   argument is passed on the stack, and is normally expressed
dd89bb
   in terms of an offset from the ds register.
dd89bb
dd89bb
   On the 64-bit variant of the operating system, the first
dd89bb
   argument is passed in the rcx register.  Here, again,
dd89bb
   machine code optimization suggests that the address of
dd89bb
   CsrPortHandle will be provided as a 32-bit relative address,
dd89bb
   or else the code will be larger by several bytes.
dd89bb
dd89bb
   The rest is based on simple logic and straight-forward
dd89bb
   heuristics.  Since we know the addresses of CsrClientCallSertver
dd89bb
   and ZwRequestWaitReplyPort, we first find the call to the latter
dd89bb
   function within the former.  Once we have found that call, we
dd89bb
   start going back to look for the argument-passing
dd89bb
   opcode, and finally do the math to obtain the address of
dd89bb
   CsrPortHandle.
dd89bb
**/
dd89bb
dd89bb
dd89bb
#if defined(__X86_MODEL)
dd89bb
void ** __cdecl __ntapi_tt_get_csr_port_handle_addr_by_logic_i386(void)
dd89bb
{
dd89bb
	#define MAX_BYTES_BETWEEN_ARG1_PUSH_AND_E8_CALL 0x20
dd89bb
	#define MAX_FN_BYTES_TO_TEST			0x800
dd89bb
dd89bb
	typedef struct __attr_aligned__ (1) __attr_packed__ __x86_e8_call_signature {
dd89bb
		unsigned char	__opcode_current_e8;
dd89bb
		unsigned char	__addr_relative[4];
dd89bb
		unsigned char	__opcode_next_any;
dd89bb
	} _x86_e8_call_signature;
dd89bb
dd89bb
	typedef struct __attr_aligned__ (1) __attr_packed__ __x86_push_ds_signature {
dd89bb
		unsigned char	__push;
dd89bb
		unsigned char	__ds;
dd89bb
		unsigned char	__push_ds_arg;
dd89bb
	} _x86_push_ds_signature;
dd89bb
dd89bb
	unsigned char *			ptr_test;
dd89bb
	_x86_e8_call_signature *	ptr_e8_call;
dd89bb
	_x86_push_ds_signature *	ptr_push_ds;
dd89bb
	int32_t				offset;
dd89bb
dd89bb
	/* type-punned tyrants */
dd89bb
	int32_t *			prelative;
dd89bb
	int32_t				relative;
dd89bb
	uintptr_t *			pport_addr;
dd89bb
dd89bb
dd89bb
	/* calling a function within the same library: assume E8 call */
dd89bb
	for (offset = 0; offset < MAX_FN_BYTES_TO_TEST; offset++) {
dd89bb
		ptr_test = (unsigned char *)__ntapi->csr_client_call_server
dd89bb
				+ offset;
dd89bb
dd89bb
		if (*ptr_test == 0xE8) {
dd89bb
			ptr_e8_call = (_x86_e8_call_signature *)ptr_test;
dd89bb
dd89bb
			/* make our type-punned tyrant compiler happy */
dd89bb
			prelative = (int32_t *)&(ptr_e8_call->__addr_relative);
dd89bb
			relative = *prelative;
dd89bb
dd89bb
			/* are we calling ZwRequestWaitReplyPort? */
dd89bb
			if ((uintptr_t)(__ntapi->zw_request_wait_reply_port) ==
dd89bb
					(uintptr_t)&(ptr_e8_call->__opcode_next_any)
dd89bb
					+ relative) {
dd89bb
				/* assume ds relative address for arg1, go back to find it */
dd89bb
				for (offset = 0; offset < MAX_BYTES_BETWEEN_ARG1_PUSH_AND_E8_CALL; offset++) {
dd89bb
					ptr_push_ds = (_x86_push_ds_signature *)((uintptr_t)ptr_e8_call - offset);
dd89bb
dd89bb
					if ((ptr_push_ds->__push == 0xFF) &&
dd89bb
							(ptr_push_ds->__ds == 0x35)) {
dd89bb
						/* bingo */
dd89bb
						/* make our type-punned tyrant compiler happy */
dd89bb
						pport_addr = (uintptr_t *)&(ptr_push_ds->__push_ds_arg);
dd89bb
dd89bb
						/* all done */
dd89bb
						return *(void ***)pport_addr;
dd89bb
					}
dd89bb
				}
dd89bb
			}
dd89bb
		}
dd89bb
	}
dd89bb
dd89bb
	/* CsrPortHandle not found */
dd89bb
	return (void **)0;
dd89bb
}
dd89bb
#endif
dd89bb
dd89bb
dd89bb
#if defined(__X86_64_MODEL)
dd89bb
void ** __ntapi_tt_get_csr_port_handle_addr_by_logic_x86_64(void)
dd89bb
{
dd89bb
	#define MAX_BYTES_BETWEEN_ARG1_PUSH_AND_E8_CALL 0x20
dd89bb
	#define MAX_FN_BYTES_TO_TEST			0x800
dd89bb
dd89bb
	typedef struct __attr_aligned__ (1) __attr_packed__ __x86_e8_call_signature {
dd89bb
		unsigned char	__opcode_current_e8;
dd89bb
		unsigned char	__addr_relative[4];
dd89bb
		unsigned char	__opcode_next_any;
dd89bb
	} _x86_e8_call_signature;
dd89bb
dd89bb
	typedef struct __attr_aligned__ (1) __attr_packed__ __x86_move_rcx_rel_signature {
dd89bb
		unsigned char	__move;
dd89bb
		unsigned char	__rcx;
dd89bb
		unsigned char	__relative;
dd89bb
		unsigned char	__arg_32_relative[4];
dd89bb
		unsigned char	__opcode_next_any;
dd89bb
	} _x86_move_rcx_rel_signature;
dd89bb
dd89bb
	unsigned char *			ptr_test;
dd89bb
	_x86_e8_call_signature *	ptr_e8_call;
dd89bb
	_x86_move_rcx_rel_signature *	ptr_move_rcx_rel;
dd89bb
	int32_t				offset;
dd89bb
	int32_t				relative;
439a91
	int32_t *			prelative;
439a91
	int				bias;
dd89bb
dd89bb
dd89bb
	/* calling a function within the same library: assume E8 call and 32-bit relative addressing */
439a91
	for (bias = 0; bias < MAX_FN_BYTES_TO_TEST; bias++) {
439a91
		ptr_test  = (unsigned char *)__ntapi->csr_client_call_server;
439a91
		ptr_test += bias;
dd89bb
dd89bb
		if (*ptr_test == 0xE8) {
dd89bb
			ptr_e8_call = (_x86_e8_call_signature *)ptr_test;
dd89bb
dd89bb
			/* please our type-punned tyrant compiler */
dd89bb
			prelative = (int32_t *)&(ptr_e8_call->__addr_relative);
dd89bb
			relative = *prelative;
dd89bb
dd89bb
			/* are we calling ZwRequestWaitReplyPort? */
dd89bb
			/* comparing, not writing; ignore type-punned msgs. */
dd89bb
			if ((uintptr_t)(__ntapi->zw_request_wait_reply_port) ==
dd89bb
					(uintptr_t)&(ptr_e8_call->__opcode_next_any)
dd89bb
					+ relative) {
dd89bb
				/* arg1 must be passed in rcx, so go back to find it */
dd89bb
				for (offset = 0; offset < MAX_BYTES_BETWEEN_ARG1_PUSH_AND_E8_CALL; offset++) {
dd89bb
					ptr_move_rcx_rel = (_x86_move_rcx_rel_signature *)((uintptr_t)ptr_e8_call - offset);
dd89bb
dd89bb
					if ((ptr_move_rcx_rel->__move == 0x48) &&
dd89bb
							(ptr_move_rcx_rel->__rcx == 0x8b) &&
767992
							(ptr_move_rcx_rel->__relative == 0x0d)) {
dd89bb
						/* bingo */
dd89bb
						/* make our type-punned tyrant compiler happy */
dd89bb
						prelative = (int32_t *)&(ptr_move_rcx_rel->__arg_32_relative);
dd89bb
						relative  = *prelative;
dd89bb
dd89bb
						/* all done */
dd89bb
						return (void **)(
dd89bb
							(uintptr_t)&ptr_move_rcx_rel->__opcode_next_any
dd89bb
							+ relative);
767992
					}
dd89bb
				}
dd89bb
			}
dd89bb
		}
dd89bb
	}
dd89bb
dd89bb
	/* CsrPortHandle not found */
439a91
	return 0;
dd89bb
}
dd89bb
#endif