/// <summary> /// Merges regions of a given set of normalized regions using a fast stack based algorithm O(nlogn + n). /// </summary> /// <param name="regions">The regions to merge and sort.</param> /// <returns>The merged and sorted regions.</returns> private IEnumerable <NormalizedRegion> MergeAndSortRegions(IEnumerable <NormalizedRegion> regions) { if (regions == null || regions.Count() <= 0) { return(null); } // First, sort by start address IList <NormalizedRegion> sortedRegions = regions.OrderBy(x => x.BaseAddress.ToUInt64()).ToList(); // Create and initialize the stack with the first region Stack <NormalizedRegion> combinedRegions = new Stack <NormalizedRegion>(); combinedRegions.Push(sortedRegions[0]); // Build the remaining regions for (Int32 index = combinedRegions.Count; index < sortedRegions.Count; index++) { NormalizedRegion top = combinedRegions.Peek(); if (top.EndAddress.ToUInt64() < sortedRegions[index].BaseAddress.ToUInt64()) { // If the interval does not overlap, put it on the top of the stack combinedRegions.Push(sortedRegions[index]); } else if (top.EndAddress.ToUInt64() == sortedRegions[index].BaseAddress.ToUInt64()) { // The regions are adjacent; merge them top.RegionSize = sortedRegions[index].EndAddress.Subtract(top.BaseAddress).ToInt32(); } else if (top.EndAddress.ToUInt64() <= sortedRegions[index].EndAddress.ToUInt64()) { // The regions overlap top.RegionSize = sortedRegions[index].EndAddress.Subtract(top.BaseAddress).ToInt32(); } } return(combinedRegions.ToList().OrderBy(x => x.BaseAddress.ToUInt64()).ToList()); }
/// <summary> /// Initializes a new instance of the <see cref="RegionProperties" /> class. /// </summary> /// <param name="region">The region that this chunk spans.</param> public RegionProperties(NormalizedRegion region) : this(region.BaseAddress, region.RegionSize) { }
/// <summary> /// Queries virtual pages from the OS to dertermine if any allocations or deallocations have happened. /// </summary> /// <returns>The collected pages.</returns> private IEnumerable <RegionProperties> CollectNewPages() { List <RegionProperties> newRegions = new List <RegionProperties>(); // Gather current regions from the target process IEnumerable <NormalizedRegion> queriedVirtualRegions = SnapshotManager.GetInstance().CollectSnapshotRegions(); List <NormalizedRegion> queriedChunkedRegions = new List <NormalizedRegion>(); // Chunk all virtual regions into a standardized size queriedVirtualRegions.ForEach(x => queriedChunkedRegions.AddRange(x.ChunkNormalizedRegion(ChunkLinkedListPrefilter.ChunkSize))); // Sort our lists (descending) IOrderedEnumerable <NormalizedRegion> queriedRegionsSorted = queriedChunkedRegions.OrderByDescending(x => x.BaseAddress.ToUInt64()); IOrderedEnumerable <RegionProperties> currentRegionsSorted; lock (this.ChunkLock) { currentRegionsSorted = this.ChunkList.OrderByDescending(x => x.BaseAddress.ToUInt64()); } // Create comparison stacks Stack <RegionProperties> currentRegionStack = new Stack <RegionProperties>(); Stack <NormalizedRegion> queriedRegionStack = new Stack <NormalizedRegion>(); currentRegionsSorted.ForEach(x => currentRegionStack.Push(x)); queriedRegionsSorted.ForEach(x => queriedRegionStack.Push(x)); // Begin stack based comparison algorithm to resolve our chunk processing queue NormalizedRegion queriedRegion = queriedRegionStack.Count == 0 ? null : queriedRegionStack.Pop(); RegionProperties currentRegion = currentRegionStack.Count == 0 ? null : currentRegionStack.Pop(); while (queriedRegionStack.Count > 0 && currentRegionStack.Count > 0) { if (queriedRegion < currentRegion) { // New region we have not seen yet newRegions.Add(new RegionProperties(queriedRegion)); queriedRegion = queriedRegionStack.Pop(); } else if (queriedRegion > currentRegion) { // Region that went missing (deallocated) currentRegion = currentRegionStack.Pop(); } else { // Region we have already seen queriedRegion = queriedRegionStack.Pop(); currentRegion = currentRegionStack.Pop(); } } // Add remaining queried regions while (queriedRegionStack.Count > 0) { newRegions.Add(new RegionProperties(queriedRegion)); queriedRegion = queriedRegionStack.Pop(); } return(newRegions); }
/// <summary> /// Masks the given memory regions against the given memory regions, keeping the common elements of the two in O(n). /// </summary> /// <param name="groundTruth">The snapshot regions to mask the target regions against.</param> public void MaskRegions(IEnumerable <NormalizedRegion> groundTruth) { List <SnapshotRegion> resultRegions = new List <SnapshotRegion>(); groundTruth = this.MergeAndSortRegions(groundTruth); // if (this.SnapshotRegions == null || groundTruth == null || this.SnapshotRegions.Count <= 0 || groundTruth.Count() <= 0) { // this.SnapshotRegions = resultRegions; // return; } this.MergeAndSortRegions(); // TODO: Resolve the masking issues below: return; // Initialize stacks with regions and masking regions Queue <SnapshotRegion> candidateRegions = new Queue <SnapshotRegion>(); Queue <NormalizedRegion> maskingRegions = new Queue <NormalizedRegion>(); // Build candidate region queue from target region array foreach (SnapshotRegion region in this.SnapshotRegions.OrderBy(x => x.BaseAddress.ToUInt64())) { candidateRegions.Enqueue(region); } // Build masking region queue from snapshot foreach (NormalizedRegion maskRegion in groundTruth.OrderBy(x => x.BaseAddress.ToUInt64())) { maskingRegions.Enqueue(maskRegion); } if (candidateRegions.Count <= 0 || maskingRegions.Count <= 0) { this.SnapshotRegions = resultRegions; return; } SnapshotRegion currentRegion; NormalizedRegion currentMask = maskingRegions.Dequeue(); while (candidateRegions.Count > 0) { // Grab next region currentRegion = candidateRegions.Dequeue(); // Grab the next mask following the current region while (currentMask.EndAddress.ToUInt64() < currentRegion.BaseAddress.ToUInt64() && maskingRegions.Count > 0) { currentMask = maskingRegions.Dequeue(); } // Check for mask completely removing this region if (currentMask.EndAddress.ToUInt64() < currentRegion.BaseAddress.ToUInt64() || currentMask.BaseAddress.ToUInt64() > currentRegion.EndAddress.ToUInt64()) { continue; } // Mask completely overlaps, just use the original region if (currentMask.BaseAddress == currentRegion.BaseAddress && currentMask.EndAddress == currentRegion.EndAddress) { resultRegions.Add(currentRegion); continue; } // Mask is within bounds; Grab the masked portion of this region Int32 baseOffset = currentMask.BaseAddress.ToUInt64() <= currentRegion.BaseAddress.ToUInt64() ? 0 : currentMask.BaseAddress.Subtract(currentRegion.BaseAddress).ToInt32(); SnapshotRegion newRegion = new SnapshotRegion(currentRegion as NormalizedRegion); newRegion.BaseAddress = currentRegion.BaseAddress + baseOffset; newRegion.BaseAddress = Math.Min(currentMask.EndAddress.ToUInt64(), currentRegion.EndAddress.ToUInt64()).ToIntPtr(); newRegion.SetCurrentValues(currentRegion.GetCurrentValues().LargestSubArray(baseOffset, newRegion.RegionSize)); newRegion.SetPreviousValues(currentRegion.GetPreviousValues().LargestSubArray(baseOffset, newRegion.RegionSize)); newRegion.SetElementLabels(currentRegion.GetElementLabels().LargestSubArray(baseOffset, newRegion.RegionSize)); newRegion.ElementType = currentRegion.ElementType; newRegion.Alignment = currentRegion.Alignment; resultRegions.Add(newRegion); } this.SnapshotRegions = resultRegions; }
/// <summary> /// Combines the given memory regions with the given memory regions, only keeping the common elements of the two in O(nlogn + n). /// </summary> /// <param name="otherSnapshot">The snapshot regions to mask the target regions against.</param> private void Intersect(Snapshot otherSnapshot) { List <SnapshotRegion> resultRegions = new List <SnapshotRegion>(); otherSnapshot.MergeAndSortRegions(); IEnumerable <NormalizedRegion> otherRegions = otherSnapshot.GetSnapshotRegions(); if (this.SnapshotRegions.IsNullOrEmpty() || otherRegions.IsNullOrEmpty()) { this.SnapshotRegions = resultRegions; return; } this.MergeAndSortRegions(); // Initialize stacks with regions and masking regions Queue <SnapshotRegion> snapshotRegionQueue = new Queue <SnapshotRegion>(); Queue <NormalizedRegion> groundTruthQueue = new Queue <NormalizedRegion>(); // Build candidate region queue from snapshot region array foreach (SnapshotRegion region in this.SnapshotRegions.OrderBy(x => x.BaseAddress.ToUInt64())) { snapshotRegionQueue.Enqueue(region); } // Build masking region queue from snapshot foreach (NormalizedRegion maskRegion in otherRegions.OrderBy(x => x.BaseAddress.ToUInt64())) { groundTruthQueue.Enqueue(maskRegion); } if (snapshotRegionQueue.Count <= 0 || groundTruthQueue.Count <= 0) { this.SnapshotRegions = resultRegions; return; } SnapshotRegion nextSnapshotRegion; NormalizedRegion groundTruthMask = groundTruthQueue.Dequeue(); while (snapshotRegionQueue.Count > 0) { // Grab next region nextSnapshotRegion = snapshotRegionQueue.Dequeue(); // Grab the next mask following the current region while (groundTruthMask.EndAddress.ToUInt64() < nextSnapshotRegion.BaseAddress.ToUInt64() && groundTruthQueue.Count > 0) { groundTruthMask = groundTruthQueue.Dequeue(); } // Check for mask completely removing this region if (groundTruthMask.EndAddress.ToUInt64() < nextSnapshotRegion.BaseAddress.ToUInt64() || groundTruthMask.BaseAddress.ToUInt64() > nextSnapshotRegion.EndAddress.ToUInt64()) { continue; } // Check for mask completely engulfing this region else if (groundTruthMask.BaseAddress.ToUInt64() <= nextSnapshotRegion.BaseAddress.ToUInt64() && groundTruthMask.EndAddress.ToUInt64() >= nextSnapshotRegion.EndAddress.ToUInt64()) { resultRegions.Add(nextSnapshotRegion); continue; } // There are no edge cases, we must mask and copy the valid portion of this region else { UInt64 baseAddress = Math.Max(groundTruthMask.BaseAddress.ToUInt64(), nextSnapshotRegion.BaseAddress.ToUInt64()); UInt64 endAddress = Math.Min(groundTruthMask.EndAddress.ToUInt64(), nextSnapshotRegion.EndAddress.ToUInt64()); Int64 baseOffset = unchecked ((Int64)(baseAddress - nextSnapshotRegion.BaseAddress.ToUInt64())); SnapshotRegion newRegion = new SnapshotRegion(nextSnapshotRegion as NormalizedRegion); newRegion.BaseAddress = baseAddress.ToIntPtr(); newRegion.EndAddress = endAddress.ToIntPtr(); newRegion.SetCurrentValues(nextSnapshotRegion.CurrentValues.LargestSubArray(baseOffset, newRegion.RegionSize.ToInt64())); newRegion.SetPreviousValues(nextSnapshotRegion.PreviousValues.LargestSubArray(baseOffset, newRegion.RegionSize.ToInt64())); newRegion.SetElementLabels(nextSnapshotRegion.ElementLabels.LargestSubArray(baseOffset, newRegion.RegionSize.ToInt64())); newRegion.ElementType = nextSnapshotRegion.ElementType; newRegion.Alignment = nextSnapshotRegion.Alignment; resultRegions.Add(newRegion); } } this.SnapshotRegions = resultRegions; }
/// <summary> /// Initializes a new instance of the <see cref="SnapshotRegion" /> class. /// </summary> /// <param name="normalizedRegion">The region on which to base this snapshot region.</param> public SnapshotRegion(NormalizedRegion normalizedRegion) : this(normalizedRegion == null ? IntPtr.Zero : normalizedRegion.BaseAddress, normalizedRegion == null ? 0 : normalizedRegion.RegionSize) { }