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

Bugs found while testing on malware unusual packers + Hooks implemented #70

Open
wants to merge 110 commits into
base: master
Choose a base branch
from

Conversation

mmn3mm
Copy link
Contributor

@mmn3mm mmn3mm commented May 17, 2020

Issue 1: Parsing Resources
Functions related to resource section(eg: LoadStringA) failed in binee when I set a partial hook, so when I looked into it I and started analyzing I found that for the functions to work, they require DllMain for dlls to be run, when I enabled the option in binee there is much more headache to deal with, that's because most of DllMains require fields from undocumented windows structures(PEB). I did some research but I found it will probably take lots of time to handle. I also thought parsing all resources might come in handy later upon analysis of parts in the binary or feature extraction.
It wasn't a very easy process since its not really well documented, but I successfully parsed it and have an article explaining how I did it.
Link:
https://github.com/mmn3mm/peresources/blob/master/README.md

To reproduce the issue:
    I added a test binary in the tests folder to check the behavior.

Issue 2: Forwarded exports
Binee didn't take into consideration the forwarded exports at all, I don't have to explain how this works as it is explained here: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#export-address-table.
As always I tried to mimic the windows behavior as much as I can. I always use ReactOs's (https://reactos.org/) project as a reference as its very close to windows implementation.I implemented it here so it can be recursive just like windows.
The problem came out first due to the method binee uses to hook functions, it depends on EAT hooking, but since in forwarded export the address is pointing to a string which specifies the function it is pointing to, binee hooked wrong location and didn't resolve it correctly and executed the actual string as if it were the start of the actual function.
I also added the functionality/option to parse and print this.

To reproduce the issue:
        SHA256(sample)= 7bf87e4afdfc2a385a2a3d045fffed45ad0f56d7ca4ebe5dad03e18f376e9f4d
        SHA1(sample)= f11e5edb86148f31ccae18bc7d3029f500298e7c
        MD5(sample)= 0695c74d1f11b201eab7dd5b31808bc2
    I can provide the sample if requested but it can be found easily by searching, this is a malware that calls the "DeleteCriticalSection" function in kernel32.dll, but this function is in fact just a forwarded export to RtlDeleteCriticalSection in ntdll.
    To test parsing, find any version of kernel32.dll and start binee with this command:
    ```binee -e kernel32.dll```

Issue 3: GetProcAddress()
I don't know but I think when you guys started working on binee you had performance first in your priorities, the way you cached libraries/functions and just return the address was awesome, but it wasn't windows way. When I started working on forwarded exports I found it will be much better to actually implement GetProcAddress() to mimic windows, so in case the malware modified the tables in memory in a way to trick the emulator, binee will now handle this smoothly since it will do the behavior exactly like windows.
I also added the feature of importing by ordinal since it wasn't previously handled and it should be noted that in windows if the function is a forwarded export, then its resolved recursively, I implemented this too.

To reproduce the issue:
    We wrote this small C program that modifies the entries in export table in memory then uses GetProcAddress(), previously this failed but now it works fine.

Issue 4: Added ordinal support for forwarded exports and imports.
This is really straight forward I had this in mind since I first faced the issues in imports in binee, but I just postponed it.

Issue 5: Process Manager
Most of malwares check open processes for anti-analysis techniques, others just use to check for processes, I wanted to have a single component to handle functions as (CreateProcess,TerminateProcess,Process32First), because handling each function on its own would have been a mess, I created the file processmanager.go, which can be seen as kernel storage for processes on the emulator. I then added the functionalities to add stub programs before starting emulation, extracted some processes from a windows used by a normal user to give an illusion of a normal machine and added them by default.
I also wrote a simple C program to test it, its actually the same code I used to extract the processes.

Issue 6: NoLog Feature
Added a bool variable in hook's structure that can be set in hook's declaration to allow not logging some hooked functions, as some function may cause noise since they are called a lot (memory functions) and they don't give much details about behavior.

Issue 7: LoadLibrary
In forwarded exports, there was an issue, a library might not exist in the import table, but still be needed due to forwarding, so it has to be loaded. since loading a library in binee was done in steps and not a specific function that loads functions, I created a function that does the work for loading (retrieving from disk and adding ldr entries)

