From b578a52428c64aaac905cdb60302810811df35b3 Mon Sep 17 00:00:00 2001 From: MBellahcene Date: Thu, 9 Jul 2026 11:25:13 +0200 Subject: [PATCH 1/2] ACI0106381 Signature on Mac --- Build4D/Project/Sources/Classes/Client.4dm | 1 + Build4D/Project/Sources/Classes/Server.4dm | 1 + .../Project/Sources/Classes/Standalone.4dm | 1 + Build4D/Project/Sources/Classes/_core.4dm | 49 +++++++++++++++++++ 4 files changed, 52 insertions(+) diff --git a/Build4D/Project/Sources/Classes/Client.4dm b/Build4D/Project/Sources/Classes/Client.4dm index e054545..4047b1d 100644 --- a/Build4D/Project/Sources/Classes/Client.4dm +++ b/Build4D/Project/Sources/Classes/Client.4dm @@ -503,6 +503,7 @@ Function build() : Boolean $success:=($success) ? This._checkDestinationFolder() : False //$success:=($success) ? This._compileProject() : False // util ? $success:=($success) ? This._copySourceApp() : False + $success:=($success) ? This._removeSignature() : False $success:=($success) ? This._renameExecutable() : False $success:=($success) ? This._setAppOptions() : False $success:=($success) ? This._excludeModules() : False diff --git a/Build4D/Project/Sources/Classes/Server.4dm b/Build4D/Project/Sources/Classes/Server.4dm index 4bd3011..9fd7e23 100644 --- a/Build4D/Project/Sources/Classes/Server.4dm +++ b/Build4D/Project/Sources/Classes/Server.4dm @@ -872,6 +872,7 @@ Function build() : Boolean $success:=($success) ? This._createStructure() : False $success:=($success) ? This._fix_settings() : False $success:=($success) ? This._copySourceApp() : False + $success:=($success) ? This._removeSignature() : False $success:=($success) ? This._renameExecutable() : False $success:=($success) ? This._setAppOptions() : False $success:=($success) ? This._excludeModules() : False //#2029 diff --git a/Build4D/Project/Sources/Classes/Standalone.4dm b/Build4D/Project/Sources/Classes/Standalone.4dm index 2d1bd27..4c083db 100644 --- a/Build4D/Project/Sources/Classes/Standalone.4dm +++ b/Build4D/Project/Sources/Classes/Standalone.4dm @@ -142,6 +142,7 @@ Function build()->$success : Boolean $success:=($success) ? This._compileProject() : False $success:=($success) ? This._createStructure() : False $success:=($success) ? This._copySourceApp() : False + $success:=($success) ? This._removeSignature() : False $success:=($success) ? This._renameExecutable() : False $success:=($success) ? This._setAppOptions() : False $success:=($success) ? This._excludeModules() : False diff --git a/Build4D/Project/Sources/Classes/_core.4dm b/Build4D/Project/Sources/Classes/_core.4dm index 42634e5..045d60d 100644 --- a/Build4D/Project/Sources/Classes/_core.4dm +++ b/Build4D/Project/Sources/Classes/_core.4dm @@ -1432,6 +1432,55 @@ Function _change_uuid() : Boolean return True End if + //MARK:- Removes the code signature inherited from the copied source application. + +/* +Function _removeSignature()-> $status : Boolean +.................................................................................... +Parameter Type in/out Description +.................................................................................... +$status Boolean out True if the signature has been removed or removal is not applicable. +.................................................................................... + +Removes the macOS code signature inherited from the copied source application (4D Volume Desktop / 4D Server) +before it gets modified. Renaming the executable, editing the Info.plist, excluding modules or changing the +uuids invalidates the inherited signature, so it is stripped right after the copy to guarantee a clean state +before the application is eventually re-signed by _sign(). +*/ + +Function _removeSignature() : Boolean + + var $commandLine : Text + var $worker : 4D.SystemWorker + + If (This.is_mac_target && Is macOS) + + If (This.settings.destinationFolder.exists) + + $commandLine:="/usr/bin/codesign --remove-signature --deep '"+This.toPosix(This.settings.destinationFolder).path+"'" + + $worker:=4D.SystemWorker.new($commandLine) + $worker.wait() + + If ($worker.terminated && ($worker.exitCode=0)) + This._log(New object(\ + "function"; "Remove signature"; \ + "message"; "Source application signature removed."; \ + "severity"; Information message)) + Else + This._log(New object(\ + "function"; "Remove signature"; \ + "message"; "Unable to remove the source application signature."; \ + "severity"; Warning message; \ + "signatureReturn"; $worker.response)) + End if + + End if + + End if + + return True + //MARK:- Signs the project /* From 2f9a45aacf2ca4bc635a1d82512f6ab43765dd05 Mon Sep 17 00:00:00 2001 From: MBellahcene Date: Thu, 9 Jul 2026 11:45:26 +0200 Subject: [PATCH 2/2] Remowe Windows signature --- Build4D/Project/Sources/Classes/_core.4dm | 165 +++++++++++++++++++--- 1 file changed, 144 insertions(+), 21 deletions(-) diff --git a/Build4D/Project/Sources/Classes/_core.4dm b/Build4D/Project/Sources/Classes/_core.4dm index 045d60d..f200ae0 100644 --- a/Build4D/Project/Sources/Classes/_core.4dm +++ b/Build4D/Project/Sources/Classes/_core.4dm @@ -1442,43 +1442,166 @@ Parameter Type in/out Description $status Boolean out True if the signature has been removed or removal is not applicable. .................................................................................... -Removes the macOS code signature inherited from the copied source application (4D Volume Desktop / 4D Server) +Removes the code signature inherited from the copied source application (4D Volume Desktop / 4D Server) before it gets modified. Renaming the executable, editing the Info.plist, excluding modules or changing the uuids invalidates the inherited signature, so it is stripped right after the copy to guarantee a clean state -before the application is eventually re-signed by _sign(). +before the application is eventually re-signed. +On macOS the whole bundle signature is removed with codesign. On Windows the Authenticode signature is stripped +from the main executable (4D Volume Desktop.4DE / 4D Server.exe) by clearing its PE certificate table, so that +a customer re-signing the built application starts from a clean, unsigned executable. */ Function _removeSignature() : Boolean var $commandLine : Text var $worker : 4D.SystemWorker + var $executable : 4D.File - If (This.is_mac_target && Is macOS) - - If (This.settings.destinationFolder.exists) + Case of - $commandLine:="/usr/bin/codesign --remove-signature --deep '"+This.toPosix(This.settings.destinationFolder).path+"'" + : (This.is_mac_target && Is macOS) - $worker:=4D.SystemWorker.new($commandLine) - $worker.wait() + If (This.settings.destinationFolder.exists) + + $commandLine:="/usr/bin/codesign --remove-signature --deep '"+This.toPosix(This.settings.destinationFolder).path+"'" + + $worker:=4D.SystemWorker.new($commandLine) + $worker.wait() + + If ($worker.terminated && ($worker.exitCode=0)) + This._log(New object(\ + "function"; "Remove signature"; \ + "message"; "Source application signature removed."; \ + "severity"; Information message)) + Else + This._log(New object(\ + "function"; "Remove signature"; \ + "message"; "Unable to remove the source application signature."; \ + "severity"; Warning message; \ + "signatureReturn"; $worker.response)) + End if + + End if - If ($worker.terminated && ($worker.exitCode=0)) - This._log(New object(\ - "function"; "Remove signature"; \ - "message"; "Source application signature removed."; \ - "severity"; Information message)) - Else - This._log(New object(\ - "function"; "Remove signature"; \ - "message"; "Unable to remove the source application signature."; \ - "severity"; Warning message; \ - "signatureReturn"; $worker.response)) + : (This.is_win_target) + + $executable:=This.settings.destinationFolder.file("4D Volume Desktop.4DE") + If (Not($executable.exists)) + $executable:=This.settings.destinationFolder.file("4D Server.exe") End if + This._removeWindowsSignature($executable) - End if - + End case + + return True + + //MARK:- Removes the Windows Authenticode signature from a PE executable. + +/* +Function _removeWindowsSignature($executable : 4D.File)-> $status : Boolean +.................................................................................... +Parameter Type in/out Description +.................................................................................... +$executable 4D.File in PE executable to strip (4D Volume Desktop.4DE / 4D Server.exe). +$status Boolean out True once processed (best effort, never blocks the build). +.................................................................................... + +Clears the attribute certificate table referenced by the PE "security" data directory (entry #4) and truncates +the appended signature block. The PE checksum is reset because it is no longer valid. This leaves a clean, +unsigned executable that can be modified by the build and, if needed, re-signed afterwards by the customer. +*/ + +Function _removeWindowsSignature($executable : 4D.File) : Boolean + + var $blob : Blob + var $fileSize; $peOffset; $optOffset; $magic; $dataDirOffset; $secDirOffset : Integer + var $certOffset; $certSize; $i : Integer + + If (Not($executable.exists)) + return True End if + $blob:=$executable.getContent() + $fileSize:=BLOB size($blob) + + // Must hold a full PE header + If ($fileSize<512) + return True + End if + + // DOS signature 'MZ' ('M'=77, 'Z'=90) + If (($blob{0}#77) || ($blob{1}#90)) + return True + End if + + // e_lfanew: file offset of the PE header, 4-byte little-endian value at offset 60 (0x3C) + $peOffset:=$blob{60}+($blob{61}*256)+($blob{62}*65536)+($blob{63}*16777216) + + If (($peOffset<64) || (($peOffset+64)>=$fileSize)) + return True + End if + + // PE signature 'PE\0\0' ('P'=80, 'E'=69) + If (($blob{$peOffset}#80) || ($blob{$peOffset+1}#69) || ($blob{$peOffset+2}#0) || ($blob{$peOffset+3}#0)) + return True + End if + + // Optional header starts after the PE signature (4 bytes) and the COFF header (20 bytes) + $optOffset:=$peOffset+24 + + // Optional header magic: 267 (0x10B) = PE32, 523 (0x20B) = PE32+ + $magic:=$blob{$optOffset}+($blob{$optOffset+1}*256) + + Case of + + : ($magic=267) // PE32: data directories start at optional header offset 96 + $dataDirOffset:=$optOffset+96 + + : ($magic=523) // PE32+: data directories start at optional header offset 112 + $dataDirOffset:=$optOffset+112 + + Else + return True // Not a recognized PE optional header + + End case + + // "Security" directory is data directory entry #4 (each entry is 8 bytes: 4 offset + 4 size) + $secDirOffset:=$dataDirOffset+(4*8) + + If (($secDirOffset+8)>$fileSize) + return True + End if + + $certOffset:=$blob{$secDirOffset}+($blob{$secDirOffset+1}*256)+($blob{$secDirOffset+2}*65536)+($blob{$secDirOffset+3}*16777216) + $certSize:=$blob{$secDirOffset+4}+($blob{$secDirOffset+5}*256)+($blob{$secDirOffset+6}*65536)+($blob{$secDirOffset+7}*16777216) + + If (($certOffset<=0) || ($certSize<=0)) + return True // No Authenticode signature present + End if + + // Clear the security data directory entry (8 bytes) + For ($i; 0; 7) + $blob{$secDirOffset+$i}:=0 + End for + + // Reset the PE checksum (optional header offset 64): it is no longer valid + For ($i; 0; 3) + $blob{$optOffset+64+$i}:=0 + End for + + // The certificate table is stored at the very end of the file: remove it + If (($certOffset<$fileSize) && (($certOffset+$certSize)>=($fileSize-16))) + SET BLOB SIZE($blob; $certOffset) + End if + + $executable.setContent($blob) + + This._log(New object(\ + "function"; "Remove signature"; \ + "message"; "Source application signature removed."; \ + "severity"; Information message; \ + "path"; $executable.path)) + return True //MARK:- Signs the project