/* Pseudo code in HTTP.sys to understand flow related to MS15-034 All pseudo code are reversed from vulnerable HTTP.sys on Windows 7 SP1 x86 For anyone want to know what function are patched. Just open patched version and find all functions reference to RtlULongLongAdd(). */ /***************************** * handling http request *****************************/ // the received request buffers are processed in UlpParseNextRequest(). NTSTATUS UlpParseNextRequest() { while (!doneParseHttpRequest) { // fetch next request buffer // ... // parse request text to request struct UlParseHttp(); // Note: UlContentRangeHeaderHandler() convert Range header string to // array of HTTP_BYTE_RANGE struct. // From RFC Range header value is inclusive range, so range length // must be end-start+1. // ... } UlpDeliverHttpRequest(); } NTSTATUS UlpDeliverHttpRequest() { if (UlCheckCachePreconditions(req) && doSendCachedResponse) { UlSendCachedResponse(); } // ... if (!doSendCachedResponse || sendCachedResponseFailed) { UlDeliverRequestToProcess(); // dispatch HTTP_REQUEST to w3wp.exe process } } char UlCheckCachePreconditions(req) { req->flags |= 2u; if (UlpQueryTranslateHeader(req)) { // has 'Translate' header with value 'f' or 'F' req->flags &= 0xFFFFFFFD; } else if (req->hasHdrFileds[HttpHeaderAuthorization]) { req->flags &= 0xFFFFFFFD; // ... } else if (!g_UriCacheConfig.uriEnableCache || xxx) { req->flags &= 0xFFFFFFFD; // ... } return (req->flags >> 1) & 1; } /**************************************************************/ /****************************************** * handling http response from w3wp.exe ******************************************/ // UlSendHttpResponseIoctl() in HTTP.sys is used to handle HTTP_RESPONSE from w3wp.exe process NTSTATUS UlSendHttpResponseIoctl(PIRP Irp, PIO_STACK_LOCATION StackLocation) { doCacheResponse = 0; if (HTTP_RESPONSE->pCachePolicy.Policy) { doCacheResponse = (req->flags >> 1) & 1; // this bit is (un)set in UlCheckCachePreconditions() } // copy and convert HTTP_RESPONSE to internel HTTP.sys response struct UlCaptureHttpResponse(&resp); if (!doCacheResponse || (UlCacheAndSendResponse(req, resp, ..., &cacheSuccess) >= 0 && !cacheSuccess)) { // UlSendHttpResponse() below is safe path for leaking info in user space UlSendHttpResponse(req, resp, ...); } } // The UlpBuildSliceRangeMdl() function is called when sending data from cache. // Normally, UlSendCachedResponse() and UlCacheAndSendResponse() functions use // this function. // Note: this path for leaking info is not safe (might crash target OS) void UlpBuildSliceRangeMdl(ULONGLONG sliceStart, void *out, PMDL sliceMdl, HTTP_BYTE_RANGE *range) { PMDL outMdl; DWORD sliceSize; DWORD sliceOutSize; DWORD sliceOffset; // find slice offset sliceOffset = 0; if (range->StartingOffset > sliceStart) sliceOffset = range->StartingOffset - sliceStart; // find this slice size sliceSize = sliceMdl->ByteCount; rangeSize = range->Length; if (sliceStart + sliceSize > range->StartingOffset + range->Length) { // when overflowed, range->StartingOffset + range->Length == 0 // so sliceSize = -sliceStart; // normally, sliceStart is 0 sliceSize = range->StartingOffset + range->Length - sliceStart; } // compute the used length sliceOutSize = sliceSize - sliceOffset; // when overflowed, sliceOutSize is very large // compute the start address sliceOutAddr = sliceMdl->StartVa + sliceMdl->ByteOffset + sliceOffset; // allocate MDL outMdl = IoAllocateMdl(sliceOutAddr, sliceOutSize, 0, 0, 0); // ... assign outMdl to out ... if (outMdl) { // when overflowed, sliceOutSize normally is 0xff?????? // - number of PFN is 0xff?????? >> 12 = 0x003f???? ~ 1M // below IoBuildPartialMdl might crash a OS because the memory address // after sliceMdl is invalid. // even if IoBuildPartialMdl() returns successfully, PFN array of outMdl // might contain invalid PFN. // Note: outMdl flag is MDL_SOURCE_IS_NONPAGED_POOL | MDL_PARTIAL IoBuildPartialMdl(sliceMdl, outMdl, sliceOutAddr, sliceOutSize); } }