NTSTATUS DllInject(HANDLE hProcessID, PEPROCESS pepProcess, PKTHREAD pktThread) { HANDLE hProcess; OBJECT_ATTRIBUTES oaAttributes={sizeof(OBJECT_ATTRIBUTES)}; CLIENT_ID cidProcess; PVOID pvMemory = 0; DWORD dwSize = 0x1000; cidProcess.UniqueProcess = hProcessID; cidProcess.UniqueThread = 0; if (NT_SUCCESS(ZwOpenProcess(&hProcess, PROCESS_ALL_ACCESS, &oaAttributes, &cidProcess))) { if (NT_SUCCESS(ZwAllocateVirtualMemory(hProcess, &pvMemory, 0, &dwSize, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE))) { KAPC_STATE kasState; PKAPC pkaApc; PVOID FunctionAddress; KeStackAttachProcess((PKPROCESS)pepProcess, &kasState); FunctionAddress = GetProcAddressInModule(L"kernel32.dll", "LoadLibraryExW"); if (!FunctionAddress) DbgPrint("GetProcAddressInModule error\n"); wcscpy((PWCHAR)pvMemory, g_wcDllName); KeUnstackDetachProcess(&kasState); pkaApc = (PKAPC)ExAllocatePool(NonPagedPool, sizeof(KAPC)); if (pkaApc) { KeInitializeApc(pkaApc, pktThread, 0, APCKernelRoutine, 0, (PKNORMAL_ROUTINE)FunctionAddress, UserMode, pvMemory); KeInsertQueueApc(pkaApc, 0, 0, IO_NO_INCREMENT); return STATUS_SUCCESS; } } else { DbgPrint("ZwAllocateVirtualMemory error\n"); } ZwClose(hProcess); } else { DbgPrint("ZwOpenProcess error\n"); } return STATUS_NO_MEMORY; }
/// <summary> /// Allocate/Free process memory /// </summary> /// <param name="pAllocFree">Request params.</param> /// <param name="pResult">Allocated region info.</param> /// <returns>Status code</returns> NTSTATUS BBAllocateFreeMemory( IN PALLOCATE_FREE_MEMORY pAllocFree, OUT PALLOCATE_FREE_MEMORY_RESULT pResult ) { NTSTATUS status = STATUS_SUCCESS; PEPROCESS pProcess = NULL; ASSERT( pResult != NULL ); if (pResult == NULL) return STATUS_INVALID_PARAMETER; status = PsLookupProcessByProcessId( (HANDLE)pAllocFree->pid, &pProcess ); if (NT_SUCCESS( status )) { KAPC_STATE apc; PVOID base = (PVOID)pAllocFree->base; ULONG_PTR size = pAllocFree->size; KeStackAttachProcess( pProcess, &apc ); if (pAllocFree->allocate) { if (pAllocFree->physical != FALSE) { status = BBAllocateFreePhysical( pProcess, pAllocFree, pResult ); } else { status = ZwAllocateVirtualMemory( ZwCurrentProcess(), &base, 0, &size, pAllocFree->type, pAllocFree->protection ); pResult->address = (ULONGLONG)base; pResult->size = size; } } else { MI_VAD_TYPE vadType = VadNone; BBGetVadType( pProcess, pAllocFree->base, &vadType ); if (vadType == VadDevicePhysicalMemory) status = BBAllocateFreePhysical( pProcess, pAllocFree, pResult ); else status = ZwFreeVirtualMemory( ZwCurrentProcess(), &base, &size, pAllocFree->type ); } KeUnstackDetachProcess( &apc ); } else DPRINT( "BlackBone: %s: PsLookupProcessByProcessId failed with status 0x%X\n", __FUNCTION__, status ); if (pProcess) ObDereferenceObject( pProcess ); return status; }
VP_STATUS NTAPI IntInt10AllocateBuffer( IN PVOID Context, OUT PUSHORT Seg, OUT PUSHORT Off, IN OUT PULONG Length) { PVOID MemoryAddress; NTSTATUS Status; PKPROCESS CallingProcess = (PKPROCESS)PsGetCurrentProcess(); KAPC_STATE ApcState; TRACE_(VIDEOPRT, "IntInt10AllocateBuffer\n"); IntAttachToCSRSS(&CallingProcess, &ApcState); MemoryAddress = (PVOID)0x20000; Status = ZwAllocateVirtualMemory(NtCurrentProcess(), &MemoryAddress, 0, Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (!NT_SUCCESS(Status)) { WARN_(VIDEOPRT, "- ZwAllocateVirtualMemory failed\n"); IntDetachFromCSRSS(&CallingProcess, &ApcState); return ERROR_NOT_ENOUGH_MEMORY; } if (MemoryAddress > (PVOID)(0x100000 - *Length)) { ZwFreeVirtualMemory(NtCurrentProcess(), &MemoryAddress, Length, MEM_RELEASE); WARN_(VIDEOPRT, "- Unacceptable memory allocated\n"); IntDetachFromCSRSS(&CallingProcess, &ApcState); return ERROR_NOT_ENOUGH_MEMORY; } *Seg = (USHORT)((ULONG)MemoryAddress >> 4); *Off = (USHORT)((ULONG)MemoryAddress & 0xF); INFO_(VIDEOPRT, "- Segment: %x\n", (ULONG)MemoryAddress >> 4); INFO_(VIDEOPRT, "- Offset: %x\n", (ULONG)MemoryAddress & 0xF); INFO_(VIDEOPRT, "- Length: %x\n", *Length); IntDetachFromCSRSS(&CallingProcess, &ApcState); return NO_ERROR; }
static PGDI_POOL_SECTION GdiPoolAllocateSection(PGDI_POOL pPool) { PGDI_POOL_SECTION pSection; PVOID pvBaseAddress; SIZE_T cjSize; NTSTATUS status; /* Allocate a section object */ cjSize = sizeof(GDI_POOL_SECTION) + pPool->cSlotsPerSection / sizeof(ULONG); pSection = EngAllocMem(0, cjSize, pPool->ulTag); if (!pSection) { return NULL; } /* Reserve user mode memory */ cjSize = GDI_POOL_ALLOCATION_GRANULARITY; pvBaseAddress = NULL; status = ZwAllocateVirtualMemory(NtCurrentProcess(), &pvBaseAddress, 0, &cjSize, MEM_RESERVE, PAGE_READWRITE); if (!NT_SUCCESS(status)) { EngFreeMem(pSection); return NULL; } /* Initialize the section */ pSection->pvBaseAddress = pvBaseAddress; pSection->ulCommitBitmap = 0; pSection->cAllocCount = 0; RtlInitializeBitMap(&pSection->bitmap, pSection->aulBits, pPool->cSlotsPerSection); RtlClearAllBits(&pSection->bitmap); /* Return the section */ return pSection; }
void test(PUCHAR arg1) { PUCHAR shellcodeAddress = 0; SIZE_T RegionSize = sizeof(new_code); NTSTATUS status; DWORD ContinueAddress; KAPC_STATE kapc; PMDL p_mdl; PDWORD MappedImTable; if (KeGetCurrentIrql() == PASSIVE_LEVEL) { status = ZwAllocateVirtualMemory(ZwCurrentProcess(), &shellcodeAddress, 0, &RegionSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (!NT_SUCCESS(status)) { return; } shellcodeAddress += 8; ContinueAddress = *(PDWORD)(arg1 + 0xB8); RtlCopyMemory((PVOID)(new_code + 4), &shellcodeAddress, 4); RtlCopyMemory((PVOID)(new_code + 8), &ContinueAddress, 4); RtlCopyMemory((PVOID)(shellcodeAddress-8), new_code, sizeof(new_code)); //KdPrint(("%08X\n", shellcodeAddress - 8)); if (ContinueAddress > 0x7fff0000) { //KdPrint(("in kernel address:%08X!\n", ContinueAddress)); } else { *(PDWORD)(arg1 + 0xB8) = shellcodeAddress - 8; //KdPrint(("in user address:%08X!\n", ContinueAddress)); } } }
int __cdecl main() { PVOID address = ULongToPtr(1); SIZE_T length = 512; NTSTATUS status = ZwAllocateVirtualMemory(GetCurrentProcess(), &address, 0, &length, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if (NT_SUCCESS(status)) { *static_cast<volatile int*>(ULongToPtr(0)) = 0; puts_red(strcpy(static_cast<char*>(ULongToPtr(1)), "null page allocated")); } else switch (status) { case STATUS_INVALID_PARAMETER_2: puts_green("null page alloc failed (may Windows 8 or MS13-031)"); break; case STATUS_CONFLICTING_ADDRESSES: puts_green("null page alloc failed (may EMET)"); break; default: printf_red("!ZwAllocateVirtualMemory"); printf("->0x%lx\n", status); } return EXIT_SUCCESS; }
SECURITY_STATUS SEC_ENTRY AcceptSecurityContext( PCredHandle phCredential, // Cred to base context PCtxtHandle phContext, // Existing context (OPT) PSecBufferDesc pInput, // Input buffer unsigned long fContextReq, // Context Requirements unsigned long TargetDataRep, // Target Data Rep PCtxtHandle phNewContext, // (out) New context handle PSecBufferDesc pOutput, // (inout) Output buffers unsigned long SEC_FAR * pfContextAttr, // (out) Context attributes PTimeStamp ptsExpiry // (out) Life span (OPT) ) { SECURITY_STATUS scRet; NTSTATUS SubStatus; PAUTHENTICATE_MESSAGE AuthenticateMessage; ULONG AuthenticateMessageSize; PNTLM_AUTHENTICATE_MESSAGE NtlmAuthenticateMessage; ULONG NtlmAuthenticateMessageSize; PNTLM_ACCEPT_RESPONSE NtlmAcceptResponse = NULL; PMSV1_0_LM20_LOGON LogonBuffer = NULL; ULONG LogonBufferSize; PMSV1_0_LM20_LOGON_PROFILE LogonProfile = NULL; ULONG LogonProfileSize; PSecBuffer AcceptResponseToken = NULL; PClient Client = NULL; ANSI_STRING SourceName; LUID LogonId; LUID UNALIGNED * TempLogonId; LARGE_INTEGER UNALIGNED * TempKickoffTime; HANDLE TokenHandle = NULL; QUOTA_LIMITS Quotas; PUCHAR Where; STRING DomainName; STRING UserName; STRING Workstation; STRING NtChallengeResponse; STRING LmChallengeResponse; ULONG EffectivePackageId; RtlInitString( &SourceName, NULL ); PAGED_CODE(); // // Check for valid sizes, pointers, etc.: // if (!phCredential) { return(SEC_E_INVALID_HANDLE); } // // Check that we can indeed call the LSA and get the client // handle to it. // scRet = IsOkayToExec(&Client); if (!NT_SUCCESS(scRet)) { return(scRet); } // // Locate the buffers with the input data // if (!GetTokenBuffer( pInput, 0, // get the first security token (PVOID *) &AuthenticateMessage, &AuthenticateMessageSize, TRUE // may be readonly ) || (!GetTokenBuffer( pInput, 1, // get the second security token (PVOID *) &NtlmAuthenticateMessage, &NtlmAuthenticateMessageSize, TRUE // may be readonly ))) { scRet = SEC_E_INVALID_TOKEN; goto Cleanup; } // // Get the output tokens // if (!GetSecurityToken( pOutput, 0, &AcceptResponseToken ) ) { scRet = SEC_E_INVALID_TOKEN; goto Cleanup; } // // Make sure the sizes are o.k. // if ((AuthenticateMessageSize < sizeof(AUTHENTICATE_MESSAGE)) || (NtlmAuthenticateMessageSize < sizeof(NTLM_AUTHENTICATE_MESSAGE))) { scRet = SEC_E_INVALID_TOKEN; } // // Make sure the caller does not want us to allocate memory: // if (fContextReq & ISC_REQ_ALLOCATE_MEMORY) { scRet = SEC_E_UNSUPPORTED_FUNCTION; goto Cleanup; } if (AcceptResponseToken->cbBuffer < sizeof(NTLM_ACCEPT_RESPONSE)) { scRet = SEC_E_INVALID_TOKEN; goto Cleanup; } // // Verify the validity of the Authenticate message. // if (strncmp( AuthenticateMessage->Signature, NTLMSSP_SIGNATURE, sizeof(NTLMSSP_SIGNATURE))) { scRet = SEC_E_INVALID_TOKEN; goto Cleanup; } if (AuthenticateMessage->MessageType != NtLmAuthenticate) { scRet = SEC_E_INVALID_TOKEN; goto Cleanup; } // // Fixup the buffer pointers // UserName = AuthenticateMessage->UserName; UserName.Buffer = UserName.Buffer + (ULONG) AuthenticateMessage; DomainName = AuthenticateMessage->DomainName; DomainName.Buffer = DomainName.Buffer + (ULONG) AuthenticateMessage; Workstation = AuthenticateMessage->Workstation; Workstation.Buffer = Workstation.Buffer + (ULONG) AuthenticateMessage; NtChallengeResponse = AuthenticateMessage->NtChallengeResponse; NtChallengeResponse.Buffer = NtChallengeResponse.Buffer + (ULONG) AuthenticateMessage; LmChallengeResponse = AuthenticateMessage->LmChallengeResponse; LmChallengeResponse.Buffer = LmChallengeResponse.Buffer + (ULONG) AuthenticateMessage; // // Allocate a buffer to pass into LsaLogonUser // LogonBufferSize = sizeof(MSV1_0_LM20_LOGON) + UserName.Length + DomainName.Length + Workstation.Length + LmChallengeResponse.Length + NtChallengeResponse.Length; scRet = ZwAllocateVirtualMemory( NtCurrentProcess(), &LogonBuffer, 0L, &LogonBufferSize, MEM_COMMIT, PAGE_READWRITE ); if (!NT_SUCCESS(scRet)) { goto Cleanup; } // // Fill in the fixed-length portions // LogonBuffer->MessageType = MsV1_0NetworkLogon; RtlCopyMemory( LogonBuffer->ChallengeToClient, NtlmAuthenticateMessage->ChallengeToClient, MSV1_0_CHALLENGE_LENGTH ); // // Fill in the variable length pieces // Where = (PUCHAR) (LogonBuffer + 1); PutString( (PSTRING) &LogonBuffer->LogonDomainName, &DomainName, 0, &Where ); PutString( (PSTRING) &LogonBuffer->UserName, &UserName, 0, &Where ); PutString( (PSTRING) &LogonBuffer->Workstation, &Workstation, 0, &Where ); PutString( (PSTRING) &LogonBuffer->CaseSensitiveChallengeResponse, &NtChallengeResponse, 0, &Where ); PutString( (PSTRING) &LogonBuffer->CaseInsensitiveChallengeResponse, &LmChallengeResponse, 0, &Where ); LogonBuffer->ParameterControl = MSV1_0_CLEARTEXT_PASSWORD_ALLOWED | NtlmAuthenticateMessage->ParameterControl; scRet = RtlUnicodeStringToAnsiString( &SourceName, (PUNICODE_STRING) &Workstation, TRUE ); if (!NT_SUCCESS(scRet)) { goto Cleanup; } if ( fContextReq & ASC_REQ_LICENSING ) { EffectivePackageId = PackageId | LSA_CALL_LICENSE_SERVER ; } else { EffectivePackageId = PackageId ; } scRet = LsaLogonUser( Client->hPort, &SourceName, Network, EffectivePackageId, LogonBuffer, LogonBufferSize, NULL, // token groups &KsecTokenSource, (PVOID *) &LogonProfile, &LogonProfileSize, &LogonId, &TokenHandle, &Quotas, &SubStatus ); if (scRet == STATUS_ACCOUNT_RESTRICTION) { scRet = SubStatus; } if (!NT_SUCCESS(scRet)) { // // LsaLogonUser returns garbage for the token if it fails, // so zero it now so we don't try to close it later. // TokenHandle = NULL; goto Cleanup; } // // Create the kernel context // scRet = NtlmInitKernelContext( LogonProfile->UserSessionKey, LogonProfile->LanmanSessionKey, TokenHandle, phNewContext ); if (!NT_SUCCESS(scRet)) { goto Cleanup; } TokenHandle = NULL; // // Allocate the return buffer. // NtlmAcceptResponse = (PNTLM_ACCEPT_RESPONSE) AcceptResponseToken->pvBuffer; if (NtlmAcceptResponse == NULL) { scRet = SEC_E_INSUFFICIENT_MEMORY; goto Cleanup; } TempLogonId = (LUID UNALIGNED *) &NtlmAcceptResponse->LogonId; *TempLogonId = LogonId; NtlmAcceptResponse->UserFlags = LogonProfile->UserFlags; RtlCopyMemory( NtlmAcceptResponse->UserSessionKey, LogonProfile->UserSessionKey, MSV1_0_USER_SESSION_KEY_LENGTH ); RtlCopyMemory( NtlmAcceptResponse->LanmanSessionKey, LogonProfile->LanmanSessionKey, MSV1_0_LANMAN_SESSION_KEY_LENGTH ); TempKickoffTime = (LARGE_INTEGER UNALIGNED *) &NtlmAcceptResponse->KickoffTime; *TempKickoffTime = LogonProfile->KickOffTime; AcceptResponseToken->cbBuffer = sizeof(NTLM_ACCEPT_RESPONSE); if ( fContextReq & ASC_REQ_LICENSING ) { *pfContextAttr = ASC_RET_ALLOCATED_MEMORY | ASC_RET_LICENSING ; } else { *pfContextAttr = ASC_RET_ALLOCATED_MEMORY; } *ptsExpiry = LogonProfile->LogoffTime; scRet = SEC_E_OK; Cleanup: if (SourceName.Buffer != NULL) { RtlFreeAnsiString(&SourceName); } if (LogonBuffer != NULL) { ZwFreeVirtualMemory( NtCurrentProcess(), &LogonBuffer, &LogonBufferSize, MEM_RELEASE ); } if (LogonProfile != NULL) { LsaFreeReturnBuffer(LogonProfile); } if (TokenHandle != NULL) { NtClose(TokenHandle); } return(scRet); }
SECURITY_STATUS SEC_ENTRY InitializeSecurityContextW( PCredHandle phCredential, // Cred to base context PCtxtHandle phContext, // Existing context (OPT) PSECURITY_STRING pssTargetName, // Name of target unsigned long fContextReq, // Context Requirements unsigned long Reserved1, // Reserved, MBZ unsigned long TargetDataRep, // Data rep of target PSecBufferDesc pInput, // Input Buffers unsigned long Reserved2, // Reserved, MBZ PCtxtHandle phNewContext, // (out) New Context handle PSecBufferDesc pOutput, // (inout) Output Buffers unsigned long SEC_FAR * pfContextAttr, // (out) Context attrs PTimeStamp ptsExpiry // (out) Life span (OPT) ) { SECURITY_STATUS scRet; PMSV1_0_GETCHALLENRESP_REQUEST ChallengeRequest = NULL; ULONG ChallengeRequestSize; PMSV1_0_GETCHALLENRESP_RESPONSE ChallengeResponse = NULL; ULONG ChallengeResponseSize; PCHALLENGE_MESSAGE ChallengeMessage = NULL; ULONG ChallengeMessageSize; PNTLM_CHALLENGE_MESSAGE NtlmChallengeMessage = NULL; ULONG NtlmChallengeMessageSize; PAUTHENTICATE_MESSAGE AuthenticateMessage = NULL; ULONG AuthenticateMessageSize; PNTLM_INITIALIZE_RESPONSE NtlmInitializeResponse = NULL; PClient Client = NULL; UNICODE_STRING PasswordToUse; UNICODE_STRING UserNameToUse; UNICODE_STRING DomainNameToUse; ULONG ParameterControl = USE_PRIMARY_PASSWORD | RETURN_PRIMARY_USERNAME | RETURN_PRIMARY_LOGON_DOMAINNAME; NTSTATUS FinalStatus = STATUS_SUCCESS; PUCHAR Where; PSecBuffer AuthenticationToken = NULL; PSecBuffer InitializeResponseToken = NULL; BOOLEAN UseSuppliedCreds = FALSE; PAGED_CODE(); RtlInitUnicodeString( &PasswordToUse, NULL ); RtlInitUnicodeString( &UserNameToUse, NULL ); RtlInitUnicodeString( &DomainNameToUse, NULL ); // // Check for valid sizes, pointers, etc.: // if (!phCredential) { return(SEC_E_INVALID_HANDLE); } // // Check that we can indeed call the LSA and get the client // handle to it. // scRet = IsOkayToExec(&Client); if (!NT_SUCCESS(scRet)) { return(scRet); } // // Locate the buffers with the input data // if (!GetTokenBuffer( pInput, 0, // get the first security token (PVOID *) &ChallengeMessage, &ChallengeMessageSize, TRUE // may be readonly )) { scRet = SEC_E_INVALID_TOKEN; goto Cleanup; } // // If we are using supplied creds, get them now too. // if (fContextReq & ISC_REQ_USE_SUPPLIED_CREDS) { if (!GetTokenBuffer( pInput, 1, // get the second security token (PVOID *) &NtlmChallengeMessage, &NtlmChallengeMessageSize, TRUE // may be readonly )) { scRet = SEC_E_INVALID_TOKEN; goto Cleanup; } else { UseSuppliedCreds = TRUE; } } // // Get the output tokens // if (!GetSecurityToken( pOutput, 0, &AuthenticationToken) || !GetSecurityToken( pOutput, 1, &InitializeResponseToken ) ) { scRet = SEC_E_INVALID_TOKEN; goto Cleanup; } // // Make sure the sizes are o.k. // if ((ChallengeMessageSize < sizeof(CHALLENGE_MESSAGE)) || (UseSuppliedCreds && !(NtlmChallengeMessageSize < sizeof(NTLM_CHALLENGE_MESSAGE)))) { scRet = SEC_E_INVALID_TOKEN; } // // Make sure the caller wants us to allocate memory: // if (!(fContextReq & ISC_REQ_ALLOCATE_MEMORY)) { scRet = SEC_E_UNSUPPORTED_FUNCTION; goto Cleanup; } // // BUGBUG: allow calls requesting PROMPT_FOR_CREDS to go through. // We won't prompt, but we will setup a context properly. // // if ((fContextReq & ISC_REQ_PROMPT_FOR_CREDS) != 0) // { // scRet = SEC_E_UNSUPPORTED_FUNCTION; // goto Cleanup; // } // // Verify the validity of the challenge message. // if (strncmp( ChallengeMessage->Signature, NTLMSSP_SIGNATURE, sizeof(NTLMSSP_SIGNATURE))) { scRet = SEC_E_INVALID_TOKEN; goto Cleanup; } if (ChallengeMessage->MessageType != NtLmChallenge) { scRet = SEC_E_INVALID_TOKEN; goto Cleanup; } if (ChallengeMessage->NegotiateFlags & NTLMSSP_REQUIRED_NEGOTIATE_FLAGS != NTLMSSP_REQUIRED_NEGOTIATE_FLAGS) { scRet = SEC_E_UNSUPPORTED_FUNCTION; goto Cleanup; } if ((ChallengeMessage->NegotiateFlags & NTLMSSP_REQUEST_NON_NT_SESSION_KEY) != 0) { ParameterControl |= RETURN_NON_NT_USER_SESSION_KEY; } if ((fContextReq & ISC_REQ_USE_SUPPLIED_CREDS) != 0) { if ( NtlmChallengeMessage->Password.Buffer != NULL) { ParameterControl &= ~USE_PRIMARY_PASSWORD; PasswordToUse = NtlmChallengeMessage->Password; PasswordToUse.Buffer = (LPWSTR) ((PCHAR) PasswordToUse.Buffer + (ULONG) NtlmChallengeMessage); } if (NtlmChallengeMessage->UserName.Length != 0) { UserNameToUse = NtlmChallengeMessage->UserName; UserNameToUse.Buffer = (LPWSTR) ((PCHAR) UserNameToUse.Buffer + (ULONG) NtlmChallengeMessage); ParameterControl &= ~RETURN_PRIMARY_USERNAME; } if (NtlmChallengeMessage->DomainName.Length != 0) { DomainNameToUse = NtlmChallengeMessage->DomainName; DomainNameToUse.Buffer = (LPWSTR) ((PCHAR) DomainNameToUse.Buffer + (ULONG) NtlmChallengeMessage); ParameterControl &= ~RETURN_PRIMARY_LOGON_DOMAINNAME; } } // // Package up the parameter for a call to the LSA. // ChallengeRequestSize = sizeof(MSV1_0_GETCHALLENRESP_REQUEST) + PasswordToUse.Length; scRet = ZwAllocateVirtualMemory( NtCurrentProcess(), &ChallengeRequest, 0L, &ChallengeRequestSize, MEM_COMMIT, PAGE_READWRITE ); if (!NT_SUCCESS(scRet)) { goto Cleanup; } // // Build the challenge request message. // ChallengeRequest->MessageType = MsV1_0Lm20GetChallengeResponse; ChallengeRequest->ParameterControl = ParameterControl; ChallengeRequest->LogonId = * (PLUID) phCredential; RtlCopyMemory( ChallengeRequest->ChallengeToClient, ChallengeMessage->Challenge, MSV1_0_CHALLENGE_LENGTH ); if ((ParameterControl & USE_PRIMARY_PASSWORD) == 0) { ChallengeRequest->Password.Buffer = (LPWSTR) (ChallengeRequest+1); RtlCopyMemory( ChallengeRequest->Password.Buffer, PasswordToUse.Buffer, PasswordToUse.Length ); ChallengeRequest->Password.Length = PasswordToUse.Length; ChallengeRequest->Password.MaximumLength = PasswordToUse.Length; } // // Call the LSA to get the challenge response. // scRet = LsaCallAuthenticationPackage( Client->hPort, PackageId, ChallengeRequest, ChallengeRequestSize, &ChallengeResponse, &ChallengeResponseSize, &FinalStatus ); if (!NT_SUCCESS(scRet)) { goto Cleanup; } if (!NT_SUCCESS(FinalStatus)) { scRet = FinalStatus; goto Cleanup; } ASSERT(ChallengeResponse->MessageType == MsV1_0Lm20GetChallengeResponse); // // Now prepare the output message. // if (UserNameToUse.Buffer == NULL) { UserNameToUse = ChallengeResponse->UserName; } if (DomainNameToUse.Buffer == NULL) { DomainNameToUse = ChallengeResponse->LogonDomainName; } AuthenticateMessageSize = sizeof(AUTHENTICATE_MESSAGE) + UserNameToUse.Length + DomainNameToUse.Length + ChallengeResponse->CaseSensitiveChallengeResponse.Length + ChallengeResponse->CaseInsensitiveChallengeResponse.Length; // // BUGBUG: where do I get the workstation name from? // AuthenticateMessage = (PAUTHENTICATE_MESSAGE) SecAllocate(AuthenticateMessageSize); if (AuthenticateMessage == NULL) { scRet = SEC_E_INSUFFICIENT_MEMORY; goto Cleanup; } Where = (PUCHAR) (AuthenticateMessage + 1); RtlCopyMemory( AuthenticateMessage->Signature, NTLMSSP_SIGNATURE, sizeof(NTLMSSP_SIGNATURE) ); AuthenticateMessage->MessageType = NtLmAuthenticate; PutString( &AuthenticateMessage->LmChallengeResponse, &ChallengeResponse->CaseInsensitiveChallengeResponse, AuthenticateMessage, &Where ); PutString( &AuthenticateMessage->NtChallengeResponse, &ChallengeResponse->CaseSensitiveChallengeResponse, AuthenticateMessage, &Where ); PutString( &AuthenticateMessage->DomainName, (PSTRING) &DomainNameToUse, AuthenticateMessage, &Where ); PutString( &AuthenticateMessage->UserName, (PSTRING) &UserNameToUse, AuthenticateMessage, &Where ); // // BUGBUG: no workstation name to fill in. // AuthenticateMessage->Workstation.Length = 0; AuthenticateMessage->Workstation.MaximumLength = 0; AuthenticateMessage->Workstation.Buffer = NULL; // // Build the initialize response. // NtlmInitializeResponse = (PNTLM_INITIALIZE_RESPONSE) SecAllocate(sizeof(NTLM_INITIALIZE_RESPONSE)); if (NtlmInitializeResponse == NULL) { scRet = SEC_E_INSUFFICIENT_MEMORY; goto Cleanup; } RtlCopyMemory( NtlmInitializeResponse->UserSessionKey, ChallengeResponse->UserSessionKey, MSV1_0_USER_SESSION_KEY_LENGTH ); RtlCopyMemory( NtlmInitializeResponse->LanmanSessionKey, ChallengeResponse->LanmanSessionKey, MSV1_0_LANMAN_SESSION_KEY_LENGTH ); // // Fill in the output buffers now. // AuthenticationToken->pvBuffer = AuthenticateMessage; AuthenticationToken->cbBuffer = AuthenticateMessageSize; InitializeResponseToken->pvBuffer = NtlmInitializeResponse; InitializeResponseToken->cbBuffer = sizeof(NTLM_INITIALIZE_RESPONSE); // // Make a local context for this // scRet = NtlmInitKernelContext( NtlmInitializeResponse->UserSessionKey, NtlmInitializeResponse->LanmanSessionKey, NULL, // no token, phNewContext ); if (!NT_SUCCESS(scRet)) { goto Cleanup; } scRet = SEC_E_OK; Cleanup: if (ChallengeRequest != NULL) { ZwFreeVirtualMemory( NtCurrentProcess(), &ChallengeRequest, &ChallengeRequestSize, MEM_RELEASE ); } if (ChallengeResponse != NULL) { LsaFreeReturnBuffer( ChallengeResponse ); } if (!NT_SUCCESS(scRet)) { if (AuthenticateMessage != NULL) { SecFree(AuthenticateMessage); } if (NtlmInitializeResponse != NULL) { SecFree(NtlmInitializeResponse); } } return(scRet); }
SECURITY_STATUS SEC_ENTRY GetSecurityUserInfo( IN PLUID pLogonId, IN ULONG fFlags, OUT PSecurityUserData * ppUserInfo) { NTSTATUS Status, FinalStatus; PVOID GetInfoBuffer = NULL; PVOID GetInfoResponseBuffer = NULL; PMSV1_0_GETUSERINFO_REQUEST GetInfoRequest; PMSV1_0_GETUSERINFO_RESPONSE GetInfoResponse; ULONG ResponseSize; ULONG RegionSize = sizeof(MSV1_0_GETUSERINFO_REQUEST); PSecurityUserData UserInfo = NULL; ULONG UserInfoSize; PUCHAR Where; SECURITY_STATUS scRet; PClient Client = NULL; scRet = IsOkayToExec(&Client); if (!NT_SUCCESS(scRet)) { return(scRet); } // // Allocate virtual memory for the response buffer. // Status = ZwAllocateVirtualMemory( NtCurrentProcess(), &GetInfoBuffer, 0L, &RegionSize, MEM_COMMIT, PAGE_READWRITE ); GetInfoRequest = GetInfoBuffer; if (!NT_SUCCESS(Status)) { scRet = Status; goto Cleanup; } GetInfoRequest->MessageType = MsV1_0GetUserInfo; RtlCopyLuid(&GetInfoRequest->LogonId, pLogonId); Status = LsaCallAuthenticationPackage( Client->hPort, PackageId, GetInfoRequest, RegionSize, &GetInfoResponseBuffer, &ResponseSize, &FinalStatus); GetInfoResponse = GetInfoResponseBuffer; if (!NT_SUCCESS(Status)) { GetInfoResponseBuffer = NULL; scRet = Status; goto Cleanup; } if (!NT_SUCCESS(FinalStatus)) { scRet = FinalStatus; goto Cleanup; } ASSERT(GetInfoResponse->MessageType == MsV1_0GetUserInfo); // // Build a SecurityUserData // UserInfoSize = sizeof(SecurityUserData) + GetInfoResponse->UserName.MaximumLength + GetInfoResponse->LogonDomainName.MaximumLength + GetInfoResponse->LogonServer.MaximumLength + RtlLengthSid(GetInfoResponse->UserSid); scRet = ZwAllocateVirtualMemory( NtCurrentProcess(), &UserInfo, 0L, &UserInfoSize, MEM_COMMIT, PAGE_READWRITE ); if (!NT_SUCCESS(scRet)) { goto Cleanup; } // // Pack in the SID first, to respectalignment boundaries. // Where = (PUCHAR) (UserInfo + 1); UserInfo->pSid = (PSID) (Where); RtlCopySid( UserInfoSize, Where, GetInfoResponse->UserSid ); Where += RtlLengthSid(Where); // // Pack in the strings // PutString( (PSTRING) &UserInfo->UserName, (PSTRING) &GetInfoResponse->UserName, 0, &Where ); PutString( (PSTRING) &UserInfo->LogonDomainName, (PSTRING) &GetInfoResponse->LogonDomainName, 0, &Where ); PutString( (PSTRING) &UserInfo->LogonServer, (PSTRING) &GetInfoResponse->LogonServer, 0, &Where ); *ppUserInfo = UserInfo; UserInfo = NULL; scRet = STATUS_SUCCESS; Cleanup: if (GetInfoRequest != NULL) { ZwFreeVirtualMemory( NtCurrentProcess(), &GetInfoRequest, &RegionSize, MEM_RELEASE ); } if (UserInfo != NULL) { FreeContextBuffer(UserInfo); } if (GetInfoResponseBuffer != NULL) { LsaFreeReturnBuffer(GetInfoResponseBuffer); } return(scRet); }
/* * @implemented */ PRTL_HANDLE_TABLE_ENTRY NTAPI RtlAllocateHandle( PRTL_HANDLE_TABLE HandleTable, PULONG Index) { PRTL_HANDLE_TABLE_ENTRY CurrentEntry, NextEntry; NTSTATUS Status; PRTL_HANDLE_TABLE_ENTRY HandleEntry; PVOID ArrayPointer; SIZE_T ArraySize; ULONG i, NumberOfEntries; /* Check if we are out of free handles entries */ if (HandleTable->FreeHandles == NULL) { /* Check if we don't have uncomitted handle entries yet */ if (HandleTable->UnCommittedHandles == NULL) { /* Use the maximum number of handle entries */ ArraySize = HandleTable->SizeOfHandleTableEntry * HandleTable->MaximumNumberOfHandles; ArrayPointer = NULL; /* Reserve memory */ Status = ZwAllocateVirtualMemory(NtCurrentProcess(), &ArrayPointer, 0, &ArraySize, MEM_RESERVE, PAGE_READWRITE); if (!NT_SUCCESS(Status)) return NULL; /* Update handle array pointers */ HandleTable->UnCommittedHandles = (PRTL_HANDLE_TABLE_ENTRY)ArrayPointer; HandleTable->MaxReservedHandles = (PRTL_HANDLE_TABLE_ENTRY)((ULONG_PTR)ArrayPointer + ArraySize); } /* Commit one reserved handle entry page */ ArraySize = PAGE_SIZE; ArrayPointer = HandleTable->UnCommittedHandles; Status = ZwAllocateVirtualMemory(NtCurrentProcess(), &ArrayPointer, 0, &ArraySize, MEM_COMMIT, PAGE_READWRITE); if (!NT_SUCCESS(Status)) return NULL; /* Update handle array pointers */ HandleTable->FreeHandles = (PRTL_HANDLE_TABLE_ENTRY)ArrayPointer; HandleTable->CommittedHandles = (PRTL_HANDLE_TABLE_ENTRY)ArrayPointer; HandleTable->UnCommittedHandles = (PRTL_HANDLE_TABLE_ENTRY)((ULONG_PTR)ArrayPointer + ArraySize); /* Calculate the number of entries we can store in the array */ NumberOfEntries = ArraySize / HandleTable->SizeOfHandleTableEntry; /* Loop all entries, except the last one */ CurrentEntry = HandleTable->FreeHandles; for (i = 0; i < NumberOfEntries - 1; i++) { /* Calculate the address of the next handle entry */ NextEntry = (PRTL_HANDLE_TABLE_ENTRY)((ULONG_PTR)CurrentEntry + HandleTable->SizeOfHandleTableEntry); /* Link the next entry */ CurrentEntry->NextFree = NextEntry; /* Continue with the next entry */ CurrentEntry = NextEntry; } /* CurrentEntry now points to the last entry, terminate the list here */ CurrentEntry->NextFree = NULL; } /* remove handle from free list */ HandleEntry = HandleTable->FreeHandles; HandleTable->FreeHandles = HandleEntry->NextFree; HandleEntry->NextFree = NULL; if (Index) { *Index = ((ULONG)((ULONG_PTR)HandleEntry - (ULONG_PTR)HandleTable->CommittedHandles) / HandleTable->SizeOfHandleTableEntry); } return HandleEntry; }
NTSTATUS RtlpCreateStack( IN HANDLE Process, IN SIZE_T MaximumStackSize OPTIONAL, IN SIZE_T CommittedStackSize OPTIONAL, IN ULONG ZeroBits OPTIONAL, OUT PINITIAL_TEB InitialTeb ) { NTSTATUS Status; PCH Stack; SYSTEM_BASIC_INFORMATION SysInfo; BOOLEAN GuardPage; SIZE_T RegionSize; ULONG OldProtect; Status = ZwQuerySystemInformation( SystemBasicInformation, (PVOID)&SysInfo, sizeof( SysInfo ), NULL ); if ( !NT_SUCCESS( Status ) ) { return( Status ); } // // if stack is in the current process, then default to // the parameters from the image // if ( Process == NtCurrentProcess() ) { PPEB Peb; PIMAGE_NT_HEADERS NtHeaders; Peb = NtCurrentPeb(); NtHeaders = RtlImageNtHeader(Peb->ImageBaseAddress); if (!NtHeaders) { return STATUS_INVALID_IMAGE_FORMAT; } if (!MaximumStackSize) { MaximumStackSize = NtHeaders->OptionalHeader.SizeOfStackReserve; } if (!CommittedStackSize) { CommittedStackSize = NtHeaders->OptionalHeader.SizeOfStackCommit; } } else { if (!CommittedStackSize) { CommittedStackSize = SysInfo.PageSize; } if (!MaximumStackSize) { MaximumStackSize = SysInfo.AllocationGranularity; } } // // Enforce a minimal stack commit if there is a PEB setting // for this. // if ( CommittedStackSize >= MaximumStackSize ) { MaximumStackSize = ROUND_UP(CommittedStackSize, (1024*1024)); } CommittedStackSize = ROUND_UP( CommittedStackSize, SysInfo.PageSize ); MaximumStackSize = ROUND_UP( MaximumStackSize, SysInfo.AllocationGranularity ); Stack = NULL; Status = ZwAllocateVirtualMemory( Process, (PVOID *)&Stack, ZeroBits, &MaximumStackSize, MEM_RESERVE, PAGE_READWRITE ); if ( !NT_SUCCESS( Status ) ) { #if DBG DbgPrint( "NTRTL: RtlpCreateStack( %lx ) failed. Stack Reservation Status == %X\n", Process, Status ); #endif // DBG return( Status ); } InitialTeb->OldInitialTeb.OldStackBase = NULL; InitialTeb->OldInitialTeb.OldStackLimit = NULL; InitialTeb->StackAllocationBase = Stack; InitialTeb->StackBase = Stack + MaximumStackSize; Stack += MaximumStackSize - CommittedStackSize; if (MaximumStackSize > CommittedStackSize) { Stack -= SysInfo.PageSize; CommittedStackSize += SysInfo.PageSize; GuardPage = TRUE; } else { GuardPage = FALSE; } Status = ZwAllocateVirtualMemory( Process, (PVOID *)&Stack, 0, &CommittedStackSize, MEM_COMMIT, PAGE_READWRITE ); InitialTeb->StackLimit = Stack; if ( !NT_SUCCESS( Status ) ) { #if DBG DbgPrint( "NTRTL: RtlpCreateStack( %lx ) failed. Stack Commit Status == %X\n", Process, Status ); #endif // DBG return( Status ); } // // if we have space, create a guard page. // if (GuardPage) { RegionSize = SysInfo.PageSize; Status = ZwProtectVirtualMemory( Process, (PVOID *)&Stack, &RegionSize, PAGE_GUARD | PAGE_READWRITE, &OldProtect); if ( !NT_SUCCESS( Status ) ) { #if DBG DbgPrint( "NTRTL: RtlpCreateStack( %lx ) failed. Guard Page Creation Status == %X\n", Process, Status ); #endif // DBG return( Status ); } InitialTeb->StackLimit = (PVOID)((PUCHAR)InitialTeb->StackLimit + RegionSize); } return( STATUS_SUCCESS ); }
PVOID NTAPI GdiPoolAllocate( PGDI_POOL pPool) { PGDI_POOL_SECTION pSection; ULONG ulIndex, cjOffset, ulPageBit; PLIST_ENTRY ple; PVOID pvAlloc, pvBaseAddress; SIZE_T cjSize; NTSTATUS status; /* Disable APCs and acquire the pool lock */ KeEnterCriticalRegion(); ExAcquirePushLockExclusive(&pPool->pushlock); /* Check if we have a ready section */ if (!IsListEmpty(&pPool->leReadyList)) { /* Get a free section */ ple = pPool->leReadyList.Flink; pSection = CONTAINING_RECORD(ple, GDI_POOL_SECTION, leReadyLink); if (pSection->cAllocCount >= pPool->cSlotsPerSection) { DPRINT1("pSection->cAllocCount=%lu, pPool->cSlotsPerSection=%lu\n", pSection->cAllocCount, pPool->cSlotsPerSection); DBG_DUMP_EVENT_LIST(&pPool->slhLog); ASSERT(FALSE); } ASSERT(pSection->cAllocCount < pPool->cSlotsPerSection); } else { /* No, check if we have something on the empty list */ if (!IsListEmpty(&pPool->leEmptyList)) { /* Yes, remove it from the empty list */ ple = RemoveHeadList(&pPool->leEmptyList); pSection = CONTAINING_RECORD(ple, GDI_POOL_SECTION, leInUseLink); pPool->cEmptySections--; ASSERT(pSection->cAllocCount == 0); } else { /* No, allocate a new section */ pSection = GdiPoolAllocateSection(pPool); if (!pSection) { DPRINT1("Couldn't allocate a section\n"); pvAlloc = NULL; goto done; } } /* Insert it into the in-use and ready list */ InsertHeadList(&pPool->leInUseList, &pSection->leInUseLink); InsertHeadList(&pPool->leReadyList, &pSection->leReadyLink); } /* Find and set a single bit */ ulIndex = RtlFindClearBitsAndSet(&pSection->bitmap, 1, 0); ASSERT(ulIndex != MAXULONG); /* Calculate the allocation address */ cjOffset = ulIndex * pPool->cjAllocSize; pvAlloc = (PVOID)((ULONG_PTR)pSection->pvBaseAddress + cjOffset); /* Check if memory is comitted */ ulPageBit = 1 << (cjOffset / PAGE_SIZE); ulPageBit |= 1 << ((cjOffset + pPool->cjAllocSize - 1) / PAGE_SIZE); if ((pSection->ulCommitBitmap & ulPageBit) != ulPageBit) { /* Commit the pages */ pvBaseAddress = PAGE_ALIGN(pvAlloc); cjSize = ADDRESS_AND_SIZE_TO_SPAN_PAGES(pvAlloc, pPool->cjAllocSize) * PAGE_SIZE; status = ZwAllocateVirtualMemory(NtCurrentProcess(), &pvBaseAddress, 0, &cjSize, MEM_COMMIT, PAGE_READWRITE); if (!NT_SUCCESS(status)) { pvAlloc = NULL; goto done; } pSection->ulCommitBitmap |= ulPageBit; } /* Increase alloc count */ pSection->cAllocCount++; ASSERT(RtlNumberOfSetBits(&pSection->bitmap) == pSection->cAllocCount); DBG_LOGEVENT(&pPool->slhLog, EVENT_ALLOCATE, pvAlloc); /* Check if section is now busy */ if (pSection->cAllocCount == pPool->cSlotsPerSection) { /* Remove the section from the ready list */ RemoveEntryList(&pSection->leReadyLink); } done: /* Release the pool lock and enable APCs */ ExReleasePushLockExclusive(&pPool->pushlock); KeLeaveCriticalRegion(); DPRINT("GdiPoolallocate: %p\n", pvAlloc); return pvAlloc; }
NTSTATUS VirtualAlloc(HANDLE hProcess, SIZE_T Size, OUT PVOID *VirtualAddress) { PVOID BaseAddress = NULL; NTSTATUS Status = ZwAllocateVirtualMemory(hProcess, &BaseAddress, 0, &Size, MEM_COMMIT, PAGE_EXECUTE_READWRITE); *VirtualAddress = BaseAddress; return Status; };
int GetHarddiskId(PWCHAR pwchDir) { NTSTATUS ntStatus; int nRet = -1; OBJECT_ATTRIBUTES ObjAttr; HANDLE hDirectoryObject; UNICODE_STRING Name; WCHAR DriverStr[32]; PWCHAR pdrvid; int len ; PMY_QUERY_DIRECTORY_STRUCT qds; POBJECT_NAMETYPE_INFO p; ULONG RegionSize; int cou; qds = NULL; RegionSize = sizeof(MY_QUERY_DIRECTORY_STRUCT); if(!NT_SUCCESS(ntStatus = ZwAllocateVirtualMemory((HANDLE)-1, (PVOID*) &qds, 0, &RegionSize, MEM_COMMIT,PAGE_READWRITE))) { return nRet; } wcscpy(DriverStr, L"DR0"); len = wcslen(DriverStr); pdrvid = &DriverStr[len - 1]; RtlInitUnicodeString(&Name, pwchDir); Name.Length -= sizeof(WCHAR); InitializeObjectAttributes(&ObjAttr, &Name, OBJ_CASE_INSENSITIVE, NULL, NULL); ntStatus = ZwOpenDirectoryObject(&hDirectoryObject, DIRECTORY_QUERY, &ObjAttr); if(NT_SUCCESS(ntStatus)) { BOOLEAN First = TRUE; while(NtQueryDirectoryObject(hDirectoryObject, &qds->Buffer, QUERY_DIRECTORY_BUFF_SIZE, TRUE, First, &qds->Index, &qds->Retlen) >= 0 && nRet == -1) { PWCHAR obj_name; int tmp_len; p = (POBJECT_NAMETYPE_INFO)&qds->Buffer; First = FALSE; obj_name = p->ObjectName.Buffer; tmp_len = p->ObjectName.Length / sizeof(WCHAR); if (tmp_len >= 3) { // TAK POLUCHILOS! :) if (obj_name[0] == L'D' && obj_name[1] == L'R') { BOOLEAN bisdigit = TRUE; int cou2; int ntmp = 0; for (cou2 = 2; cou2 < tmp_len; cou2++) { if (obj_name[cou2] < L'0' || obj_name[cou2] > L'9') { bisdigit = FALSE; break; } ntmp = ntmp * 10 + (obj_name[cou2] - L'0'); } if (bisdigit) nRet = ntmp; } } } ZwClose(hDirectoryObject); } _pfZwFreeVirtualMemory((HANDLE)-1,(PVOID*) &qds, &RegionSize,MEM_RELEASE); return nRet; }
NTSTATUS NTAPI RtlpCreateUserStack(IN HANDLE hProcess, IN SIZE_T StackReserve OPTIONAL, IN SIZE_T StackCommit OPTIONAL, IN ULONG StackZeroBits OPTIONAL, OUT PINITIAL_TEB InitialTeb) { NTSTATUS Status; SYSTEM_BASIC_INFORMATION SystemBasicInfo; PIMAGE_NT_HEADERS Headers; ULONG_PTR Stack = 0; BOOLEAN UseGuard = FALSE; ULONG Dummy; SIZE_T GuardPageSize; /* Get some memory information */ Status = ZwQuerySystemInformation(SystemBasicInformation, &SystemBasicInfo, sizeof(SYSTEM_BASIC_INFORMATION), NULL); if (!NT_SUCCESS(Status)) return Status; /* Use the Image Settings if we are dealing with the current Process */ if (hProcess == NtCurrentProcess()) { /* Get the Image Headers */ Headers = RtlImageNtHeader(NtCurrentPeb()->ImageBaseAddress); /* If we didn't get the parameters, find them ourselves */ if (!StackReserve) StackReserve = Headers->OptionalHeader. SizeOfStackReserve; if (!StackCommit) StackCommit = Headers->OptionalHeader. SizeOfStackCommit; } else { /* Use the System Settings if needed */ if (!StackReserve) StackReserve = SystemBasicInfo.AllocationGranularity; if (!StackCommit) StackCommit = SystemBasicInfo.PageSize; } /* Align everything to Page Size */ StackReserve = ROUND_UP(StackReserve, SystemBasicInfo.AllocationGranularity); StackCommit = ROUND_UP(StackCommit, SystemBasicInfo.PageSize); // FIXME: Remove once Guard Page support is here #if 1 StackCommit = StackReserve; #endif /* Reserve memory for the stack */ Status = ZwAllocateVirtualMemory(hProcess, (PVOID*)&Stack, StackZeroBits, &StackReserve, MEM_RESERVE, PAGE_READWRITE); if (!NT_SUCCESS(Status)) return Status; /* Now set up some basic Initial TEB Parameters */ InitialTeb->PreviousStackBase = NULL; InitialTeb->PreviousStackLimit = NULL; InitialTeb->AllocatedStackBase = (PVOID)Stack; InitialTeb->StackBase = (PVOID)(Stack + StackReserve); /* Update the Stack Position */ Stack += StackReserve - StackCommit; /* Check if we will need a guard page */ if (StackReserve > StackCommit) { /* Remove a page to set as guard page */ Stack -= SystemBasicInfo.PageSize; StackCommit += SystemBasicInfo.PageSize; UseGuard = TRUE; } /* Allocate memory for the stack */ Status = ZwAllocateVirtualMemory(hProcess, (PVOID*)&Stack, 0, &StackCommit, MEM_COMMIT, PAGE_READWRITE); if (!NT_SUCCESS(Status)) return Status; /* Now set the current Stack Limit */ InitialTeb->StackLimit = (PVOID)Stack; /* Create a guard page */ if (UseGuard) { /* Attempt maximum space possible */ GuardPageSize = SystemBasicInfo.PageSize; Status = ZwProtectVirtualMemory(hProcess, (PVOID*)&Stack, &GuardPageSize, PAGE_GUARD | PAGE_READWRITE, &Dummy); if (!NT_SUCCESS(Status)) return Status; /* Update the Stack Limit keeping in mind the Guard Page */ InitialTeb->StackLimit = (PVOID)((ULONG_PTR)InitialTeb->StackLimit - GuardPageSize); } /* We are done! */ return STATUS_SUCCESS; }
BOOLEAN InjectDll(PINJECT_INFO InjectInfo) { PEPROCESS Process; PETHREAD Thread; PKINJECT mem; ULONG size; PKAPC_STATE ApcState; PKAPC apc; PVOID buffer; PSYSTEM_PROCESS_INFO pSpi; LARGE_INTEGER delay; buffer=ExAllocatePool(NonPagedPool,1024*1024); if(!buffer) { DbgPrint("Error: Unable to allocate memory for the process thread list."); return FALSE; } //5 SystemProcessInformation, if(!NT_SUCCESS(ZwQuerySystemInformation(5,buffer,1024*1024,NULL))) { DbgPrint("Error: Unable to query process thread list."); ExFreePool(buffer); return FALSE; } pSpi=(PSYSTEM_PROCESS_INFO)buffer; //找到目标进程 while(pSpi->NextEntryOffset) { if(pSpi->UniqueProcessId==InjectInfo->ProcessId) { DbgPrint("Target thread found. TID: %d",pSpi->Threads[0].ClientId.UniqueThread); break; } pSpi=(PSYSTEM_PROCESS_INFO)((PUCHAR)pSpi+pSpi->NextEntryOffset); } // 引用目标进程EProcess, if(!NT_SUCCESS(PsLookupProcessByProcessId(InjectInfo->ProcessId,&Process))) { DbgPrint("Error: Unable to reference the target process."); ExFreePool(buffer); return FALSE; } DbgPrint("Process name: %s",PsGetProcessImageFileName(Process)); DbgPrint("EPROCESS address: %#x",Process); //目标进程主线程 if(!NT_SUCCESS(PsLookupThreadByThreadId(pSpi->Threads[0].ClientId.UniqueThread,&Thread))) { DbgPrint("Error: Unable to reference the target thread."); ObDereferenceObject(Process); ExFreePool(buffer); return FALSE; } DbgPrint("ETHREAD address: %#x",Thread); ExFreePool(buffer); //切入到目标进程 KeAttachProcess(Process); mem=NULL; size=4096; //在目标进程申请内存 if(!NT_SUCCESS(ZwAllocateVirtualMemory(NtCurrentProcess(),(PVOID*)&mem,0,&size,MEM_COMMIT|MEM_RESERVE,PAGE_EXECUTE_READWRITE))) { DbgPrint("Error: Unable to allocate memory in the target process."); KeDetachProcess(); ObDereferenceObject(Process); ObDereferenceObject(Thread); return FALSE; } DbgPrint("Memory allocated at %#x",mem); mem->LdrLoadDll=LdrLoadDll; wcscpy(mem->Buffer,InjectInfo->DllName); RtlInitUnicodeString(&mem->DllName,mem->Buffer); ApcState=(PKAPC_STATE)((PUCHAR)Thread+ApcStateOffset); ApcState->UserApcPending=TRUE; memcpy((PKINJECT)(mem+1),InjectDllApc,(ULONG)KernelRoutine-(ULONG)InjectDllApc); DbgPrint("APC code address: %#x",(PKINJECT)(mem+1)); //申请apc对象 apc=(PKAPC)ExAllocatePool(NonPagedPool,sizeof(KAPC)); if(!apc) { DbgPrint("Error: Unable to allocate the APC object."); size=0; ZwFreeVirtualMemory(NtCurrentProcess(),(PVOID*)&mem,&size,MEM_RELEASE); KeDetachProcess(); ObDereferenceObject(Process); ObDereferenceObject(Thread); return FALSE; } KeInitializeApc(apc, Thread, //目标进程主线程 OriginalApcEnvironment, //目标apcz状态 KernelRoutine, //内核apc总入口 NULL, //Rundown Rounine=NULL (PKNORMAL_ROUTINE)((PKINJECT)mem+1), //用户空间的总apc UserMode, //插入到用户apc队列 mem); // 自己的apc队列 DbgPrint("Inserting APC to target thread"); // 插入apc队列 if(!KeInsertQueueApc(apc,NULL,NULL,IO_NO_INCREMENT)) { DbgPrint("Error: Unable to insert APC to target thread."); size=0; ZwFreeVirtualMemory(NtCurrentProcess(),(PVOID*)&mem,&size,MEM_RELEASE); KeDetachProcess(); ObDereferenceObject(Process); ObDereferenceObject(Thread); ExFreePool(apc); return FALSE; } delay.QuadPart=-100*10000; while(!mem->Executed) { KeDelayExecutionThread(KernelMode,FALSE,&delay); //等待apc执行 } if(!mem->DllBase) { DbgPrint("Error: Unable to inject DLL into target process."); size=0; ZwFreeVirtualMemory(NtCurrentProcess(),(PVOID*)&mem,&size,MEM_RELEASE); KeDetachProcess(); ObDereferenceObject(Process); ObDereferenceObject(Thread); return FALSE; } DbgPrint("DLL injected at %#x",mem->DllBase); size=0; ZwFreeVirtualMemory(NtCurrentProcess(),(PVOID*)&mem,&size,MEM_RELEASE); ObDereferenceObject(Process); ObDereferenceObject(Thread); return TRUE; }
PRTL_STACK_TRACE_ENTRY RtlpExtendStackTraceDataBase( IN PRTL_STACK_TRACE_ENTRY InitialValue, IN ULONG Size ) /*++ Routine Description: This routine extends the stack trace database in order to accomodate the new stack trace that has to be saved. Arguments: InitialValue - stack trace to be saved. Size - size of the stack trace in bytes. Note that this is not the depth of the trace but rather `Depth * sizeof(PVOID)'. Return Value: The address of the just saved stack trace or null in case we have hit the maximum size of the database or we get commit errors. Environment: User mode. Note. In order to make all this code work in kernel mode we have to rewrite this function that relies on VirtualAlloc. --*/ { NTSTATUS Status; PRTL_STACK_TRACE_ENTRY p, *pp; ULONG CommitSize; PSTACK_TRACE_DATABASE DataBase; DataBase = RtlpStackTraceDataBase; // // We will try to find space for one stack trace entry in the // upper part of the database. // pp = (PRTL_STACK_TRACE_ENTRY *)DataBase->NextFreeUpperMemory; if ((! DataBase->PreCommitted) && ((PCHAR)(pp - 1) < (PCHAR)DataBase->CurrentUpperCommitLimit)) { // // No more committed space in the upper part of the database. // We need to extend it downwards. // DataBase->CurrentUpperCommitLimit = (PVOID)((PCHAR)DataBase->CurrentUpperCommitLimit - PAGE_SIZE); if (DataBase->CurrentUpperCommitLimit < DataBase->CurrentLowerCommitLimit) { // // No more space at all. We have got over the lower part of the db. // We failed therefore increase back the UpperCommitLimit pointer. // DataBase->CurrentUpperCommitLimit = (PVOID)((PCHAR)DataBase->CurrentUpperCommitLimit + PAGE_SIZE); return( NULL ); } CommitSize = PAGE_SIZE; Status = ZwAllocateVirtualMemory( NtCurrentProcess(), (PVOID *)&DataBase->CurrentUpperCommitLimit, 0, &CommitSize, MEM_COMMIT, PAGE_READWRITE ); if (!NT_SUCCESS( Status )) { // // We tried to increase the upper part of the db by one page. // We failed therefore increase back the UpperCommitLimit pointer // DataBase->CurrentUpperCommitLimit = (PVOID)((PCHAR)DataBase->CurrentUpperCommitLimit + PAGE_SIZE); KdPrint(( "RTL: Unable to commit space to extend stack trace data base - Status = %lx\n", Status )); return( NULL ); } } // // We managed to make sure we have usable space in the upper part // therefore we take out one stack trace entry address. // DataBase->NextFreeUpperMemory -= sizeof( *pp ); // // Now we will try to find space in the lower part of the database for // for the eactual stack trace. // p = (PRTL_STACK_TRACE_ENTRY)DataBase->NextFreeLowerMemory; if ((! DataBase->PreCommitted) && (((PCHAR)p + Size) > (PCHAR)DataBase->CurrentLowerCommitLimit)) { // // We need to extend the lower part. // if (DataBase->CurrentLowerCommitLimit >= DataBase->CurrentUpperCommitLimit) { // // We have hit the maximum size of the database. // return( NULL ); } // // Extend the lower part of the database by one page. // CommitSize = Size; Status = ZwAllocateVirtualMemory( NtCurrentProcess(), (PVOID *)&DataBase->CurrentLowerCommitLimit, 0, &CommitSize, MEM_COMMIT, PAGE_READWRITE ); if (! NT_SUCCESS( Status )) { KdPrint(( "RTL: Unable to commit space to extend stack trace data base - Status = %lx\n", Status )); return( NULL ); } DataBase->CurrentLowerCommitLimit = (PCHAR)DataBase->CurrentLowerCommitLimit + CommitSize; } // // Take out the space for the stack trace. // DataBase->NextFreeLowerMemory += Size; // // Deal with a precommitted database case. If the lower and upper // pointers have crossed each other then rollback and return failure. // if (DataBase->PreCommitted && DataBase->NextFreeLowerMemory >= DataBase->NextFreeUpperMemory) { DataBase->NextFreeUpperMemory += sizeof( *pp ); DataBase->NextFreeLowerMemory -= Size; return( NULL ); } // // Save the stack trace in the database // RtlMoveMemory( p, InitialValue, Size ); p->HashChain = NULL; p->TraceCount = 0; p->Index = (USHORT)(++DataBase->NumberOfEntriesAdded); // // Save the address of the new stack trace entry in the // upper part of the databse. // *--pp = p; // // Return address of the saved stack trace entry. // return( p ); }
NTSTATUS RtlInitStackTraceDataBaseEx( IN PVOID CommitBase, IN ULONG CommitSize, IN ULONG ReserveSize, IN PRTL_INITIALIZE_LOCK_ROUTINE InitializeLockRoutine, IN PRTL_ACQUIRE_LOCK_ROUTINE AcquireLockRoutine, IN PRTL_RELEASE_LOCK_ROUTINE ReleaseLockRoutine, IN PRTL_OKAY_TO_LOCK_ROUTINE OkayToLockRoutine ) { NTSTATUS Status; PSTACK_TRACE_DATABASE DataBase; DataBase = (PSTACK_TRACE_DATABASE)CommitBase; if (CommitSize == 0) { CommitSize = PAGE_SIZE; Status = ZwAllocateVirtualMemory( NtCurrentProcess(), (PVOID *)&CommitBase, 0, &CommitSize, MEM_COMMIT, PAGE_READWRITE ); if (!NT_SUCCESS( Status )) { KdPrint(( "RTL: Unable to commit space to extend stack trace data base - Status = %lx\n", Status )); return Status; } DataBase->PreCommitted = FALSE; } else if (CommitSize == ReserveSize) { RtlZeroMemory( DataBase, sizeof( *DataBase ) ); DataBase->PreCommitted = TRUE; } else { return STATUS_INVALID_PARAMETER; } DataBase->CommitBase = CommitBase; DataBase->NumberOfBuckets = 37; DataBase->NextFreeLowerMemory = (PCHAR) (&DataBase->Buckets[ DataBase->NumberOfBuckets ]); DataBase->NextFreeUpperMemory = (PCHAR)CommitBase + ReserveSize; if(!DataBase->PreCommitted) { DataBase->CurrentLowerCommitLimit = (PCHAR)CommitBase + CommitSize; DataBase->CurrentUpperCommitLimit = (PCHAR)CommitBase + ReserveSize; } else { RtlZeroMemory( &DataBase->Buckets[ 0 ], DataBase->NumberOfBuckets * sizeof( DataBase->Buckets[ 0 ] ) ); } DataBase->EntryIndexArray = (PRTL_STACK_TRACE_ENTRY *)DataBase->NextFreeUpperMemory; DataBase->AcquireLockRoutine = AcquireLockRoutine; DataBase->ReleaseLockRoutine = ReleaseLockRoutine; DataBase->OkayToLockRoutine = OkayToLockRoutine; Status = (InitializeLockRoutine)( &DataBase->Lock.CriticalSection ); if (!NT_SUCCESS( Status )) { KdPrint(( "RTL: Unable to initialize stack trace data base CriticalSection, Status = %lx\n", Status )); return( Status ); } RtlpStackTraceDataBase = DataBase; return( STATUS_SUCCESS ); }
// injects the function UserApcNormalRoutine() as a user mode APC // into the process selected by Process Pid NTSTATUS InjectApc( HANDLE ProcessId, PVOID User32MessageBoxA ) { NTSTATUS Status = STATUS_UNSUCCESSFUL; HANDLE ProcessHandle = NULL; PEPROCESS ProcessObject = NULL; BOOLEAN Attached = FALSE; BOOLEAN Suspended = FALSE; PCONTEXT_DATA ContextData = NULL; PUCHAR AllocationBase = NULL; SIZE_T AllocationSize; ULONG ShellcodeSize; PUCHAR ShellcodeBase; KAPC_STATE ApcState; // get the size of the shell code that will be copied into user VAS ShellcodeSize = (ULONG)Getx64FunctionSize ( (PVOID)UserApcNormalRoutine ); if ( ! ShellcodeSize ) { DPF(("%s!%s Getx64FunctionSize(UserApcNormalRoutine) FAIL\n", __MODULE__, __FUNCTION__ )); goto Exit; } // Step #1 : Obtain the EPROCESS pointer from ProcessId (PsLookupProcessByProcessId()) // and store it in ProcessObject if (!NT_SUCCESS(Status = PsLookupProcessByProcessId(ProcessId, &ProcessObject))) { DbgPrint("ERROR PsLookupProcessByProcessId (%x)\n", Status); goto Exit; } // Step #2 : Suspend the target process (PsSuspendProcess()) if (!NT_SUCCESS(Status = PsSuspendProcess(ProcessObject))) { DbgPrint("ERROR PsSuspendProcess (%x)\n", Status); goto Exit; } Suspended = TRUE; // Step #3 : Attach to the target process (KeStackAttachProcess()) KeStackAttachProcess(ProcessObject, &ApcState); Attached = TRUE; AllocationSize = ShellcodeSize + sizeof ( CONTEXT_DATA ) ; // Step #4 : Allocate memory in the target process large enough // to hold the shellcode and CONTEXT_DATA (ZwAllocateVirtualMemory()) // and store the result in AllocationBase if (!NT_SUCCESS(Status = ZwAllocateVirtualMemory(NtCurrentProcess(), &AllocationBase, 0, &AllocationSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE))) { DbgPrint("ERROR ZwAllocateVirtualMemory (%x)\n", Status); goto Exit; } ProcessHandle = NULL; ShellcodeBase = AllocationBase; ContextData = (PCONTEXT_DATA)(AllocationBase + ShellcodeSize); // Step #5 : Copy the user mode APC code into the newly allocated // memory (RtlCopyMemory()) i.e. @ ShellCodeBase RtlCopyMemory(ShellcodeBase, (PVOID)UserApcNormalRoutine, ShellcodeSize); //setup the context structure with data that will be required by the APC routine ContextData->ShellCodeBase = ShellcodeBase; ContextData->ShellCodeSize = ShellcodeSize; strncpy ( ContextData->Text, "Hello from Kernel", STRING_SIZE ); strncpy ( ContextData->Caption, "P0wned", STRING_SIZE ); //user32.dll base + offset of MessageBoxA; ContextData->MessageBox = (MESSAGEBOXA)User32MessageBoxA; // queue the APC Status = RequestApc( ProcessId, ShellcodeBase, ContextData ); if ( ! NT_SUCCESS( Status ) ) { DPF(("%s!%s RequestApc() FAIL=%08x\n", __MODULE__, __FUNCTION__, Status )); goto Exit; } Exit : // in case of an error free the memory that was allocated if ( ! NT_SUCCESS ( Status ) ) { if ( AllocationBase ) { AllocationSize = 0; // Step #6 : Free the virtual memory that was allocated in the // target process (ZwFreeVirtualMemory()) ZwFreeVirtualMemory(ProcessHandle, &AllocationBase, &AllocationSize, MEM_RELEASE | MEM_DECOMMIT); } } if ( Attached ) { // Step #7 : Detach from the target process (KeUnstackDetachProcess()) KeUnstackDetachProcess(&ApcState); } if ( Suspended ) { // Step #8 : Resume the target process (PsResumeProcess()) PsResumeProcess(ProcessObject); } if ( ProcessObject ) { // Step #9 : Dereference the process object (ObDereferenceObject()) ObDereferenceObject(ProcessObject); } return Status; }
BOOL CProcess::HideProcess() { EnableDebugPriv(SE_DEBUG_NAME); (InitializeFunction()); //进程信息及长度 PSYSTEM_HANDLE_INFORMATION pHandleInfo = NULL; DWORD buflen=0x10000,needlen=0; ULONG uObjCnt = 0; NTSTATUS status; BOOL bRet; HANDLE hProcess; //通过打开进程获取进程句柄 hProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE,GetCurrentProcessId()); //获得进程对象的地址 PBYTE pBuf = NULL; do { //申请查询句柄信息所需的内存 ZwAllocateVirtualMemory(GetCurrentProcess(),(PVOID*)&pBuf,0,&buflen,MEM_COMMIT,PAGE_READWRITE); if (pBuf == NULL) return FALSE; //查询系统句柄信息 status=NtQuerySystemInformation(SystemHandleInformation,(PVOID)pBuf,buflen,&needlen); if (NT_SUCCESS(status)) break; //不成功,则释放内存 //这里只要一块大内存够放这些内容就行,或者直接申请一块足够大的也可以 //返回的needlen可以做为参考 ZwFreeVirtualMemory(GetCurrentProcess(),(PVOID*)&pBuf,&buflen,MEM_RELEASE); //然后把要申请的内存大小乘2,直至成功为止 buflen *= 2; pBuf = NULL; } while(TRUE); uObjCnt = (ULONG)*(ULONG*)pBuf; DWORD dwEPROCESS; //ULONG dwCurrentPID; pHandleInfo = (PSYSTEM_HANDLE_INFORMATION)(pBuf+sizeof(ULONG)); if(NT_SUCCESS(status)) { for(int i=0;i<(int)uObjCnt;i++) { ( Out(Dbg,"pHandleInfo->Handle:%d pHandleInfo->ProcessID:%d \n",pHandleInfo->Handle,pHandleInfo->ProcessID)); if(pHandleInfo->Handle==(USHORT)hProcess && pHandleInfo->ProcessID==(ULONG)GetCurrentProcessId())/*pHandleInfo->ProcessID==dwPID && pHandleInfo->Handle==(USHORT)GetProcessHand()*/ { dwEPROCESS = (DWORD)pHandleInfo->Object; Out(Dbg,"dwEPROCESS: 0x%x",(ULONG)dwEPROCESS); //dwCurrentPID = pHandleInfo->ProcessID; break; } pHandleInfo++; } //在拿到当前进程的EPROCESS基址后,释放掉内存 ZwFreeVirtualMemory(GetCurrentProcess(),(PVOID*)&pBuf,&buflen,MEM_RELEASE); //关闭句柄 CloseHandle(hProcess); bRet = TRUE; } //FCHK(SystemDebugControl(dwEPROCESS+0x088,&list,sizeof(list),SysDbgCopyMemoryChunks_0)); //FCHK(SystemDebugControl((ULONG)(list.Blink)+0x4,&list.Blink,sizeof(list.Blink),SysDbgCopyMemoryChunks_1)); //FCHK(SystemDebugControl((ULONG)(list.Blink),&list.Flink,sizeof(list.Flink),SysDbgCopyMemoryChunks_1)); MEMORY_CHUNKS datas; datas.Address = dwEPROCESS+0x088; LIST_ENTRY list = {0}; datas.Data =&list; datas.Length = sizeof(list); OperateSystemMemory(datas,SysDbgCopyMemoryChunks_0); datas.Address = (ULONG)(list.Blink)+0x4; datas.Data =&list.Blink; datas.Length = sizeof(list.Blink); OperateSystemMemory(datas,SysDbgCopyMemoryChunks_1); datas.Address = (ULONG)(list.Blink); datas.Data =&list.Flink; datas.Length = sizeof(list.Flink); OperateSystemMemory(datas,SysDbgCopyMemoryChunks_1); return TRUE; }
NTSTATUS NTAPI RtlpInitEnvironment(HANDLE ProcessHandle, PPEB Peb, PRTL_USER_PROCESS_PARAMETERS ProcessParameters) { NTSTATUS Status; PVOID BaseAddress = NULL; SIZE_T EnviroSize; SIZE_T Size; PWCHAR Environment = NULL; DPRINT("RtlpInitEnvironment(ProcessHandle: %p, Peb: %p Params: %p)\n", ProcessHandle, Peb, ProcessParameters); /* Give the caller 1MB if he requested it */ if (ProcessParameters->Flags & RTL_USER_PROCESS_PARAMETERS_RESERVE_1MB) { /* Give 1MB starting at 0x4 */ BaseAddress = (PVOID)4; EnviroSize = (1024 * 1024) - 256; Status = ZwAllocateVirtualMemory(ProcessHandle, &BaseAddress, 0, &EnviroSize, MEM_RESERVE, PAGE_READWRITE); if (!NT_SUCCESS(Status)) { DPRINT1("Failed to reserve 1MB of space \n"); return Status; } } /* Find the end of the Enviroment Block */ if ((Environment = (PWCHAR)ProcessParameters->Environment)) { while (*Environment++) while (*Environment++); /* Calculate the size of the block */ EnviroSize = (ULONG)((ULONG_PTR)Environment - (ULONG_PTR)ProcessParameters->Environment); /* Allocate and Initialize new Environment Block */ Size = EnviroSize; Status = ZwAllocateVirtualMemory(ProcessHandle, &BaseAddress, 0, &Size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if (!NT_SUCCESS(Status)) { DPRINT1("Failed to allocate Environment Block\n"); return Status; } /* Write the Environment Block */ ZwWriteVirtualMemory(ProcessHandle, BaseAddress, ProcessParameters->Environment, EnviroSize, NULL); /* Save pointer */ ProcessParameters->Environment = BaseAddress; } /* Now allocate space for the Parameter Block */ BaseAddress = NULL; Size = ProcessParameters->MaximumLength; Status = ZwAllocateVirtualMemory(ProcessHandle, &BaseAddress, 0, &Size, MEM_COMMIT, PAGE_READWRITE); if (!NT_SUCCESS(Status)) { DPRINT1("Failed to allocate Parameter Block\n"); return Status; } /* Write the Parameter Block */ Status = ZwWriteVirtualMemory(ProcessHandle, BaseAddress, ProcessParameters, ProcessParameters->Length, NULL); if (!NT_SUCCESS(Status)) { DPRINT1("Failed to write the Parameter Block\n"); return Status; } /* Write pointer to Parameter Block */ Status = ZwWriteVirtualMemory(ProcessHandle, &Peb->ProcessParameters, &BaseAddress, sizeof(BaseAddress), NULL); if (!NT_SUCCESS(Status)) { DPRINT1("Failed to write pointer to Parameter Block\n"); return Status; } /* Return */ return STATUS_SUCCESS; }
NTSTATUS RtlCreateUserProcess( IN PUNICODE_STRING NtImagePathName, IN ULONG Attributes, IN PRTL_USER_PROCESS_PARAMETERS ProcessParameters, IN PSECURITY_DESCRIPTOR ProcessSecurityDescriptor OPTIONAL, IN PSECURITY_DESCRIPTOR ThreadSecurityDescriptor OPTIONAL, IN HANDLE ParentProcess OPTIONAL, IN BOOLEAN InheritHandles, IN HANDLE DebugPort OPTIONAL, IN HANDLE ExceptionPort OPTIONAL, OUT PRTL_USER_PROCESS_INFORMATION ProcessInformation ) /*++ Routine Description: This function creates a user mode process with a single thread with a suspend count of one. The address space of the new process is initialized with the contents of specified image file. The caller can specify the Access Control List for the new process and thread. The caller can also specify the parent process to inherit process priority and processor affinity from. The default is to inherit these from the current process. Finally the caller can specify whether the new process is to inherit any of the object handles from the specified parent process or not. Information about the new process and thread is returned via the ProcessInformation parameter. Arguments: NtImagePathName - A required pointer that points to the NT Path string that identifies the image file that is to be loaded into the child process. ProcessParameters - A required pointer that points to parameters that are to passed to the child process. ProcessSecurityDescriptor - An optional pointer to the Security Descriptor give to the new process. ThreadSecurityDescriptor - An optional pointer to the Security Descriptor give to the new thread. ParentProcess - An optional process handle that will used to inherit certain properties from. InheritHandles - A boolean value. TRUE specifies that object handles associated with the specified parent process are to be inherited by the new process, provided they have the OBJ_INHERIT attribute. FALSE specifies that the new process is to inherit no handles. DebugPort - An optional handle to the debug port associated with this process. ExceptionPort - An optional handle to the exception port associated with this process. ProcessInformation - A pointer to a variable that receives information about the new process and thread. --*/ { NTSTATUS Status; HANDLE Section, File; OBJECT_ATTRIBUTES ObjectAttributes; PRTL_USER_PROCESS_PARAMETERS Parameters; SIZE_T ParameterLength; PVOID Environment; PWCHAR s; SIZE_T EnvironmentLength; SIZE_T RegionSize; PROCESS_BASIC_INFORMATION ProcessInfo; PPEB Peb; UNICODE_STRING Unicode; // // Zero output parameter and probe the addresses at the same time // RtlZeroMemory( ProcessInformation, sizeof( *ProcessInformation ) ); ProcessInformation->Length = sizeof( *ProcessInformation ); // // Open the specified image file. // Status = RtlpOpenImageFile( NtImagePathName, Attributes & (OBJ_INHERIT | OBJ_CASE_INSENSITIVE), &File, TRUE ); if (!NT_SUCCESS( Status )) { return( Status ); } // // Create a memory section backed by the opened image file // Status = ZwCreateSection( &Section, SECTION_ALL_ACCESS, NULL, NULL, PAGE_EXECUTE, SEC_IMAGE, File ); ZwClose( File ); if ( !NT_SUCCESS( Status ) ) { return( Status ); } // // Create the user mode process, defaulting the parent process to the // current process if one is not specified. The new process will not // have a name nor will the handle be inherited by other processes. // if (!ARGUMENT_PRESENT( ParentProcess )) { ParentProcess = NtCurrentProcess(); } InitializeObjectAttributes( &ObjectAttributes, NULL, 0, NULL, ProcessSecurityDescriptor ); if ( RtlGetNtGlobalFlags() & FLG_ENABLE_CSRDEBUG ) { if ( wcsstr(NtImagePathName->Buffer,L"csrss") || wcsstr(NtImagePathName->Buffer,L"CSRSS") ) { // // For Hydra we don't name the CSRSS process to avoid name // collissions when multiple CSRSS's are started // if (ISTERMINALSERVER()) { InitializeObjectAttributes( &ObjectAttributes, NULL, 0, NULL, ProcessSecurityDescriptor ); } else { RtlInitUnicodeString(&Unicode,L"\\WindowsSS"); InitializeObjectAttributes( &ObjectAttributes, &Unicode, 0, NULL, ProcessSecurityDescriptor ); } } } if ( !InheritHandles ) { ProcessParameters->CurrentDirectory.Handle = NULL; } Status = ZwCreateProcess( &ProcessInformation->Process, PROCESS_ALL_ACCESS, &ObjectAttributes, ParentProcess, InheritHandles, Section, DebugPort, ExceptionPort ); if ( !NT_SUCCESS( Status ) ) { ZwClose( Section ); return( Status ); } // // Retrieve the interesting information from the image header // Status = ZwQuerySection( Section, SectionImageInformation, &ProcessInformation->ImageInformation, sizeof( ProcessInformation->ImageInformation ), NULL ); if ( !NT_SUCCESS( Status ) ) { ZwClose( ProcessInformation->Process ); ZwClose( Section ); return( Status ); } Status = ZwQueryInformationProcess( ProcessInformation->Process, ProcessBasicInformation, &ProcessInfo, sizeof( ProcessInfo ), NULL ); if ( !NT_SUCCESS( Status ) ) { ZwClose( ProcessInformation->Process ); ZwClose( Section ); return( Status ); } Peb = ProcessInfo.PebBaseAddress; // // Duplicate Native handles into new process if any specified. // Note that the duplicated handles will overlay the input values. // try { Status = STATUS_SUCCESS; if ( ProcessParameters->StandardInput ) { Status = ZwDuplicateObject( ParentProcess, ProcessParameters->StandardInput, ProcessInformation->Process, &ProcessParameters->StandardInput, 0L, 0L, DUPLICATE_SAME_ACCESS | DUPLICATE_SAME_ATTRIBUTES ); if ( !NT_SUCCESS(Status) ) { __leave; } } if ( ProcessParameters->StandardOutput ) { Status = ZwDuplicateObject( ParentProcess, ProcessParameters->StandardOutput, ProcessInformation->Process, &ProcessParameters->StandardOutput, 0L, 0L, DUPLICATE_SAME_ACCESS | DUPLICATE_SAME_ATTRIBUTES ); if ( !NT_SUCCESS(Status) ) { __leave; } } if ( ProcessParameters->StandardError ) { Status = ZwDuplicateObject( ParentProcess, ProcessParameters->StandardError, ProcessInformation->Process, &ProcessParameters->StandardError, 0L, 0L, DUPLICATE_SAME_ACCESS | DUPLICATE_SAME_ATTRIBUTES ); if ( !NT_SUCCESS(Status) ) { __leave; } } } finally { if ( !NT_SUCCESS(Status) ) { ZwClose( ProcessInformation->Process ); ZwClose( Section ); } } if ( !NT_SUCCESS(Status) ) { return Status; } // // Possibly reserve some address space in the new process // if (ProcessInformation->ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_NATIVE ) { if ( ProcessParameters->Flags & RTL_USER_PROC_RESERVE_1MB ) { Environment = (PVOID)(4); RegionSize = (1024*1024)-(256); Status = ZwAllocateVirtualMemory( ProcessInformation->Process, (PVOID *)&Environment, 0, &RegionSize, MEM_RESERVE, PAGE_READWRITE ); if ( !NT_SUCCESS( Status ) ) { ZwClose( ProcessInformation->Process ); ZwClose( Section ); return( Status ); } } } // // Allocate virtual memory in the new process and use NtWriteVirtualMemory // to write a copy of the process environment block into the address // space of the new process. Save the address of the allocated block in // the process parameter block so the new process can access it. // if (s = (PWCHAR)ProcessParameters->Environment) { while (*s++) { while (*s++) { } } EnvironmentLength = (SIZE_T)(s - (PWCHAR)ProcessParameters->Environment) * sizeof(WCHAR); Environment = NULL; RegionSize = EnvironmentLength; Status = ZwAllocateVirtualMemory( ProcessInformation->Process, (PVOID *)&Environment, 0, &RegionSize, MEM_COMMIT, PAGE_READWRITE ); if ( !NT_SUCCESS( Status ) ) { ZwClose( ProcessInformation->Process ); ZwClose( Section ); return( Status ); } Status = ZwWriteVirtualMemory( ProcessInformation->Process, Environment, ProcessParameters->Environment, EnvironmentLength, NULL ); if ( !NT_SUCCESS( Status ) ) { ZwClose( ProcessInformation->Process ); ZwClose( Section ); return( Status ); } ProcessParameters->Environment = Environment; } // // Allocate virtual memory in the new process and use NtWriteVirtualMemory // to write a copy of the process parameters block into the address // space of the new process. Set the initial parameter to the new thread // to be the address of the block in the new process's address space. // Parameters = NULL; ParameterLength = ProcessParameters->MaximumLength; Status = ZwAllocateVirtualMemory( ProcessInformation->Process, (PVOID *)&Parameters, 0, &ParameterLength, MEM_COMMIT, PAGE_READWRITE ); if ( !NT_SUCCESS( Status ) ) { ZwClose( ProcessInformation->Process ); ZwClose( Section ); return( Status ); } Status = ZwWriteVirtualMemory( ProcessInformation->Process, Parameters, ProcessParameters, ProcessParameters->Length, NULL ); if ( !NT_SUCCESS( Status ) ) { ZwClose( ProcessInformation->Process ); ZwClose( Section ); return( Status ); } Status = ZwWriteVirtualMemory( ProcessInformation->Process, &Peb->ProcessParameters, &Parameters, sizeof( Parameters ), NULL ); if ( !NT_SUCCESS( Status ) ) { ZwClose( ProcessInformation->Process ); ZwClose( Section ); return( Status ); } // // Create a suspended thread in the new process. Specify the size and // position of the stack, along with the start address, initial parameter // and an SECURITY_DESCRIPTOR. The new thread will not have a name and its handle will // not be inherited by other processes. // Status = RtlCreateUserThread( ProcessInformation->Process, ThreadSecurityDescriptor, TRUE, ProcessInformation->ImageInformation.ZeroBits, ProcessInformation->ImageInformation.MaximumStackSize, ProcessInformation->ImageInformation.CommittedStackSize, (PUSER_THREAD_START_ROUTINE) ProcessInformation->ImageInformation.TransferAddress, (PVOID)Peb, &ProcessInformation->Thread, &ProcessInformation->ClientId ); if ( !NT_SUCCESS( Status ) ) { ZwClose( ProcessInformation->Process ); ZwClose( Section ); return( Status ); } // // Now close the section and file handles. The objects they represent // will not actually go away until the process is destroyed. // ZwClose( Section ); // // Return success status // return( STATUS_SUCCESS ); }
NTSTATUS NTAPI IntInitializeVideoAddressSpace(VOID) { OBJECT_ATTRIBUTES ObjectAttributes; UNICODE_STRING PhysMemName = RTL_CONSTANT_STRING(L"\\Device\\PhysicalMemory"); NTSTATUS Status; HANDLE PhysMemHandle; PVOID BaseAddress; LARGE_INTEGER Offset; SIZE_T ViewSize; CHAR IVTAndBda[1024+256]; /* Free the 1MB pre-reserved region. In reality, ReactOS should simply support us mapping the view into the reserved area, but it doesn't. */ BaseAddress = 0; ViewSize = 1024 * 1024; Status = ZwFreeVirtualMemory(NtCurrentProcess(), &BaseAddress, &ViewSize, MEM_RELEASE); if (!NT_SUCCESS(Status)) { DPRINT1("Couldn't unmap reserved memory (%x)\n", Status); return 0; } /* Open the physical memory section */ InitializeObjectAttributes(&ObjectAttributes, &PhysMemName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); Status = ZwOpenSection(&PhysMemHandle, SECTION_ALL_ACCESS, &ObjectAttributes); if (!NT_SUCCESS(Status)) { DPRINT1("Couldn't open \\Device\\PhysicalMemory\n"); return Status; } /* Map the BIOS and device registers into the address space */ Offset.QuadPart = 0xa0000; ViewSize = 0x100000 - 0xa0000; BaseAddress = (PVOID)0xa0000; Status = ZwMapViewOfSection(PhysMemHandle, NtCurrentProcess(), &BaseAddress, 0, ViewSize, &Offset, &ViewSize, ViewUnmap, 0, PAGE_EXECUTE_READWRITE); if (!NT_SUCCESS(Status)) { DPRINT1("Couldn't map physical memory (%x)\n", Status); ZwClose(PhysMemHandle); return Status; } /* Close physical memory section handle */ ZwClose(PhysMemHandle); if (BaseAddress != (PVOID)0xa0000) { DPRINT1("Couldn't map physical memory at the right address (was %x)\n", BaseAddress); return STATUS_UNSUCCESSFUL; } /* Allocate some low memory to use for the non-BIOS * parts of the v86 mode address space */ BaseAddress = (PVOID)0x1; ViewSize = 0xa0000 - 0x1000; Status = ZwAllocateVirtualMemory(NtCurrentProcess(), &BaseAddress, 0, &ViewSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (!NT_SUCCESS(Status)) { DPRINT1("Failed to allocate virtual memory (Status %x)\n", Status); return Status; } if (BaseAddress != (PVOID)0x0) { DPRINT1("Failed to allocate virtual memory at right address (was %x)\n", BaseAddress); return 0; } /* Get the real mode IVT and BDA from the kernel */ Status = NtVdmControl(VdmInitialize, IVTAndBda); if (!NT_SUCCESS(Status)) { DPRINT1("NtVdmControl failed (status %x)\n", Status); return Status; } /* Return success */ return STATUS_SUCCESS; }
NTSTATUS UserCommitMemory( PVOID pBase, PVOID *ppCommit, PULONG pCommitSize) { PDESKTOPVIEW pdv; DWORD dwCommitOffset; PWINDOWSTATION pwinsta; PDESKTOP pdesk; PBYTE pUserBase; NTSTATUS Status; /* * If this is a system thread, we have no view of the desktop * and must map it in. Fortunately, this does not happen often. */ if (IS_SYSTEM_THREAD(PsGetCurrentThread())) { /* * Find the desktop that owns the section. */ for (pwinsta = grpwinstaList; pwinsta; pwinsta = pwinsta->rpwinstaNext) { for (pdesk = pwinsta->rpdeskList; pdesk; pdesk = pdesk->rpdeskNext) { if (pdesk->pDeskInfo->pvDesktopBase == pBase) goto FoundIt; } } FoundIt: if (pwinsta == NULL) return STATUS_NO_MEMORY; /* * Map the section into the current process and commit the * first page of the section. */ dwCommitOffset = (ULONG)((PBYTE)*ppCommit - (PBYTE)pBase); Status = CommitReadOnlyMemory(pdesk->hsectionDesktop, PAGE_SIZE, dwCommitOffset); } else { /* * Find the current process' view of the desktop */ for (pdv = PpiCurrent()->pdvList; pdv != NULL; pdv = pdv->pdvNext) { if (pdv->pdesk->pDeskInfo->pvDesktopBase == pBase) break; } UserAssert(pdv); /* * Commit the memory */ pUserBase = (PVOID)((PBYTE)*ppCommit - pdv->ulClientDelta); Status = ZwAllocateVirtualMemory(NtCurrentProcess(), &pUserBase, 0, pCommitSize, MEM_COMMIT, PAGE_EXECUTE_READ ); if (NT_SUCCESS(Status)) *ppCommit = (PVOID)((PBYTE)pUserBase + pdv->ulClientDelta); } return Status; }
NTSTATUS FindHardDiskForPartition(PUNICODE_STRING pNameUnicodeString, PWCHAR pOutRequest, ULONG OutRequestSize, ULONG *pRetSize) { NTSTATUS ntRetStatus = STATUS_NOT_FOUND; NTSTATUS ntStatus; BYTE cou; OBJECT_ATTRIBUTES ObjAttr; HANDLE hDirectoryObject; BOOLEAN First; POBJECT_NAMETYPE_INFO p; HANDLE DrvHandle; PDRIVER_OBJECT pDrv; UNICODE_STRING Name; PWCHAR DrvName; PWCHAR DriverStr=L"Partition"; WCHAR Dir[128]; ULONG RegionSize; PMY_QUERY_DIRECTORY_STRUCT qds; qds = NULL; RegionSize = sizeof(MY_QUERY_DIRECTORY_STRUCT); if(!NT_SUCCESS(ntStatus = ZwAllocateVirtualMemory((HANDLE)-1, (PVOID*) &qds, 0, &RegionSize, // ^ - Whistler beta 2 doesn't work with 8 MEM_COMMIT,PAGE_READWRITE))) { DbPrint(DC_LLDISKIO, DL_ERROR,("ZwAllocateVirtualMemory fail. Status=%x\n", ntStatus)); } else { PWCHAR pdrvid; for(cou = 0; cou <= 9 && ntRetStatus != STATUS_SUCCESS; cou++) { wcscpy(Dir, L"\\Device\\Harddisk0"); pdrvid = &Dir[wcslen(Dir) - 1]; *(BYTE*)pdrvid += cou; RtlInitUnicodeString(&Name, Dir); InitializeObjectAttributes(&ObjAttr, &Name, OBJ_CASE_INSENSITIVE, NULL, NULL); ntStatus=ZwOpenDirectoryObject(&hDirectoryObject, DIRECTORY_QUERY, &ObjAttr); if(NT_SUCCESS(ntStatus)) { First=TRUE; DrvName=Dir+wcslen(Dir); *DrvName++='\\'; while(NtQueryDirectoryObject(hDirectoryObject, &qds->Buffer, QUERY_DIRECTORY_BUFF_SIZE, TRUE, First, &qds->Index, &qds->Retlen) >= 0 && ntRetStatus != STATUS_SUCCESS) { p=(POBJECT_NAMETYPE_INFO)&qds->Buffer; First=FALSE; *DrvName=0; if(wcsncmp(p->ObjectName.Buffer, DriverStr, 9)) continue; else { HANDLE hLink; InitializeObjectAttributes(&ObjAttr, &p->ObjectName, OBJ_CASE_INSENSITIVE, hDirectoryObject, NULL); ntStatus = ZwOpenSymbolicLinkObject(&hLink, SYMBOLIC_LINK_QUERY, &ObjAttr); if(NT_SUCCESS(ntStatus)) { WCHAR targetNameBuffer[260]; UNICODE_STRING targetNameUnicodeString; RtlZeroMemory(targetNameBuffer, sizeof(targetNameBuffer)); targetNameUnicodeString.Buffer = targetNameBuffer; targetNameUnicodeString.MaximumLength = sizeof(targetNameBuffer); ntStatus = ZwQuerySymbolicLinkObject(hLink, &targetNameUnicodeString, NULL); if(NT_SUCCESS(ntStatus) && (RtlCompareUnicodeString(&targetNameUnicodeString, pNameUnicodeString, FALSE) == 0)) { int number = GetHarddiskId(Dir); if (number != -1) { int cou2; int drvstrlen; char drstr[32]; sprintf(drstr, "%d", number); drvstrlen = strlen(drstr); wcscpy(pOutRequest, Dir); wcscat(pOutRequest, L"DR"); pdrvid = &pOutRequest[wcslen(pOutRequest) - 1] + 1; for (cou2 = 0; cou2 < drvstrlen; cou2++) { *pdrvid = (WCHAR) (drstr[cou2]); pdrvid++; } *pdrvid = 0; DbPrint(DC_LLDISKIO, DL_INFO, ("FindHardDiskForPartition found %S\n", pOutRequest)); *pRetSize = (wcslen(pOutRequest) + 1 ) * 2; ntRetStatus = STATUS_SUCCESS; } } ZwClose(hLink); } } } ZwClose(hDirectoryObject); } } } RegionSize = 0; _pfZwFreeVirtualMemory((HANDLE)-1,(PVOID*) &qds,&RegionSize,MEM_RELEASE); return ntRetStatus; }
BOOLEAN InjectDll(PINJECT_INFO InjectInfo) { PEPROCESS Process; PETHREAD Thread; PKINJECT mem; ULONG size; PKAPC_STATE ApcState; PKAPC apc; PVOID buffer; PSYSTEM_PROCESS_INFO pSpi; LARGE_INTEGER delay; buffer=ExAllocatePool(NonPagedPool,1024*1024); // Allocate memory for the system information if(!buffer) { DbgPrint("Error: Unable to allocate memory for the process thread list."); return FALSE; } // Get the process thread list if(!NT_SUCCESS(ZwQuerySystemInformation(5,buffer,1024*1024,NULL))) { DbgPrint("Error: Unable to query process thread list."); ExFreePool(buffer); return FALSE; } pSpi=(PSYSTEM_PROCESS_INFO)buffer; // Find a target thread while(pSpi->NextEntryOffset) { if(pSpi->UniqueProcessId==InjectInfo->ProcessId) { DbgPrint("Target thread found. TID: %d",pSpi->Threads[0].ClientId.UniqueThread); break; } pSpi=(PSYSTEM_PROCESS_INFO)((PUCHAR)pSpi+pSpi->NextEntryOffset); } // Reference the target process if(!NT_SUCCESS(PsLookupProcessByProcessId(InjectInfo->ProcessId,&Process))) { DbgPrint("Error: Unable to reference the target process."); ExFreePool(buffer); return FALSE; } DbgPrint("Process name: %s",PsGetProcessImageFileName(Process)); DbgPrint("EPROCESS address: %#x",Process); // Reference the target thread if(!NT_SUCCESS(PsLookupThreadByThreadId(pSpi->Threads[0].ClientId.UniqueThread,&Thread))) { DbgPrint("Error: Unable to reference the target thread."); ObDereferenceObject(Process); // Dereference the target process ExFreePool(buffer); // Free the allocated memory return FALSE; } DbgPrint("ETHREAD address: %#x",Thread); ExFreePool(buffer); // Free the allocated memory KeAttachProcess(Process); // Attach to target process's address space mem=NULL; size=4096; // Allocate memory in the target process if(!NT_SUCCESS(ZwAllocateVirtualMemory(NtCurrentProcess(),(PVOID*)&mem,0,&size,MEM_COMMIT|MEM_RESERVE,PAGE_EXECUTE_READWRITE))) { DbgPrint("Error: Unable to allocate memory in the target process."); KeDetachProcess(); // Detach from target process's address space ObDereferenceObject(Process); // Dereference the target process ObDereferenceObject(Thread); // Dereference the target thread return FALSE; } DbgPrint("Memory allocated at %#x",mem); mem->LdrLoadDll=LdrLoadDll; // Write the address of LdrLoadDll to target process wcscpy(mem->Buffer,InjectInfo->DllName); // Write the DLL name to target process RtlInitUnicodeString(&mem->DllName,mem->Buffer); // Initialize the UNICODE_STRING structure ApcState=(PKAPC_STATE)((PUCHAR)Thread+ApcStateOffset); // Calculate the address of the ApcState structure ApcState->UserApcPending=TRUE; // Force the target thread to execute APC memcpy((PKINJECT)(mem+1),InjectDllApc,(ULONG)KernelRoutine-(ULONG)InjectDllApc); // Copy the APC code to target process DbgPrint("APC code address: %#x",(PKINJECT)(mem+1)); apc=(PKAPC)ExAllocatePool(NonPagedPool,sizeof(KAPC)); // Allocate the APC object if(!apc) { DbgPrint("Error: Unable to allocate the APC object."); size=0; ZwFreeVirtualMemory(NtCurrentProcess(),(PVOID*)&mem,&size,MEM_RELEASE); // Free the allocated memory KeDetachProcess(); // Detach from target process's address space ObDereferenceObject(Process); // Dereference the target process ObDereferenceObject(Thread); // Dereference the target thread return FALSE; } KeInitializeApc(apc,Thread,OriginalApcEnvironment,KernelRoutine,NULL,(PKNORMAL_ROUTINE)((PKINJECT)mem+1),UserMode,mem); // Initialize the APC DbgPrint("Inserting APC to target thread"); // Insert the APC to the target thread if(!KeInsertQueueApc(apc,NULL,NULL,IO_NO_INCREMENT)) { DbgPrint("Error: Unable to insert APC to target thread."); size=0; ZwFreeVirtualMemory(NtCurrentProcess(),(PVOID*)&mem,&size,MEM_RELEASE); // Free the allocated memory KeDetachProcess(); // Detach from target process's address space ObDereferenceObject(Process); // Dereference the target process ObDereferenceObject(Thread); // Dereference the target thread ExFreePool(apc); // Free the APC object return FALSE; } delay.QuadPart=-100*10000; while(!mem->Executed) { KeDelayExecutionThread(KernelMode,FALSE,&delay); // Wait for the injection to complete } if(!mem->DllBase) { DbgPrint("Error: Unable to inject DLL into target process."); size=0; ZwFreeVirtualMemory(NtCurrentProcess(),(PVOID*)&mem,&size,MEM_RELEASE); KeDetachProcess(); ObDereferenceObject(Process); ObDereferenceObject(Thread); return FALSE; } DbgPrint("DLL injected at %#x",mem->DllBase); size=0; ZwFreeVirtualMemory(NtCurrentProcess(),(PVOID*)&mem,&size,MEM_RELEASE); // Free the allocated memory KeDetachProcess(); // Detach from target process's address space ObDereferenceObject(Process); // Dereference the target process ObDereferenceObject(Thread); // Dereference the target thread return TRUE; }