Skip to content

Commit d0a5c9d

Browse files
Merge pull request #13 from 4d/MB/sfix
ACI0106381 Build4D : Muist remove 4DServer-copy signature
2 parents 850ba60 + 2f9a45a commit d0a5c9d

4 files changed

Lines changed: 175 additions & 0 deletions

File tree

Build4D/Project/Sources/Classes/Client.4dm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,7 @@ Function build() : Boolean
503503
$success:=($success) ? This._checkDestinationFolder() : False
504504
//$success:=($success) ? This._compileProject() : False // util ?
505505
$success:=($success) ? This._copySourceApp() : False
506+
$success:=($success) ? This._removeSignature() : False
506507
$success:=($success) ? This._renameExecutable() : False
507508
$success:=($success) ? This._setAppOptions() : False
508509
$success:=($success) ? This._excludeModules() : False

Build4D/Project/Sources/Classes/Server.4dm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -872,6 +872,7 @@ Function build() : Boolean
872872
$success:=($success) ? This._createStructure() : False
873873
$success:=($success) ? This._fix_settings() : False
874874
$success:=($success) ? This._copySourceApp() : False
875+
$success:=($success) ? This._removeSignature() : False
875876
$success:=($success) ? This._renameExecutable() : False
876877
$success:=($success) ? This._setAppOptions() : False
877878
$success:=($success) ? This._excludeModules() : False //#2029

Build4D/Project/Sources/Classes/Standalone.4dm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ Function build()->$success : Boolean
142142
$success:=($success) ? This._compileProject() : False
143143
$success:=($success) ? This._createStructure() : False
144144
$success:=($success) ? This._copySourceApp() : False
145+
$success:=($success) ? This._removeSignature() : False
145146
$success:=($success) ? This._renameExecutable() : False
146147
$success:=($success) ? This._setAppOptions() : False
147148
$success:=($success) ? This._excludeModules() : False

Build4D/Project/Sources/Classes/_core.4dm

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)