/*++ Module Name: nrun.c Abstract: Program to run native application. Author: Andrey Shedel (andreys@cr.cyco.com) Compile instructions: cl /Ox nrun.c You may distribute under the terms of the GNU General Public License You can contact author at andreys@cr.cyco.com or http://www.chat.ru/~ashedel --*/ #define WIN32_LEAN_AND_MEAN #define _DLL #include #include #include #pragma warning(push, 4) #pragma warning(disable:4204) // nonstandard extension used : non-constant aggregate initializer #pragma comment(lib,"ntdll.lib") #pragma comment(lib,"kernel32.lib") #pragma comment(linker, "-entry:raw_main") #pragma comment(linker, "-opt:nowin98") #pragma comment(linker, "-merge:.rdata=.text") #pragma comment(linker, "-subsystem:console") // // Some prototypes from nt.h & ntrtl.h // typedef ULONG NTSTATUS; typedef struct { USHORT Length; USHORT MaximumLength; PWSTR Buffer; } UNICODE_STRING, *PUNICODE_STRING; #define STATUS_SUCCESS 0 typedef struct _RTL_USER_PROCESS_PARAMETERS *PRTL_USER_PROCESS_PARAMETERS; NTSYSAPI NTSTATUS NTAPI RtlCreateProcessParameters( OUT PRTL_USER_PROCESS_PARAMETERS *ProcessParameters, IN PUNICODE_STRING ImagePathName OPTIONAL, IN PUNICODE_STRING DllPath OPTIONAL, IN PUNICODE_STRING CurrentDirectory OPTIONAL, IN PUNICODE_STRING CommandLine OPTIONAL, IN PUNICODE_STRING Environment OPTIONAL, IN PUNICODE_STRING WindowTitle OPTIONAL, IN PUNICODE_STRING DesktopInfo OPTIONAL, IN PUNICODE_STRING ShellInfo OPTIONAL, IN PUNICODE_STRING RuntimeInfo OPTIONAL ); NTSYSAPI VOID NTAPI RtlDestroyProcessParameters( IN PRTL_USER_PROCESS_PARAMETERS ProcessParameters ); NTSYSAPI PRTL_USER_PROCESS_PARAMETERS NTAPI RtlNormalizeProcessParams( IN PRTL_USER_PROCESS_PARAMETERS ProcessParameters ); NTSYSAPI VOID NTAPI RtlDeNormalizeProcessParams( IN OUT PRTL_USER_PROCESS_PARAMETERS ProcessParameters ); typedef struct _RTL_PROCESS_INFO *PRTL_PROCESS_INFO; typedef struct _CLIENT_ID { HANDLE UniqueProcess; HANDLE UniqueThread; } CLIENT_ID; typedef CLIENT_ID *PCLIENT_ID; typedef struct _SECTION_IMAGE_INFORMATION{ PVOID ProcessEntryPoint; ULONG StackZero; ULONG StackReserve; ULONG StackCommit; ULONG SubsystemType; USHORT MinorImageVersion; USHORT MajorImageVersion; ULONG u4; ULONG Characteristics; USHORT Machine; BOOLEAN Executable; USHORT u6; ULONG u7; ULONG u8; ULONG u9; }SECTION_IMAGE_INFORMATION, *PSECTION_IMAGE_INFORMATION; // 0x30 typedef struct _RTL_PROCESS_INFO { ULONG Size; // 0 HANDLE ProcessHandle; //0x04 HANDLE ThreadHandle; //0x08 CLIENT_ID ClientId; //0x0c SECTION_IMAGE_INFORMATION ImageInfo;//0x14 }RTL_PROCESS_INFO; // 0x44 NTSYSAPI NTSTATUS NTAPI RtlCreateUserProcess( IN PUNICODE_STRING ImageFileName, IN ULONG Attributes, // only OBJ_INHERIT and OBJ_CASE_INSENSITIVE used IN PRTL_USER_PROCESS_PARAMETERS ProcessParameters, IN PSECURITY_DESCRIPTOR ProcessSecurityDescriptor OPTIONAL, IN PSECURITY_DESCRIPTOR ThreadSecurityDescriptor OPTIONAL, IN HANDLE ParentProcess OPTIONAL, IN BOOLEAN CurrentDirectory, IN HANDLE DebugPort OPTIONAL, IN HANDLE ExceptionPort OPTIONAL, OUT PRTL_PROCESS_INFO ProcessInfo ); NTSYSAPI BOOLEAN NTAPI RtlDosPathNameToNtPathName_U( IN PCWSTR DosName, OUT PUNICODE_STRING NtName, OUT PCWSTR* DosFilePath OPTIONAL, OUT PUNICODE_STRING NtFilePath OPTIONAL ); NTSYSAPI VOID NTAPI RtlInitUnicodeString( PUNICODE_STRING DestinationString, PCWSTR SourceString ); #define OBJ_CASE_INSENSITIVE 0x00000040L NTSYSAPI NTSTATUS NTAPI NtResumeThread( IN HANDLE ThreadHandle, OUT PULONG SuspendCount OPTIONAL ); NTSYSAPI NTSTATUS NTAPI NtWaitForSingleObject( IN HANDLE Handle, IN BOOLEAN Alertable, IN PLARGE_INTEGER Timeout OPTIONAL ); NTSYSAPI NTSTATUS NTAPI NtClose( IN HANDLE Handle ); NTSYSAPI NTSTATUS NTAPI NtTerminateProcess( IN HANDLE ProcessHandle, // '0' - test current process for termination. IN LONG ExitStatus ); typedef enum _PROCESSINFOCLASS { ProcessBasicInformation } PROCESSINFOCLASS; // // Basic Process Information // NtQueryInformationProcess using ProcessBasicInfo // typedef struct _PEB* PPEB; typedef ULONG KAFFINITY; typedef LONG KPRIORITY; typedef struct _PROCESS_BASIC_INFORMATION { NTSTATUS ExitStatus; PPEB PebBaseAddress; KAFFINITY AffinityMask; KPRIORITY BasePriority; ULONG UniqueProcessId; ULONG InheritedFromUniqueProcessId; } PROCESS_BASIC_INFORMATION; typedef PROCESS_BASIC_INFORMATION *PPROCESS_BASIC_INFORMATION; NTSYSAPI NTSTATUS NTAPI NtQueryInformationProcess( IN HANDLE ProcessHandle, IN PROCESSINFOCLASS ProcessInformationClass, OUT PVOID ProcessInformation, IN ULONG ProcessInformationLength, OUT PULONG ReturnLength OPTIONAL ); #define STATUS_INVALID_IMAGE_FORMAT ((NTSTATUS)0xC000007BL) // // Implementation part. // // // Same as GetExitCodeProcess() // static NTSTATUS __fastcall GetProcessExitCode( IN HANDLE ProcessHandle ) { PROCESS_BASIC_INFORMATION Buffer; ULONG cb; NTSTATUS Status; Status = NtQueryInformationProcess(ProcessHandle, ProcessBasicInformation, &Buffer, sizeof(PROCESS_BASIC_INFORMATION), &cb); if(STATUS_SUCCESS == Status) Status = Buffer.ExitStatus; return Status; } // // Function to execute native image. // static NTSTATUS __fastcall _ExecuteImage( IN PUNICODE_STRING ImageFileName, IN PUNICODE_STRING CommandLine OPTIONAL, IN PUNICODE_STRING Curdir OPTIONAL, IN PUNICODE_STRING Environment OPTIONAL, OUT NTSTATUS* ExitCode OPTIONAL ) { NTSTATUS Status; PRTL_USER_PROCESS_PARAMETERS pp; RTL_PROCESS_INFO pi; // // Create process. // Status = RtlCreateProcessParameters(&pp, ImageFileName, NULL, Curdir, CommandLine, Environment, NULL, NULL, NULL, NULL ); if(STATUS_SUCCESS != Status) { return Status; } // // Call Rtl to create the process. // Status = RtlCreateUserProcess( ImageFileName, OBJ_CASE_INSENSITIVE, pp, NULL, NULL, NULL, TRUE, NULL, NULL, &pi ); RtlDestroyProcessParameters(pp); // // Return status if failed. // if(STATUS_SUCCESS != Status) { return Status; } // // Allow only native applications to be executed. // if(IMAGE_SUBSYSTEM_NATIVE != pi.ImageInfo.SubsystemType) { Status = STATUS_INVALID_IMAGE_FORMAT; NtTerminateProcess(pi.ProcessHandle, Status); } else { NtResumeThread(pi.ThreadHandle, NULL); NtWaitForSingleObject(pi.ThreadHandle, FALSE, NULL); if(NULL != ExitCode) *ExitCode = GetProcessExitCode(pi.ProcessHandle); } // // Close our handles. // NtClose(pi.ThreadHandle); NtClose(pi.ProcessHandle); // // And return to our caller. // return Status; } // // Get next arg in 'arg' and modify 's' to point to the rest. // BOOLEAN __fastcall _next_arg( IN OUT PUNICODE_STRING s, OUT PUNICODE_STRING arg ) { arg->Length = 0; while((s->Length >= sizeof(WCHAR)) && (L' ' == s->Buffer[0])) { s->Buffer += 1; s->Length -= sizeof(WCHAR); s->MaximumLength -= sizeof(WCHAR); } if(s->Length < sizeof(WCHAR)) { return FALSE; } if(L'"' == s->Buffer[0]) { s->Buffer += 1; s->Length -= sizeof(WCHAR); s->MaximumLength -= sizeof(WCHAR); *arg = *s; arg->Length = 0; while(s->Length >= sizeof(WCHAR)) { s->Buffer += 1; s->Length -= sizeof(WCHAR); s->MaximumLength -= sizeof(WCHAR); if(L'"' == *(s->Buffer - 1)) { break; } else { arg->Length += sizeof(WCHAR); } } } else { *arg = *s; arg->Length = 0; while((s->Length >= sizeof(WCHAR)) && (L' ' != s->Buffer[0])) { s->Buffer += 1; s->Length -= sizeof(WCHAR); s->MaximumLength -= sizeof(WCHAR); arg->Length += sizeof(WCHAR); } } while((s->Length >= sizeof(WCHAR)) && (L' ' == s->Buffer[0])) { s->Buffer += 1; s->Length -= sizeof(WCHAR); s->MaximumLength -= sizeof(WCHAR); } return TRUE; } // // UI stuff. // static void __fastcall _puts(PCSTR s) { ULONG cb; WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), s, strlen(s), &cb, NULL); } static const char usage_string[] = "nrun: NT Native Application launcher.\n" " (c) 1999 Andrey Shedel \n" "\n" "usage: nrun []\n"; static const char crlf[] = "\n"; static const WCHAR NTDLL_DLL[] = L"ntdll.dll"; static const char exited_with[] = "Process exited with code 0x"; static void __fastcall usage(void) { _puts(usage_string); } // //Get system error string // static PSTR __fastcall GetNtErrorString(ULONG error) { PSTR lpMsgBuf; FormatMessageA( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE, GetModuleHandleW(NTDLL_DLL), error, 0, (LPSTR)&lpMsgBuf, 0, NULL); return lpMsgBuf; } static void __fastcall pstatus(ULONG Status) { PSTR b = GetNtErrorString(Status); if(NULL != b) { _puts(b); _puts(crlf); LocalFree((HLOCAL)b); } } // // raw main entry. // void __cdecl raw_main(void) { UNICODE_STRING AppCommandLine; UNICODE_STRING SubCmdLine; WCHAR old; NTSTATUS ExitCode; UNICODE_STRING ImagePathNameNt; UNICODE_STRING ImagePathNameDos; NTSTATUS Status; RtlInitUnicodeString(&AppCommandLine, GetCommandLineW()); // // Build sub commandline and image name. // _next_arg(&AppCommandLine, &SubCmdLine); SubCmdLine = AppCommandLine; _next_arg(&AppCommandLine, &ImagePathNameDos); if(0 == ImagePathNameDos.Length) { usage(); ExitProcess(0); } // // Convert from DOS to NT format. // old = ImagePathNameDos.Buffer[ImagePathNameDos.Length / sizeof(WCHAR)]; ImagePathNameDos.Buffer[ImagePathNameDos.Length / sizeof(WCHAR)] = L'\0'; RtlDosPathNameToNtPathName_U(ImagePathNameDos.Buffer, &ImagePathNameNt, NULL, NULL); ImagePathNameDos.Buffer[ImagePathNameDos.Length / sizeof(WCHAR)] = old; // // Execute. // Status = _ExecuteImage(&ImagePathNameNt, &SubCmdLine, NULL, NULL, &ExitCode); // // Done. // if(STATUS_SUCCESS != Status) pstatus(Status); else { char buffer[20]; _puts(exited_with); _puts(_ultoa(ExitCode, buffer, 16)); _puts(crlf); } ExitProcess(Status); }