Menu

#280 Add !unfinalize to makensis

3.x Stable
closed-accepted
nobody
None
5
2021-09-01
2016-12-20
Jason
No

I wrote this patch based on the idea in this thread: http://forums.winamp.com/showthread.php?t=399106.

Basically it allows the uninstaller to be finalized outside of makensis before being added to the installer.

I wrote this on ubuntu 12.04. I refactored most of !tempfile so that I can use it for the uninstaller as well. I also refactored the postbuild commands so that it can be called for both the installer and the uninstaller.

I've only tested a few output installers to check the operation, I haven't compiled the source code on windows yet so that side will have to be checked.

1 Attachments

Discussion

1 2 > >> (Page 1 of 2)
  • Jason

    Jason - 2016-12-20

    Whoops, I noticed I missed a block of code when I was moving stuff around. Here's an updated patch.

     
  • Jason

    Jason - 2016-12-24

    I've improved the code again, this time refactoring the file handling parts out to their own functions so that I can use them in more than one location.

    The linux side appears to be working fine, running !finalize 'nautilus "%1"' opens a window with the uninstaller, !unfinalize does the same if I disable file deletion within nsis. I did run two tests on windows with !unfinalize 'explorer.exe "%1"' but it always exits with an error code, but !finalize does work (it launches the installer).

    I don't know how much further I can improve the code, so this is probably the last version from me.

     

    Last edit: Jason 2016-12-24
  • Jason

    Jason - 2017-01-17

    Well I was wrong, I can improve the code more. Now makensis can change the type of uninstaller it produces based on the existance of any !unfinalize commands. This means it acts the same as standard NSIS by default (to save installer space), and any !unfinalize command forces the whole uninstaller out to a file so that it can be used by other programs, for example signing the uninstaller.

    Once again, this patch was written on ubuntu 12.04, and only resulting installers have been tested on windows.

     
  • Mukund Sivaraman

    Hi Jason

    I patched NSIS 3.01 with this patch and tried signing with osslsigncode on Linux using a self-signed certificate for testing. The installer (created via !finalize) appears signed properly and I get a "Digital Signatures" tab in the properties dialog on Windows for the installer .exe file. The uninstall.exe (that was passed through !unfinalize) that the installer installs works fine as an uninstaller, but it lacks a "Digital Signatures" tab in the properties dialog. So I copied this uninstall.exe back to Linux and tried to verify its signature with "osslsigncode verify". It returns:

    Current PE checksum : 0001AF54
    Calculated PE checksum: 0002F0BB MISMATCH!!!!

    No signature found.
    Failed

    It appears the checksum mismatches.

    The installer .exe file passes verification by "osslsigncode verify":

    Current PE checksum : 0003D280
    Calculated PE checksum: 0003D280

    Message digest algorithm : SHA256
    Current message digest : 23BC373BE1016C40D86A2A1B01D8E302D7A2DC5C4C688428DC53C96E74E69B65
    Calculated message digest : 23BC373BE1016C40D86A2A1B01D8E302D7A2DC5C4C688428DC53C96E74E69B65

    Signature verification: ok

    So the embedded uninstaller doesn't appear to be correct.

    Mukund

     
    • Jason

      Jason - 2017-02-15

      Ok, in build.cpp on about line 3356 there is this command: _tremove(fpath);
      Comment out that line and that will leave the uninstaller file in /tmp/. Then check the hash of this file with the one extracted from the installer on windows. This will give a clue of where the problem is.

       
  • Roberto Bagnara

    Roberto Bagnara - 2018-10-20

    I am wondering whether this patch actually works with NSIS 3.03. If so, is there any hope it will be integrated into the main sources anytime soon?

     
  • Jason

    Jason - 2018-10-22

    It should work with 3.03, the core uninstaller code hasn't changed much at all.

    read_data_from_file() could be optimized a bit, other than that I'm still happy with this patch.

     
  • Roberto Bagnara

    Roberto Bagnara - 2018-10-22

    Thanks Jason. We have tried hard, but we failed: the patch nsis_uninst4.diff cannot be applied to nsis 3.03. There are many rejects: in some cases the changes in 3.03 seem to go in the direction of the patch, in other case the changes in 3.03 are in contrast with the ones of the patch. After a day of work we had to give up. Porting the patch required knowledge of nsis internals that we do not have.

     
    • Anders

      Anders - 2018-10-23

      We have not discussed this internally IIRC. Kichik is away for a couple of weeks and I'm a bit busy but I can refactor the post build commands to make this patch easier. We already have getfilesize and readfile helpers in util.cpp now.

       
  • Jason

    Jason - 2018-10-23

    I had a look and there has been some refactoring of the code, so I spent a few hours updating the patch. Try this out, I haven't tested it at all though.

     
  • Jason

    Jason - 2018-10-23

    I saw that there are some new helpers, so I'm using those helpers instead of my versions now. And I already refactored the post build commands, but it could be improved further in the preprocessing side to remove duplicate code.

     
  • Roberto Bagnara

    Roberto Bagnara - 2018-10-23

    Thanks! The new patch applies smoothly to nsis 3.03. Apparently, the patch does not break !finalize or other functionalities, but !unfinalize crashes, apparently because of a buffer overflow. I attach the valgrind output.

     
    • Jason

      Jason - 2018-10-23

      Yeah, that's exactly where I thought it would be, it's in alloc_and_get_temp_file_path(). I'll have to find a different way to get the length. Notice how the temp filename is truncated? sizeof() isn't the right way to get the length. I'll see what I can do.

       
  • Roberto Bagnara

    Roberto Bagnara - 2018-10-23

    Yes, in the output I see "Adding Authenticode signature to /tmp/ma", whereas an empty file called "makensis0A4FoY" was created under /tmp.

     
  • Jason

    Jason - 2018-10-23

    Yep, try this one. I hardcoded the length for it because the string is hardcoded too.

     
  • Roberto Bagnara

    Roberto Bagnara - 2018-10-23

    The crash has gone, and in the valgrind output we now have only the two warnings on "Conditional jump or move depends on uninitialised value(s)" in CEXEBuild::GetMacro(wchar_t const, wchar_t*) (scriptpp.cpp:238), which perhaps are unrelated to the patch.

    The code-signing script is invoked once on the uninstaller and once on the installer, and it terminates successfully in both cases. However, while the installer is correctly signed (according to Windows 10), the uninstaller is not (according to the same). We will now try to follow the advice given to Mukund Sivaraman (commenting out the removal of the uninstaller) and see if we can get more information on the issue.

     
  • Jason

    Jason - 2018-10-23

    I think I found the problem, try this one.

     
    • Anders

      Anders - 2018-10-23

      alloc_and_get_temp_file_path returns the wrong size on Windows. Well, it returns the allocated size but I think if we care at all, we would care about the length of the path, not the actual memory. my_strncpy returns the string length (excluding \0).

       
  • Roberto Bagnara

    Roberto Bagnara - 2018-10-23

    Here is what we now know:

    1) the /tmp/makensisDqv5p6 file has actually been signed;
    2) the uninstall.exe that ends up being installed by the installer is not signed;
    3) /tmp/makensisDqv5p6 size is around 1 MB, half the size of the installed uninstall.exe, which is around 2 MB;
    4) despite the small size, /tmp/makensisDqv5p6, once renamed and substituted to uninstall.exe, seems to work as expected (it deletes all files, I am not sure it does the other things, such as restoring the registry);
    5) if we take the installed uninstall.exe and we sign it, its size increases by around 7 kB, i.e., it remains around 2 MB.

     
  • Roberto Bagnara

    Roberto Bagnara - 2018-10-23

    (The previous post referred to nsis_uninst6.diff, now trying the new patch nsis_uninst7.diff.)

     
  • Roberto Bagnara

    Roberto Bagnara - 2018-10-23

    The same observations obtained with nsis_uninst6.diff have been obtained with nsis_uninst7.diff. How can such a big difference in size be explained?

     
  • Jason

    Jason - 2018-10-24

    So, that took longer than I thought. I ended up separating the uninstallers out to their own datablock and now it works (most of the code was correct, it was just the final bit with the reading into the datablock I had wrong). I used a blank command (!unfinalize "") and a linux build of upx (!unfinalize "upx '%1'") to test the branches. The upx one fails because it compresses the file, it only works if you are appending stuff on the end of it.

    Try it out.

    [edit] There is a little code clean up to do, I'll do that another day since it's not urgent.

     

    Last edit: Jason 2018-10-24
    • Anders

      Anders - 2018-10-24
      1. What does "it only works if you are appending stuff on the end of it" mean, does it crash or create a corrupted uninstaller? If that is the case then you must abort with a error message or pad the uninstaller with \0.

      2. Why is write_data_to_file() part of CEXEBuild? It looks like a generic helper function.

      I created helper functions like run_postbuild_cmds and alloc_and_get_temp_file_path in trunk so we can concentrate on the uninstaller bits here but it probably invalidated your previous patch. diff8 must be applied to SVN r7024 if that is the case.

       
      • Jason

        Jason - 2018-10-24
        1. when the uninstaller is run, it fails with a crc failure. I guess technically upx should be used on the exehead only, like an uninstaller version of !packhdr. I'll have to check what upx does to the uninstaller before I read the uninstaller back in to makensis. !unpackhdr would be relatively straight forward to add, because the exehead with patched icons is in a buffer, so we can just refactor pack_exe_header() so that it calls a helper that writes it out and runs the packer.

        2. Yeah it is, I should move it to util.cpp.

         
  • Roberto Bagnara

    Roberto Bagnara - 2018-10-24

    It works: thanks! I hope this patch can be merged soon.

     
1 2 > >> (Page 1 of 2)

Log in to post a comment.