There was a spike in Explorer crashes that resulted in the instruction pointer out in the middle of nowhere.
0:000> r eax=00000001 ebx=008bf8aa ecx=77231cf3 edx=00000000 esi=008bf680 edi=008bf8a8 eip=7077c100 esp=008bf664 ebp=008bf678 iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010246 7077c100 ?? ???
Maybe the return address tells us something.
0:000> u poi esp 008bf6d4 test eax,eax 008bf6d6 je 008bf6b9 008bf6d8 xor edi,edi 008bf6da cmp dword ptr [esi+430h],edi
It’s strange that we’re executing from someplace that has no name. If you look closely, you’ll see that we are executing code from the stack: esp
is 008bf664
, so the code that went haywire is on the stack.
Who executes code from the stack?
Malware, that’s who.
Let’s see what this malware is trying to do.
Disassembling around the last known good code address gives us this:
008bf6c4 call dword ptr [esi+214h] 008bf6ca inc dword ptr [ebp+8] 008bf6cd push edi 008bf6ce call dword ptr [esi+210h] ; this called into space 008bf6d4 test eax,eax 008bf6d6 je 008bf6b9 008bf6d8 xor edi,edi 008bf6da cmp dword ptr [esi+430h],edi 008bf6e0 je 008bf70d
It looks like the payload stored function pointers at esi+210
and esi+214
. Let’s see what’s there. This is probably where the payload stashed all its call targets.
0:000> dps @esi+200 008bf880 1475ff71 008bf884 00000004 008bf888 76daecf0 kernel32!WaitForSingleObject 008bf88c 76daeb00 kernel32!CloseHandle 008bf890 7077c100 008bf894 76dada90 kernel32!SleepStub 008bf898 76db6a40 kernel32!ExitProcessImplementation 008bf89c 76daf140 kernel32!RemoveDirectoryW 008bf8a0 76da6e30 kernel32!GetLastErrorStub 008bf8a4 770d53f0 user32!ExitWindowsEx 008bf8a8 003a0043 008bf8ac 0050005c 008bf8b0 006f0072 008bf8b4 00720067 008bf8b8 006d0061
Yup, there’s a payload of function pointers here. It looks like this malware is going to wait for something, and then exit the process, or remove a directory, or exit Windows. Those bytes after user32!ExitWindowsEx
look like a Unicode string, so let’s dump them as a string:
0:000> du 008bf8a8 008bf8a8 "C:\Program Files\Contoso\contoso_update.exe"
Wait, what? It is trying to mess around with Contoso’s auto-updater?
Let’s take a look at more of the malware payload. Maybe we can figure out what it’s doing. It looks like it’s using esi
as its base of operations, so let’s disassemble from esi
.
008bf684 push ebp ; build stack frame 008bf685 mov ebp,esp 008bf687 push ebx ; save ebx 008bf688 push esi ; save esi 008bf689 mov esi,dword ptr [ebp+8] ; parameter 008bf68c push edi ; save edi 008bf68d push 0FFFFFFFFh ; INFINITE 008bf68f push dword ptr [esi+204h] ; data->hProcess 008bf695 lea ebx,[esi+22Ah] ; address of path + 2 008bf69b call dword ptr [esi+208h] ; WaitForSingleObject 008bf6a1 push dword ptr [esi+204h] ; data->hProcess 008bf6a7 call dword ptr [esi+20Ch] ; CloseHandle 008bf6ad and dword ptr [ebp+8],0 ; count = 0 008bf6b1 lea edi,[esi+228h] ; address of path 008bf6b7 jmp 008bf6cd ; enter loop 008bf6b9 cmp dword ptr [ebp+8],28h ; waited too long? 008bf6bd jge 008bf6d8 ; then stop 008bf6bf push 1F4h ; 500 008bf6c4 call dword ptr [esi+214h] ; Sleep 008bf6ca inc dword ptr [ebp+8] ; count++ 008bf6cd push edi ; path 008bf6ce call dword ptr [esi+210h] ; DeleteFile 008bf6d4 test eax,eax ; Q: Did it delete? 008bf6d6 je 008bf6b9 ; N: Loop and try again 008bf6d8 xor edi,edi 008bf6da cmp dword ptr [esi+430h],edi ; data->fRemoveDirectory? 008bf6e0 je 008bf70d ; N: Skip 008bf6e2 jmp 008bf6f0 ; Enter loop for trimming file name 008bf6e4 cmp ax,5Ch ; Q: Backslash? 008bf6e8 jne 008bf6ed ; N: Ignore 008bf6ea mov dword ptr [ebp+8],ebx ; Remember location of last backslash 008bf6ed add ebx,2 ; Move to character 008bf6f0 movzx eax,word ptr [ebx] ; Fetch next character 008bf6f3 cmp ax,di ; Q: End of string? 008bf6f6 jne 008bf6e4 ; N: Keep looking 008bf6f8 mov ecx,dword ptr [ebp+8] ; Get location of last backslash 008bf6fb xor eax,eax ; eax = 0 008bf6fd mov word ptr [ecx],ax ; Terminate string at last backslash 008bf700 lea eax,[esi+228h] ; Get path (now without file name) 008bf706 push eax ; Push address 008bf707 call dword ptr [esi+21Ch] ; RemoveDirectory 008bf70d cmp dword ptr [esi+434h],edi ; data->fExitWindows? 008bf713 je 008bf71e ; N: Skip 008bf715 push edi ; dwReason = 0 008bf716 push 12h ; EWX_REBOOT | EWX_FORCEIFHUNG 008bf718 call dword ptr [esi+224h] ; ExitWindowsEx 008bf71e push edi ; dwExitCode = 0 008bf71f call dword ptr [esi+218h] ; ExitProcess 008bf725 pop edi 008bf726 pop esi 008bf727 pop ebx 008bf728 pop ebp 008bf729 ret ; This code appears to be unused 008bf72a push ebp 008bf72b mov ebp,esp 008bf72d push esi 008bf72e mov esi,dword ptr [ebp+10h] 008bf731 test esi,esi 008bf733 jle 008bf746 ...
Reverse-compiling back to C, we have
struct Data { char code[0x0204]; HANDLE hProcess; DWORD (CALLBACK* WaitForSingleObject)(HANDLE, DWORD); BOOL (CALLBACK* CloseHandle)(HANDLE); DWORD (CALLBACK* MysteryFunction)(PCWSTR); void (CALLBACK* Sleep)(DWORD); void (CALLBACK* ExitProcess)(UINT); BOOL (CALLBACK* RemoveDirectoryW)(PCWSTR); DWORD (CALLBACK* GetLastError)(); BOOL (CALLBACK* ExitWindowsEx)(UINT, DWORD); wchar_t path[MAX_PATH]; BOOL fRemoveDirectory; BOOL fExitWindows; }; void Payload(Data* data) { // Wait for the process to exit data->WaitForSingleObject(data->hProcess, INFINITE); data->CloseHandle(data->hProcess); // Try up to 20 seconds to do something with the file for (int count = 0; !data->MysteryFunction(data->path) && count < 40; count++) { Sleep(500); } if (data->fRemoveDirectory) { PWSTR p = &data->path[1]; PWSTR lastBackslash = p; while (*p != L'\0') { if (*p == L'\\') lastBackslash = p; p++; } *lastBackslash = L'\0'; RemoveDirectoryW(data->path); } if (data->fExitWindows) { ExitWindowsEx(EWX_REBOOT | EWX_FORCEIFHUNG, 0); } }
Aha, this isn’t malware. This is an uninstaller!
The mystery function is almost certainly DeleteFileW
. It’s waiting for the main uninstaller to exit, so it can delete the binary.
There is a page on CodeProject that shows how to write a self-deleting file, and it seems that multiple companies have decided to use that code to implement their own uninstallers. (Whether they follow the licensing terms for that code I do not know.)
Okay, so why did we crash? What went wrong with DeleteFileW
?
According to the dump file, the spot where DeleteFileW
was supposed to be instead holds 7077c100
. This is a function pointer into some mystery DLL that isn’t loaded. How did that happen?
My guess is that the DeleteFileW
function was detoured in the Contoso uninstaller. When the uninstaller tried to built its table of useful functions, it ended up getting not the address of DeleteFileW
but the address of a detour. It then tried to call that detour from its payload, but since the detour is not installed in Explorer (or if it is, the detour is in some other location), it ended up calling into space.
Neither code injection nor detouring is officially supported. I can’t tell who did the detouring. Maybe somebody added a detour to the uninstaller, unaware that the uninstaller is going to inject a call to the detour into Explorer. Or maybe the detour was injected by anti-malware software. Or maybe the detour was injected by Windows’ own application compatibility layer. Whatever the reason, the result was a crash in Explorer.
Which means that people like me spend a lot of time studying these crashes to figure out what is going on, only to conclude that they were caused by other people abusing the system.
If you want to create a self-deleting binary, please don’t use code injection into somebody else’s process. Here’s a way to delete a binary and leave no trace:
Create a temporary file called cleanup.js
that goes like this:
var fso = new ActiveXObject("Scripting.FileSystemObject"); fso.DeleteFile("C:\\Users\\Name\\AppData\\Local\\Temp\\cleanup.js"); var path = "C:\\Program Files\\Contoso\\contoso_update.exe"; for (var count = 0; fso.FileExists(path) && count < 40; count++) { try { fso.DeleteFile(path); break; } catch (e) { } WSH.Sleep(500); }
This script deletes itself and then tries to delete contoso_update.exe
for 20 seconds. Run it with wscript cleanup.js
and let it do its thing. No code injection, no detours, all documented.
I haven’t used an uninstaller program in years. Probably not since I stopped using Windows. Seems like poor OS design is the cause of this.
I was thinking about such uninstaller recently. And came up with an idea: Uninstaller is a DLL, because running EXE cannot be deleted. That DLL gets loaded by rundll.exe. When invoked it allocates executable memory and copies (part of) itself over there. Possibly also allocates and copies both read-only and read-write sections of itself. Does pointer relocation fixups, jumps to the new code. Now the new code can unload the old self (the DLL to...
And then when there’s a problem with the code, there will be a spike of crashes in rundll.exe, and I will have to investigate them and come to the conclusion that they are coming from another “sufficiently advanced uninstaller”.
I actually did this. The 32 bit version builds a ROP chain and uses a single ASM instruction to enter it. All code frames are inside kernel32.dll (now kernelbase.dll) This works because in 32 bit, all arguments are on the stack so I can just return into the entry point of the next function.
The 64 bit version however can't do that, so the asm code is allocated with VirtualAlloc and looks like this:
```
...
I don’t like the idea of invoking wscript from uninstaller. Windows Script Host may be disabled on some hardened PC.
Under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Script Host\Settings, create a new DWORD value named “Enabled” and set the value data to “0”.
A pop up will show that “Windows Script Host access is disabled on this computer. Contact your administrator for details.”
If a jscript dependency is somehow a concern, you can fairly easily rewrite the script in batch. Then all you need is cmd.exe which everyone has.
A bit of IF EXISTS, a bit of DEL, a GOTO loop and label, and finally run cmd.exe with no window to keep things nice and tidy.
The only bit that could be trouble is there's no built in delay function. But it's fairly easy to find a command that implements...
“The only bit that could be trouble is there’s no built in delay function.”
No built-in delay function? Does the TIMEOUT command not work, for some reason?
Is there really no registry key or API to call, to have Windows cleanup the last bits, after custom uninstall process completes? I suppose I always assumed there was.. but I’ve never implemented an uninstaller, obvs.
MoveFileEx can, but it defers to next boot though. This is mostly useful for removing or replacing files that are in use at the time you’re trying to remove/replace them . Many installers or uninstallers use it for that purpose (any installer that asks you to reboot usually does so because it has done this, especially if you find the app in question does not work until you actually reboot).
Insane way for simple tasks, lol. Usually in my case I just call the cmd with an safe timeout to delete the remaining files and never got trouble.
Who needs JavaScript when you can have a self-deleting batch file? 🙂
https://stackoverflow.com/a/20333575/124386
Noone?
I’ve always used the age old method of the self deleting batch file to finally remove the uninstaller and then itself.
Write a batch file to the temp folder, do your work and as last line a del on itself, without(!) line break.
Voilá, everything gone, as intended.
Works a treat since Win 3.x.
Batch files can be disabled by Group Policy.
Could a process mark itself for deletion using SetFileInformationByHandle() with FileDispositionInfo? I have not tried this, but if it worked when the last handle for the process was closed the system would delete the file. The same as if the file were created/opened with FILE_DELETE_ON_CLOSE. I don't think this requires special permissions either, as long as you could delete the file if it wasn't busy then you should be able to mark...
If FileDispositionInfo or FILE_DELETE_ON_CLOSE worked on a running exe file then neither this blog post nor the injection code would exist in the first place.
CreateProcess does not open the file with FILE_SHARE_DELETE to stop you from doing this in the name of compatibility (with DOS?, Windows 3.x?).
You can create an executable file with the flag set to delete it on close, and then before closing the last handle to it, you can execute it. Then you can exit your own process, and that executable you created will be deleted once it exits too since that will close the last handle.
In my experience most IT groups block VBS on machines because of security issues.
Personally I always found it easier to just start a new batch script (or perhaps PS these days) that polled the desired file into it could be deleted. But that is only for cases where I needed the file deleted ASAP.
I think the preferred approach is to use `PendingFileRenameOperations` to schedule the file for removal on restart. If the user opts to...
PendingFileRenameOperations (MoveFileEx) only works for administrators since it needs to write to HKLM, not usable for things installed in FOLDERID_UserProgramFiles.
As does most other approaches that need to clean up files. Since there was no contextual information about user privileges the solution I used to use (pre per-user installs) is sufficient. Note that in modern IT systems you also cannot run scripts of any sort without being an admin (and even then) and JS doesn't work outside the sandbox unless you have Java installed (which, again most computers won't for security reasons). So the solution...
Not sure what you mean by “JS doesn’t work outside the sandbox unless you have Java installed.” The wscript program happily runs JS outside any sandbox. Go ahead and create a FileSystemObject like the one in the article. (And Java is completely unrelated to Javascript.)