static int RpcRequest(const RPC_REQUEST *const Request, RPC_RESPONSE *const Response, const DWORD RpcAssocGroup, const SOCKET sock, const unsigned int len) { uint_fast16_t _v; _v = LE16(((WORD*)Request->Data)[1]) - 4; int ResponseSize = _Versions[_v].CreateResponse(Request->Data, Response->Data); if ( ResponseSize ) { Response->Ndr.DataSizeIs1 = LE32(0x00020000); Response->Ndr.DataLength = Response->Ndr.DataSizeIs2 = LE32(ResponseSize); int len = ResponseSize + sizeof(Response->Ndr); BYTE* pRpcReturnCode = ((BYTE*)&Response->Ndr) + len; UA32(pRpcReturnCode) = 0; //LE16 not needed for 0 len += sizeof(DWORD); // Pad zeros to 32-bit align (seems not neccassary but Windows RPC does it this way) int pad = ((~len & 3) + 1) & 3; memset(pRpcReturnCode + sizeof(DWORD), 0, pad); len += pad; Response->AllocHint = LE32(len); Response->AllocHint += Response->ContextId = Request->ContextId; *((WORD*)&Response->CancelCount) = 0; // CancelCount + Pad1 } return ResponseSize; }
/* * Sends a KMS request via RPC and receives a response. * Parameters are raw (encrypted) reqeuests / responses. * Returns 0 on success. */ RpcStatus rpcSendRequest(const RpcCtx sock, const BYTE *const kmsRequest, const size_t requestSize, BYTE **kmsResponse, size_t *const responseSize) { #define MAX_EXCESS_BYTES 16 RPC_HEADER *RequestHeader, ResponseHeader; RPC_REQUEST64 *RpcRequest; RPC_RESPONSE64 _Response; int status; int_fast8_t useNdr64 = RpcFlags.HasNDR64 && UseClientRpcNDR64 && firstPacketSent; size_t size = sizeof(RPC_HEADER) + (useNdr64 ? sizeof(RPC_REQUEST64) : sizeof(RPC_REQUEST)) + requestSize; size_t responseSize2; *kmsResponse = NULL; BYTE *_Request = (BYTE*)vlmcsd_malloc(size); RequestHeader = (RPC_HEADER*)_Request; RpcRequest = (RPC_REQUEST64*)(_Request + sizeof(RPC_HEADER)); createRpcHeader(RequestHeader, RPC_PT_REQUEST, (WORD)size); // Increment CallId for next Request CallId++; RpcRequest->Opnum = 0; if (useNdr64) { RpcRequest->ContextId = LE16(1); // We negotiate NDR64 always as context 1 RpcRequest->AllocHint = LE32((DWORD)(requestSize + sizeof(RpcRequest->Ndr64))); RpcRequest->Ndr64.DataLength = LE64((uint64_t)requestSize); RpcRequest->Ndr64.DataSizeIs = LE64((uint64_t)requestSize); memcpy(RpcRequest->Ndr64.Data, kmsRequest, requestSize); } else { RpcRequest->ContextId = 0; // We negotiate NDR32 always as context 0 RpcRequest->AllocHint = LE32((DWORD)(requestSize + sizeof(RpcRequest->Ndr))); RpcRequest->Ndr.DataLength = LE32((DWORD)requestSize); RpcRequest->Ndr.DataSizeIs = LE32((DWORD)requestSize); memcpy(RpcRequest->Ndr.Data, kmsRequest, requestSize); } for (;;) { int bytesread; if (!_send(sock, _Request, (int)size)) { printerrorf("\nFatal: Could not send RPC request\n"); status = RPC_S_COMM_FAILURE; break; } if (!_recv(sock, &ResponseHeader, sizeof(RPC_HEADER))) { printerrorf("\nFatal: No RPC response received from server\n"); status = RPC_S_COMM_FAILURE; break; } if ((status = checkRpcResponseHeader(&ResponseHeader, RequestHeader, RPC_PT_RESPONSE, &printerrorf))) break; size = useNdr64 ? sizeof(RPC_RESPONSE64) : sizeof(RPC_RESPONSE); if (size > LE16(ResponseHeader.FragLength) - sizeof(ResponseHeader)) size = LE16(ResponseHeader.FragLength) - sizeof(ResponseHeader); if (!_recv(sock, &_Response, (int)size)) { printerrorf("\nFatal: RPC response is incomplete\n"); status = RPC_S_COMM_FAILURE; break; } if (_Response.CancelCount != 0) { printerrorf("\nFatal: RPC response cancel count is not 0\n"); status = RPC_S_CALL_CANCELLED; break; } if (_Response.ContextId != (useNdr64 ? LE16(1) : 0)) { printerrorf("\nFatal: RPC response context id %u is not bound\n", (unsigned int)LE16(_Response.ContextId)); status = RPC_X_SS_CONTEXT_DAMAGED; break; } int_fast8_t sizesMatch; if (useNdr64) { *responseSize = (size_t)LE64(_Response.Ndr64.DataLength); responseSize2 = (size_t)LE64(_Response.Ndr64.DataSizeIs); if (/*!*responseSize ||*/ !_Response.Ndr64.DataSizeMax) { status = (int)LE32(_Response.Ndr64.status); break; } sizesMatch = (size_t)LE64(_Response.Ndr64.DataLength) == responseSize2; } else { *responseSize = (size_t)LE32(_Response.Ndr.DataLength); responseSize2 = (size_t)LE32(_Response.Ndr.DataSizeIs); if (/*!*responseSize ||*/ !_Response.Ndr.DataSizeMax) { status = (int)LE32(_Response.Ndr.status); break; } sizesMatch = (size_t)LE32(_Response.Ndr.DataLength) == responseSize2; } if (!sizesMatch) { printerrorf("\nFatal: NDR data length (%u) does not match NDR data size (%u)\n", (uint32_t)*responseSize, (uint32_t)LE32(_Response.Ndr.DataSizeIs) ); status = RPC_S_PROTOCOL_ERROR; break; } *kmsResponse = (BYTE*)vlmcsd_malloc(*responseSize + MAX_EXCESS_BYTES); // If RPC stub is too short, assume missing bytes are zero (same ill behavior as MS RPC) memset(*kmsResponse, 0, *responseSize + MAX_EXCESS_BYTES); // Read up to 16 bytes more than bytes expected to detect faulty KMS emulators if ((bytesread = recv(sock, (char*)*kmsResponse, (int)(*responseSize) + MAX_EXCESS_BYTES, 0)) < (int)*responseSize) { printerrorf("\nFatal: No or incomplete KMS response received. Required %u bytes but only got %i\n", (uint32_t)*responseSize, (int32_t)(bytesread < 0 ? 0 : bytesread) ); status = RPC_S_PROTOCOL_ERROR; break; } DWORD *pReturnCode; size_t len = *responseSize + (useNdr64 ? sizeof(_Response.Ndr64) : sizeof(_Response.Ndr)) + sizeof(*pReturnCode); size_t pad = ((~len & 3) + 1) & 3; if (len + pad != LE32(_Response.AllocHint)) { printerrorf("\nWarning: RPC stub size is %u, should be %u (probably incorrect padding)\n", (uint32_t)LE32(_Response.AllocHint), (uint32_t)(len + pad)); } else { size_t i; for (i = 0; i < pad; i++) { if (*(*kmsResponse + *responseSize + sizeof(*pReturnCode) + i)) { printerrorf("\nWarning: RPC stub data not padded to zeros according to Microsoft standard\n"); break; } } } pReturnCode = (DWORD*)(*kmsResponse + *responseSize + pad); status = LE32(UA32(pReturnCode)); break; } free(_Request); firstPacketSent = TRUE; return status; #undef MAX_EXCESS_BYTES }
/* * Handles the actual KMS request from the client. * Calls KMS functions (CreateResponseV4 or CreateResponseV6) in kms.c * Returns size of the KMS response packet or 0 on failure. * * The RPC packet size (excluding header) is actually in Response->AllocHint */ static int rpcRequest(const RPC_REQUEST64 *const Request, RPC_RESPONSE64 *const Response, const DWORD RpcAssocGroup_unused, const SOCKET sock_unused, WORD* NdrCtx, WORD* Ndr64Ctx, BYTE isValid, const char* const ipstr) { int ResponseSize; // <0 = Errorcode (HRESULT) BYTE* requestData; BYTE* responseData; BYTE* pRpcReturnCode; int len; # ifndef SIMPLE_RPC WORD Ctx = LE16(Request->ContextId); if (Ctx == *NdrCtx) { requestData = (BYTE*)&Request->Ndr.Data; responseData = (BYTE*)&Response->Ndr.Data; } else if (Ctx == *Ndr64Ctx) { requestData = (BYTE*)&Request->Ndr64.Data; responseData = (BYTE*)&Response->Ndr64.Data; } else { return SendError(Response, RPC_NCA_UNK_IF); } # else // SIMPLE_RPC requestData = (BYTE*)&Request->Ndr.Data; responseData = (BYTE*)&Response->Ndr.Data; # endif // SIMPLE_RPC ResponseSize = 0x8007000D; // Invalid Data if (isValid) { uint16_t majorIndex = LE16(((WORD*)requestData)[1]) - 4; if (!((ResponseSize = _Versions[majorIndex].CreateResponse(requestData, responseData, ipstr)))) ResponseSize = 0x8007000D; } # ifndef SIMPLE_RPC if (Ctx != *Ndr64Ctx) { # endif // !SIMPLE_RPC if (ResponseSize < 0) { Response->Ndr.DataSizeMax = Response->Ndr.DataLength = 0; len = sizeof(Response->Ndr) - sizeof(Response->Ndr.DataSizeIs); } else { Response->Ndr.DataSizeMax = LE32(0x00020000); Response->Ndr.DataLength = Response->Ndr.DataSizeIs = LE32(ResponseSize); len = ResponseSize + sizeof(Response->Ndr); } # ifndef SIMPLE_RPC } else { if (ResponseSize < 0) { Response->Ndr64.DataSizeMax = Response->Ndr64.DataLength = 0; len = sizeof(Response->Ndr64) - sizeof(Response->Ndr64.DataSizeIs); } else { Response->Ndr64.DataSizeMax = LE64(0x00020000ULL); Response->Ndr64.DataLength = Response->Ndr64.DataSizeIs = LE64((uint64_t)ResponseSize); len = ResponseSize + sizeof(Response->Ndr64); } } # endif // !SIMPLE_RPC pRpcReturnCode = ((BYTE*)&Response->Ndr) + len; UA32(pRpcReturnCode) = ResponseSize < 0 ? LE32(ResponseSize) : 0; len += sizeof(DWORD); // Pad zeros to 32-bit align (seems not neccassary but Windows RPC does it this way) int pad = ((~len & 3) + 1) & 3; memset(pRpcReturnCode + sizeof(DWORD), 0, pad); len += pad; Response->AllocHint = LE32(len); Response->ContextId = Request->ContextId; *((WORD*)&Response->CancelCount) = 0; // CancelCount + Pad1 return len + 8; }
int RpcSendRequest(const SOCKET sock, const BYTE *const KmsRequest, const size_t requestSize, BYTE **KmsResponse, size_t *const responseSize) { #define MAX_EXCESS_BYTES 16 RPC_HEADER *RequestHeader, ResponseHeader; RPC_REQUEST *RpcRequest; RPC_RESPONSE _Response; int status = 0; size_t size = sizeof(RPC_HEADER) + sizeof(RPC_REQUEST) + requestSize; *KmsResponse = NULL; BYTE *_Request = (BYTE*)malloc(size); if (!_Request) return !0; RequestHeader = (RPC_HEADER*)_Request; RpcRequest = (RPC_REQUEST*)(_Request + sizeof(RPC_HEADER)); CreateRpcRequestHeader(RequestHeader, RPC_PT_REQUEST, size); // Increment CallId for next Request CallId++; RpcRequest->ContextId = 0; RpcRequest->Opnum = 0; RpcRequest->AllocHint = requestSize + sizeof(RpcRequest->Ndr); RpcRequest->Ndr.DataLength = LE32(requestSize); RpcRequest->Ndr.DataSizeIs = LE32(requestSize); memcpy(RpcRequest->Data, KmsRequest, requestSize); for(;;) { int bytesread; if (!_send(sock, _Request, size)) { errorout("\nFatal: Could not send RPC request\n"); status = !0; break; } if (!_recv(sock, &ResponseHeader, sizeof(RPC_HEADER))) { errorout("\nFatal: No RPC response received from server\n"); status = !0; break; } if ((status = CheckRpcHeaders(&ResponseHeader, RequestHeader, RPC_PT_RESPONSE, &errorout))) break; if (!_recv(sock, &_Response, sizeof(_Response))) { errorout("\nFatal: RPC response is incomplete\n"); status = !0; break; } if (_Response.CancelCount != 0) { errorout("\nFatal: RPC response cancel count is not 0\n"); status = !0; } if (_Response.ContextId != 0) { errorout("\nFatal: RPC response context id is not 0\n"); status = !0; } *responseSize = LE32(_Response.Ndr.DataLength); if (!*responseSize || !_Response.Ndr.DataSizeIs1) { status = LE32(_Response.Ndr.DataSizeIs2); break; } if (_Response.Ndr.DataLength != _Response.Ndr.DataSizeIs2) { errorout("\nFatal: NDR data length (%u) does not match NDR data size (%u)\n", (uint32_t)*responseSize, (uint32_t)LE32(_Response.Ndr.DataSizeIs2) ); status = !0; } *KmsResponse = (BYTE*)malloc(*responseSize + MAX_EXCESS_BYTES); if (!*KmsResponse) { errorout("\nFatal: Could not allocate memory for KMS response\n"); status = !0; break; } // If RPC stub is too short, assume missing bytes are zero (same ill behavior as MS RPC) memset(*KmsResponse, 0, *responseSize + MAX_EXCESS_BYTES); // Read up to 16 bytes more than bytes expected to detect faulty KMS emulators if ((bytesread = recv(sock, (char*)*KmsResponse, *responseSize + MAX_EXCESS_BYTES, 0)) < (int)*responseSize) { errorout("\nFatal: No or incomplete KMS response received. Required %u bytes but only got %i\n", (uint32_t)*responseSize, (int32_t)(bytesread < 0 ? 0 : bytesread) ); status = !0; break; } DWORD *pReturnCode; size_t len = *responseSize + sizeof(_Response.Ndr) + sizeof(*pReturnCode); size_t pad = ((~len & 3) + 1) & 3; if (len + pad != LE32(_Response.AllocHint)) { errorout("\nWarning: RPC stub size is %u, should be %u (probably incorrect padding)\n", (uint32_t)LE32(_Response.AllocHint), (uint32_t)(len + pad)); } else { size_t i; for (i = 0; i < pad; i++) { if (*(*KmsResponse + *responseSize + sizeof(*pReturnCode) + i)) { errorout("\nWarning: RPC stub data not padded to zeros according to Microsoft standard\n"); break; } } } pReturnCode = (DWORD*)(*KmsResponse + *responseSize); status = LE32(UA32(pReturnCode)); if (status) errorout("\nWarning: RPC stub data reported Error %u\n", (uint32_t)status); break; } free(_Request); return status; #undef MAX_EXCESS_BYTES }