No, no it is not and it's quite complex to prevent.
Sandbox's usually hook internal APIs and execute code at an lowest level in ring 3. Let's talk about file API to begin with. If your familiar with lower level programming languages such as C, C++ and so on you'll know about the ReadFile
API. So, let's check out ReadFile in an disassembly.
ReadFile is situated as in the EAT (Export Address table) in Kernel32.
KERNEL32.ReadFile - FF 25 8803D674 - jmp dword ptr [KERNEL32.PssWalkSnapshot+9588] { ->KERNELBASE.ReadFile }
So, it jump to KernelBase.dll to an ReadFile. Don't worry about not understanding all the opcodes but just be interested in the CALLs.
KERNELBASE.ReadFile - 8B FF - mov edi,edi
KERNELBASE.ReadFile+2- 55 - push ebp
KERNELBASE.ReadFile+3- 8B EC - mov ebp,esp
KERNELBASE.ReadFile+5- 6A FE - push -02 { 254 }
KERNELBASE.ReadFile+7- 68 A806D576 - push KERNELBASE.ReadFile+B8 { [FFFFFFFE] }
KERNELBASE.ReadFile+C- 68 5098D576 - push KERNELBASE.OutputDebugStringA+C0 { [8B55FF8B] }
KERNELBASE.ReadFile+11- 64 A1 00000000 - mov eax,fs:[00000000] { 0 }
KERNELBASE.ReadFile+17- 50 - push eax
KERNELBASE.ReadFile+18- 83 EC 18 - sub esp,18 { 24 }
KERNELBASE.ReadFile+1B- 53 - push ebx
KERNELBASE.ReadFile+1C- 56 - push esi
KERNELBASE.ReadFile+1D- 57 - push edi
KERNELBASE.ReadFile+1E- A1 683BE076 - mov eax,[KERNELBASE.dll+C3B68] { [1EB677D9] }
KERNELBASE.ReadFile+23- 31 45 F8 - xor [ebp-08],eax
KERNELBASE.ReadFile+26- 33 C5 - xor eax,ebp
KERNELBASE.ReadFile+28- 50 - push eax
KERNELBASE.ReadFile+29- 8D 45 F0 - lea eax,[ebp-10]
KERNELBASE.ReadFile+2C- 64 A3 00000000 - mov fs:[00000000],eax { 0 }
KERNELBASE.ReadFile+32- 89 65 E8 - mov [ebp-18],esp
KERNELBASE.ReadFile+35- C7 45 E0 00000000 - mov [ebp-20],00000000 { 0 }
KERNELBASE.ReadFile+3C- C7 45 E4 00000000 - mov [ebp-1C],00000000 { 0 }
KERNELBASE.ReadFile+43- 8B 75 14 - mov esi,[ebp+14]
KERNELBASE.ReadFile+46- 85 F6 - test esi,esi
KERNELBASE.ReadFile+48- 74 06 - je KERNELBASE.ReadFile+50
KERNELBASE.ReadFile+4A- C7 06 00000000 - mov [esi],00000000 { 0 }
KERNELBASE.ReadFile+50- 8B 5D 08 - mov ebx,[ebp+08]
KERNELBASE.ReadFile+53- 83 FB F4 - cmp ebx,-0C { 244 }
KERNELBASE.ReadFile+56- 0F83 60D30400 - jae KERNELBASE.InterlockedExchangeAdd+528C
KERNELBASE.ReadFile+5C- 8B 7D 18 - mov edi,[ebp+18]
KERNELBASE.ReadFile+5F- 85 FF - test edi,edi
KERNELBASE.ReadFile+61- 75 71 - jne KERNELBASE.ReadFile+D4
KERNELBASE.ReadFile+63- 57 - push edi
KERNELBASE.ReadFile+64- 57 - push edi
KERNELBASE.ReadFile+65- FF 75 10 - push [ebp+10]
KERNELBASE.ReadFile+68- FF 75 0C - push [ebp+0C]
KERNELBASE.ReadFile+6B- 8D 45 E0 - lea eax,[ebp-20]
KERNELBASE.ReadFile+6E- 50 - push eax
KERNELBASE.ReadFile+6F- 57 - push edi
KERNELBASE.ReadFile+70- 57 - push edi
KERNELBASE.ReadFile+71- 57 - push edi
KERNELBASE.ReadFile+72- 53 - push ebx
KERNELBASE.ReadFile+73- FF 15 5C66E076 - call dword ptr [KERNELBASE.dll+C665C] { ->ntdll.NtReadFile }
KERNELBASE.ReadFile+79- 8B C8 - mov ecx,eax
KERNELBASE.ReadFile+7B- 81 F9 03010000 - cmp ecx,00000103 { 259 }
KERNELBASE.ReadFile+81- 0F84 B9D30400 - je KERNELBASE.InterlockedExchangeAdd+5310
KERNELBASE.ReadFile+87- 85 C9 - test ecx,ecx
KERNELBASE.ReadFile+89- 0F88 380A0000 - js KERNELBASE.GetModuleHandleExW+277
KERNELBASE.ReadFile+8F- 85 F6 - test esi,esi
KERNELBASE.ReadFile+91- 74 05 - je KERNELBASE.ReadFile+98
KERNELBASE.ReadFile+93- 8B 45 E4 - mov eax,[ebp-1C]
KERNELBASE.ReadFile+96- 89 06 - mov [esi],eax
KERNELBASE.ReadFile+98- B8 01000000 - mov eax,00000001 { 1 }
KERNELBASE.ReadFile+9D- 8B 4D F0 - mov ecx,[ebp-10]
KERNELBASE.ReadFile+A0- 64 89 0D 00000000 - mov fs:[00000000],ecx { 0 }
KERNELBASE.ReadFile+A7- 59 - pop ecx
KERNELBASE.ReadFile+A8- 5F - pop edi
KERNELBASE.ReadFile+A9- 5E - pop esi
KERNELBASE.ReadFile+AA- 5B - pop ebx
KERNELBASE.ReadFile+AB- 8B E5 - mov esp,ebp
KERNELBASE.ReadFile+AD- 5D - pop ebp
KERNELBASE.ReadFile+AE- C2 1400 - ret 0014 { 20 }
So, the next level API which is lower down is NtReadFile which is located in ntdll.dll.
ntdll.NtReadFile - B8 05001A00 - mov eax,001A0005 { [0] }
ntdll.ZwReadFile+5- 64 FF 15 C0000000 - call fs:[000000C0]
ntdll.ZwReadFile+C- C2 2400 - ret 0024 { 36 }
ntdll.ZwReadFile+F- 90 - nop
This is an special type of call named syscall which then goes to the SSDT (System Service Descriptor Table) which has the low level APIs which situated in ring 0 which is not accessible directly via ring 3 other than syscall which are exposed.
So, sandboxes can use an range of hooking methods but most common are:
- Memory detours (JMP, PUSH RET and so on)
- Hooking IAT tables
- VEH (Unlikely, since big impact on performance)
So, just to know what going on is we would hook the NtReadFile
which would jump to our code and we have complete control over what is executed. For example, let's say your going to filter out any file named SteveEnix. In our trampoline function (Where the NtReadFile has been hooked and jumped to) we can read the parameters and decide to call it or not. So, it will look like this:
NTSYSAPI NTSTATUS NTAPI t_NtReadFile(
IN HANDLE FileHandle,
IN HANDLE Event OPTIONAL,
IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
IN PVOID ApcContext OPTIONAL,
OUT PIO_STATUS_BLOCK IoStatusBlock,
OUT PVOID Buffer,
IN ULONG Length,
IN PLARGE_INTEGER ByteOffset OPTIONAL,
IN PULONG Key OPTIONAL )
{
// Your check on buffer to filter files or whatever you want and return an error or call the original function
}
If the internal application has access to using VirtualProtect
API or the memory region is read and write then these hooks can easily be removed.
However, if you step down an level even deeper to the SSDT which on x64 has PatchGuard to prevent these hooks so generally sandboxes don't touch SSDT then you would need an driver to be loaded into system to be able to unpatch the SSDT or patch it with your own hook.