Can I decompile an existing installer?: Difference between revisions

From NSIS Wiki
Jump to navigationJump to search
(Added _LANG_INVALIDCRC 2.x detection)
 
(26 intermediate revisions by 3 users not shown)
Line 5: Line 5:
==Extraction Tools==
==Extraction Tools==
There are, however, external tools that allow this:
There are, however, external tools that allow this:
* Since version 4.42[may 2006] [http://www.7-zip.org 7-zip] supports decompressing NSIS installers.<br />Since version 9.34 [Jun 2014] 7-zip is also able to extract the compiled scriptcode.
* Since version 4.42 [May 2006] [http://www.7-zip.org 7-zip] supports decompressing NSIS installers.<br />Since version 9.34 [Jun 2014] 7-zip was also able to extract the compiled scriptcode, but this functionality [https://sourceforge.net/p/sevenzip/discussion/45797/thread/5d10a376/ was removed in 15.06].
* The decompression plug-in '''InstallExplorer''' [http://www.totalcmd.net/plugring/installexplorer.html InstExpl.wcx] is also available for '''TotalCommander'''. Beside the files it'll create the file ''' 'script.bin' ''' compiled scriptcode.For use without the TotalCommander the [http://legroom.net/software/uniextract Universal Extractor] is a good option.<br />
* The decompression plug-in '''InstallExplorer''' [http://www.totalcmd.net/plugring/installexplorer.html InstExpl.wcx] is also available for '''TotalCommander'''. Beside the files it'll create the file ''' 'script.bin' ''' compiled scriptcode.For use without the TotalCommander the [http://legroom.net/software/uniextract Universal Extractor] is a good option.<br />
Well since [Feb 2014] InstExpl suffers from problems naming file names and dirs correctly that were created with NSIS 3. ''That's because the implementation [http://sourceforge.net/p/sevenzip/bugs/1398 GetNSISString()] was slightly changed so names like $INSTDIR, $PROGRAMFILE ... inside strings are not expanded correctly.''
Well since [Feb 2014] InstExpl suffers from problems naming file names and dirs correctly that were created with NSIS 3. ''That's because the implementation [http://sourceforge.net/p/sevenzip/bugs/1398 GetNSISString()] was slightly changed so names like $INSTDIR, $PROGRAMFILE ... inside strings are not expanded correctly.''
Line 11: Line 11:


==Decompilers==
==Decompilers==
* [http://www.7-zip.org 7-zip] Since version 9.34 [Jun 2014] it'll extract *beside the files of the setup* the compiled script code to a file named '''[NSIS].nsi'''
* [http://www.7-zip.org 7-zip] Since version 9.34 [Jun 2014] [https://sourceforge.net/p/sevenzip/discussion/45797/thread/5d10a376/#6e1d/3fa3/6840/fe9c till 15.06. [Jun 2015]]  it'll extract *beside the files of the setup* the compiled script code to a file named '''[NSIS].nsi'''
* '''[[NullsoftDecompiler]]''' or NSIDis<br />NSIDis is a open source Python script that'll help you to nearly fully recover your NSIS-installation scripts. Its state is currently alpha - and so not very user friendly and stable.
* '''[[NullsoftDecompiler]]''' or NSIDis<br />NSIDis is a open source Python script that'll help you to nearly fully recover your NSIS-installation scripts. Its state is currently alpha - and so not very user friendly and stable.
* [https://github.com/isra17/nrs NSIS Reversing Suite] NRS is a set of Python librairies used to unpack and analysis NSIS installer's data. It also has an IDA plugin used to disassembly the NSIS Script of an installer.
* [https://github.com/Noice2k/NsisDecompiler NsisDecompiler]
* [https://github.com/Noice2k/NsisDecompiler NsisDecompiler]
 
* [https://github.com/myfreeer/7z-build-nsis 7z-build-nsis]
* [https://github.com/M2Team/NanaZip NanaZip]


== Protection against Decompilers==
== Protection against Decompilers==
Line 26: Line 28:


==Technical Details==
==Technical Details==
NSIS instruction encoding was never designed to be stable across versions and any information listed here should be taken with a grain of salt.


====Detection====
====Detection====
* 2.0rc1 [3306] _LANG_INVALIDCRC string changed
* 1.00 Signature EF BE AD DE 6E 73 69 73 69 6E 73 74 61 6C 6C 00
* 2.35 [5459] _LANG_INVALIDCRC string changed
* 1.1e Signature ED BE AD DE 4E 75 6C 6C 53 6F 66 74 49 6E 73 74
* 1.30 Signature EF BE AD DE 4E 75 6C 6C 53 6F 66 74 49 6E 73 74
* 1.60b2 Signature EF BE AD DE 4E 75 6C 6C 73 6F 66 74 49 6E 73 74 (last signature change)
* 1.70b Imports SearchPathA
* 1.90a Imports GetTempFileNameA
* 2.0a0 Imports ImageList_Destroy
* 2.0b0 [r884] String <code>RichEd20</code>
* 2.0rc1 [r3306] _LANG_INVALIDCRC string changed
* 2.01 [r3560] Only imports CreateFontIndirectA, not CreateFontA
* 2.26 [r5036] Imports MessageBoxIndirectA instead of MessageBoxA
* 2.29 [r5195] String <code>SHGetFolderPathA</code>
* 2.35 [r5459] _LANG_INVALIDCRC string changed
* 2.45 PE.OptionalHeader.MajorImageVersion == [https://sourceforge.net/p/nsis/bugs/909/ 6]
* 2.45 PE.OptionalHeader.MajorImageVersion == [https://sourceforge.net/p/nsis/bugs/909/ 6]
* 2.47 [r6659] & v3.0b1 [r6506] String: <code>InitiateShutdownA</code>/<code>W</code>
* 2.47 [r6659] & v3.0b1 [r6506] String: <code>InitiateShutdownA</code>/<code>W</code>
Line 40: Line 55:
<!-- * 3.0b1 [r6537] Changed default DllCharacteristics to TS_AWARE+NO_SEH+NX_COMPAT+DYNAMIC_BASE (Do not use these for version detection). -->
<!-- * 3.0b1 [r6537] Changed default DllCharacteristics to TS_AWARE+NO_SEH+NX_COMPAT+DYNAMIC_BASE (Do not use these for version detection). -->
* 3.02 [r6839] ShellExecuteA/W has been replaced by ShellExecuteExA/W.
* 3.02 [r6839] ShellExecuteA/W has been replaced by ShellExecuteExA/W.
* 3.05 [r7084] No longer links to LoadBitmapA/W
* 3.06 [r7154] Links to IIDFromString (or SHCLSIDFromString) (possibly by ordinal) and String: <code>SHGetKnownFolderPath</code>
* 3.08 [r7309] Links to GetVersionExA/W and not GetVersion.
* 3.09 No longer links to SHGetSpecialFolderLocation. StringTable: <code>$TEMP\Un_$1.exe</code> changed to <code>$TEMP\Un.exe</code>.


====EW_ASSIGNVAR====
====EW_ASSIGNVAR====
Line 50: Line 69:
* 3.0b0 [r6452] 0x8000 in parm4 will disable SetWorkingDirectory
* 3.0b0 [r6452] 0x8000 in parm4 will disable SetWorkingDirectory
* 3.0b3 [r6638] parm4 packing changed to support larger icon index.
* 3.0b3 [r6638] parm4 packing changed to support larger icon index.
====EW_DELREG====
* 3.02 [r6871] ent.offsets[4] can also contain (shifted) KEY_WOW64_xxKEY bits.
* 3.06 [r7166] parm4: 0x04 = ONLYIFNOVALUES


====EW_FPUTWS====
====EW_FPUTWS====
* 3.0b3 [r6626] parm3 = TryWriteBOM
* 3.0b3 [r6626] parm3 = TryWriteBOM
====EW_GETDLLVERSION====
* 3.08 [r7310] parm3 is 2 for /ProductVersion
====EW_GETOSINFO====
* 3.06 [r7154] parm0: 0 == TOK_GETKNOWNFOLDERPATH (v1=output, param2=KF string).
* 3.08 [r7309] parm3: 0 == TOK_GETKNOWNFOLDERPATH, 1 == TOK_READMEMORY.
====EW_INTCMP====
* 3.03 [r6929] parm5 is no longer a BOOL, it is a flags DWORD: bit 0x01 is set for unsigned operations and bit 0x8000 is set for 64-bit operations.
====EW_INTFMT====
* 3.03 [r6931] parm3 is != 0 for 64-bit operations
====EW_INTOP====
* 2.0b4 [r2387] (parm3 >= 7) operators changed because the old 7 (~ bneg) is implemented using ^
* 3.03 [r6926] Added >>> (SHR) operator (parm3 == 13)
====EW_LOADANDSETIMAGE====
* 3.05 Renamed EW_SETBRANDINGIMAGE. parm2 now contains flags describing parm0 and parm1 and LR_*.
* 3.06 [7179] parm0 is now the output var index, the other parameters shifted one place.


====EW_SETFLAG====
====EW_SETFLAG====
* 3.02 [r6841] alter_reg_view can be KEY_WOW64_32KEY in 64-bit stubs.  
* 3.02 [r6841] alter_reg_view can be KEY_WOW64_32KEY in 64-bit stubs and KEY_WOW64_64KEY in 32-bit stubs.
* 3.09 [r7341] parm2 can also be < 0


====EW_SHELLEXEC====
====EW_SHELLEXEC====

Latest revision as of 14:31, 19 August 2024

About

Currently NSIS installers cannot be fully decompiled. The installer itself doesn't provide any method to extract files or the script without installation. It is the developer's choice whether the source code and/or the files for the installer are available to the public or not.


Extraction Tools

There are, however, external tools that allow this:

  • Since version 4.42 [May 2006] 7-zip supports decompressing NSIS installers.
    Since version 9.34 [Jun 2014] 7-zip was also able to extract the compiled scriptcode, but this functionality was removed in 15.06.
  • The decompression plug-in InstallExplorer InstExpl.wcx is also available for TotalCommander. Beside the files it'll create the file 'script.bin' compiled scriptcode.For use without the TotalCommander the Universal Extractor is a good option.

Well since [Feb 2014] InstExpl suffers from problems naming file names and dirs correctly that were created with NSIS 3. That's because the implementation GetNSISString() was slightly changed so names like $INSTDIR, $PROGRAMFILE ... inside strings are not expanded correctly.


Decompilers

  • 7-zip Since version 9.34 [Jun 2014] till 15.06. [Jun 2015] it'll extract *beside the files of the setup* the compiled script code to a file named [NSIS].nsi
  • NullsoftDecompiler or NSIDis
    NSIDis is a open source Python script that'll help you to nearly fully recover your NSIS-installation scripts. Its state is currently alpha - and so not very user friendly and stable.
  • NSIS Reversing Suite NRS is a set of Python librairies used to unpack and analysis NSIS installer's data. It also has an IDA plugin used to disassembly the NSIS Script of an installer.
  • NsisDecompiler
  • 7z-build-nsis
  • NanaZip

Protection against Decompilers

As a general note to software developers, you should use a plugin like DCryptDll if you need to protect certain files in your installer.

.. or if ya in the mood for compiling the NSIS have a look into nsis-3.xx-src\Source\exehead\fileform.h. Mixing up the order of the enum with all the EW_* a little bit as recommend in the Comment. It will mess up decompilers output that expect these tokes to be in the standard order.

Or shift or enlarge the .reloc section in the PE-header by 0x400. <-I saw that trick @ some old 'conduit'-adwareinstaller. Inserting so fill bytes between the EOF-exe at the start of the script might also do the trick to stop 7-zip and maybe some Antiviruses.


Technical Details

NSIS instruction encoding was never designed to be stable across versions and any information listed here should be taken with a grain of salt.

Detection

  • 1.00 Signature EF BE AD DE 6E 73 69 73 69 6E 73 74 61 6C 6C 00
  • 1.1e Signature ED BE AD DE 4E 75 6C 6C 53 6F 66 74 49 6E 73 74
  • 1.30 Signature EF BE AD DE 4E 75 6C 6C 53 6F 66 74 49 6E 73 74
  • 1.60b2 Signature EF BE AD DE 4E 75 6C 6C 73 6F 66 74 49 6E 73 74 (last signature change)
  • 1.70b Imports SearchPathA
  • 1.90a Imports GetTempFileNameA
  • 2.0a0 Imports ImageList_Destroy
  • 2.0b0 [r884] String RichEd20
  • 2.0rc1 [r3306] _LANG_INVALIDCRC string changed
  • 2.01 [r3560] Only imports CreateFontIndirectA, not CreateFontA
  • 2.26 [r5036] Imports MessageBoxIndirectA instead of MessageBoxA
  • 2.29 [r5195] String SHGetFolderPathA
  • 2.35 [r5459] _LANG_INVALIDCRC string changed
  • 2.45 PE.OptionalHeader.MajorImageVersion == 6
  • 2.47 [r6659] & v3.0b1 [r6506] String: InitiateShutdownA/W
  • A Unicode stub imports CreateFileW and other wide API functions. Theses stubs support EW_FPUTWS and EW_FGETWS.
  • 2.47 [r6658] & 3.0b3 [r6657] SetFileSecurityA/W
  • 2.51 [r6719] & 3.0b3 [r6706] String: CLBCATQ
  • 3.0rc1 [r6721] StringTable: $TEMP\$1u_.exe changed to $TEMP\Un_$1.exe.
  • 3.02 [r6839] ShellExecuteA/W has been replaced by ShellExecuteExA/W.
  • 3.05 [r7084] No longer links to LoadBitmapA/W
  • 3.06 [r7154] Links to IIDFromString (or SHCLSIDFromString) (possibly by ordinal) and String: SHGetKnownFolderPath
  • 3.08 [r7309] Links to GetVersionExA/W and not GetVersion.
  • 3.09 No longer links to SHGetSpecialFolderLocation. StringTable: $TEMP\Un_$1.exe changed to $TEMP\Un.exe.

EW_ASSIGNVAR

  • 3.01 [r6810] Empty maxlen string is treated as parameter not present.

EW_CREATEDIR

  • 2.51 [r6701] & 3.0b3 [r6657] parm2 = CreateRestrictedDirectory

EW_CREATESHORTCUT

  • 3.0b0 [r6452] 0x8000 in parm4 will disable SetWorkingDirectory
  • 3.0b3 [r6638] parm4 packing changed to support larger icon index.

EW_DELREG

  • 3.02 [r6871] ent.offsets[4] can also contain (shifted) KEY_WOW64_xxKEY bits.
  • 3.06 [r7166] parm4: 0x04 = ONLYIFNOVALUES

EW_FPUTWS

  • 3.0b3 [r6626] parm3 = TryWriteBOM

EW_GETDLLVERSION

  • 3.08 [r7310] parm3 is 2 for /ProductVersion

EW_GETOSINFO

  • 3.06 [r7154] parm0: 0 == TOK_GETKNOWNFOLDERPATH (v1=output, param2=KF string).
  • 3.08 [r7309] parm3: 0 == TOK_GETKNOWNFOLDERPATH, 1 == TOK_READMEMORY.

EW_INTCMP

  • 3.03 [r6929] parm5 is no longer a BOOL, it is a flags DWORD: bit 0x01 is set for unsigned operations and bit 0x8000 is set for 64-bit operations.

EW_INTFMT

  • 3.03 [r6931] parm3 is != 0 for 64-bit operations

EW_INTOP

  • 2.0b4 [r2387] (parm3 >= 7) operators changed because the old 7 (~ bneg) is implemented using ^
  • 3.03 [r6926] Added >>> (SHR) operator (parm3 == 13)

EW_LOADANDSETIMAGE

  • 3.05 Renamed EW_SETBRANDINGIMAGE. parm2 now contains flags describing parm0 and parm1 and LR_*.
  • 3.06 [7179] parm0 is now the output var index, the other parameters shifted one place.

EW_SETFLAG

  • 3.02 [r6841] alter_reg_view can be KEY_WOW64_32KEY in 64-bit stubs and KEY_WOW64_64KEY in 32-bit stubs.
  • 3.09 [r7341] parm2 can also be < 0

EW_SHELLEXEC

  • 3.02 [r6839] Parameter count changed. parm4 is SHELLEXECUTEINFO.fMask and SEE_MASK_NOCLOSEPROCESS is set for ExecShellWait.

EW_WRITEREG

  • 3.02 [r6829] ent.offsets[5] is REG_MULTI_SZ for WriteRegMultiStr.