Modifying Manifests stored in exe results in cannot run.
Posted: Sat Dec 01, 2012 12:40 pm
I was hoping to automate 'fixing' the manifest. (author, product,version,etc)
(Ideally I think we should be able to use a myprogram.exe.manifest that is auto-imported, or at the very least, populate it with information from the "Compiler Options->Version Info")
I have some simple code that works in updating the manifest, but the resulting exe yields an error:
"The system cannot execute the specified program."
Doing a byte-by-byte compare, the only differences between the 'original' and the 'modified' is the xml for the manifest. filesize remains unchanged.
Is there a magic byte or two in the exe that specifies a validation of the manifest?
I have tried keeping the file the same size and also keeping the same # of 'nulls' at the end of the file. Either way, the resulting file cannot be executed, so there must be something telling the os that the manifest isn't right...
FWIW, here's the code i'm using:
test Exe:
Code to read/modify/save the manifest w/in the exe (This isn't great code, it was just test code to see it can be done...)
(Ideally I think we should be able to use a myprogram.exe.manifest that is auto-imported, or at the very least, populate it with information from the "Compiler Options->Version Info")
I have some simple code that works in updating the manifest, but the resulting exe yields an error:
"The system cannot execute the specified program."
Doing a byte-by-byte compare, the only differences between the 'original' and the 'modified' is the xml for the manifest. filesize remains unchanged.
Is there a magic byte or two in the exe that specifies a validation of the manifest?
I have tried keeping the file the same size and also keeping the same # of 'nulls' at the end of the file. Either way, the resulting file cannot be executed, so there must be something telling the os that the manifest isn't right...
FWIW, here's the code i'm using:
test Exe:
Code: Select all
MessageRequester("Test","Test")
; IDE Options = PureBasic 5.00 (Windows - x86)
; CursorPosition = 1
; EnableUser
; Executable = manifestHackTest.exe
; DisableDebugger
; IncludeVersionInfo
; VersionField0 = 1,2,3,4
; VersionField1 = 1,2,3,5
; VersionField2 = Test Company
; VersionField3 = Product Name
; VersionField4 = 1.2.3.6
; VersionField5 = 1.2.3.7
; VersionField6 = 1.2.3.8
; VersionField15 = VOS_NT_WINDOWS32
; VersionField16 = VFT_APPCode: Select all
;- Setting
;{
Enumeration ; file Operation mode
#KeepFileSameSize
#KeepTrailingNullsConsistient
EndEnumeration
#testing = #True
#mode = #KeepTrailingNullsConsistient
;#mode = #KeepFileSameSize
EnableExplicit ;}
Procedure ModifyManfiest( cFile.s, cNewAuthorInfo.s, cNewVersion.s )
Protected nStart, nEnd, nLen, nWritten, i
Protected fh, hXML, *mem, *xml
Protected bSuccess = #False
CompilerIf #mode = #KeepTrailingNullsConsistient
Protected nEndNullCount
CompilerEndIf
;- Read file
fh = OpenFile(#PB_Any, cFile) ;{
If fh
*mem = AllocateMemory(Lof(fh)) ;{
If *mem
If ReadData(fh, *mem, Lof(fh)) = Lof(fh) ;{
;- Find the xml code
For i = Lof(fh)-10 To 0 Step -1 ;{
If PeekS(*mem+i, 11) = "</assembly>"
nEnd = *mem+i+11
ElseIf PeekS(*mem+i, 10) = "<assembly "
nStart = *mem+i
FileSeek(fh, i) ; Reset file pointer to start of xml
Break
EndIf
Next ;}
Debug "original XML Length: "+Str(nEnd-nStart)
;Debug "XML: "+PeekS(nStart, nEnd-nStart)
CompilerIf #mode = #KeepTrailingNullsConsistient
; Count the nulls, only needed if we're keeping the same null count of the new file--this way either shortens or grows the exe
For i = *mem+Lof(fh) To *mem Step -1 ;{
If PeekB( i-1 ) = 0
nEndNullCount+1
Else : Break;
EndIf
Next ;}
Debug "End Null Count: "+Str(nEndNullCount)
CompilerEndIf
;- Work with the xml
hXML = CatchXML(#PB_Any, nStart, nEnd-nStart) ;{
If hXML
*xml = MainXMLNode(hXML)
*xml = XMLNodeFromPath(*xml, "/assembly/assemblyIdentity")
; Set the new values we want to use.
SetXMLAttribute(*xml, "name", cNewAuthorInfo)
SetXMLAttribute(*xml, "version", cNewVersion)
;- Save the xml to memory
nLen = ExportXMLSize(hXML, #PB_XML_NoDeclaration) ;{
Debug "New XML Length: "+Str(nLen )
If ExportXML(hXML, nStart, nLen, #PB_XML_NoDeclaration)
; Close out the xml stuff.
FreeXML(hXML)
;- Write the new manifest to the fle
For i = 0 To nLen ;{
; this 1st case is only trying to keep the xml output similar.
If PeekS(nStart+i, Len("<description/>"))="<description/>"
i+Len("<description/>")
WriteString(fh, "<description></description>")
nWritten + Len("<description></description>")
ElseIf PeekB(nStart+i)<>$0D ; the xml didn't have any $0d (Linefeed), so write any bytes that are not $0d
nWritten+1
WriteByte(fh, PeekB(nStart+i))
EndIf
Next ;}
;- Now either pad the same # of nulls or keep the file the same length...
CompilerSelect #mode
CompilerCase #KeepFileSameSize ; Pad file With nulls To original file length.
Debug "Keeping the file the same size" ;{
While Loc(fh)<Lof(fh)
WriteByte(fh, 0)
Wend ;}
CompilerCase #KeepTrailingNullsConsistient ; pad file with the same # of nulls the original file had.
Debug "Padding file with nulls (truncate or grow file as needed)" ;{
For i = 1 To nEndNullCount
WriteByte(fh, 0)
Next
TruncateFile(fh) ;}
CompilerEndSelect
bSuccess=#True
Else : Debug "Failed to Export XML"
EndIf ;}
Else : Debug "Failed to Catch XML"
EndIf ;}
Else : Debug "Failed to read complete file"
EndIf ;}
FreeMemory(*mem)
Else : Debug "Failed to allocate memory"
EndIf ;}
CloseFile(fh)
Else : Debug "Failed to open file"
EndIf ;}
ProcedureReturn bSuccess
EndProcedure
CompilerIf #testing ;{
If FileSize("manifestHackTest.exe") < 0
Debug "Please build the hack test program 1st"
EndIf
If FileSize("manifestHackTest.exe-org") = -1
Debug "Creating back/original"
CopyFile("manifestHackTest.exe","manifestHackTest.exe-org")
Else : Debug "Restoring original"
CopyFile("manifestHackTest.exe-org","manifestHackTest.exe")
EndIf
If ModifyManfiest("manifestHackTest.exe","New Author.New Company.New Product", "1.2.3.4")
Debug "Modified OK"
Else : Debug "Failed to modify"
EndIf
CompilerEndIf ;}