Get the top HN stories in your inbox every day.
8organicbits
xg15
At least the author seems to agree with Raymond Chan on the similarities between his approach and malware...
> shellcode is the technical term (in security circles) for binary machine code that is typically used in exploits as the payload. Here's a quick and dirty way of generating the shellcode from the obj file generated when you compile your source files. In our case, we are interested in whipping the shellcode up for the remote_thread routine. Here's what you've got to do:
The whole article has the vibes of some questionable DIY blog along the lines of "Your house is infested by vermin? Here is an easy way to get rid of them using a small, homebuilt neutron bomb!"
lloydatkinson
Unfortunatelt CodeProject is full of code like this.
taway1237
Funnily enough, real malware does this correctly. Usually by just ShellExecing "ping -n 3 127.0.0.1 >nul" followed by "del" - no temporary file needed.
sim7c00
The author says the binary looks like malware because it self-deletes, sleeps and touches this uninstaller thing. But the script he proposes, which would be triggered by this same thing, does the same. I am ignoring the injection thing since he guesses at it (likely correct) and also because, lots of things inject into processes without being malware. (monitoring stuff like AV etc.) Additionally, binaries which terminate with a run some script via a scripthost... this could just as well be some malware? (stage1 malware downloads script, runs it via scripthost?)
my question(s): How is the proposed solution better than the original thing? Isn't this a case of using bad heuristics to determine maliciousness?
In the end, he goes a bit further, and sees its non-malicious. So, with a more elaborate rule or heuristic, wouldn't it be clear its not malicious?
kazinator
The .js script isn't injecting code into another program in order to deletee itself; it is deleeting itself directly.
It can do that because, I'm guessing, the file isn't open; the run-time isn't executing instructions from that file. The file was read, the content compiled into memory, and closed.
The script is deleting its source code, not itself. What actually deletes the script itself is the garbage collector in the run-time. Once the fso.Deletefile call and the for loop are executed, they are no longer reachable and so they are garbage. If there is a way for the "var fso" and "var path" variables to go out of scope, they become garbage also.
A binary executable is mapped into memory while the process is running it. In Windows, an open file cannot be deleted.
But, even on Windows, a prog.exe could delete the prog.c source code it was compiled from, right? Same thing, sort of.
masfuerte
Being pedantic, you can delete open files on Windows if you open them with FILE_SHARE_DELETE.
sim7c00
The script doesn't inject. But a lot of malware downloads a script and runs it, so you'd hit another rule.
kazinator
The way I understand it, the uninstaller program that wants to delete itself doesn't have to download the script from anywhere; it generates the script out to a file.
dmazzoni
Is the behavior that a running .js script is fully loaded into memory and the file doesn't need to exist documented, supported behavior?
What if, hypothetically, the system was suspended in the middle of script execution, and the resume function was designed to reload the script from disk?
It just feels like a different hack to me.
Also - trying 20 times and pausing 500 ms seems wasteful. What are the chances that it's going to succeed a subsequent try if it fails the first try? Why not catch the error message and only retry for errors that have a plausible chance of succeeding if you retry?
Arainach
There is never a good reason to inject code into another process - particularly a system process. At the point at which you believe this is necessary you are several layers of hackiness deep and should go find a beverage and think over what your actual goal is.
As a metaphor: you find the instructions to sweep your floor cumbersome so you reprogram your neighbor's Roomba to come clean your floor. Sure, it may well go back to their house and no harm done, but it's hacky, socially unacceptable, and no matter how hard your broom is to use it's not OK.
omnicognate
> Is the behavior that a running .js script is fully loaded into memory and the file doesn't need to exist documented, supported behavior?
If Raymond Chen says to rely on it then yes.
slaymaker1907
Even if it doesn't, I think you could do something similar by just spawning a shell (command prompt) that executes a small script trying to delete the file. You just have to take care to make sure the process is detached from the original one and then let the spawning process terminate to release the lock on the executable. PowerShell could also work, but I know it is pretty restricted in a lot of environments. These completely avoid any intermediate file.
I think the retry is necessary because if you launch "wscript cleanup.js" from the process that wants to be cleaned up, you then need to wait for the spawning process to finish executing. I agree if it fails after 20 times, you should probably spawn an alert or something letting the user know that uninstall failed. There are also so many random processes that might take a reference on the file like antivirus in Windows so just spamming retry will help wait that stuff out (this problem does not exist on Linux since Linux generally just does garbage collection which has the downside of not keeping specific paths around, just file inodes).
chrchang523
Agree, I'm shocked at how ugly the recommended alternative is. This does not make MS look good.
ghostpepper
Once you move past comparing hashes against known malware (by definition useless against novel malware), and the slightly more complex matching of specific binary strings, detecting malware with "shitty heuristics" is basically all we've got.
Companies that buy AV/EDR products expect them to detect unwanted behaviour while allowing any sort of weird, hacky, abuses of the system that they rely upon for their business.
It's never been entirely clear to me why windows provides such a rich interface for one process to inject and start executing code in the address space of another but IMHO I want to know when this is happening even when it's done by a "legitimate" uninstaller.
sim7c00
The first point is true i admit. There are very complex and good ways to identify stuff but those perform so bad they cannot be used in practice.
AV/EDR products do try to prevent a lot of stuff. They can 'generically' block things like injection etc. by 'hooking all the things' and injecting into everything (yes, yuck :D and still kind of heuristic based i admit!) to make certain sections read only or remove executable mappings etc. (got/plt/stack/...)
Linux or more specifically ELF files also have an easy vector to allow injection by having a dynamic table entry for debugging purposes which can be trivially overwritten for example. "ibc.so" :(). I'm not sure anyone uses that entry validly... especially since there's better/less awkward debugging interfaces than injecting a debugger DLL into something :') at least in x86/64 Linux land. (ELFShell sure was fun tho!)
monocasa
If you're talking about LD_PRELOAD, I used it for an integration test suite of low level system components.
PcChip
>How is the proposed solution better than the original thing?
I'm only assuming here, but maybe because it won't crash explorer and it's just a few lines of self-documenting code?
sim7c00
Haha, well fair enough the crash is bad indeed, good point! This isn't intended behavior though and presumably, it doesn't crash on in cases of this technique being implemented in uninstallers. (a bit of a guess i admit!)
ChrisSD
The fact it injects into another process means they can't know if it'll crash or not. You're just one Explorer update away from things changing enough for the hack to crash it.
I guess they could do this more robustly. I.e pause the entire explorer process, save all its state, remotely allocate new memory to inject their code, remotely create a new thread, run only that thread using the injected code, restore all the process' state and finally start it running where it left off. A script would be easier though.
Arainach
You find the instructions to sweep your floor cumbersome so you reprogram your neighbor's Roomba to come clean your floor. Sure, it may well go back to their house and no harm done, but it's hacky, socially unacceptable, and no matter how hard your broom is to use it's not OK.
S3rM5Y5
The technique could have been made a little more robust by calling GetProcAddress to get the function pointers in Explorer's context, assuming GetProcAddress wasn't itself detoured.
some_random
Cybersecurity is pretty much all bad heuristics with the belief that if you use enough of them they average out to an ok determination of maliciousness. It works alright, sometimes.
thombat
It's more about how it does it: injecting executable code directly into the stack so that some other code unwittingly transfers control to it. Stack-smashing is a lot more malware-ISH than a few lines of shell script.
sim7c00
They inject something into Explorer. I would assume that to be some DLL that is injected?
---- 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. ----
I'd think the anti-malware guess here would be correct, and that (DLL) injection was stopped, and thus some crash happened. Thanks for your reply.
The DLL would execute something from its stack so it can be somewhat dynamic (perhaps some path or something is generated before the injection or so - really little to go on here...) and not need to make a heap allocation within Explorer.exe. (this is perhaps a bit too much to assume idk.)
Thanks for your insights!
darkclouds
>I can’t tell who did the detouring
I was thinking hmm and so it continues that game of whack a mole.
If a new OS could be designed from scratch, there must be a way to prevent this sort of stuff.
oefrha
Maybe it’s less likely to be flagged or interfered with by antivirus? Antivirus uses all kinds of shitty heuristics, seeing that my Go executables built with -ldflags="-H windowsgui" are flagged as malware by Windows Defender and co. all the time. It’s maddening.
sim7c00
that's fair enough. perhaps they can more easily in AV land make some kind of signature that whitelists this self-deletion javascript method because the script is more readable. though i'd expect a good AV to be at least worried if some executable on my system runs a script file via a scripthost.
blackpill0w
Why do Windows programs need special installers/uninstallers? Why isn't this handled by Windows itself?
EvanAnderson
Windows has had an installer as an OS component since the late 90s (called Windows Installer). As a sysadmin I'd prefer apps use it. Many application developers do not. It's maddening. (Doubly so when Microsoft themselves don't use it-- newer versions of Office, Teams, etc. Microsoft suffers from too much NIH.)
I get unattended installs and uninstalls "for free" when well-behaved applications use Windows Installer. Patching is included, too. Customizing installations is fairly straightforward.
On the developer side it has historically used a very quirky proprietary file format (MSI) with a fairly steep learning curve and a ton of "tribal knowledge" required to make it work for all but the most trivial cases. (Though, to be fair, most installs are the trivial case-- copy some files, throw some stuff into the registry, make some shortcuts.)
Worse, it allows for arbitrary code execution ("Custom Actions"), at which point all bets are off re: unattended installs, removal, etc. Some Windows Installer packages are just "wrapped" EXEs (Google Chrome, for example).
I've packaged a ton of 3rd party software as Windows Installer packages. It's an ugly system with lots of warts and legacy crap, but if you need to load an application on a large number of Windows PCs reliably unattended it's decently fit for purpose.
There is reasonable free and libre tooling to generate MSI packages from plain text source (the WiX toolkit) and it can be used in a CI pipeline.
RajT88
Can confirm. I would be considered by most to have been a Windows Installer expert at one point. Installshield / Wix / Whatever else.
It is intentionally obtuse at times (MSIFileHash table uses rearranged MD5 hashes for example), and also many features made sense for the late 90's/Early 2000's era where bandwidth was low and connectivity limited, and lots of stuff was distributed on CD's. The look on people's faces when you explain advertisement to them the first time... How their unrelated app can get stuck in a loop of repair for a piece of unrelated software...
It was deprecated by the newer AppX/MSIx/AppV format which uses sandboxes, binary chunks/streaming and no executable code to install stuff.
For my own desktop computing, I prefer MSI packages because I prefer having control post-install to tinker with things if I feel like it. Also, I have the skillset to modify the installer to my whims if I so choose.
ripley12
> It was deprecated by the newer AppX/MSIx/AppV format which uses sandboxes, binary chunks/streaming and no executable code to install stuff.
I can offer a little perspective on MSIX, having devoted months of my life to it in a past job.
MSIX is nearly unusable outside the Store. It will work in a tightly controlled environment, but when you try to deploy it to a wide variety of users you will run into 1) unhelpful errors that basically can't be diagnosed, 2) enterprise environments that cannot/will not allow MSIX installs. I get the impression that the MSIX team is uninterested in solving either of those issues.
It's not a coincidence that virtually no first-party teams use MSIX to install their product outside the Store. Office tried for a while and eventually gave up.
Despite all that, there are still a few people at MS who will tell you that MSIX is the future. I don't really understand it and I assume there's a weird political reason for this.
jjcoffman
Orca ftw
mariusmg
> with a fairly steep learning curve and a ton of "tribal knowledge"
Yes, people preffer to debug their own code rather than spend shitload of time to understand Wix/MSI.
Microsoft deciding early on to not produce low cost tools for Windows Installer also didn't helped with the adoption.
delfinom
The joke is, Microsoft devs even now use NSIS for things like VSCode rather than deal with MSIs lol
But there is the modern implementation of AppX Bundles which was later extended to create MSIX which allows app distribution without the windows store. There are still drawbacks to using MSIX usually because you want to touch Windows in ways you can't inside the sandbox.
sebazzz
> Doubly so when Microsoft themselves don't use it
Often, as you mentioned, Windows Installer packages are wrapped by an executable (in WiX this is called a "bundle" because you may also choose to add redistributables like the C++ runtime).
However, what you see in installations like SQL Server, Office and Visual Studio is that the installers are bundles as well - of a large amount of MSIs that need to be installed and uninstalled in a specific order. A single Microsoft Installer package is transactional and can be rolled back on failure, but bundles are not as well defined and left open to the implementation of the bundle. Windows Installer does not reach beyond the single msi package.
mavhc
As soon as Office 2007 didn't use MSI the format was doomed.
I assume the Here in NIH refers to an individual team, not MS as a whole.
Teams is entirely NIH https://github.com/Squirrel/Squirrel.Windows for updates to the Electron app.
I would use winget, but MS made it weirdly hard to run as a script on multiple computers, it installs per user, because... who knows.
So still using chocolatey
WorldMaker
To be fair, Squirrel came from GitHub and early Electron before Microsoft bought GitHub, so it wasn't Microsoft's NIH that built Squirrel originally.
crabbone
I had a friend who worked for a company that specialized in Web browser bars and MSIs. In other words, they were a shop to put all kinds of malware into these things. It was a viable business model for a company of something like 50 people.
The whole story and ideas put into Windows installing programs are a stupid joke. It's designed by project managers who have no idea what they are doing and no imagination and is executed by the cheapest least motivated programmers from South Asia Microsoft can possibly find.
A lot of people who do useful stuff for Windows try to stay away from as much of Microsoft's stuff as possible, and integrate it as little as possible with anything else coming from the system because it's just maddening how horrible everything there is.
elzbardico
A lot of weird things in windows are reflections of the gestalt in the 90's and early 2000. People went all in on all sort of OOP-derived weirdness, like CORBA, COM.
"Plain-Text files for configuration? what do you think we are? savages? no, we need a hierarchical registry! every serious program must use some opaque binary format to store stuff!" seem to be the general animus at that time. Nowadays, even if you really hated the idea of a text files for configuration in your home direction, people would probably do something more straight-forward like using a SQLite db.
HdS84
Wix is part of the problem. It's basically making money for the developers who offer consultancy for it.
Therefore the documentation is poor , like the absolute worst I've ever seen. Opening issues for doc issues never results in anything. Pointing out UX issues is usually shot down. Finally, until this year you needed .net 2 installed to build it, which does not play well with windows docker.
highwaylights
I don't think any major desktop OS handles this well.
I suspect the final form for software installation is probably where iOS and Android are going in the EU, where there's a single means of installing software to the device so that everything can be sandboxed properly, but the acquisition/update process can be pointed to a URL/Store that the user has pre-approved.
macOS comes pretty close to what I'd ideally want in an OS with regards to installation - independent packages that are certified/notarised, but I'd like to see the OS allow for user-specified authorities beyond just Apple. That being said, I'm not sure I'd ever use them as it's part of what I'm paying Apple for, I'm really thinking more of Linux there.
A kind of flatpak/snap approach, but that has signing of the package and centralised management of the permissions for the sandbox at an OS level would be ideal in my view. That way it's still free-as-in-speech as the user can specify which notarisation authority to use (or none at all).
I really don't understand why seperate programs are handling removing their mother program in 2023, that's registry spaghetti messy.
irusensei
Everyone is pointing at Windows but there are still installer software on MacOS. Normally crusty old corpoware like Citrix that needs to extend its tentacles to the whole system.
On Unix/Linux land the prevalence of pipe curl to bash type installers is not much different.
I normally keep both types away from my computers.
Matl
> On Unix/Linux land the prevalence of pipe curl to bash type installers is not much different.
This is a problem but only if you install software on Linux by manually going to the project page and copy-pasting whatever curl they have there, I think the difference is that mostly you're encouraged to go the package manager route, whereas on windows downloading .exes directly (ala the curl example) is the norm.
madeofpalk
Only installers I’ve seen are the .installer bundles, which leave behind a manifest for automated uninstalling.
bombolo
[dead]
jjgreen
On Unix/Linux land the prevalence of pipe curl to bash type installers is not much different.
True, but saying so will likely to earn you downvotes from those committed to this unhygenic practice ...
tobias3
You are basically describing what Windows has as appx/msix. The decentrialized notarization authorities are the code signing certificate providers.
highwaylights
I had not seen this, but it absolutely does (on the surface) seem like a solution to this problem. Thanks!
I’d need to educate myself a bit more in terms of whether there are third-party authorities beyond Microsoft for the packages.
Found this introductory video for anyone else interested:
https://www.youtube.com/watch?v=phrD081sMWc
Note: I didn’t intend the Surface pun above, but it happened and we can all be glad that it did.
mschuster91
> macOS comes pretty close to what I'd ideally want in an OS with regards to installation - independent packages that are certified/notarised, but I'd like to see the OS allow for user-specified authorities beyond just Apple.
It's easy to run unsigned binaries/app packages on macOS: right click on the .app, hold down Option, then click Open and confirm the warning.
tmpX7dMeXU
That is not a user-specified authority.
gjsman-1000
Or just run `sudo spctl --master-disable` one time; and it will change the allowed app sources to the invisible "Anywhere" option.
alerighi
> I suspect the final form for software installation is probably where iOS and Android are going in the EU, where there's a single means of installing software to the device so that everything can be sandboxed properly, but the acquisition/update process can be pointed to a URL/Store that the user has pre-approved.
Basically how Linux distributions works since the beginning. Tough at the start the installation source was not remote but a CD-ROM things didn't change.
You have a repository of packages (that can be on a local source as a CD or remote source such as an HTTP/FTP server), that have some sort of signature (on Linux usually the pagkage is signed with GPG) with some keys that the user trusts (and the default are installed on the system), and a software that allows to install, uninstall and update the packages.
Android/iOS arrived later, but they didn't invent anything.
efreak
Android/iOS didn't invent this, no, however you're missing the sandbox part. Most Linux package managers don't sandbox anything.
brap
iOS is the gold standard IMO. Apps are sandboxed, can only interact with the outside world via APIs (that the user needs to approve), one click uninstall and it’s all gone without a trace (at least in theory). Love it.
fomine3
I think Android does it better with third party store and sideload support. It seems that iOS depends some security to their own the AppStore. (example: disallow dynamic code generation like JIT)
BoppreH
How could Windows handle it by itself?
If it provides a framework for installers/uninstallers, it'll be fighting the inertia of decades of legacy software, programmer habits, and old tutorials.
If it tracks file ownership by program, it might accidentally delete user files. How would it differentiate between a VSCode extension that should be uninstalled, and a binary compiled with VSCode for a user project? A false positive could be catastrophic.
If it restricts what programs can do to accurately track file ownership, you end up with Android. Which is fantastic for security, but is a royal pain in the ass for all parties:
- The app developers have to jump through hoops for the simplest actions, and rewrite most of their code in the new style.
- The operating system has to implement a ton of scaffolding, like permissions-restricted file pickers and ways to hand off "intents" between applications.
- The user is faced with confusing dialogs, and software with seemingly arbitrary limitations.
In the age of shared runtimes, auto-updaters, extension marketplaces, and JIT compilers, managing installed applications is harder than ever.
Edit: the answer above applies only to Windows, because of its baggage. Linux'es are in a much better position, for example, though their solution is still not perfect.
Tuna-Fish
The same way any linux distro does?
Define a separate directory for program installations, that user processes cannot write to. Only program that can do so is the package manager, which other programs can call to install packages. Uninstall removes everything related to a program from this directory.
> In the age of shared runtimes, auto-updaters, extension marketplaces, and JIT compilers, managing installed applications is harder than ever.
The only reason these make things hard is that windows lacks any facility to deal with them. Solutions going forward: Outright ban having your own auto-updater, to auto-update you register your program and where to update it from with the package manager. Shared runtimes are trivial for package managers to handle, it's just a package that many other ones depend on. Extensions can be handled as packages.
jraph
I agree with you, now for completeness I should mention that Linux package formats usually allow packagers to provide arbitrary pre- and post- install shell scripts ran as root.
(which means that if you don't trust a provider, not only it's not safe to run the program, but it's also unsafe to install it)
tssva
> The same way any linux distro does?
I'm going to assume you are talking about rpm and deb packages since they are still currently the dominant installation packages on Linux.
> Define a separate directory for program installations, that user processes cannot write to. Only program that can do so is the package manager, which other programs can call to install packages.
Windows does this. Programs are installed in directories under "C:\Program Files" which is only writable with elevated system rights.
> Uninstall removes everything related to a program from this directory.
rpm and debs don't install all the files needed for a program in a single directory. They are scattershot all over the file system and in many of these directories comingled with files from other programs. Windows comes closer than Linux in this regard since it does create the directory under "C:\Progtam Files" which while unfortunately doesn't always contain all the required files usually contains the vast majority.
mastax
This is exactly how AppX/MSIX packages work, with C:\Program Files\WindowsApps (by default) being pretty substantially locked down. They even use filesystem/registry virtualization by default to isolate packages even further from each other. They also have solutions for framework packages and extensions though I haven't tried those out and suspect they have annoying practical limitations around edge cases.
Of course, a decade later almost nobody uses those because they botched the rollout by limiting AppX to the Microsoft Store and an entirely new poorly documented and very restrictive set of windows APIs and app frameworks. They've made huge progress on all of those problems with MSIX to the point that it's a reasonably good and easy to use choice for most apps with some neat benefits like updates only downloading the changes between versions. Of course if your app pushes the boundaries of the sandbox or capabilities or runs into a bug it becomes a huge pain.
BoppreH
That was the first option, "provides a framework for installers/uninstallers".
But what would you do with the millions of existing programs, most unmaintained? And what about programs with strong opinions on update schedules, or built-in extension marketplaces?
It's easy to solve this problem if your first step is "replace every program".
gruez
>The same way any linux distro does? >Define a separate directory for program installations, that user processes cannot write to.
What about /usr/local/bin? Isn't that specifically for putting non package manager binaries into?
the_third_wave
> How could Windows handle it by itself?
In the same way 'Linux' (in the widest sense of the term, i.e. Linux distributions like Debian) handles this. User data is not touched by the (un)installer, configuration files are checked for changes from the package default and left alone unless explicitly purged. Files which do not belong to any package are left alone as well so that binary compiled with VSCode for a user project will not be touched:
warning: while removing directory /splurge/blargle/buzz not empty so not removed
This has worked fine for decades and is one of the areas where those Linux distributions were and are ahead of the competition. It works fine because the package manager has clearly delineated responsibilities and does (or rather should) not go outside of those. Do not expect the package manager to clean up your home directory for you, that is not part of its duty.> In the age of shared runtimes, auto-updaters, extension marketplaces, and JIT compilers, managing installed applications is harder than ever.
Most auto-updaters should be disabled on systems with functioning package management - Thanks Firefox but I'll update my browser through either the package manager as I prefer my executables to be read-only for users.
Some packages - the whole Javascript rats' nest being a good example - move too fast to be usefully packaged by volunteer maintainers so those are relegated to a different area which is not touched by the package manager. Other packages - anything Python fits here - are such a tangled mess of mutually incompatible versioned spaghetti that they are hard to fit inside the idiom of package managers so they get their own treatment - python -m venv ... etc. These are the exceptions to the rule that package management can be made to work well. By keeping those exceptions in areas where the package manager does not go - e.g. your home directory - the two can and do coexist without problems.
gwbas1c
It's called MSI, and it's been in Windows for 20 years.
The issue is that MSI is very buggy when handling explorer extensions. If you're not careful, when you uninstall it'll prompt you to close explorer.
(I know because I shipped a product that installed via MSI and had an explorer plugin. The installer issues were more complicated than the plugin.)
In this case, the issue is that when explorer loads a plugin, it keeps an open file handle to the dll. This gives the installer two options: Restart explorer.exe, or somehow finish uninstalling when explorer.exe terminates.
The product that I shipped just restarted explorer.exe.
bayindirh
Oh. I thought MSI and WinGet (sorry, AppGet in fact) designed to solve these problems.
daemin
It allows you to install applications from any source, not only the official store.
It allows for a variety of installers to exist with different features for different use cases.
It allows you to install the application in any location you choose.
It allows for portable installations and to run software just copied from other sources.
damentz
What is "it"?
daemin
Special installers / uninstallers and also the ability to install and run things outside the official OS store.
remram
I guess "special installers/uninstallers"
hilbert42
In principle I have no objection with those options as I've had to use all of them given the nature of the Windows ecosystem.
The trouble is that MS never paid much attention to tracking and cleaning up after installations or after uninstallers has finished. Often this doesn't matter but when something seriously goes wrong untangling the mess can be almost impossible, it's often easier to reinstall Windows and usually much quicker (that's if one has a simple installation).
Unfortunately, my installations aren't simple so I take snapshots at various stages of the installion—stage-1 raw install with all drivers, stage-2 essential utilities, and so on. By stage-4, I have a basic working system with most of my programs. Come the inevitable Windows stuff-up I reinstall from a backup image, it's much quicker than starting from scratch.
Between those major backups, I use the registry backup utility ERUNT, it not only takes registry snapshots on demand but also automatically backs up the registry on a daily basis. This, I'd venture, is the most important utility I've ever put on a Windows computer, I cannot recall how many times it's gotten me out of trouble.
Just several days ago I had a problem reinstalling an update to a corrupted Java jre/runtime. Nothing I did would make the installer install as the earlier installation was not fully uninstalled, thus log files etc. weren't a help.
In the end I had to delete the program dir and other Java files I could find, same with registry entries. As expected, this didn't work, as I hadn't found everything.
Knowing the previous version number of Java I did a string search across the OS and found another half dozen or so Java files. Retried the install again and it still failed. I then ran ERUNT which replaced the registry with an earlier pre-Java one and the install now worked. This still meant that some programs that were added later, LibreOffice for example, had to be reinstalled to update the registry.
If I hadn't had ERUNT installed I'd have had to go back to reinstalling an earlier partition backup. And if I'd not had those then I'd have been in real trouble.
That's the short version. Fact is, Windows is an unmitigated mess when it comes to installations. Why can't I force an installer to complete even with faults? Why doesn't Windows remember exactly what happens during an installation so it can be easily undone?
_
Edit: if you've never used ERUNT and decide to do so, always ensure you shut Windows down and restart it after installing a backup registry before you do anything else—that's in addition to the mandatory reboot required to install the backup.
You may have multiple registry backups and decide the version you've just loaded wasn't the one you want. Loading another without this additional reboot [refresh] will blue-screen the O/S. You'll then have to install the backup manually and that's very messy.
mike_hearn
It is, these days. Windows 10 onwards has a native package format called MSIX that somewhat resembles packages on Linux. They're special zips containing an XML file that declares how the software should be integrated into the OS (start menu, commands on the PATH, file associations etc). Windows takes care of installation, update and uninstallation.
The system is great, in theory. In practice adoption has been held back by the fact that it was originally only for UWP apps which almost nobody writes, and also only for the MS Store. These days you can use it for Win32 apps outside the store but then you will hit bugs in Windows. And packages must be signed.
Still, the feature set is pretty great if you can make it work. For example you can get Chrome-style updates where Windows will keep the app fresh in the background even if it's not running. And it will share files on disk between apps if they're the same, avoid downloading them, do delta updates and more. It also tracks all the files your app writes to disk outside of the user's home directory so they can be cleanly uninstalled, without needing any custom uninstaller logic.
One interesting aspect of the format is that because it's a "special" (read: weird) kind of zip, you can make them on non-Windows platforms. Not using any normal zip tool of course, oh no, that would be too easy. You can only extract them using normal zip tools. But if you write your own zip library you can create them.
A couple of years ago I sat down to write a tool that would let anyone ship apps to Win/Mac/Linux in one command from whatever OS they liked, no harder than copying a website to a server. I learned about MSIX and decided to make this package format. It took us a while to work around all the weird bugs in Windows that only show up on some machines and not others for no explicable reason, but it's stable now and it works pretty well. For example you can take some HTML and JS files, write a 5 line config file pointing at those files, run one command and now you have a download page pointing to fully signed (or self signed) self-updating Windows, Mac and Linux Electron app. Or a JVM app. Or a Flutter app. Or any kind of app, really! Also IT departments love it because, well, it's a real package format and not an installer.
Writing more about this tech has been on my todo list for a while, but I have now published something about the delta update scheme it uses which is based on block maps, it's somewhat unusual (a bit Flatpak like):
https://hydraulic.dev/blog/20-deltas-diffed.html
The tool is free to download, and free for open source projects if anyone is wanting to ship stuff to Windows without installers:
jahav
> For example you can get Chrome-style updates where Windows will keep the app fresh in the background even if it's not running
Considering the ability to update itself is a requirement of Cyber Resilience Act in EU, I foresee a big uptick in usage (and app stores usage of course).
rootw0rm
that's a cool project, will definitely try it out later
GuB-42
Besides the "special uninstaller" thing. One of the things I hate the most with Windows filesystem management compared to Unix-like OSes.
On Windows, opening a file locks it. So you can't delete a program that is running, you will get an error. It means of course that an executable can't delete itself without resorting to ugly tricks like the one mentioned in the article. That's also why you get all these annoying "in use" popups.
On Unix, files (more precisely: directory entries) are just reference-counted pointers to the actual data (inode on Linux), removing a file is never a problem: remove the pointer, decrement the reference counter. If the file wasn't in use or referenced from elsewhere, the counter will go to zero and the actual data will be deleted. If the file is in use, for example because it is an executable that is running, the file will disappear from the directory, but the data will only be deleted when it stops being in use, in this case, when the executable process terminates. So you can write your uninstaller in the most straightforward way possible and it will do as expected.
EustassKid
I feel like this is some stupid question but aren't exexutables and their libraries loaded to RAM? If yes then why can't it just delete itself (from disk)?
GuB-42
I don't know the details but I think executable files are mapped into memory, and needed sections are loaded on demand. In case the system is low on RAM, little used sections can be evicted, to be reloaded the next time they are needed. This requires the file to be present on disk.
tetris11
One thing I like about Linux package managers is that you can query any file to see which package owns it. How does Windows not track this?
yankput
Except they all leave files everywhere in ~, ~/.cache, ~/.config, ~/.whatevertheyfeellike
mijoharas
The ~/.whatevertheyfeellike is an antipattern (that is annoying) but the others are well defined in the xdg_desktop spec[0].
Personally I appreciate knowing where the config/cache for each application is. (Though it does annoy me when programs don't follow this as in your third example)
[0] https://specifications.freedesktop.org/basedir-spec/basedir-...
parchley
Those files are user data, not part of the software package.
prmoustache
That is not part of the software itself so it is still correctly installed/uninstalled.
Now I believe all software should have a manpage, dialog and a cli argument that describes where all the files[1] generated by default go but that is another subject.
[1] cache, config and even default save
goodpoint
That's a feature so that users can keep configuration files and even move them across systems.
devnullbrain
Try opening C:\Users\%USERNAME%\Documents\My Games
mike_hearn
MSIX packaged apps do support this, Windows redirects file writes outside of home dirs and other user locations to a package-specific directory that's overlayed back onto the system so the app thinks it's writing to wherever, but it's actually a package-private location.
chimprich
> you can query any file to see which package owns it
Presumably you mean something like using dpkg/apt for a Debian-style system?
I think that only works if a file is actually installed from within the framework. As soon as you've installed a file via npm, flatpak, pip, snap, specialist plug-in, standalone binary, that ancient program you had to install by hand, or one of the other squillions of ways of getting an executable program, you're out of luck and have to figure it out manually.
blackpill0w
Ok, I see what you're saying here, still, Linux's way is better, I'd rather have my system cluttered with useless files of deleted programs than be exploited because of something that was solved decades ago.
gwbas1c
> Why do Windows programs need special installers/uninstallers?
This is supposed to happen using MSI-based installers. It's a windows component.
> Why isn't this handled by Windows itself?
Now, here's where things get tricky.
In the article, the issue is an explorer plugin. MSI is notoriously buggy with installing and uninstalling explorer plugins. If you don't jump through hoops, your installer will have a bug where it prompts the user to close Explorer.exe.
I know because I shipped a product with an explorer plugin. The installer was always a thorn in our side; and the workarounds, ect, that we had to do to install / uninstall / delete our plugin were more complicated than the plugin itself.
turndown
I had never heard of detours before, but I guess it isn’t any different that a good old fashioned LD_PRELOAD
kevingadd
it's a little more general, I think, since one common use case for it is to use it on your own process in order to intercept calls to stdlib/OS code from libraries you don't control.
For example, in the bad old days I used detours to virtualize the windows registry so that I could do "fake installs" of COM components inside of a VB6 app, allowing it to run without an install and without administrator permissions. This worked by detouring all the Win32 registry APIs, which was sufficient to intercept registry accesses performed by the COM infrastructure.
wahern
> it's a little more general, I think, since one common use case for it is to use it on your own process in order to intercept calls to stdlib/OS code from libraries you don't control.
This capability is intrinsic to how ELF linking works. The main application or even any library can interpose a libc function just by defining and exporting a function with the same name, and that definition will be preferentially linked in both the main application and all subsequently loaded dynamic libraries and modules. Your definition can then use dlsym(RTLD_NEXT, "foo") to obtain a function pointer to the next definition, which would normally be libc itself but may be from another library. A running application could actually have several implementations of a function, all proxying calls onward until the terminal (usually libc) implementation.
Basically, the way ELF linking works by default is that the first definition loaded is the preferred global symbol used to satisfy any symbol dependency with that name. It follows that there's normally a singular global symbol table. Though there are features and extensions that can be explicitly used to get different behaviors.
There's nothing magical about LD_PRELOAD within the context of ELF linking. LD_PRELOAD support in the linker (which is the first bit of code the kernel loads on exec(2)) is very simple; all the linker does is load the specified libraries first, even before the main application, so symbols exported therein become the initial and therefore default definition for satisfying subsequent symbol dependencies, including in the main application binary, and even if the main application binary also defines and exports those symbols.
All of this is basically the exact opposite behavior of how PE linking works on Windows, for better and worse--depending on your disposition and problems at hand.
Also note that all of this is different than so-called "weak" symbols, which is a mechanism for achieving one of the same behaviors--overriding another definition--when statically linking. Otherwise, when statically linking, multiple definitions are either an error or it's difficult (i.e. confusing, especially in complex builds) to control when and where one definition is chosen over another.
[1] Though main application symbols aren't usually exported by default, so you need to explicitly mark a definition for export or build the entire main binary with a compiler flag like `-rdynamic`, which is the main binary analog to the `-shared` flag used for building shared libraries. The Python and Perl interpreters, for example, are built with -rdynamic as the interpreter binary itself exports all the implementation symbols required by binary modules, rather than defining them in a separate shared library against which modules explicitly link themselves against at compile time. (This is also why when building Perl, Python, and similar language modules you have to tell the compile-time linker to ignore unresolved symbols.)
cornel_io
For those of us who don't Windows, can you explain what a detour is?
jborean93
You essentially replace a function with your own. The project is at https://github.com/microsoft/Detours.
I’ve created a PowerShell module that wraps this library to make it easier to hook functions on the fly for testing https://github.com/jborean93/PSDetour. For example I used it to capture TLS session data for decryption https://gist.github.com/jborean93/6c1f1b3130f2675f1618da5663... as well as create an strace like functionality for various Win32 APIs (still expanding as I find more use cases) https://github.com/jborean93/PSDetour-Hooks
coldtea
Detours is a library for instrumenting arbitrary Win32 functions Windows-compatible processors. Detours intercepts Win32 functions by re-writing the in-memory code for target functions. The Detours package also contains utilities to attach arbitrary DLLs and data segments (called payloads) to any Win32 binary.
Detours preserves the un-instrumented target function (callable through a trampoline) as a subroutine for use by the instrumentation. Our trampoline design enables a large class of innovative extensions to existing binary software.
sim-
Imagine if Windows just allowed DeleteFile() even if the file was open, like unlink() on almost any other OS...
ChrisSD
It does. But this issue arises because of file locks. Running an executable holds a lock that prevents deletions (but not renames).
Many OSes have file locks, though they often don't use them as liberally as Windows.
nikanj
Imagine all the "WinBLOWZ is bullshit, I deleted 200 gigs of shit and my C: still has no more free space" posts if Windows started doing soft deletes
tgtweak
Dropping wscripts is a good way to get malware profiled too, and no way to code sign it or verify it's integrity before executing.
javajosh
If your program creates the script and executes it, is verification necessary? This would be like verifying your 1st party scripts in a webpage that you wrote. It won't really hurt anything, but I'm not sure there's a point.
readyplayernull
Reminds me of a simple app I made for Windows 95/98 to add every directory including System32 to the uninstallers' lists. No AV, neither Norton or McAfee, saw that timebomb comming. Good times.
fennecfoxy
Well maybe if Windows applications were packaged similar to MacOS (one of the few things I like) with the application data and user data for the application in 2 folders then it wouldn't be such an issue.
Most Windows apps sit under program files, some sit directly on drive root. But all spray configuration/user data files for themselves all over the damn place, requiring unique uninstallers.
MS, build app install/uninstall into Windows directly...
wolfendin
This tracks, I’ve flagged the nvidia uninstall for hours work because it code injected and flagged behavioral consistent with malware
bdamm
And today I learned that Windows supports running Javascript as shell script. huh
technion
Malware delivered as an email with a link to a zip file containing a .js file is one of the most common methods of delivery, right behind word macros. The "map the .js extension to notepad.exe" is a common security trick with a measurable, immediate drop in malware in large orgs. You can deploy it via GPO or InTune.
Personal promotion, I built this as a better alternative:
https://github.com/technion/open_safety
Note the built in .js parser hasn't basically ever updated, if you're writing for this you're writing like you're targetting IE5.
notpushkin
> It creates the file "example.com" in the same directory containing the EICAR test string. This should set off appropriate alarms
Huh, neat!
Roark66
It is very common for malware to contain java script payloads that try to obfuscate themselves like like this:
Seemingly_random_code(seemingly_random_string)
The seemingly_random_code decompresses/decodes whatever is in the seemingly_random_string and hands over control to it. Interestingly the decoded code is another version of the same with different code and string. This goes on for ~100 layers deep then at the end it just downloads and executes some file from the net.
soneil
It’s amazing how much we haven’t moved on since iloveyou.txt.vbs
yjftsjthsd-h
> This goes on for ~100 layers deep then at the end it just downloads and executes some file from the net.
I understand doing one layer. I guess I could maybe see two layers. But why would it bother with 100 layers? Either the antivirus or reverse-engineering tool can grab the final product or it can't.
tsimionescu
Typically scanning tools have some limit to how much they probe complex formats, to avoid stalling the entire system while they're scanning. It's very much conceivable that a scan tool will try to resolve code like this for 10 layers, and then if the result is not found to be malicious, consider it safe.
This is similar to how compilers will often have recursion limits for things like generics, though in that case it's easier to reject the program if the recursion limit is reached.
sim7c00
Because of potential false positives, and the speed at which files need to be analyzed at runtime (suspend process executing it and then analyze it), having files which take a long time to unpack and identify can cause these to be allowed to run. They get offloaded to a sandbox or other systems to be analyzed while the file is already being executed. The sandboxes are too slow to return a verdict before the main logic of the file will be executed. IF those dynamic systems cannot identify a file, an engineer will manually need to look at it.
In very strict environments or certain systems it might be practical to block all unknown files, but this is uncommon for user systems for example where users are actively using javascript or macro documents etc. (developers, HR, finance etc.) The FP rates are too high and productivity can take a big hit. If all users do 20% less work that's a big loss in revenue (the productivity hit can be much more severe even!). perhaps this impact / loss of revenue ends up being bigger than a malware being executed depending on the rest of the security posture/measures.
technically its possible to identify (nearly?) all malware by tracking p-states/symbolic execution/very clever sanboxing etc.. but this simply takes much too long. Especially if the malware authors are aware of sandboxing techniques and symbolic execution and such things as they can make those processes also take extra long or sometimes even evade them totally with further techniques.
I wish it _was_ possible to do all of the cleverness that malware researchers have invented to detect things, but unfortunately, in practice this cannot happen on like 90+% of environments.
If you run like a DNS server or such things, it's possible to do it as such a system would not be expected (ever?) to have unknown files. (gotta test each update and analyze new versions before deploying to prod). As you can imagine, this is also kind of a bummer process but imho for such 'static' systems its worth it.
rightbyte
With enough conditional evals() with dynamic inputs you can make the search space unsearchable big.
mark_and_sweep
Been using this for years. Mostly really useful. Sometimes tricky to get right since the available APIs are semi-well documented and it's JScript, which is some sort of old Internet Explorer-ish version of JavaScript.
By the way, there are also HTAs, which are Microsoft HTML Applications. You can create a simple double-clickable GUI with these using only HTML and JScript.
jeroenhd
Pretty crazy how Microsoft basically invented the Electron app as HTAs all the way back in 1999. Of course we browsers weren't as capable as they are today, but "I just want a HTML+CSS GUI" had been a solved problem for over ten years when Electron first came out.
jraph
Yes, and XULRunner allowed this too, using Gecko, Firefox's web engine, to render HTML-like markup specifically designed to build native-like GUIs.
Apparently XULRunner was first released in 2006, but Thunderbird, which uses (used?) the same technology, was released as early as 2003, and maybe this was existing in the Mozilla Suite even before.
mark_and_sweep
Well, you can use IE 9 in HTAs - that browser is plenty capable. :) Been using this as a Windows-only Electron alternative for years.
dolmen
This feature has existed for more than 25 years.
My concern is more than Raymond Chen suggest that using it is still the recommended way. So much malware came through WScript.
pie_flavor
Scripting is normal functionality for an OS to support. I don't know why people pretend JScript/WScript are evil but Bash is fine.
criddell
Well, he did warn you it would be indistinguishable from malware…
jraph
Yes, the same way one could write VBS (Visual Basic Script).
I think Windows 98 already had this ability. Possibly Windows 95 as well. It's a variant of the language called JScript, which is what was used in old versions of IE too.
p_l
It was about Windows 98 that Windows Scripting Host ended up prominent.
WSH btw allowed you to run any language that you had interpreter for - they had to support necessary COM interfaces (and to be truly usable, allow you to call COM objects), and register their interpreter class with ActiveScripting (WSH internal) engine.
Then you could use them not just for desktop automation, but also for scripts inside Internet Explorer (essentially, classic IE used WSH engines to implement scripts, iirc)
I've seen WSH (including HTAs) used with Perl, Python, Tcl, Rexx...so long as you install the interpreter with compatible COM service, you could use it.
parasti
It's technically JScript.
xxs
about what a sibling mentioned 'JScript' - not javascript; the infamous Microsoft EEE (the 2nd part) It has been there for decades.
drbig
I still long for the approach many software used on the AmigaOS - the app is a folder, the folder has the main exec and any assets it needs (libraries, images, etc.) and documentation and... That's it.
Install? Copy the directory to where you like. Uninstall? Delete the directory.
And if you wish you could keep any files used/generated with such an app in the same folder, making it 100% self-contained.
I remember being rather grossed out when I learnt Windows has "a registry" (that was a long time ago). "Why would you have a global registry? Whatever preferences a piece of software has they should live where the exe is".
(and yes, I am aware AmigaOS had an installer and dir structure not that unlike of Unix, with `sys:`, `devs:` and so on)
ryandrake
To be fair, Windows applications can be designed to be installable this way: a single executable, with everything it needs sitting next to it in the folder. Even better, a single executable with no other dependent files at all! Lots of little utilities used to be distributed this way. But many developers deliberately choose to structure their monster such that it needs to spread its tentacles all over the filesystem for it to work.
And for legacy/backward compatibility reasons, once MS allowed this behavior to go unchecked, there was no way to put the genie back in the bottle and stop it, without giving up backward compatibility. It didn't help that Microsoft software tended to be the "tentacle" kind as well.
TremendousJudge
It sounds great but there are simple use cases where the "portable" app isn't enough. For example, if you want multiple users to be able to use the program and have their own settings, you need something to be saved to the user folder. Or, if you want any basic interaction with the system (run on startup, run from a browser address, etc), you need to start messing with the registry.
So in theory apps could be distributed portable .exes but in practice Windows doesn't any ways of interacting with the rest of the system that are that nice.
robinsonb5
I still love most aspects of the Amiga user experience, but a lot of Amiga applications would need libraries installed to Libs: and deleting the application's "drawer" would leave those libraries behind. (Having said that, by default libs: is assigned to sys:libs but you could assign extra targets, so that libraries would be sought from application-specific directories.)
Also, it suffers from the same problem as Windows here, in that you can't delete a file or directory which is currently open. The executable itself wouldn't be open after launch is complete (with the possible exception of overlay executables, but they were pretty rare) but the directory would be locked for as long as it was the program's current directory. If a subdirectory with app-specific libraries was assigned to Libs: that would also prevent deletion.
realslimjd
This is how a lot of apps on MacOS still work.
justusthane
Sort of. They still leave garbage behind in ~/Library though.
undefined
unnouinceput
Does that means the corollary: "Any sufficiently advanced malware is indistinguishable from an uninstaller" would be true as well?
I mean can you write a simulation of an uninstaller to create havoc on target's system and still remain "the good guy, the OS is at fault" type of situation when you write a malware?
doloputer
I've heard this before, about cryptolockers. It's hard for the OS to know if you're encrypting all of your files on purpose, because you might actually want to do that.
Get the top HN stories in your inbox every day.
Here's the codeproject link the code came from.
https://www.codeproject.com/Articles/17052/Self-Deleting-Exe...
> Whether they follow the licensing terms for that code I do not know.
I'm guessing they didn't ship the binary with a link pointing back to this page?
These's also another codeproject example that uses a bat file, which is fairly similar to the recommendation in the post. I guess that's the better example.
https://www.codeproject.com/Articles/4027/Writing-a-self-des...