kevoreilly / capemon Goto Github PK
View Code? Open in Web Editor NEWcapemon: CAPE's monitor
License: GNU General Public License v3.0
capemon: CAPE's monitor
License: GNU General Public License v3.0
Hi all,
Capemon failed to build with msvc due to error C2362 error2440 on Windows. Could you please help look at this issue?
Environment:
Windows: windows server 2019
VS: 2019
Repro steps:
1.open VS2019 x64 tools command
2.git clone https://github.com/kevoreilly/capemon F:\gitP\kevoreilly\capemon
3.cd F:\gitP\kevoreilly\capemon
4.mkdir build_amd64 & cd F:\gitP\kevoreilly\capemonl\build_amd64
5.set VSCMD_SKIP_SENDTELEMETRY=1 & "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\Tools\VsDevCmd.bat" -host_arch=amd64 -arch=amd64 & set CL= /permissive-
6.msbuild /m /p:Platform=x64 /p:Configuration=Release /p:WindowsTargetPlatformVersion=10.0.18362.0 /p:PlatformToolset=v142 capemon.sln /t:Rebuild
Error info:
F:\gitP\kevoreilly\capemon\CAPE\AmsiDumper.cpp(163,13): error C2362: initialization of 'hr' is skipped by 'got
o error' [F:\gitP\kevoreilly\capemon\capemon.vcxproj]
F:\gitP\kevoreilly\capemon\CAPE\Scylla\IATReferenceScan.cpp(425,15): error C2440: 'initializing': cannot conve
rt from 'const char [2]' to 'CHAR *' [F:\gitP\kevoreilly\capemon\capemon.vcxproj]
F:\gitP\kevoreilly\capemon\CAPE\Scylla\IATReferenceScan.cpp(429,11): error C2440: '=': cannot convert from 'co
nst char [5]' to 'CHAR *' [F:\gitP\kevoreilly\capemon\capemon.vcxproj]
F:\gitP\kevoreilly\capemon\CAPE\Scylla\IATReferenceScan.cpp(433,11): error C2440: '=': cannot convert from 'co
nst char [4]' to 'CHAR *' [F:\gitP\kevoreilly\capemon\capemon.vcxproj]
F:\gitP\kevoreilly\capemon\CAPE\Scylla\IATReferenceScan.cpp(437,11): error C2440: '=': cannot convert from 'co
nst char [4]' to 'CHAR *' [F:\gitP\kevoreilly\capemon\capemon.vcxproj]
F:\gitP\kevoreilly\capemon\CAPE\Scylla\IATReferenceScan.cpp(441,11): error C2440: '=': cannot convert from 'co
nst char [5]' to 'CHAR *' [F:\gitP\kevoreilly\capemon\capemon.vcxproj]
F:\gitP\kevoreilly\capemon\CAPE\Scylla\IATReferenceScan.cpp(445,11): error C2440: '=': cannot convert from 'co
nst char [4]' to 'CHAR *' [F:\gitP\kevoreilly\capemon\capemon.vcxproj]
log
build.log
Starting a 64-bit Winword on current Windows 10 under observation by capemon gets stuck very early on. Attaching to the stuck winword.exe
shows the following call stack:
Apparently, ntdll!RtlInsertInvertedFunctonTable
is called and (according to disassembly of the function) exclusively acquires Slim Reader/Writer lock (SRW) ntdll!LdrpInvertedFunctionTableSRWLock
. This would make sense as the function is likely to modify import tables.
While holding the lock, it calls ntdll!LdrProtectMrData
which, according to my debugging, eventually calls ntdll!NtProtectVirtualMemory
, likely to protect access to those tables (again). Since ntdll!NtProtectVirtualMemory
is hooked by capemon, this triggers capemon_x64!enter_hook
in order to decide whether to enter the capemon hook for that function or not. This decision is, amongst other things. based on whether the hook is itself called from a hook. To determine this, the stack is unwound by calling capemon_x64!our_stackwalk
which on x64 is implemented using ntdll!RtlLookupFunctionEntry
.
Unfortunately, this function does not appear to be safe for this kind of thing because it also acquires the already exclusively held ntdll!LdrpInvertedFunctionTableSRWLock
. This leads to the observed deadlock.
Disabling hooking of ntdll!NtProtectVirtualMemory
without involvement of stack unwinding mitigates the issue:
diff --git a/hooking.c b/hooking.c
index 46e6560..d0d566f 100644
--- a/hooking.c
+++ b/hooking.c
@@ -259,6 +260,9 @@ int WINAPI enter_hook(hook_t *h, ULONG_PTR sp, ULONG_PTR ebp_or_rip)
{
hook_info_t *hookinfo;
+ if (h->new_func == &New_NtProtectVirtualMemory)
+ return 0;
+
if (h->fully_emulate)
return 1;
This, however, leaves a massive blindspot regarding all calls to that function. This also only works because ntdll!NtProtectVirtualMemory
appears to be the only hooked function called while ntdll!LdrpInvertedFunctionTableSRWLock
is held.
Trying to hook ntdll!RtlInsertInvertedFunctonTable
to temporarily disable hooking for all other APIs called from it (again without involving stack-based decisions) have not been successful because the hook does not seem to be called:
diff --git a/hook_special.c b/hook_special.c
index e0ad8ea..a81cbdc 100644
--- a/hook_special.c
+++ b/hook_special.c
@@ -39,6 +39,15 @@ static int bits_sent = 0;
static int tasksched_sent = 0;
static int interop_sent = 0;
+extern int g_hooking_disabled;
+
+HOOKDEF(VOID, WINAPI, RtlInsertInvertedFunctionTable, __in PVOID ImageBase, __in ULONG SizeOfImage) {
+ g_hooking_disabled = 1;
+ DebugOutput("RtlInsertInvertedFunctionTable called");
+ Old_RtlInsertInvertedFunctionTable(ImageBase, SizeOfImage);
+ g_hooking_disabled = 0;
+}
+
HOOKDEF_NOTAIL(WINAPI, LdrLoadDll,
__in_opt PWCHAR PathToFile,
__in_opt PULONG Flags,
diff --git a/hooking.c b/hooking.c
index 46e6560..361b250 100644
--- a/hooking.c
+++ b/hooking.c
@@ -42,6 +42,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define HOOK_RATE_LIMIT 0x100
#define HOOK_LIMIT 0x10000
+int g_hooking_disabled = 0;
static lookup_t g_hook_info;
lookup_t g_caller_regions;
@@ -259,6 +260,9 @@ int WINAPI enter_hook(hook_t *h, ULONG_PTR sp, ULONG_PTR ebp_or_rip)
{
hook_info_t *hookinfo;
+ if (g_hooking_disabled)
+ return 0;
+
if (h->fully_emulate)
return 1;
diff --git a/hooks.c b/hooks.c
index 6613af9..277af9d 100644
--- a/hooks.c
+++ b/hooks.c
@@ -184,6 +186,8 @@ hook_t full_hooks[] = {
HOOK_NOTAIL_ALT(kernelbase, MoveFileWithProgressTransactedW, 6),
HOOK_NOTAIL_ALT(kernel32, MoveFileWithProgressTransactedW, 6),
+ HOOK(ntdll, RtlInsertInvertedFunctionTable),
+
// File Hooks
HOOK(ntdll, NtQueryAttributesFile),
HOOK(ntdll, NtQueryFullAttributesFile),
diff --git a/hooks.h b/hooks.h
index 38203c6..d2d9a74 100644
--- a/hooks.h
+++ b/hooks.h
@@ -22,6 +22,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "ntapi.h"
#include <tlhelp32.h>
+HOOKDEF(VOID, WINAPI, RtlInsertInvertedFunctionTable,
+ __in PVOID ImageBase, __in ULONG SizeOfImage
+);
+
//
// File Hooks
//
diff --git a/misc.h b/misc.h
index 1d9ac32..2e222d1 100644
--- a/misc.h
+++ b/misc.h
@@ -101,6 +101,10 @@ typedef HRESULT (WINAPI *_ProgIDFromCLSID)(
_Out_ LPOLESTR *lplpszProgID
);
+typedef VOID(WINAPI* _RtlInsertInvertedFunctionTable)(
+ _In_ PVOID ImageBase,
+ _In_ ULONG SizeOfImage);
+
void resolve_runtime_apis(void);
DWORD parent_process_id(); // By Napalm @ NetCore2K (rohitab.com)
Another idea I had (and found elsewhere: https://microsoft.public.win32.programmer.kernel.narkive.com/qxCAoEXI/using-rtllookupfunctionentry-for-profiling) was to try to acquire the lock from our_stackwalk
to see if it was held or free and RtlLookupFunctionEntry
would block or not. Unfortunately, the symbol is not exported from ntdll
, so I cannot get at its address.
Other projects have run into this problem as well and proposed a number of solutions, e.g.: dotnet/runtime#32286, DynamoRIO/drmemory#1222
The issue seems to be somewhat Windows-10-specific, because the same capemon_x64.dll is able to start up and monitor the same version of 64-bit Winword on a 64-bit Windows 7 without above workarounds. My guess is that import table mechanics, at least regarding memory protections on them, have changed between Windows 7 and Windows 10. I have not analysed the differences in detail though.
Is my understanding of the mechanics at play correct?
Could my attempts at hooking RtlInsertInvertedFunctionTable
or inspecting the state of LdrpInvertedFunctionTableSRWLock
from our_stackwalk
be made to work somehow?
Any ideas what else could be done about this issue?
Thanks!
In Windows NT, the function RtlDispatchException
is responsible for sending the exception to each registered frame, until it finds one that handles the exception. After this function is done, it will not return to the caller, but will pass control directly to the exception handler.
At hook_process.c(1049), there is a construction that attempts to handle exception. The log_flush
call that follows up after, is potentially never called.
So, to fix this problem:
log_flush
call before the call to CAPEExceptionDispatcher
New_RtlDispatchException
as HOOKDEF_NOTAIL
, which will auto-call the original function after the hook is done.The problem caused by the current state is the following. Let's have a simple program:
static ULONG ExceptionHandler(LPEXCEPTION_POINTERS ExceptionPointers)
{
PEXCEPTION_RECORD ExceptionRecord = ExceptionPointers->ExceptionRecord;
PCONTEXT pContext = ExceptionPointers->ContextRecord;
printf("Exception %08X at %p\n", ExceptionRecord->ExceptionCode, ExceptionRecord->ExceptionAddress);
printf("Trying to skip the instruction ...\n");
#ifdef _WIN64
pContext->Rip += GetInstructionLength((unsigned char *)ExceptionRecord->ExceptionAddress, 64);
#else
pContext->Eip += GetInstructionLength((unsigned char *)ExceptionRecord->ExceptionAddress, 32);
#endif
return EXCEPTION_EXECUTE_HANDLER;
}
//int WINAPI _tWinMain(HINSTANCE, HINSTANCE, LPTSTR, int)
void _cdecl main()
{
printf("Hello, World, I am a test program for cuckoo/CAPE.\n\n");
//printf("Capemon base address: %p\n", GetModuleHandle(_T("capemon_x64.dll")));
printf("Before __try\n");
//__debugbreak();
__try
{
printf("Inside __try\n");
*(char *)0 = 0;
printf("After exception\n");
}
__except(ExceptionHandler(GetExceptionInformation()))
{
printf("Inside __except\n");
}
printf("After __except\n");
printf("\nTestprogram exiting ...\n");
_getch();
}
When 64-bit version executed in CAPE, it only gives the following console output:
Hello, World, I am a test program for cuckoo/CAPE.
* (0): Before __try
* (1): Inside __try
* (4): Inside __except
* (5): After __except
Testprogram exiting ...
if hooks = full_hooks;
are applied on a .net executable , capemon fails to load . .net Error message pops up .
if hooks are changed to hooks = min_hooks;
, it works fine
Sample : ba593b4f4a1ab0b96b2a38e851aed89e
windows 7 sp1, .net 4.0 installed
The sample 5e56a3c4d4c304ee6278df0b32afb62bd0dd01e2a9894ad007f4cc5f873ab5cf
(PowerLoader) fails to detonate properly with 64-bit capemon compiled with VS2022 on Windows 7 x64.
The issue appears to be with the WriteProcessMemory
hook which is called early in capture of the injected explorer.exe
. Even with an 'empty' hook detonation fails.
If capemon is compiled with VS2017 the sample detonates fine, as seen below:
I notice that the value of BaseAddress
in the LdrLoadDll
is always null. Because we call the LOQ_ntstatus
to log the api before we call Old_LdrLoadDll
. And the ModuleHandle
is an out parameter which is set after the function call. I am wondering why the original implementation is designed this way and if it is intentional.
Lines 74 to 79 in 9b7258b
In addition, I attempted to move the logging to the HOOKDEF_ALT
of LdrLoadDll
after the function call. It can correctly display the BaseAddress
of the loaded DLL. But some of the DLL, which is nonexist dll, will not enter this HOOKDEF_ALT
of LdrLoadDll
.
Hi.
Some hooks we are using in the original Cuckoo monitor are commented out with the // maldoc detonation issues
message.
Can you please explain what this comment means?
The hooks themselves seem to be same otherwise, but I am not sure what I am risking by enabling them.
We did not notice any issue with these hooks in the original monitor.
Thanks.
The read_config function bails out without setting default values, when the config file doesn't exist at all.
Use case: For simple capemon debugging, where i just run loader_x64.exe load c:\test\capemon_x64_dbg.dll sample.exe
when having kernel debugger on.
Problem: Default value of g_config.hook_type
is 0, which is HOOK_HOTPATCH_JMP_INDIRECT
. In x64, it causes total havoc in the hooked functions.
https://capesandbox.com/analysis/352187/
This Lockbit sample has both Import Directory and Import Address Table (IAT) empty.
loader.exe will use InjectDllViaIAT to inject the capemon.dll into process.
capemon/loader/loader/Loader.c
Lines 1081 to 1089 in 95ed91f
Because both Import Directory and IAT is 0, NtHeader.IAT_DIRECTORY.VirtualAddress = ImportsSection.VirtualAddress;
this line will do nothing and set IAT to 0 again. Now, Only Import Directory will have valid value and IAT set to 0. According to my test results, this is fine in Windows 7, but it fails to load the capemon in Windows 10.
A quick fix is to set IAT to the FirstThunk we created. ( or just disable InjectDllViaIAT )
diff --git a/loader/loader/Loader.c b/loader/loader/Loader.c
index 96478bb..e73f4b5 100644
--- a/loader/loader/Loader.c
+++ b/loader/loader/Loader.c
@@ -1081,11 +1081,17 @@ rebase:
// If IAT zero, set it to section that contains original import table to prevent LdrpSnapIAT failure
if (NtHeader.IAT_DIRECTORY.VirtualAddress == 0)
{
- NtHeader.IAT_DIRECTORY.VirtualAddress = ImportsSection.VirtualAddress;
- if (ImportsSection.Misc.VirtualSize)
- NtHeader.IAT_DIRECTORY.Size = ImportsSection.Misc.VirtualSize;
- else
- NtHeader.IAT_DIRECTORY.Size = ImportsSection.SizeOfRawData;
+ if (ImportsSection.VirtualAddress == 0) {
+ NtHeader.IAT_DIRECTORY.VirtualAddress = pImageDescriptor->FirstThunk;
+ NtHeader.IAT_DIRECTORY.Size = 8;
+ }
+ else {
+ NtHeader.IAT_DIRECTORY.VirtualAddress = ImportsSection.VirtualAddress;
+ if (ImportsSection.Misc.VirtualSize)
+ NtHeader.IAT_DIRECTORY.Size = ImportsSection.Misc.VirtualSize;
+ else
+ NtHeader.IAT_DIRECTORY.Size = ImportsSection.SizeOfRawData;
+ }
}
// Now set the import table directory entry to point to the new table
I will open a pull request if this fix is ok.
Hi,
I am noticing a huge difference between the capemon I compile and the one downloaded from CAPEv2 repo.
Could you please share how you compile the libraries ?
Debug or Release?
I am using both VS2017 and VS2019 and with this release of capemon I keep having the same issue.
Thank you
Capemon failed to build with "fatal error LNK1104: cannot open file 'atls.lib' " with MSVC on Windows arm64ec. It can reproduce on latest version on capemon branch. Could you please help look at this issue?
Repro steps:
Error info:
LINK : fatal error LNK1104: cannot open file 'atls.lib' [F:\capemon\capemon.vcxproj]
Error log:
Capemon_build_LNK1104.log
.jars and Java based .exes crash unless I use exclude-apis=RtlDispatchException:NtProtectVirtualMemory
To make sure it wasn't an issue specific with the samples that I'm using, I compiled a simple jar file that just prints "hello world"
exclude-apis=RtlDispatchException:NtProtectVirtualMemory
Individually, I noticed that:
RtlDispatchException
causes Java to crashNtProtectVirtualMemory
causes Java to hangQuestion | Answer |
---|---|
Git commit (CAPE) | 2391d5ad343f5f307dee4c0b053da64d3c1e9452 |
OS version | Ubuntu 20.04 host, Windows 10 64-bit guest |
Java version | 11.0.11 |
I am interested in testing Yara rules that involve the debugging. Is there any specific technique or development path you follow to test the yara debugging rules? Example would be GuLoader.
Do you think it'd be worth adding in a hook for DsEnumerateDomainTrusts
? Specifically that just shows what ServerName
and Flags
value is passed.
Here's a run for an EXE that collects a bunch of information and then (I think, but haven't verified) POSTs it to a C2 server: https://capesandbox.com/analysis/81358/ . It would have been useful to see that it also calls DsEnumerateDomainTrustsA to collect domain info.
For future reference, what's the criteria for considering when something like this is worth adding in a hook for?
Thank you!
Reference: https://attack.mitre.org/techniques/T1482/
Hello, sorry for bothering again.
I was wondering if capemon gets initialized from scratch for every process/thread which is forked from the initial one.
I am creating an inner structure for taking track of some info and it seems it gets initialized once the new process is spawned.
Thank you :)
Hi,
I'm observing crashes caused by Yara rule matching when analysing program call chains containing both 32 and 64 bit programs on Windows 10. My test case is Microsoft Excel 2013 with a small XLS file containing a macro that does a WMI query. This causes (amongst lots of other things) svchost.exe
and wmiprvse.exe
processes being spawned which are 64 bit. These I have seen fail in three ways:
Access violation when trying to find the SRW lock using InternalYaraScan:
Hang in RtlLookupFunctionEntry as if the SRW lock could not be found using InternalYaraScan (but didn't crash either):
On a hunch I had capemon.dll
write/load the compiled rules to/from separate files like so:
diff --git a/CAPE/YaraHarness.c b/CAPE/YaraHarness.c
index 982bc99..c7d2e0b 100644
--- a/CAPE/YaraHarness.c
+++ b/CAPE/YaraHarness.c
@@ -345,7 +345,11 @@ BOOL YaraInit()
PathRemoveFileSpec(analyzer_path);
PathRemoveFileSpec(analyzer_path);
sprintf(yara_dir, "%s\\data\\yara", analyzer_path);
+#ifdef _WIN64
+ sprintf(compiled_rules, "%s\\capemon64.yac", yara_dir);
+#else
sprintf(compiled_rules, "%s\\capemon.yac", yara_dir);
+#endif
yr_initialize();
That made these problems go away.
It appears, the initial hooking of the 32 bit excel.exe
writes out rules which confuse a 64 bit yara in programs spawned later.
What's peculiar is that the yara docs promise exactly this not to be the case: https://yara.readthedocs.io/en/stable/capi.html#saving-and-retrieving-compiled-rules
What could be the cause for me to see this behaviour?
Thanks!
Michael
Hello.
I work in a company that deals with cybersecurity, my colleague and I are also analyzing capemon and we have found some problems in detonation phase with the golang and with edr bypass techniques used by some malware.
Since cape is also used by companies it would be more appropriate not to disclose certain information in public until it is fixed.
@kevoreilly is it possible to contact you privately to discuss how to fix?
Thanks for your attention.
The password is infected. The final payload should be ServHelper RAT. The dropper should load the .NET ServHelper dropper into memory to execute it.
I have submitted a sample to CAPE community (https://capesandbox.com/analysis/331258/) that at some point during execution performs several GetCommandLineA()
calls. I was expecting to see that particular call (or GetCommandLineW()
) in the behavioral analysis. However, it is not there.
Inspecting the analysis logs, there is an explicit error about the API call:
2022-11-09 14:04:25,796 [root] WARNING: b'Unable to place hook on GetCommandLineW'
2022-11-09 14:04:25,812 [root] WARNING: b'Unable to hook GetCommandLineW'
I will dig into this trying to find out where the issue arises from, but it could happen with other API calls.
We are building Capemon with Visual Studio 2022 17.8.4 on Windows. It failed to build with fatal error C1047: The object or library file 'F:\gitP\kevoreilly\capemon\libyara\lib\libyara64.lib' was created by a different version of the compiler than other objects like 'x64\Release\alloc.obj'; rebuild all objects and libraries with the same compiler. Could you please help to take a look? Thanks in advance!
Steps to reproduce:
Actual result:
LINK : fatal error C1047: The object or library file 'F:\gitP\kevoreilly\capemon\libyara\lib\libyara64.lib' was created by a different version of the compiler than other objects like 'x64\Release\alloc.obj'; rebuild all objects and libraries with the same compiler [F:\gitP\kevoreilly\capemon\capemon.vcxproj]
LINK : fatal error LNK1257: code generation failed [F:\gitP\kevoreilly\capemon\capemon.vcxproj]
LINK : fatal error C1047: The object or library file 'F:\gitP\kevoreilly\capemon\libyara\lib\libyara64.lib' was created by a different version of the compiler than other objects like 'x64\Release\alloc.obj'; rebuild all objects and libraries with the same compiler [F:\gitP\kevoreilly\capemon\capemon.vcxproj]
LINK : fatal error LNK1257: code generation failed [F:\gitP\kevoreilly\capemon\capemon.vcxproj]
Attached build log file:
build.log
Please answer the following questions for yourself before submitting an issue.
Running without concern and encrypt all the files.
Dos device mode utility crash, if I disable hooks ( zerohooks=1) will run as expected.
SHA256: b23eb66e588b47a73b393c87467b0b2b0431d9d346368efeaa36a76c7877cd27
I am importing the actual VS Project but it seems that some dependencies are broken, are you still working on it?
Thank you
We're trying to use CAPEv2 to analyse malware on Windows 10. After we had various types of analyses fail with changing errors on Windows 10 but succeeding on Windows 7, we dug into a simple test case to try and determine the root cause. We used a simple downloadExe.bat
which initially employed powershell.exe
but eventually only consisted of an echo hi
.
By itself it (obviously) runs fine. When run under observation by capemon.dll
it reliably ends in a state where a console window is open and shows error message:
Not enough memory resources are available to process this command.
but no hi
. This is when started from CAPE via its web frontend in a freshly resumed Windows 10 VM as well as when run from a debug setup with a manually set up analyzer, analysis.conf
and various debug and devel tools at hand (particularly Visual Studio Community 2019 and x96dbg). We focused on only the 32-bit cmd.exe
and capemon.dll
for now.
Digging into this further we determined that this message is a misleading catch-all follow-up error and that the actual cause is a memory access violation exception in cmd.exe
. Digging into this showed LdrResolveDelayLoadedAPI
returning a Null Pointer for the ShellExecuteExW
function which is indeed delay-loaded in cmd.exe
. We were not able to determine a cause for this or reproduce it in simpler hand-written test cases involving a small C program using a delay-loaded ShellExecuteExW
to run e.g. nodepad.exe
.
By pure coincidence when doing a debug build we then found that disabling the call to hide_module_from_peb()
in capemon.c
makes our test case run successfully.
Line 584 in de2c595
Further tests revealed that clearing the LDR_MODULE
element using memset()
after removing it from the various linked lists seems to cause the fallout we're seeing.
Line 799 in 5f9e800
Considering the observed behaviour, it would make sense for LdrResolveDelayLoadedAPI
to become confused if module bookkeeping became corrupted ever so slightly by the memset()
. It seems unlikely though, that the structure became smaller with Windows 10. Rather I'd speculate there to be more references on it which then point to zeroed memory, perhaps as part of an explicit hardening measure.
What do you make of this?
Are you aware of any security measure introduced with Windows 10 that would prevent modification of the PEB and could be disabled?
What might point to a security measure is a peculiar behaviour of Visual Studio we've observed: While Visual Studio is running (even just the startup project/solution selection screen), there seems to be some kind of DLL hosting and/or debugging aid provided by it which tolerates the memset()
in capemon.dll
and obscures the problem. While it runs, successive analysis runs not only succeed but also seem to share the same, seemingly preloaded capemon.dll
even if it's been replaced on disk in the meantime. As soon as Visual Studio is closed, the error behaviour returns. We've not been able to determine what functionality we're dealing with there and any pointer would be welcome.
Also: Disabling the memset()
doesn't solve all our problems. Analyses still fail, but increasingly randomly so. What's particularly confounding is that memory access violation exceptions seem to be silently ignored with release as well as debug builds of capemon.dll
which greatly hinders debugging. In the case of cmd.exe
they lead to above error message (Not enough resources...
) but do not terminate the process so we can not get an automatic post-mortem debugger attach e.g. from WinDbg. The same seems to be the case on Windows 7. We've looked but not been able to find a Windows configuration setting for this. Is there any intentional compiler/linker setting or code causing this behaviour we could disable to trigger faulting on all access errors (not explicitly handled) so we can find more of these problems quicker?
Hey,
More of a question then an issue, but is it possible to analyze generic Linux Binaries using capemon or CAPEv2 more generally?
I want to analyze some Binaries inside of a docker container. I successfully wrote a machinery module to work with docker and CAPEv2, but I'm quickly realizing now that it looks like (based on capemon code) that it likely only supports Windows APIs.
So, my question is, is it possible with CAPEv2 to analyze generic linux binaries (either in or out of a container) and if not how difficult would it be to add functionality to capemon for this?
I have a simple question. Does capemon cover the whole set of Windows API or only a subset of them?
Thank you
hello team I am using a capesendbox rest API but its not working I am using postman to run this API but I am getting 520 error, please help me to resolve that issue
The structure LDR_MODULE should be renamed to LDR_DATA_TABLE_ENTRY. This is the name under which the structure is publicky known
These functions, parsing the InLoadOrderModuleList
have their ending condition wrong and are touching random data via mod->BaseAddress
.
I'll prepare pull request for this.
I've been seeing crashes in the NtAllocateVirtualMemoryEx
hook as can be seen here:
(please excuse the German UI, the debug machine was not mine)
It looked like a call-by-value with a large operand to me and pointed me towards the __inout MEM_EXTENDED_PARAMETER Parameters
argument to the function. Since I've never seen such a large structure being passed by value in any API I dug a bit and found this alternative usage much more in line with my experience: dotnet/runtime#12779
After changing the prototype to use a pointer like so, the crashes went away:
__inout MEM_EXTENDED_PARAMETER *Parameters,
See also: https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc2
This also jives with Parameters
being an array of MEM_EXTENDED_PARAMETER
s defined by last argument ParameterCount
.
https://capesandbox.com/analysis/193484/
The sample payload is a CobaltStrike stage beacon, wrapped by a loader written in golang. The C2 addr of it is 39.103.191.231,but in the network analysis of the task, there is no host item of the C2.
When i try to debug the sample in my local cape sandbox, i set a breakpoint at next instruction of the shellcode call LoadLibraryA(wininet), then i push f9, it will never hit the breakpoint("运行中" means running).
Hi,
I'm seeing stack overflow exceptions on Windows 10 with even the simplest program doing a single API call:
#include <stdio.h>
#include <Windows.h>
int main()
{
Sleep(10000);
DWORD threadid = GetCurrentThreadId();
}
Unfortunately, I was not able to grab any meaningful backtrace beyond it happening in enter_hook()
and operate_on_backtrace()
.
Through single stepping the code I think to have found the root cause but can only describe it verbally with links into the capemon source code:
enter_hook()
calls __called_by_hook()
to prevent hook recursion: Line 293 in e62f1a4
__called_by_hook()
runs addr_in_our_dll_range()
via operate_on_backtrace()
: Line 181 in e62f1a4
operate_on_backtrace()
in the 64 bit version runs our_stackwalk()
to retrieve the number of strack frames to look at: Line 1168 in e62f1a4
our_stackwalk()
will return zero if the SRW lock is held or an EXCEPTION_EXECUTE_HANDLER exception occurs (I'm fuzzy on the details of the latter): Line 1126 in e62f1a4
Line 1150 in e62f1a4
operate_on_backtrace()
to never call addr_in_our_dll_range()
and will default to returning zeroThis in the context of __called_by_hook()
means that enter_hook()
was not triggered from another hook. This essentially creates potential for unwanted hook recursion whenever the SRW lock is held or that execution exception occurs during stack unwinding.
This seems to quite reliably be triggered and turned into infinite recursion by the Debugger:
__called_by_hook()
having told enter_hook()
that it was not called by a hook, api_dispatch()
is calledapi_dispatch()
may (and in my observation basically always does) call InitNewThreadBreakpoints()
InitNewThreadBreakpoints()
calls CreateThreadBreakpoints()
CreateThreadBreakpoints()
calls GetThreadId()
GetThreadId()
internally (at least on Windows 10) calls NtQueryInformationThread()
-> which is hookedThis causes instantaneous inifinite hook recursion on any hooked API call (at the very least if the SRW lock is held), leading to the observed stack overflow.
To recap, the call chain is:
/any API call/ -> [recurse: enter_hook()
+ __called_by_hook()
== 0 -> api_dispatch()
-> InitNewThreadBreakpoints()
-> CreateThreadBreakpoints()
->GetThreadId()
-> NtQueryInformationThread()
]
My workaround looks like this:
diff --git a/hooking.c b/hooking.c
index 443ae50..d1af8ef 100644
--- a/hooking.c
+++ b/hooking.c
@@ -178,7 +178,14 @@ int addr_in_our_dll_range(void *unused, ULONG_PTR addr)
static int __called_by_hook(ULONG_PTR stack_pointer, ULONG_PTR frame_pointer)
{
- return operate_on_backtrace(stack_pointer, frame_pointer, NULL, addr_in_our_dll_range);
+ int rc = operate_on_backtrace(stack_pointer, frame_pointer, NULL, addr_in_our_dll_range);
+ if (rc < 0) {
+ // be cautious if we couldn't operate on the backtrace at all. This can be
+ // due to SRW lock being held or exceptions when trying to evaluate the backtrace.
+ return 1;
+ }
+
+ return rc;
}
int called_by_hook(void)
diff --git a/hooking_64.c b/hooking_64.c
index 153fba5..04d3230 100644
--- a/hooking_64.c
+++ b/hooking_64.c
@@ -1112,7 +1112,7 @@ BOOL srw_lock_held()
return FALSE;
}
-static unsigned int our_stackwalk(ULONG_PTR _rip, ULONG_PTR sp, PVOID *backtrace, unsigned int count)
+static int our_stackwalk(ULONG_PTR _rip, ULONG_PTR sp, PVOID *backtrace, unsigned int count)
{
/* derived from http://www.nynaeve.net/Code/StackWalk64.cpp */
__declspec(align(64)) CONTEXT ctx;
@@ -1124,7 +1124,7 @@ static unsigned int our_stackwalk(ULONG_PTR _rip, ULONG_PTR sp, PVOID *backtrace
unsigned int frame;
if (srw_lock_held())
- return 0;
+ return -1;
__try
{
@@ -1149,17 +1149,17 @@ static unsigned int our_stackwalk(ULONG_PTR _rip, ULONG_PTR sp, PVOID *backtrace
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
- return 0;
+ return -1;
}
}
int operate_on_backtrace(ULONG_PTR sp, ULONG_PTR _rip, void *extra, int(*func)(void *, ULONG_PTR))
{
- int ret = 0;
+ int ret = -1;
PVOID backtrace[HOOK_BACKTRACE_DEPTH];
lasterror_t lasterror;
- WORD frames;
- WORD i;
+ int frames;
+ int i;
get_lasterrors(&lasterror);
What this does is make our_stackwalk()
indicate the inability to walk the stack at all by returning -1
. This will still make operate_on_backtrace()
not call addr_in_our_dll_range()
but the changed return code default of -1
will again indicate that fact to the caller. The only caller evaluating the return code at all is __called_by_hook()
. There we now cautiously return 1
, meaning "yes, we've been or at least could have been called from a hook". This successfully prevents the infinite recursion and subsequent stack overflow in my tests.
Does any of that make sense?
Hi, we are moving from our old cuckoo-modified fork to CAPEv2 and I noticed that tabs and spaces are used randomly across the code base. Would you consider fixing this? This would greatly help us with merging our changes (some of which we plan to contribute after the migration) and generally with development later. Should be easy to do with the help of the Visual Studio or some other IDE.
I am willing to do this and create PR. However, I think it would be easier for you to do this on your own then going over the diff in the PR from a stranger. If you want to this and you do not mind a PR, let me know if you would prefer tabs or spaces (we prefer tabs).
Capemon failed to build with "error C2039: 'Dr6': is not a member of '_CONTEXT' " with MSVC on Windows arm64. It can reproduce on latest version bfc50b2963d58739f77b907f1465c73880938a80 on capemon branch. Could you please help look at this issue?
Repro steps:
Error log:
Error info:
4>F:\capemon\CAPE\Debugger.c(417,38): error C2039: 'Dr6': is not a member of '_CONTEXT' [F:\capemon\capemon.vcxproj]
C:\Program Files (x86)\Windows Kits\10\Include\10.0.17763.0\um\winnt.h(6048): message : see declaration of '_CONTEXT' [F:\capemon\capemon.vcxproj]
4>F:\capemon\CAPE\Debugger.c(448,88): error C2039: 'Dr0': is not a member of '_CONTEXT' [F:\capemon\capemon.vcxproj]
C:\Program Files (x86)\Windows Kits\10\Include\10.0.17763.0\um\winnt.h(6048): message : see declaration of '_CONTEXT' [F:\capemon\capemon.vcxproj]
4>F:\capemon\CAPE\Debugger.c(449,145): error C2039: 'Dr0': is not a member of '_CONTEXT' [F:\capemon\capemon.vcxproj]
C:\Program Files (x86)\Windows Kits\10\Include\10.0.17763.0\um\winnt.h(6048): message : see declaration of '_CONTEXT' [F:\capemon\capemon.vcxproj]
4>F:\capemon\CAPE\Debugger.c(451,88): error C2039: 'Dr1': is not a member of '_CONTEXT' [F:\capemon\capemon.vcxproj]
C:\Program Files (x86)\Windows Kits\10\Include\10.0.17763.0\um\winnt.h(6048): message : see declaration of '_CONTEXT' [F:\capemon\capemon.vcxproj]
4>F:\capemon\CAPE\Debugger.c(452,145): error C2039: 'Dr1': is not a member of '_CONTEXT' [F:\capemon\capemon.vcxproj]
C:\Program Files (x86)\Windows Kits\10\Include\10.0.17763.0\um\winnt.h(6048): message : see declaration of '_CONTEXT' [F:\capemon\capemon.vcxproj]
4>F:\capemon\CAPE\Debugger.c(454,88): error C2039: 'Dr2': is not a member of '_CONTEXT' [F:\capemon\capemon.vcxproj]
C:\Program Files (x86)\Windows Kits\10\Include\10.0.17763.0\um\winnt.h(6048): message : see declaration of '_CONTEXT' [F:\capemon\capemon.vcxproj]
4>F:\capemon\CAPE\Debugger.c(455,145): error C2039: 'Dr2': is not a member of '_CONTEXT' [F:\capemon\capemon.vcxproj]
C:\Program Files (x86)\Windows Kits\10\Include\10.0.17763.0\um\winnt.h(6048): message : see declaration of '_CONTEXT' [F:\capemon\capemon.vcxproj]
4>F:\capemon\CAPE\Debugger.c(457,88): error C2039: 'Dr3': is not a member of '_CONTEXT' [F:\capemon\capemon.vcxproj]
C:\Program Files (x86)\Windows Kits\10\Include\10.0.17763.0\um\winnt.h(6048): message : see declaration of '_CONTEXT' [F:\capemon\capemon.vcxproj]
4>F:\capemon\CAPE\Debugger.c(458,145): error C2039: 'Dr3': is not a member of '_CONTEXT' [F:\capemon\capemon.vcxproj]
C:\Program Files (x86)\Windows Kits\10\Include\10.0.17763.0\um\winnt.h(6048): message : see declaration of '_CONTEXT' [F:\capemon\capemon.vcxproj]
4>F:\capemon\CAPE\Debugger.c(605,62): error C2039: 'Rip': is not a member of '_CONTEXT' [F:\capemon\capemon.vcxproj]
C:\Program Files (x86)\Windows Kits\10\Include\10.0.17763.0\um\winnt.h(6048): message : see declaration of '_CONTEXT' [F:\capemon\capemon.vcxproj]
4>F:\capemon\CAPE\Debugger.c(605,7): error C2198: 'ide': too few arguments for call [F:\capemon\capemon.vcxproj]
4>F:\capemon\CAPE\Debugger.c(609,57): error C2039: 'Rip': is not a member of '_CONTEXT' [F:\capemon\capemon.vcxproj]
C:\Program Files (x86)\Windows Kits\10\Include\10.0.17763.0\um\winnt.h(6048): message : see declaration of '_CONTEXT' [F:\capemon\capemon.vcxproj]
Hello CAPE team!
I'm working on a diploma thesis aimed at analyzing CAPEv2 abilities in tracking Meterpreter based exploits and I have noticed that no matter what my TCP sessions are pretty unstable. That means that after a sucesfull conection all sessions last about 30 secs and then they die off. Presumably because some heartbeat timer expires prematurely.
The culprit seem to be the NtWaitForSingleObject hook which does not react to the force-sleepskip setting. When force-sleepskip=0 option is set, the issue is still there. In order to remove it I had to recompile the monitor with this hook disabled.
Best
Ilzaman
Failed to build after 8bf93ca update the libyara\lib\libyara64.lib
and libyara\lib\libyara86.lib
file.
Build FAILED.
"T:\t\capemon\capemon.sln" (Rebuild target) (1) ->
"T:\t\capemon\capemon.vcxproj.metaproj" (Rebuild target) (2) ->
"T:\t\capemon\capemon.vcxproj" (Rebuild target) (5) ->
(Link target) ->
LINK : fatal error C1047: The object or library file 'T:\t\capemon\\libyara\lib\libyara64.lib' was created by a different version of the compiler than other objects like 'x64\Release\alloc.obj'; rebu
ild all objects and libraries with the same compiler [T:\t\capemon\capemon.vcxproj]
LINK : fatal error LNK1257: code generation failed [T:\t\capemon\capemon.vcxproj]
2 Error(s)
link.exe /ERRORREPORT:NONE /OUT:"x64\Release\capemon_x64.dll" /INCREMENTAL /ILK:"x64\Release\capemon_x64.ilk" /NOLOGO /LIBPATH:x64\Release /LIBPATH:libyara\lib libyara64.lib bson.lib crypt32.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /DEF:"capemon.def" /MANIFEST /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /manifest:embed /PDB:"x64\Release\capemon_x64.pdb" /SUBSYSTEM:WINDOWS /OPT:REF /OPT:ICF /LTCG:incremental /LTCGOUT:"x64\Release\capemon_x64.iobj" /TLBID:1 /RELEASE /DYNAMICBASE /NXCOMPAT /IMPLIB:"x64\Release\capemon_x64.lib" /MACHINE:X64 /DLL x64\Release\alloc.obj x64\Release\AmsiDumper.obj x64\Release\CAPE.obj x64\Release\Debugger.obj x64\Release\Injection.obj x64\Release\InstrCallback.obj x64\Release\Output.obj x64\Release\ScyllaHarness.obj x64\Release\ApiReader.obj x64\Release\DeviceNameResolver.obj x64\Release\IATReferenceScan.obj x64\Release\IATSearch.obj x64\Release\ImportRebuilder.obj x64\Release\ImportsHandling.obj x64\Release\NativeWinApi.obj x64\Release\PeParser.obj x64\Release\ProcessAccessHelp.obj x64\Release\StringConversion.obj x64\Release\SystemInformation.obj x64\Release\Trace.obj x64\Release\Unpacker.obj x64\Release\w64wow64.obj x64\Release\wow64_fix.obj x64\Release\YaraHarness.obj x64\Release\config.obj x64\Release\capemon.obj x64\Release\decoder.obj x64\Release\distorm.obj x64\Release\instructions.obj x64\Release\insts.obj x64\Release\mnemonics.obj x64\Release\operands.obj x64\Release\prefix.obj x64\Release\textdefs.obj x64\Release\wstring.obj x64\Release\hooking.obj x64\Release\hooking_32.obj x64\Release\hooking_64.obj x64\Release\hooks.obj x64\Release\hook_clr.obj x64\Release\hook_crypto.obj x64\Release\hook_file.obj x64\Release\hook_misc.obj x64\Release\hook_network.obj x64\Release\hook_process.obj x64\Release\hook_reg.obj x64\Release\hook_reg_native.obj x64\Release\hook_services.obj x64\Release\hook_sleep.obj x64\Release\hook_socket.obj x64\Release\hook_special.obj x64\Release\hook_sync.obj x64\Release\hook_thread.obj x64\Release\hook_tls.obj x64\Release\hook_window.obj x64\Release\ignore.obj x64\Release\log.obj x64\Release\lookup.obj x64\Release\misc.obj x64\Release\pipe.obj x64\Release\unhook.obj x64\Release\utf8.obj x64\Release\InstrHook64.obj
git clone https://github.com/kevoreilly/capemon.git
cd capemon
REM It can be compiled after restoring libyara lib
REM git checkout 8bf93ca~ libyara\lib\libyara*.lib
msbuild /m /p:Platform=x64 /p:Configuration=Release /p:WindowsTargetPlatformVersion=10.0.22621.0 /p:PlatformToolset=v143 capemon.sln /t:Rebuild
Currently facing exceptions with a few x86 DLL samples in Win10.
Hash are as followed:
d3095f08ae2d3f9b31dd5696bd593e5de14b4ca665389f0d480ad12318af2682
2cb8f04d41fe34706ff61cba06788faaaca87494721fcf8e86d20b897890a3b1
907b3cc7168067b2e2c4db2318cc9fa2ebc58963571c92665b447c447b6cc3a1
b7e432ebcbff1842f6639e6cc8ba2cca6a7ebe6374d40fda88b9de0fa920b225
f96a79f844cdcd2c31932452a6bf9aac7f04731f8eb72f2e1fa3f00e24d6aa98
Stack seem to get corrupted and as far as I investigated, the problem is not coming from rundll32 itself for broken signature.
Working onto that issue currently but any help would be welcome.
Happy to provide crashdump, memory dumps of the issues.
Execution of dummy x86 dlls give no exceptions.
IsPeImageRaw()
The central pointer, where the image is mapped or the raw data is located in memory, is determined. The logic inside IsPeImageRaw()
works fine but can fail in many scenarios.
extern "C" int LooksLikeSectionBoundary(DWORD_PTR Buffer)
{
if (
(*(DWORD*)((BYTE*)Buffer - 4) == 0) && // The end of the previous section has zeros
(*(DWORD*)((BYTE*)Buffer) != 0) // The beginning of the section is non-zero
)
{
// If sectionHeader.VirtualAddress == sectionHeader.SizeOfRawData, the above check would fail
}
}
For example, in the case of Guloader (7911e39e07995e3afb97ac0e5a4608c10c2e278bef29924ecc3924edfcc495ca), after RtlDecompress, the buffer is mapped into memory. AllocationHandler adds the allocation to the monitor list: AllocationHandler: Adding allocation to tracked region list: 0x00410000, size: 0x5000. Thus, during Free or processExit when the list is processed, a dump is mistakenly taken as a Virtual Section boundary. Specifically, in the case of Guloader, in the first section of the PE file, there is a code buffer placed at the PointerToRawData of the first section (which is strange).
The fix:
I have written a small function that checks the presence of relocations in the buffer containing a valid PE. Based on the validity of the relocations, it determines if the buffer is virtually mapped or raw mapped. This function can be called inside IsPeImageRaw() before the boundary checks as a precedence.
if (peFile->hasRelocationDirectory() )
{
if (CheckRelocsTest((char*)Buffer, peFile) )// Virtual mapped image
return 0;
else
return 1;
}
// Test based on validity of relocation table , if mapped images is as per virtual boundary then relocations will be parsed successfully
extern "C" int CheckRelocsTest(char *pMappedImage, PeParser *peFile)
{
PIMAGE_BASE_RELOCATION RelocTable = NULL;
unsigned int iRelocVaddr = 0;
IMAGE_DOS_HEADER DosHdr = {0};
IMAGE_FILE_HEADER FileHdr = {0};
IMAGE_OPTIONAL_HEADER OptHdr = {0};
PIMAGE_BASE_RELOCATION pRelocEntry = NULL;
unsigned int RelocBlockSize = 0;
unsigned int *FixUp = 0;
int i = 0;
if (peFile->hasRelocationDirectory())
{
pRelocEntry = (PIMAGE_BASE_RELOCATION)((unsigned int)peFile->getCurrentNtHeader()->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress + (unsigned int)pMappedImage);
DebugOutput("CheckRelocs: pRelocEntry->VirtualAddress %d.\n", pRelocEntry->VirtualAddress);
if (pRelocEntry->VirtualAddress >= peFile->getCurrentNtHeader()->OptionalHeader.SizeOfImage || pRelocEntry->VirtualAddress == 0)
{
return 0;
}
while (pRelocEntry->VirtualAddress)
{
iRelocVaddr = pRelocEntry->VirtualAddress;
RelocBlockSize = (pRelocEntry->SizeOfBlock - 8) / 2;
pRelocEntry = (PIMAGE_BASE_RELOCATION) ((unsigned char *)pRelocEntry + 8); // TypeOffset
while (RelocBlockSize--)
{
if (*(unsigned short *)pRelocEntry == 0x3000)
{
pRelocEntry = (PIMAGE_BASE_RELOCATION) ((unsigned char *)pRelocEntry + 2);
continue;
}
FixUp = (unsigned int *)(*(unsigned short *)pRelocEntry & 0x0fff);
DebugOutput("\nFixup value before adding = %x, Page base = %x", FixUp, iRelocVaddr);
FixUp = (unsigned int *)((unsigned int)FixUp + ((unsigned int)pMappedImage + (unsigned int)iRelocVaddr));
DebugOutput("\nOriginal Address = 0x%p", *FixUp);
pRelocEntry = (PIMAGE_BASE_RELOCATION) ((unsigned char *)pRelocEntry + 2);
}
if (pRelocEntry >= (PIMAGE_BASE_RELOCATION)peFile->getCurrentNtHeader()->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress)
{
break;
}
if (pRelocEntry->VirtualAddress >= peFile->getSectionHeaderBasedFileSize())
{
return 0;
}
}
}
return 1; // Virtual Image
}
My sandbox is running at Windows 10.
The crashed happened when NtQueryValueKey
hooked function is trigger from PrivateRegQueryValueExT
(I guess) in advapi32.dll
.
Lines 195 to 196 in 64d2130
In loq
function, it will handle the "k" : "FullName", KeyHandle, ValueName
Line 711 in 64d2130
Then the PUNICODE_STRING s
, which is the ValueName
, is passed into get_full_keyvalue_pathUS
Lines 1195 to 1201 in 64d2130
Inside of get_full_keyvalue_pathUS
. When accessing buf[i]
, it crashed because buf
is null.
Lines 1153 to 1154 in 64d2130
For unknown reason, the ValueName
passed to NtQueryValueKey
has Length
> 0 and Buffer
null. That's the root cause of the crash.
Fix is simple, just check in->Buffer
before doing anything.
diff --git a/misc.c b/misc.c
index 5bab94a..2df90cb 100644
--- a/misc.c
+++ b/misc.c
@@ -1192,7 +1192,7 @@ wchar_t *get_full_keyvalue_pathW(HKEY registry, const wchar_t *in, PKEY_NAME_INF
wchar_t *get_full_keyvalue_pathUS(HKEY registry, const PUNICODE_STRING in, PKEY_NAME_INFORMATION keybuf, unsigned int len)
{
wchar_t *ret;
- if (in && in->Length) {
+ if (in && in->Buffer && in->Length) {
unsigned int newlen = get_encoded_unicode_string_len(in->Buffer, in->Length);
wchar_t *incpy = malloc(newlen + (1 * sizeof(wchar_t)));
copy_encoded_unicode_string(incpy, in->Buffer, in->Length, newlen);
Hello, I am trying to extract more information from the API NtCreateUserProcess
in particular from the argument AttributeList
.
I defined the PPS_ATTRIBUTE_LIST as following, taking the definition from https://github.com/processhacker/processhacker/blob/master/phnt/include/ntpsapi.h
typedef struct _PS_ATTRIBUTE
{
ULONG_PTR Attribute;
SIZE_T Size;
union
{
ULONG_PTR Value;
PVOID ValuePtr;
};
PSIZE_T ReturnLength;
} PS_ATTRIBUTE, *PPS_ATTRIBUTE;
typedef struct _PS_ATTRIBUTE_LIST
{
SIZE_T TotalLength;
PS_ATTRIBUTE Attributes[1];
} PS_ATTRIBUTE_LIST, *PPS_ATTRIBUTE_LIST;
I am trying to log and print the ULONG_PTR VALUE but all my trials failed. Do you have any suggestion on how to do that?
Cheers
Starting Winword and Excel 2016 32 bit with capemon loaded on recent Windows 10 quickly ends in an error message The operating system is not presently configured to run this application
:
Enabling loader snaps shows that various DLLs can not be found:
DebugString: "0cb8:0cc8 @ 00250593 - LdrGetDllHandleEx - ENTER: DLL name: mso20win32client.dll"
DebugString: "0cb8:0cc8 @ 00250593 - LdrpFindLoadedDllInternal - RETURN: Status: 0xc0000135"
DebugString: "0cb8:0cc8 @ 00250593 - LdrGetDllHandleEx - RETURN: Status: 0xc0000135"
DebugString: "0cb8:0cc8 @ 00250609 - LdrLoadDll - ENTER: DLL name: C:\Program Files (x86)\Common Files\Microsoft Shared\Office16\mso20win32client.dll"
DebugString: "0cb8:0cc8 @ 00250609 - LdrpLoadDllInternal - ENTER: DLL name: C:\Program Files (x86)\Common Files\Microsoft Shared\Office16\mso20win32client.dll"
DebugString: "0cb8:0cc8 @ 00250609 - LdrpResolveDllName - ENTER: DLL name: C:\Program Files (x86)\Common Files\Microsoft Shared\Office16\mso20win32client.dll"
DebugString: "0cb8:0cc8 @ 00250609 - LdrpResolveDllName - RETURN: Status: 0xc0000135"
DebugString: "0cb8:0cc8 @ 00250609 - LdrpProcessWork - ERROR: Unable to load DLL: "C:\Program Files (x86)\Common Files\Microsoft Shared\Office16\mso20win32client.dll", Parent Module: "(null)", Status: 0xc0000135"
DebugString: "0cb8:0cc8 @ 00250609 - LdrpLoadDllInternal - RETURN: Status: 0xc0000135"
DebugString: "0cb8:0cc8 @ 00250609 - LdrLoadDll - RETURN: Status: 0xc0000135"
... and so on for mso30win32client.dll
, mso40uiwin32client.dll
, mso99Lwin32client.dll
and more.
Looking at the supposed location they indeed do not exist there. Instead they live at C:\Program Files (x86)\Microsoft Office\root\VFS\ProgramFilesCommonX86\Microsoft Shared\OFFICE16.
This is confirmed by looking at the loader snaps of an unmonitored Winword.exe
in x32dbg which read:
DebugString: "19a8:0580 @ 00954453 - LdrGetDllHandleEx - RETURN: Status: 0xc0000135"
DebugString: "19a8:0580 @ 00954453 - LdrLoadDll - ENTER: DLL name: C:\Program Files (x86)\Common Files\Microsoft Shared\Office16\mso20win32client.dll"
DebugString: "19a8:0580 @ 00954453 - LdrpLoadDllInternal - ENTER: DLL name: C:\Program Files (x86)\Common Files\Microsoft Shared\Office16\mso20win32client.dll"
DebugString: "19a8:0580 @ 00954453 - LdrpResolveDllName - ENTER: DLL name: C:\Program Files (x86)\Common Files\Microsoft Shared\Office16\mso20win32client.dll"
DebugString: "19a8:0580 @ 00954453 - LdrpResolveDllName - RETURN: Status: 0x00000000"
DebugString: "19a8:0580 @ 00954453 - LdrpMinimalMapModule - ENTER: DLL name: C:\Program Files (x86)\Common Files\Microsoft Shared\Office16\mso20win32client.dll"
DLL Loaded: 5AE10000 C:\Program Files (x86)\Microsoft Office\root\VFS\ProgramFilesCommonX86\Microsoft Shared\OFFICE16\Mso20win32client.dll
DebugString: "19a8:0580 @ 00954453 - LdrpMinimalMapModule - RETURN: Status: 0x00000000"
The mechanism behind that apparent redirection is reverse engineered and explained at https://lucasg.github.io/2018/08/22/magic-behind-appvisv/.
Indeed, in the unhooked Winword.exe
, disassembly of ntdll
exports contain hooks redirecting into module appvisvsubsystems32
:
In the monitored process, they do not. This is likely explained by the following earlier debug output and exceptions:
DebugString: "0cb8:0cc8 @ 00250500 - LdrGetDllHandleEx - ENTER: DLL name: AppVIsvSubsystems32.dll"
DebugString: "0cb8:0cc8 @ 00250500 - LdrpFindLoadedDllInternal - RETURN: Status: 0x00000000"
DebugString: "0cb8:0cc8 @ 00250500 - LdrGetDllHandleEx - RETURN: Status: 0x00000000"
DebugString: "0cb8:0cc8 @ 00250500 - LdrGetDllHandleEx - ENTER: DLL name: AppVIsvSubsystems32.dll"
DebugString: "0cb8:0cc8 @ 00250500 - LdrpFindLoadedDllInternal - RETURN: Status: 0x00000000"
DebugString: "0cb8:0cc8 @ 00250500 - LdrGetDllHandleEx - RETURN: Status: 0x00000000"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtOpenKey" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtOpenKeyEx" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtOpenKeyTransacted" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtOpenKeyTransactedEx" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtDeleteKey" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtFlushKey" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtCreateKey" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtCreateKeyTransacted" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtEnumerateKey" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtQueryKey" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtQueryObject" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtSetInformationKey" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtQueryValueKey" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtEnumerateValueKey" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtSetValueKey" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtDeleteValueKey" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtRenameKey" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtQueryMultipleValueKey" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtNotifyChangeKey" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtNotifyChangeMultipleKeys" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtQuerySecurityObject" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtSetSecurityObject" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtDuplicateObject" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtClose" by name"
EXCEPTION_DEBUG_INFO:
dwFirstChance: 1
ExceptionCode: C0000005 (EXCEPTION_ACCESS_VIOLATION)
ExceptionFlags: 00000000
ExceptionAddress: 5CCC212B appvisvsubsystems32.5CCC212B
NumberParameters: 2
ExceptionInformation[00]: 00000001 Write
ExceptionInformation[01]: 776D2AA0 <ntdll.ZwClose> Inaccessible Address
First chance exception on 5CCC212B (C0000005, EXCEPTION_ACCESS_VIOLATION)!
EXCEPTION_DEBUG_INFO:
dwFirstChance: 1
ExceptionCode: C0000005 (EXCEPTION_ACCESS_VIOLATION)
ExceptionFlags: 00000000
ExceptionAddress: 5CCC2130 appvisvsubsystems32.5CCC2130
NumberParameters: 2
ExceptionInformation[00]: 00000001 Write
ExceptionInformation[01]: 776D2AA1 ntdll.776D2AA1 Inaccessible Address
First chance exception on 5CCC2130 (C0000005, EXCEPTION_ACCESS_VIOLATION)!
EXCEPTION_DEBUG_INFO:
dwFirstChance: 1
ExceptionCode: C0000005 (EXCEPTION_ACCESS_VIOLATION)
ExceptionFlags: 00000000
ExceptionAddress: 5CCC212B appvisvsubsystems32.5CCC212B
NumberParameters: 2
ExceptionInformation[00]: 00000001 Write
ExceptionInformation[01]: 776D2D90 <ntdll.ZwDuplicateObject> Inaccessible Address
First chance exception on 5CCC212B (C0000005, EXCEPTION_ACCESS_VIOLATION)!
EXCEPTION_DEBUG_INFO:
dwFirstChance: 1
ExceptionCode: C0000005 (EXCEPTION_ACCESS_VIOLATION)
ExceptionFlags: 00000000
ExceptionAddress: 5CCC2130 appvisvsubsystems32.5CCC2130
NumberParameters: 2
ExceptionInformation[00]: 00000001 Write
ExceptionInformation[01]: 776D2D91 ntdll.776D2D91 Inaccessible Address
This in my interpretation shows how AppVIsvSubsystems32.dll
is locating the exports of the functions it wants to hook and then trying to patch them, which is denied.
In a jumping conclusion this lead me to NtProtectVirtualMemory
where I had seen an ntdll
protection functionality. And indeed, setting ntdll-protect=0
in options
of analysis.conf
of the CAPEv2 analyzer makes all the above misbehaviour disappear. Disassembly of the ntdll
entrypoints shows that AppVIsvSubsystems32.dll
is once again able to install its hooks and the loader snaps show the DLLs being loaded successfully from their actual locations. (Word and Excel still don't start up successfully but that seems to be an unrelated problem for another day.)
Should ntdll-protect=0
perhaps become part of the special office
settings profile?
Or could/should there be a more general detection of AppV Detour hooking attempts as explained in above article?
(I would suspect that 64bit Office suffers from the same problem but can not easiliy test that because the only workaround for #12 I currently have incidentally consists of disabling the hooking of NtProtectVirtualMemory
which also disables ntdll
write protection.)
The intrinsic function __readeflags()
used in get_lasterrors() returns 64-bit value on 64-bit build. It is necessary to change lasterror_t::Eflags
to DWORD_PTR
What is the meaning of the condition at line 372? It causes the problem described below
The code created by conditional __writeeflags()
at line 372 in set_lasterrors
seems to invoke error in compiler.
How to reproduce the compiler error:
New_NtOpenFile
New_NtOpenFile
:.text:00000001800A5280 sub rsp, 70h
.text:00000001800A5284 mov rsi, r9 ; RSI = IoStatusBlock
.text:00000001800A5287 mov rbx, r8 ; RBX = ObjectAttributes
.text:00000001800A528A mov ebp, edx ; EBP = DesiredAccess
.text:00000001800A528C mov rdi, rcx ; RDI = Pointer to FileHandle
.text:00000001800A528F int 3 ; Trap to Debugger (I added this here to stop in debugger)
.text:00000001800A5290 mov rcx, r8 ; obj
.text:00000001800A5293 call check_for_logging_resumption ; This call doesn't preserve RBX properly
.text:00000001800A5298 mov rcx, rbx ; obj
.text:00000001800A529B call is_protected_objattr ; This call crashes
.text:00000001800A52A0 test al, al
.text:00000001800A52A2 jz short loc_1800A52AE
.text:00000001800A52A4 mov eax, 0C0000022h
.text:00000001800A52A9 jmp loc_1800A53B7
check_for_logging_resumption()
functionI experimented a bit. The V142 platform toolset makes functions get_lasterrors
and set_lasterrors
inline. As consequence, there are "pushfq - pop" constructs (created by __readeflags()
and __writeeflags()
) all over the place. This operation causes RSP to be copied into R11 at the beginning of check_for_logging_resumption
. RBX is stored to stack cell referenced by R11+imm
, but it's restored from different stack cell referenced by RSP+imm
.
TLDR: I found that preventing both get_lasterrors
and set_lasterrors
from being inlined (via void declspec (noinline) get_lasterrors(lasterror_t *errors)
will make the bug go away.
I'm currently fixing up some code in the CoCreateInstance(Ex) hooks where a GUID for Task Scheduler 1.0 objects was missing.
In 01cc21d x64 was excluded from sending pipe commands, apparently due to some issue with maldocs, but I couldn't find any more information about what exactly the problem was.
As it stands, for 64-bit malware that persists itself using a scheduled task and only then becomes active, CAPE doesn't currently track execution of the scheduled task malware process. Do you think anything speaks against at least enabling the TASKSCHED
command for all platforms and excluding only WMI
, BITS
and INTEROP
?
If there are no concerns, I'll create a PR with the changes.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.