Issue 8: Threads (WaitForMultipleObject,WaitForSingleObject)
I found out that binee does support multi-threading and scheduling but it doesn't really handle them well, it's just giving each thread its turn, which means functions like (WaitForSingleObject,WaitForMultipleObject), so I thought I should implement them since threads are core functionality in malware. The biggest problem was how to detect that a thread exited, I researched how windows handles thread, after reading lots of articles and reversing the actual windows flow, I found out that the call stack for the thread is something like (init thread - call function - exit thread), so I decided I should do the same. I re-implemented the CreateThread so that the function directly after thread function return would be RtlExitUserThread (ROP:return oriented programming) which is fully hooked and calls ThreadEnded in scheduler. The logic of handling threads happens there.
To implement (WaitForMultipleObject,WaitForSingleObject), I made use of goroutines and channels, they are very efficient and lightweight by design. (I even researched a bit if we can actually parallelize binee to make it faster, but it came out the bottleneck of binee will be unicorn which can't be parallelized).
So the logic I implemented was when a thread calls WaitForMultipleObject, its state is changed to waiting, it creates a goroutine which creates a channel for every object (thread) and pass them to every each one, when a thread is exited, in ThreadEnded function it sends a signal to the channel to say that you no longer have to wait for this object. after the condition is met (timeout or number of wanted objects finished), the state of the thread is changed back to ready and its run normally again.

To reproduce the issue:
the tests provided has a threading example, it now should work just like windows.

Issue 9: EFlags
Upon context switching in binee, EFlags were not taken into consideration, this was very hard to notice since the behavior was very weird (jumps were taken that shouldn't be).

To reproduce the issue:
this is very hard to notice and you will have to go instruction by instruction, going with exact options I had this binary should reproduce it:
    SHA256(VirusShare_c70bea464ae1287f3b260ddc374f6c6a)= e3b870ab13a1db04c733b9c460a46fb57b8760de5fad9b613c91b902d5d2f054
    SHA1(VirusShare_c70bea464ae1287f3b260ddc374f6c6a)= 9c9111767ab702fbc7b768b2e77021b5d547fb63
    MD5(VirusShare_c70bea464ae1287f3b260ddc374f6c6a)= c70bea464ae1287f3b260ddc374f6c6a
but its really an obvious bug.

Issue 10: EnumResourceNames
https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-enumresourcenamesa
Its a function that enumerates resources by calling a passed function on the names of resources, in order to fully hook it, I needed a utility to call a std function, so I fully implemented it and created the utility.
func CallStdFunction(emu *WinEmulator, functionAddress uint64, parameters []uint64)
It takes an address and parameters, builds the stack in the std convention + the return point to return to the same IP, and then sets the current IP to the address that needs to be called.
To test:
SHA256(sample)= c9c89ce5eb015d0a364bd28de1d9b525a7d66ac4e527dfc10ade556f10c0829c
SHA1(sample)= 52bf1744ef88fb2c1999985ab551e87ff0371a0f
MD5(sample)= 0162349ac704cb1757edc73715a83a64

Issue 11: TLS wasn't allocated.
To reproduce the issue:
SHA256(sample)= 7bf87e4afdfc2a385a2a3d045fffed45ad0f56d7ca4ebe5dad03e18f376e9f4d
SHA1(sample)= f11e5edb86148f31ccae18bc7d3029f500298e7c
MD5(sample)= 0695c74d1f11b201eab7dd5b31808bc2
instructions: [1] 0x00404d00: mov edx, [+0x2c]
[1] 0x00404d07: mov eax, [edx+4*eax]
It will break and error since it will try referencing an unallocated memory.
Issue 12: Hooks
I implemented lots of hooks, partial and full hooks. I always depend on windows documentation for apis and ReactOs to implement hooks to mimic windows as much as possible.

mmn3mm and others added 30 commits May 6, 2020 23:19
Tested new feature : Handling resources and adding support to functions [LoadStringA , LoadStringW , FindResource,LoadResource,SizeOfResource,LockResource].
(feature) add flag to print resources
Windowsthreads (WaitForMultipleObject,WaitForSingleObject)
mmn3mm and others added 30 commits October 22, 2020 14:42
- If a user supplied the path to DLLs in `-r` option without a trailing
backslash, it didnt work due to path concatenation instead of using
the built-in `Path.join()` function.
- Examples of the previous issue:
  - `binee.exe -r "C:\Windows\system32\"` used to work
  - `binee.exe -r "C:\Windows\system32"` didnt work
- Now both work fine.

- Also changed the variable name `path` to `pePath` in many locations
to avoid conflict with the package `path`.
- Hardcoded paths included `%windir%\system32` only.
It should include `%windir%\SysWoW64` too.

- If a user supplied a custom path using `-r` switch, we should search
that path too; not just its `windows\system32` subdirs.

- Now all the tests (excluding x64 ones ofc) work perfectly using SysWow64 DLLs
- Now Binee auto detects the correct paths for system DLLs depending on:
  - The host machine's arch
  - The target PE file's arch

(gitignore) Untrack the damn workspace.xml
Fix path issues preventing SysWOW64 DLLs from working
…randomization of heap allocation. (this is not an original windows behaviour but it has to be done here)
Handling remote threads created by the emulated process
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.

5 participants