@@ -1432,6 +1432,178 @@ Function _change_uuid() : Boolean
14321432 return True
14331433 End if
14341434
1435+ // MARK:- Removes the code signature inherited from the copied source application.
1436+
1437+ /*
1438+ Function _removeSignature()-> $status : Boolean
1439+ ....................................................................................
1440+ Parameter Type in/out Description
1441+ ....................................................................................
1442+ $status Boolean out True if the signature has been removed or removal is not applicable.
1443+ ....................................................................................
1444+
1445+ Removes the code signature inherited from the copied source application (4D Volume Desktop / 4D Server)
1446+ before it gets modified. Renaming the executable, editing the Info.plist, excluding modules or changing the
1447+ uuids invalidates the inherited signature, so it is stripped right after the copy to guarantee a clean state
1448+ before the application is eventually re-signed.
1449+ On macOS the whole bundle signature is removed with codesign. On Windows the Authenticode signature is stripped
1450+ from the main executable (4D Volume Desktop.4DE / 4D Server.exe) by clearing its PE certificate table, so that
1451+ a customer re-signing the built application starts from a clean, unsigned executable.
1452+ */
1453+
1454+ Function _removeSignature () : Boolean
1455+
1456+ var $commandLine : Text
1457+ var $worker : 4D.SystemWorker
1458+ var $executable : 4D.File
1459+
1460+ Case of
1461+
1462+ : (This .is_mac_target && Is macOS)
1463+
1464+ If (This .settings .destinationFolder .exists )
1465+
1466+ $commandLine := "/usr/bin/codesign --remove-signature --deep '" + This .toPosix (This .settings.destinationFolder).path + "'"
1467+
1468+ $worker := 4D.SystemWorker.new($commandLine)
1469+ $worker .wait ()
1470+
1471+ If ($worker .terminated && ($worker .exitCode = 0))
1472+ This ._log (New object (\
1473+ "function" ; "Remove signature" ; \
1474+ "message" ; "Source application signature removed." ; \
1475+ "severity" ; Information message))
1476+ Else
1477+ This ._log (New object (\
1478+ "function" ; "Remove signature" ; \
1479+ "message" ; "Unable to remove the source application signature." ; \
1480+ "severity" ; Warning message; \
1481+ "signatureReturn" ; $worker .response ))
1482+ End if
1483+
1484+ End if
1485+
1486+ : (This .is_win_target )
1487+
1488+ $executable := This .settings .destinationFolder .file ("4D Volume Desktop.4DE" )
1489+ If (Not ($executable .exists ))
1490+ $executable := This .settings .destinationFolder .file ("4D Server.exe" )
1491+ End if
1492+ This ._removeWindowsSignature ($executable )
1493+
1494+ End case
1495+
1496+ return True
1497+
1498+ // MARK:- Removes the Windows Authenticode signature from a PE executable.
1499+
1500+ /*
1501+ Function _removeWindowsSignature($executable : 4D.File)-> $status : Boolean
1502+ ....................................................................................
1503+ Parameter Type in/out Description
1504+ ....................................................................................
1505+ $executable 4D.File in PE executable to strip (4D Volume Desktop.4DE / 4D Server.exe).
1506+ $status Boolean out True once processed (best effort, never blocks the build).
1507+ ....................................................................................
1508+
1509+ Clears the attribute certificate table referenced by the PE "security" data directory (entry #4) and truncates
1510+ the appended signature block. The PE checksum is reset because it is no longer valid. This leaves a clean,
1511+ unsigned executable that can be modified by the build and, if needed, re-signed afterwards by the customer.
1512+ */
1513+
1514+ Function _removeWindowsSignature ($executable : 4D.File) : Boolean
1515+
1516+ var $blob : Blob
1517+ var $fileSize ; $peOffset ; $optOffset ; $magic ; $dataDirOffset ; $secDirOffset : Integer
1518+ var $certOffset ; $certSize ; $i : Integer
1519+
1520+ If (Not ($executable .exists ))
1521+ return True
1522+ End if
1523+
1524+ $blob := $executable .getContent ()
1525+ $fileSize := BLOB size ($blob )
1526+
1527+ // Must hold a full PE header
1528+ If ($fileSize< 512)
1529+ return True
1530+ End if
1531+
1532+ // DOS signature 'MZ' ('M'=77, 'Z'=90)
1533+ If (($blob{0}# 77) || ($blob{1}# 90))
1534+ return True
1535+ End if
1536+
1537+ // e_lfanew: file offset of the PE header, 4-byte little-endian value at offset 60 (0x3C)
1538+ $peOffset := $blob {60 }+ ($blob{61}* 256)+ ($blob{62}* 65536)+ ($blob{63}* 16777216)
1539+
1540+ If (($peOffset< 64) || (($peOffset+ 64)>= $fileSize))
1541+ return True
1542+ End if
1543+
1544+ // PE signature 'PE\0\0' ('P'=80, 'E'=69)
1545+ If (($blob{$peOffset}# 80) || ($blob{$peOffset+ 1}# 69) || ($blob{$peOffset+ 2}# 0) || ($blob{$peOffset+ 3}# 0))
1546+ return True
1547+ End if
1548+
1549+ // Optional header starts after the PE signature (4 bytes) and the COFF header (20 bytes)
1550+ $optOffset := $peOffset + 24
1551+
1552+ // Optional header magic: 267 (0x10B) = PE32, 523 (0x20B) = PE32+
1553+ $magic := $blob {$optOffset }+ ($blob{$optOffset+ 1}* 256)
1554+
1555+ Case of
1556+
1557+ : ($magic= 267) // PE32: data directories start at optional header offset 96
1558+ $dataDirOffset := $optOffset + 96
1559+
1560+ : ($magic= 523) // PE32+: data directories start at optional header offset 112
1561+ $dataDirOffset := $optOffset + 112
1562+
1563+ Else
1564+ return True // Not a recognized PE optional header
1565+
1566+ End case
1567+
1568+ // "Security" directory is data directory entry #4 (each entry is 8 bytes: 4 offset + 4 size)
1569+ $secDirOffset := $dataDirOffset + (4* 8)
1570+
1571+ If (($secDirOffset+ 8)> $fileSize)
1572+ return True
1573+ End if
1574+
1575+ $certOffset := $blob {$secDirOffset }+ ($blob{$secDirOffset+ 1}* 256)+ ($blob{$secDirOffset+ 2}* 65536)+ ($blob{$secDirOffset+ 3}* 16777216)
1576+ $certSize := $blob {$secDirOffset + 4 }+ ($blob{$secDirOffset+ 5}* 256)+ ($blob{$secDirOffset+ 6}* 65536)+ ($blob{$secDirOffset+ 7}* 16777216)
1577+
1578+ If (($certOffset<= 0) || ($certSize<= 0))
1579+ return True // No Authenticode signature present
1580+ End if
1581+
1582+ // Clear the security data directory entry (8 bytes)
1583+ For ($i; 0; 7)
1584+ $blob {$secDirOffset + $i }:= 0
1585+ End for
1586+
1587+ // Reset the PE checksum (optional header offset 64): it is no longer valid
1588+ For ($i; 0; 3)
1589+ $blob {$optOffset + 64 + $i }:= 0
1590+ End for
1591+
1592+ // The certificate table is stored at the very end of the file: remove it
1593+ If (($certOffset< $fileSize) && (($certOffset+ $certSize)>= ($fileSize- 16)))
1594+ SET BLOB SIZE ($blob ; $certOffset )
1595+ End if
1596+
1597+ $executable .setContent ($blob )
1598+
1599+ This ._log (New object (\
1600+ "function" ; "Remove signature" ; \
1601+ "message" ; "Source application signature removed." ; \
1602+ "severity" ; Information message; \
1603+ "path" ; $executable .path ))
1604+
1605+ return True
1606+
14351607 // MARK:- Signs the project
14361608
14371609/*
0 commit comments