1. 47

    1. 13

      It is both scary and funny that the biggest commercial operating system requires scary hacks (such as code injection, or writing temporary JavaScript scripts) just to allow you to delete a file (which happens to currently be executed).

      In Linux and macOS, you are free to delete files. Even if they are open or being executed. It’s as simple as that. No hacks required!

      But, an even bigger issue is that something such as an uninstaller even exists. The fact that you need to not only write your software, but every software that you want to release you need a separate software to install and uninstall it. That is crazy! Even though they are not perfect, Linux’s package managers are amazing at solving that problem. MacOS is arguably even easier, you literally just copy a .app file into Applications and it’s there, and you delete it and it’s gone! ✨Magic✨


      1. 8

        MacOS is arguably even easier, you literally just copy a .app file into Applications and it’s there, and you delete it and it’s gone! ✨Magic✨

        I’ve never been convinced this really worked right when the app will still leave things like launchd plists around that it automatically created…

        1. 3

          True, I have experienced that as well. It is not very common thankfully.

          Also, some applications do require installers even on macOS. An example (shame on you!) is Microsoft Office for Mac. At least those are standardized, but it is annoying. I will not install software that requires an installer on any of my systems.

      2. 4

        Windows has the technology. it’s called “Windows Installer” and it’s built into the OS. However it required using a MSI file, which people don’t like because of the complex tooling.

        More recently there is msix which simplifies things greatly while having more features but people don’t like it because it requires signing.

        1. 6

          Kind of. The root problem here is that you cannot, with the Windows filesystem abstractions, remove an open file. With UNIX semantics, a file is deleted on disk after the link count drops to zero and the number of open file descriptors to it drops to zero.

          This is mildly annoying for uninstallation because an uninstalled can’t uninstall itself. The traditional hack for this was to use a script interpreter (cmd.exe was fine) that read the script and then executed it. This sidesteps the problem by running the uninstaller in a process that was not part of the thing being installed. MSIs formalise this hack by providing the uninstall process as a thing that consumes a declarative description.

          It’s far more problematic for updates. On *NIX, if you want to replace a system library (e.g. libc.so), you install the new one alongside the old then rename it over the top. The rename is atomic (if power goes out, either the new version will be on disk or the old one) and any running processes keep executing the old one, new processes will load the new one. You probably want to reboot at this point to ensure that everything (from init on down) is using the new version, but if you don’t then the old file remains on disk until the open count drops to zero. You can update an application while it’s running then restart it and get the new version.

          On Windows, this is not possible. You have to drop to a mode where nothing is using the library, then do the update (ideally with the same kind of atomic rename). This is why most Windows updates require at least one reboot: they drop to something equivalent to single user mode on *NIX, replace the system files, then continue the boot (or reboot). Sometimes the updates require multiple reboots because part of the process depends on being able to run old or new versions. This is a big part of the reason that I wasted hours using Windows over the last few years, arriving at work and discovering that I needed to reboot and wait 20 minutes for updates to install (my work machine was only a 10-core Xeon with an NVMe disk, so underpowered for Windows Update), whereas other systems can do most of the update in the background.

          1. 3

            This is mildly annoying for uninstallation because an uninstalled can’t uninstall itself

            I think this is only half-true, because WinAPI gives you the “delay removal until next reboot” (MOVEFILE_DELAY_UNTIL_REBOOT), so it should be possible for the uninstaller to uninstall the application, and then register itself, along with its directory, for removal until next reboot. Then Windows itself will remove the uninstaller on next reboot.

            On servers this could mean that it will be removed next month, but this in turn is a virtual problem, not a real one.

            1. 1

              Windows servers list “application maintenance” as a reason for a reboot, so it’s not culturally weird to reboot after an application update.

          2. 2

            MSIs formalise this hack by providing the uninstall process as a thing that consumes a declarative description.

            Yep, that was my point. Or to put it another way, Windows can handle the management of a package so you don’t have to. Which was the complaint in the OP.

            But on your point, it is totally possible to do in-place updates to user software. On modern Windows most files can be deleted even without waiting for all handles to close. And any executables you can’t immediately delete (due to being run) can be moved. The problem is software that holds file access locks. Unfortunately standard libraries are especially guilty of doing this by default, even newer ones like Golang do this for some inexplicable reason.

        2. 2

          True, arguably Windows also has an app store nowadays and NuGet and WinGet. I did not know about msix! Maybe a bit of an XKCD 927 situation there.

          1. 4

            Windows also has:

            So the existence of installers/uninstallers is a “cultural” thing, not a technical necessity.

            1. 1

              “if you want to use our product, install [my chosen package manager]” is pretty non viable. I write the installer for a game, none of that would be an option.

              1. 3

                Sure you do. You just call it “Steam” instead.

          2. 2

            WinGet simply downloads installer programs and runs them. This is visible in its package declarations

            NuGet is a .Net platform development package manager right? Like Maven for the JVM it is not intended to distribute finished programs but libraries that can be used to build a program. But perhaps it can be used to distribute full programs just like pip, npm et al.

            1. 2

              In theory, NuGet is not specific to .NET. You can build NuGet packages from native code. Unfortunately, it doesn’t have good platform or architecture abstractions and so it’s not very useful on non-Windows platforms for anything other than pure .NET code.

    2. 8

      Fascinating write up. Something that long kept me from using any Linux distribution before I was familiar with them was a concern over how packages were installed and uninstalled. I was used to Windows and how it had the built in uninstaller, but over time I began to see odd behaviors like mentioned in the article. After Windows, I moved to OS X and ended up installing a third party tool[0], but that always felt like the wrong approach and potentially insecure.

      I was first introduced to proper package management on a jailbroken iPhone via Installer and later Cydia. They felt so intuitive, and it was great being able to keep track of everything and feel relatively safe when uninstalling software. Later, the iPhone gained the App Store of course.

      By the time I moved to Linux for personal use, I was aware of the brilliance of package managers and have been happy ever since. I was completely naïve to this approach of handling installations and used to how Windows and Mac littered junk from installations all over.

      I now also use MacOS at my workplace and have become aware of homebrew and use it to manage my installations. I know Windows and Mac have their own stores, but the closed nature of them leads to them not having everything that I want installed. Homebrew is a step in the right direction.

      I have tried explaining the concept of package managers to friends and family who are not as tech-literate, but the concept has never stuck with anyone, and they continue to install and uninstall packages freely in multiple ways. This article highlights the danger in the approach that can occur on Windows and reaffirms why I am happy using pacman on Arch and homebrew on MacOS. Although I’m sure someone will come along and point out a downside to that approach I am unaware of!

      [0] https://freemacsoft.net/appcleaner/

    3. 3

      Interesting, and horrifying, trick.

      From the headline I had thought this might be about how NSIS installers generate the uninstaller exe at install time, which might look like an obfuscation technique to an AV.

    4. 2

      wow, I wish could interpret assembly like that. I only occasionally look at assembly to make sure the compiled code is using vectorized instructions, or simple things like that. I am not sure how I would learn that art?

      1. 4

        I think, like any other language, the best way to learn is to read, write, and debug. It’s rare to do that in assembly, but there are challenges that force you to do that, such as reverse engineering, debugging without symbols, and solving CTFs like pwnable.kr. For reverse engineering it helps to have a tangible goal in hand (e.g. bending some toaster’s firmware to your will).

      2. 3

        Maybe on https://crackmes.one/ :)