diff --git a/WDACConfig/Utilities/Hashes.csv b/WDACConfig/Utilities/Hashes.csv index 3a848cf4e..a245804e4 100644 --- a/WDACConfig/Utilities/Hashes.csv +++ b/WDACConfig/Utilities/Hashes.csv @@ -1,6 +1,6 @@ "RelativePath","FileName","FileHash" "Preloader.ps1","Preloader.ps1","4C822F6A351726D71EE95367FADE6F457093F890E5C63CC276288E6CC0DEE89E6BD6CAA8E63B16463D4ABAB1AD98C182053D123AE6428744DCE3810B73087492" -"WDACConfig.psd1","WDACConfig.psd1","7D8A75DDE1583A1412AF7D1A4B6D05BE981C61BAA2A7C388DDAAF79FA3827BCB528B07E5673A7B2F0E418E75DF86EEB2CB8535F85E0D2A72DCAE57ED704189E1" +"WDACConfig.psd1","WDACConfig.psd1","7678BA6628AE4B6CB67CA692644B3B4F6C448D87A39BE8F5752AED67D7ED7721AF52F4C5E48892C9EAACB88C986B32581C9E0F4B0086892CCB04CDB569371428" "WDACConfig.psm1","WDACConfig.psm1","E92B896C59A2A6F5680783221C4B102362E83DA36863CC29A4E8BAE55D6FEA156EF361AD7E5D443172647C2962B3B2DF67422C4C1D582A2E27E5287E13022175" "C#\AuthenticodeHashCalc.cs","AuthenticodeHashCalc.cs","6F005F578C098F0C95F4103D42B730BC5D59556CAA7EA886741C610464A798A4FD7F109F1DFF6C381983EFCEBFDCACEAA2C5D10BD733BFBB17E0F42053D98932" "C#\Crypt32CertCN.cs","Crypt32CertCN.cs","B01570F8BF0E291FE54DEB7C64953913E6EAF46D7661AB3B7F29F6861E00D93D37FBC908B2F4E4EB5D6FA4F22A3CDB091129F3510687FAEB32808957E854C312" @@ -11,13 +11,13 @@ "Core\Assert-WDACConfigIntegrity.psm1","Assert-WDACConfigIntegrity.psm1","767C9BA369CCB7AEE55B96DD01665EAFC1B2625287DE47F5EDFEF5B52F934735BDA508FD0B85E29E2A932995CF645AC9D39275D163969775D78454512A54DAAC" "Core\Build-WDACCertificate.psm1","Build-WDACCertificate.psm1","8829A0D796FDE5524EC43526DE3B174016925D4ABE62F92FD9E12BC98F35AC251E84CC71962763003EA6194AB66A24D95DA5DA1B415E41B62EB0C32120F43BD8" "Core\Confirm-WDACConfig.psm1","Confirm-WDACConfig.psm1","4D8C9149881F3874F83DE935E9ED9E79111A7EFF939B6A177F07E9D2010E57F81507FFE4AA32B8D01D4681ABC359469251C47603AAB216807D94CB64259869F6" -"Core\ConvertTo-WDACPolicy.psm1","ConvertTo-WDACPolicy.psm1","9810C717EFBDE8027ADE14A6ED00D9495B53931BC0F93B0799C8AB27B76C45EB0F74EDCD9F6CF08563857C752722C5160EF872FCD62D730C5D70BBF89F907751" +"Core\ConvertTo-WDACPolicy.psm1","ConvertTo-WDACPolicy.psm1","7C905E20741F9FBD19AA7206FCD622BC5D3A4EF9D0E3F19E5ABB32EFB419657818833EC2E0691DF302DECADD901AE8FB1BD51992E368C72B1B7CACFDFE78886D" "Core\Deploy-SignedWDACConfig.psm1","Deploy-SignedWDACConfig.psm1","A252BC251EB7773E6EBD122F820551195ECAE3A047D28019B7584A035048326DA763BCEA2CE6412DA2A22BE1C83394F9C90B7B09929F056F8FEBE4E8C33BE01F" -"Core\Edit-SignedWDACConfig.psm1","Edit-SignedWDACConfig.psm1","43F796453F5DCAAD5F0BBF515B1725D16575CD9D52DEA60E2DE09F02C4C09C150B80069C75BF70772CDA49E10F3264BA629CEF058B35891C388F7347344B71F3" -"Core\Edit-WDACConfig.psm1","Edit-WDACConfig.psm1","B084DE1D0874EADF155297B46AD0F177CAB066EF38C54DBF550839243996C8116D6B95FA4ACE80A98899F84413597D4FF08261D92C123D8B28F9869776DA86A1" +"Core\Edit-SignedWDACConfig.psm1","Edit-SignedWDACConfig.psm1","16B6BEAC28E3726E8967CCD26423C3F065F51F186054EA0702413D41416078D3D452A66B0FA6A5D5EAFC00B63E4EA3749DE278559F617DB35CA0CB03C8873F01" +"Core\Edit-WDACConfig.psm1","Edit-WDACConfig.psm1","6808087131A59527229D86512313243D7580D3F0F58F58C5680DAFCA42D5FD87686B5B732EE74C60621909DB1EB4ABF4343AEBA18FE33C4D0B187A41FF7BEEA4" "Core\Get-CiFileHashes.psm1","Get-CiFileHashes.psm1","D429847D687319F6606A6780EC7AB5413B5BFBC43A1BA65486558443740F0079EE34CD2015FA6C56B01B1FC7D75A5937B7D31D0613DEE4C74B3CF2709F7A90D1" "Core\Get-CommonWDACConfig.psm1","Get-CommonWDACConfig.psm1","067CEA3BB211D60E00185DB9679B5F3EBC099CDE260D034D97FB4E504C21BFC319422438E0802C3246D8FA669608B40CD36CECC534E9CA595911087F2E7B78DC" -"Core\Invoke-WDACSimulation.psm1","Invoke-WDACSimulation.psm1","C848AB1872AE5F38B7A2558F2E565DB7602AB9D20A9A11EB57892849B2BA822696F19CC39485AAC55193BA961CC46163AFC5C6137379225A62896379B2BCD65A" +"Core\Invoke-WDACSimulation.psm1","Invoke-WDACSimulation.psm1","AD2B167307E759A9940A9012E78AD6CD8A3B7CBBDF135C680E3482603140F92B9215B53A927288CB156F306BE0CC9542404D8DFC0D55E9644EAB998D73418CF6" "Core\New-DenyWDACConfig.psm1","New-DenyWDACConfig.psm1","1313BBCCA6638E78E7E427B3B4F5CE587C456D8097036C3EF784120B13C6B8F6A26AB0A575671345E75D777010BE57B43B34BF53EA80AEFED1700C47E280A399" "Core\New-KernelModeWDACConfig.psm1","New-KernelModeWDACConfig.psm1","1BAFE24AEE1D179ECD9C1F03A2E03515E11FE9F9180808E63A2954514CBCF7CE8A1C0DB90F433E4BCF636F471D8089D1C39115439D285836E70947FAE7C5DE48" "Core\New-SupplementalWDACConfig.psm1","New-SupplementalWDACConfig.psm1","DE2CA8BA04643A4F35AA0B15C4650FF317104B235D23EB9FE5B844A050B0D187AB71396E061BC100AAA4398BC1ECFF39491F0287AE7ECAA18CDE7D2821C918A7" @@ -27,8 +27,10 @@ "Core\Set-CommonWDACConfig.psm1","Set-CommonWDACConfig.psm1","FF3BC72C2A341343016126378A3D595EEA28A97FA590BE98DBE8AB3FA2749F5EED5FA283CA8ED6810914ED0119A54F9344CED075FC9C7EC8DE38A5435F1EB23A" "Core\Test-CiPolicy.psm1","Test-CiPolicy.psm1","269B0AAE9282F8A4BE7DA9AF21DA91508648A53FF2F398EA62E96C6E40ED6562A1677CD75BA7CBE54AACFCCEEEED8663783ACDDDC75DB9DE13ED84922CE42E96" "CoreExt\ArgumentCompleters.ps1","ArgumentCompleters.ps1","2AA08D4681A2893CDB1BFD391F590CE3E8DF24801C26F23E19AA1EEED43A31BCA7DBE48EC3F34D7EE88DB688FB6B352E2010C4C4A0373696EDBC96971E6520A9" -"CoreExt\Classes.psm1","Classes.psm1","CE3EA89356F80F071FDE2ECAC57FC1FAADD369AAA868B9BBA13716A54932E572A47A3A4A58BC5E66AEA00E1466DF0FDE5DFEE2233F1F307C13B3F761818E7829" -"CoreExt\PSDefaultParameterValues.ps1","PSDefaultParameterValues.ps1","7DFB3182D718B6A08833187B032E1D3DEBD069E4950F65A841D00942EE4735881CC6C2DF147EE031981B3150980CA572D6CB015F03548D3E0FE23D797F709621" +"CoreExt\Classes.psm1","Classes.psm1","578D7E100B3BC8EA2E131DB59E5665559FF6C5325851C0CC1694D7CED686FCD7851FE2E787E9816D48BA604195D6A24D17E62667DB1A3B12E1BD646CE24C47B7" +"CoreExt\PSDefaultParameterValues.ps1","PSDefaultParameterValues.ps1","6B169E44E0623CFBBDA3EFB1EA3F05421064E884315EE44233AEDD7A268D3F7503D6F394575CCE25E3B190F5C52F5936FC0F7D5ED761DEA919F7FACE7D9133AD" +"Help\ConvertTo-WDACPolicy.md","ConvertTo-WDACPolicy.md","291AA2EE8A6EDDEB95911D72B479CC855596A07DEA30DCA0D1475BA70816083E3DDCBFEBBE7DEB9D1C77AB26D968B928FF90D17F915D9C56903475D9C91BE71D" +"Help\ConvertTo-WDACPolicy.xml","ConvertTo-WDACPolicy.xml","4D7459682CBF6C5686463E64F60EFC7FDFD26D25DD09435BEDB8862B281FE93CFA2187DB35DF8CA3E992EB296447DB633E630BB4EF627A85AA6CF5AE1FEB7049" "Resources\User Configurations\Schema.json","Schema.json","9A20EF0148D298178B35C1AAB961C46AF62BBCC0BB0DCCBE63F2FE08E0A764406267449CDD686A01F85650622DA6E690D12FBB88BB3A7E070BA58C1AF8FBC813" "Resources\WDAC Policies\DefaultWindows_Enforced_Kernel_NoFlights.xml","DefaultWindows_Enforced_Kernel_NoFlights.xml","7E4BC35A3F0840C8F3921FB260CE84660DC3CAACB7850A1AEF13AFC48B0E069D27562C5632444926BF60B44A0E0FF522D0215F1F7DD5E1A7E51A45E86AB7F44C" "Resources\WDAC Policies\DefaultWindows_Enforced_Kernel.xml","DefaultWindows_Enforced_Kernel.xml","846663A7B0CAD90A2305F3C3322D6C2CFA6277B7E4B083CB478FF409DB29A7D0D71318845B884518B8D2F87B66A5EA327D4EB2D39A9707D1EE41B0237812FFD6" @@ -49,18 +51,35 @@ "Shared\New-EmptyPolicy.psm1","New-EmptyPolicy.psm1","1611AE2E9056EF671CA6FC89E7D6BFA8D902E13E12B54AAEE5C1A4DE46BE612D152D8DBAC1FC03114B8346E8C649A1AF846DB646CA09C028BEB475C14B94C995" "Shared\New-SnapBackGuarantee.psm1","New-SnapBackGuarantee.psm1","5AA83D8ABEA7C035BB4437FFD3A1F47CAB845153FA85C3ADB626C1D3407918AA44E4A0EAF32502DD84586A7C5CB82CDC7BA1AF98A310B07114A47E29F48C5F0F" "Shared\New-StagingArea.psm1","New-StagingArea.psm1","CD8A3848EF01ACABD69A25B63C71EA57210290E8B7C5EB3FDDD488925E7EF0C24C6C3E99B45ABEDD5B775D175B338D70805E4AC0E62FA10BDD649C2696EBF2FD" -"Shared\Receive-CodeIntegrityLogs.psm1","Receive-CodeIntegrityLogs.psm1","ECA7FBE858CB368DF30EA786983DF73E86B4FCDC4612E0A5AF84053B456E49B7DF751831C473BC19985B4DEFE00DC8E9239A8CDA0F0B6F522BEC6C7473CE98D2" +"Shared\Receive-CodeIntegrityLogs.psm1","Receive-CodeIntegrityLogs.psm1","A42AC4546926A5031F03F7D9EB4EEA7BCAC1AD8BF410F6FAE9F018AE52293E6AC7E604E696A4706375C1A65AFDDE4DFCAC15A465FDAA08C924C83880D07E00E7" "Shared\Remove-SupplementalSigners.psm1","Remove-SupplementalSigners.psm1","D84AACBCDAA29393961B288893FD1B46EABCE0ED810CA8E8B655261AC68E30537F6A43D0686942E24401DFB39066E4DF31E3ED6AA12664735BC713218997C86C" "Shared\Set-LogSize.psm1","Set-LogSize.psm1","0BAC2DF856B5C8BA403ECC913493AFB435CA3F7498D71D960150D9F9373EBE120CF78F4600755393FC69B90D326BD8C632F77E6403C027654B638D53C71B9E72" "Shared\Test-FilePath.psm1","Test-FilePath.psm1","D42A57517115D519FAE03873312528FBCF2EBD062462EADA3A86967534ED3E59D92AC80A7259115550E7F78325B62812C58CC8A5E942661426C57DD9608B0B8C" "Shared\Update-self.psm1","Update-self.psm1","38158B9DF44B512489E8FD70532C005532BB0769FE192E85FC3675F054B594AEA8BA74D85078442D4DD8831DA827427915AEBC8F17CCF76D73ED7ECA13D0F8AD" "Shared\Write-ColorfulText.psm1","Write-ColorfulText.psm1","0A5897DB6D3ACA13D0151A438CD926EF82D3242646501ED92C453E4AB09D86A7E2F3DB51FBB0DFBA989DFE0AF5F19E48EB6D3D76F0BF8EAF98BB64105CD277DC" -"WDACSimulation\Compare-SignerAndCertificate.psm1","Compare-SignerAndCertificate.psm1","E965A9AF037891697687EF72C922653EEB27F02693758E6D4234D3EC1012758297FD04E6D2485B171AC99F7C814E92AD16CCFA14FA7C9A4532853930148FD679" +"WDACSimulation\Compare-SignerAndCertificate.psm1","Compare-SignerAndCertificate.psm1","A47433F898E80653600D89D470EC8E4E4E6B2CB45FA9C90AE54E5F4286CE57F8F857339D634C18E1A78B4F78D9A88FC54F2CA4DF5AE336B05C527B4F66B41469" "WDACSimulation\Convert-HexToOID.psm1","Convert-HexToOID.psm1","B5CC722C7C5E7F5ADF6D38DF2D45A5826470033038BAE21D56AC83BCE579F5DE724D51EBCC28D4A7246A30AED6410C446A5EA1FAC58B8B3C53ED140337339790" "WDACSimulation\Get-CertificateDetails.psm1","Get-CertificateDetails.psm1","61076F1492FB5D75FEAC0C37EB75ED73D50833E3A2E2E0D07B7CB955C9E32F03AACC51F3007DC788DEA19E2B5FF756C79DF5F0287A75AF8D003A7D6863157886" "WDACSimulation\Get-ExtendedFileInfo.psm1","Get-ExtendedFileInfo.psm1","D3A92D2BBE58814434A40653B6B3B30D8387B49605B21E87B7D40F928C6793530042174C95128BA853528B394BEDF2065E795907E82BB4241B875259A229D05D" "WDACSimulation\Get-FileRuleOutput.psm1","Get-FileRuleOutput.psm1","D7571D280E41B4182CEC3B3F6B5F1578ADE75A9F49FE9D46AB6B16A7CEB66F772143FBFF320BD87A9E32515699EE6237774D4CCDE865F0810616CEA552EF3EBD" "WDACSimulation\Get-NestedSignerSignature.psm1","Get-NestedSignerSignature.psm1","424AB15055989A0FD708703B767CCDC96F57FA05CA962265E910A4913DE243C8F870919F91F896714CCC251C8B3810BB13DCD1EB3D8C5FD76DA1A1712C17AF3F" -"WDACSimulation\Get-SignedFileCertificates.psm1","Get-SignedFileCertificates.psm1","85BE8E75B3CE997970D43AD57D8A57B79E4094FF339612B2E1C93AB30F6DB44DC9D7A128427D31DE716120A11C97118684EB4221FCA0C366B98B958D00783150" +"WDACSimulation\Get-SignedFileCertificates.psm1","Get-SignedFileCertificates.psm1","1BE5EA5E43FBD2905CF11993BDA9725EA8ADD28050C522B46E8F01158390BE7DB3F1AEC9AC23B87D486CBDE41A9B130B2DB9E9C7EA16E32F5914EEABF116DC9A" "WDACSimulation\Get-SignerInfo.psm1","Get-SignerInfo.psm1","018A291BE3FA9E5C62E99D520570D4188FE63167CCE779FC61740F20736E81A0FC5A518340D35DEC4CBF6E01DF1B2A079FF1A90D01664975A159312C869EC975" "WDACSimulation\Get-TBSCertificate.psm1","Get-TBSCertificate.psm1","B24B918D618CE38F7476629E9248D8A01B244B27696C66612A1773B3EA702FA055910F51721BAB0FD74504ED6D09D73818031C4F6A9BCD978498E0C0CE3A720C" +"XMLOps\Build-SignerAndHashObjects.psm1","Build-SignerAndHashObjects.psm1","55E79A5A0C3602B600810A04D2BF6C5FC349A1F82516EC98F60DECCA31E82CC2077D9EF753B9F5C97C8FCA1410944A699552950DB0FA063F0BEDA5932ED17AA7" +"XMLOps\Clear-CiPolicy_Semantic.psm1","Clear-CiPolicy_Semantic.psm1","328613181EBC1672CB43EA2F760ECD6106BA02F909E823567DCE61DC1BAF053215C9CDFA69457145413030537954B9660FA624CDCE1793EAB3583FCE83AE3687" +"XMLOps\Close-EmptyXmlNodes_Semantic.psm1","Close-EmptyXmlNodes_Semantic.psm1","30BC9CF1913E94B4F96E5799A470FE9966BF66AFADCDEC3F30CEFDAC5FC0558830012260FFD0FB010158DE7F851699361B6B1FB90F46BFBE98E195811732DD60" +"XMLOps\Compare-CorrelatedData.psm1","Compare-CorrelatedData.psm1","A4EC285272DE36FAE33474BACB6F3FF5C86EFD6B2E51A00C24E98AB033DEB56572717D56503D095E02FAFAA35A300C4FF15D6C4FA89FABD023CEE220ED8AD8A0" +"XMLOps\Merge-Signers_Semantic.psm1","Merge-Signers_Semantic.psm1","5D4665FC53B16F5B6FA7E7D0C844BFB9319D77277A35ADD0E0B30DB16AA36A8607605CA2645FB140FD21465A0C70B6618EE1369FB18756ECA9AA0B529EBE855E" +"XMLOps\New-FilePublisherLevelRules.psm1","New-FilePublisherLevelRules.psm1","0EA0C296CDA97200CFA19EAD3F5244E2FE6330916E0A654D840A7920D40B29AD882A200A82838D617F5EFE9EE95C955D0FCA69CED195B8ED6753457A3F07D31C" +"XMLOps\New-HashLevelRules.psm1","New-HashLevelRules.psm1","B455CC2BC5A72D01D8340ECB3316D53E9A3E50345D257FDE7293CAF045B8599B34C617D5688E8BF1E0C1104909096C0FE63DA1E15F37A618DA0D58D67BD1D0A3" +"XMLOps\New-PublisherLevelRules.psm1","New-PublisherLevelRules.psm1","54E5D7F8863BFA161855F428026CA09AF8AC21B70F94693026DCB3EF9E37AF6A2F1FC7697A4DF506885DD28A0267EC8D3D0A2170C512938C3DA07AACAF30EE1B" +"XMLOps\Optimize-MDECSVData.psm1","Optimize-MDECSVData.psm1","B3C36A0256F6B5E337E8A683857CE15F1D4BC42080932B78DB40A2CBB780EE1A0AF05804D0C8E711906F12C313611E67D597FB05759244839F6AAD22229FAC11" +"XMLOps\Remove-AllowElements_Semantic.psm1","Remove-AllowElements_Semantic.psm1","6E55A3040D587927C2A1F3193C24DF9D0FCBB697FEBDD8576114D2F9B6D9D4CC8D434A2E40E061625EF67EA4A0211CC7CA2177275E2D691FAEF1F6A5F4138E90" +"XMLOps\Remove-DuplicateAllowAndFileRuleRefElements_IDBased.psm1","Remove-DuplicateAllowAndFileRuleRefElements_IDBased.psm1","3C062F7EE782DE827CA9EDA1F694F3A622E8721418AF09BC9E6BC34FBA0BE0A6A94A2DCAE395F81C40666FC9EA4A79454526CCD530A956B24D2B0B227FD772E0" +"XMLOps\Remove-DuplicateAllowedSignersAndCiSigners_IDBased.psm1","Remove-DuplicateAllowedSignersAndCiSigners_IDBased.psm1","FE9002F58E08E450898F46DEF0740C24490432F37E83451AE912296CE3D4F1B15ECD8B19725F264BA106D3EF3FB8CAD7864A18402A8FC0915F7119A991CFFA27" +"XMLOps\Remove-DuplicateFileAttrib_IDBased.psm1","Remove-DuplicateFileAttrib_IDBased.psm1","EB5DA5DF837FF949BB7577C20C663F37B57C5FB0020B90FCB8B05B1AE1B2B945A87A04D7CD6418DBBF02CAFE661D78194F99A4239CE10F6E70E8904827F8454B" +"XMLOps\Remove-DuplicateFileAttrib_Semantic.psm1","Remove-DuplicateFileAttrib_Semantic.psm1","B2E46CADAF5FFEDB0A7ADDEE5139BC9AD2D4486E7B9662AA7BE453FA9C05580E64C9C3A330EFC810D617522B80670B0EA112E82907BC568F24AE87DD41BF03FE" +"XMLOps\Remove-DuplicateFileAttribRef_IDBased.psm1","Remove-DuplicateFileAttribRef_IDBased.psm1","E1D098448B068C906F718811C4C6EC346376101C8BE6A40A34325A05B843637FB94F2E103A5CC01F2DD491EAD13BD90B6A4E35046C24BA3ECB089AC67749BD3D" +"XMLOps\Remove-OrphanAllowedSignersAndCiSigners_IDBased.psm1","Remove-OrphanAllowedSignersAndCiSigners_IDBased.psm1","FC07416A3950D23EE0681BDC5004FE012183D4CA0ADE94523EA09F07FCC50E896C5AD309DA15BD3111A602D9369562A54F1BBB20A4F756B5977A33834196058D" +"XMLOps\Remove-UnreferencedFileRuleRefs.psm1","Remove-UnreferencedFileRuleRefs.psm1","EA05168A29FEE567D6D6C4D5DB77F264D289074450EF1F178B0C9C515BD417677BE1182824493F2980B58FB96151B97F1CE04830C929B4245D08B303D675F5E1" diff --git a/WDACConfig/Utilities/Invoke-WDACConfig.ps1 b/WDACConfig/Utilities/Invoke-WDACConfig.ps1 index 64722dee3..01b068229 100644 --- a/WDACConfig/Utilities/Invoke-WDACConfig.ps1 +++ b/WDACConfig/Utilities/Invoke-WDACConfig.ps1 @@ -18,3 +18,7 @@ foreach ($File in $Files) { # Replace with any cmdlet of the WDACConfig module that is going to be debugged # Assert-WDACConfigIntegrity -SaveLocally -Verbose + +# Converts the markdown help file to XML format for the ConvertTo-WDACPolicy cmdlet +# New-ExternalHelp -Path "$ScriptFilePath\..\WDACConfig Module Files\Help\ConvertTo-WDACPolicy.md" -OutputPath "$ScriptFilePath\..\WDACConfig Module Files\Help\ConvertTo-WDACPolicy.xml" -Force | Out-Null +# Get-Help ConvertTo-WDACPolicy -Full diff --git a/WDACConfig/WDACConfig Module Files/Core/ConvertTo-WDACPolicy.psm1 b/WDACConfig/WDACConfig Module Files/Core/ConvertTo-WDACPolicy.psm1 index 706a0dcf7..b221cdb94 100644 --- a/WDACConfig/WDACConfig Module Files/Core/ConvertTo-WDACPolicy.psm1 +++ b/WDACConfig/WDACConfig Module Files/Core/ConvertTo-WDACPolicy.psm1 @@ -1,18 +1,20 @@ Function ConvertTo-WDACPolicy { [CmdletBinding( - DefaultParameterSetName = 'In-Place Upgrade' + DefaultParameterSetName = 'All' )] param( + [Alias('AddLogs')] [ValidateScript({ Test-CiPolicy -XmlFile $_ })] [Parameter(Mandatory = $false, ParameterSetName = 'In-Place Upgrade')] [System.IO.FileInfo]$PolicyToAddLogsTo, + [Alias('BaseFile')] [ValidateScript({ Test-CiPolicy -XmlFile $_ })] [Parameter(Mandatory = $false, ParameterSetName = 'Base-Policy File Association')] [System.IO.FileInfo]$BasePolicyFile, [ArgumentCompleter({ - param($CommandName, $parameterName, $wordToComplete, $CommandAst, $fakeBoundParameters) + param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $fakeBoundParameters) [System.String[]]$PolicyGUIDs = ((&'C:\Windows\System32\CiTool.exe' -lp -json | ConvertFrom-Json).Policies | Where-Object -FilterScript { ($_.IsSystemPolicy -ne 'True') -and ($_.PolicyID -eq $_.BasePolicyID) }).PolicyID @@ -23,10 +25,15 @@ Function ConvertTo-WDACPolicy { $PolicyGUIDs | Where-Object -FilterScript { $_ -notin $Existing } | ForEach-Object -Process { "'{0}'" -f $_ } })] [Parameter(Mandatory = $false, ParameterSetName = 'Base-Policy GUID Association')] + [Alias('BaseGUID')] [System.Guid]$BasePolicyGUID, + [Alias('Src')] + [ValidateSet('MDEAdvancedHunting', 'LocalEventLogs')] + [Parameter(Mandatory = $false)][System.String]$Source = 'LocalEventLogs', + [ArgumentCompleter({ - param($CommandName, $parameterName, $wordToComplete, $CommandAst, $fakeBoundParameters) + param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters) [System.String[]]$Policies = ((&'C:\Windows\System32\CiTool.exe' -lp -json | ConvertFrom-Json).Policies | Where-Object -FilterScript { ($_.FriendlyName) -and ($_.PolicyID -eq $_.BasePolicyID) }).FriendlyName @@ -36,49 +43,197 @@ Function ConvertTo-WDACPolicy { $Policies | Where-Object -FilterScript { $_ -notin $Existing } | ForEach-Object -Process { "'{0}'" -f $_ } })] - [Parameter(Mandatory = $false, ParameterSetName = 'In-Place Upgrade')] - [Parameter(Mandatory = $false, ParameterSetName = 'Base-Policy GUID Association')] - [Parameter(Mandatory = $false, ParameterSetName = 'Base-Policy File Association')] - [System.String[]]$FilterByPolicyNames, + [Alias('FilterNames')] + [Parameter(Mandatory = $false)][System.String[]]$FilterByPolicyNames, - [Parameter(Mandatory = $false, ParameterSetName = 'In-Place Upgrade')] - [Parameter(Mandatory = $false, ParameterSetName = 'Base-Policy GUID Association')] - [Parameter(Mandatory = $false, ParameterSetName = 'Base-Policy File Association')] - [System.UInt64]$MinutesAgo, + [Alias('Duration')] + [ValidateSet('Minutes', 'Hours', 'Days')] + [Parameter(Mandatory = $false)][System.String]$TimeSpan + ) - [Parameter(Mandatory = $false, ParameterSetName = 'In-Place Upgrade')] - [Parameter(Mandatory = $false, ParameterSetName = 'Base-Policy GUID Association')] - [Parameter(Mandatory = $false, ParameterSetName = 'Base-Policy File Association')] - [System.UInt64]$HoursAgo, + DynamicParam { - [Parameter(Mandatory = $false, ParameterSetName = 'In-Place Upgrade')] - [Parameter(Mandatory = $false, ParameterSetName = 'Base-Policy GUID Association')] - [Parameter(Mandatory = $false, ParameterSetName = 'Base-Policy File Association')] - [System.UInt64]$DaysAgo, + # Create a new dynamic parameter dictionary + $ParamDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new() - [Parameter(Mandatory = $false, ParameterSetName = 'In-Place Upgrade')] - [Parameter(Mandatory = $false, ParameterSetName = 'Base-Policy GUID Association')] - [Parameter(Mandatory = $false, ParameterSetName = 'Base-Policy File Association')] - [System.Management.Automation.SwitchParameter]$KernelModeOnly, + # If TimeSpanAgo parameter was used, create a mandatory parameter to ask for the value + if ($PSBoundParameters['TimeSpan']) { - [ValidateSet('Audit', 'Blocked')] - [Parameter(Mandatory = $false, ParameterSetName = 'In-Place Upgrade')] - [Parameter(Mandatory = $false, ParameterSetName = 'Base-Policy GUID Association')] - [Parameter(Mandatory = $false, ParameterSetName = 'Base-Policy File Association')] - [System.String]$LogType = 'Audit', + # Create a parameter attribute collection + $TimeSpanAgo_AttributesCollection = New-Object -TypeName System.Collections.ObjectModel.Collection[System.Attribute] - [Parameter(Mandatory = $false, ParameterSetName = 'In-Place Upgrade')] - [Parameter(Mandatory = $false, ParameterSetName = 'Base-Policy GUID Association')] - [Parameter(Mandatory = $false, ParameterSetName = 'Base-Policy File Association')] - [System.Management.Automation.SwitchParameter]$Deploy, + # Create a mandatory attribute and add it to the collection + [System.Management.Automation.ParameterAttribute]$TimeSpanAgo_MandatoryAttrib = New-Object -TypeName System.Management.Automation.ParameterAttribute + $TimeSpanAgo_MandatoryAttrib.Mandatory = $true + $TimeSpanAgo_AttributesCollection.Add($TimeSpanAgo_MandatoryAttrib) - [Parameter(Mandatory = $false, ParameterSetName = 'In-Place Upgrade')] - [Parameter(Mandatory = $false, ParameterSetName = 'Base-Policy GUID Association')] - [Parameter(Mandatory = $false, ParameterSetName = 'Base-Policy File Association')] - [System.Management.Automation.SwitchParameter]$ExtremeVisibility, + # Create an alias attribute and add it to the collection + $TimeSpanAgo_AliasAttrib = New-Object -TypeName System.Management.Automation.AliasAttribute -ArgumentList 'Past' + $TimeSpanAgo_AttributesCollection.Add($TimeSpanAgo_AliasAttrib) - [Parameter(Mandatory = $false)][System.Management.Automation.SwitchParameter]$SkipVersionCheck - ) + # Create a dynamic parameter object with the attributes already assigned: Name, Type, and Attributes Collection + [System.Management.Automation.RuntimeDefinedParameter]$TimeSpanAgo = New-Object -TypeName System.Management.Automation.RuntimeDefinedParameter('TimeSpanAgo', [System.UInt64], $TimeSpanAgo_AttributesCollection) + + # Add the dynamic parameter object to the dictionary + $ParamDictionary.Add('TimeSpanAgo', $TimeSpanAgo) + } + + # If user selected 'MDEAdvancedHunting' as the source, then create a mandatory parameter to ask for the .CSV file(s) path(s) + if ('MDEAdvancedHunting' -in $PSBoundParameters['Source']) { + + # Opens File picker GUI so that user can select an .CSV file + [System.Management.Automation.ScriptBlock]$ArgumentCompleterCSVFilePathsPicker = { + # Create a new OpenFileDialog object + [System.Windows.Forms.OpenFileDialog]$Dialog = New-Object -TypeName 'System.Windows.Forms.OpenFileDialog' + # Set the filter to show only CSV files + $Dialog.Filter = 'CSV files (*.CSV)|*.CSV' + # Set the title of the dialog + $Dialog.Title = 'Select Microsoft Defender for Endpoint Advanced Hunting CSV files' + # Allow multiple CSV files to be selected + $Dialog.Multiselect = $true + $Dialog.ShowPreview = $true + # Show the dialog and get the result + [System.String]$Result = $Dialog.ShowDialog() + # If the user clicked OK, return the selected file paths + if ($Result -eq 'OK') { + return "`"$($Dialog.FileNames -join '","')`"" + } + } + + # Create a parameter attribute collection + $MDEAHLogs_AttributesCollection = New-Object -TypeName System.Collections.ObjectModel.Collection[System.Attribute] + + # Create an argument completer attribute and add it to the collection + [System.Management.Automation.ArgumentCompleterAttribute]$MDEAHLogs_ArgumentCompleterAttrib = New-Object -TypeName System.Management.Automation.ArgumentCompleterAttribute($ArgumentCompleterCSVFilePathsPicker) + $MDEAHLogs_AttributesCollection.Add($MDEAHLogs_ArgumentCompleterAttrib) + + # Create a mandatory attribute and add it to the collection + [System.Management.Automation.ParameterAttribute]$MDEAHLogs_MandatoryAttrib = New-Object -TypeName System.Management.Automation.ParameterAttribute + $MDEAHLogs_MandatoryAttrib.Mandatory = $true + $MDEAHLogs_AttributesCollection.Add($MDEAHLogs_MandatoryAttrib) + + # Create an alias attribute and add it to the collection + $MDEAHLogs_AliasAttrib = New-Object -TypeName System.Management.Automation.AliasAttribute -ArgumentList 'MDELogs' + $MDEAHLogs_AttributesCollection.Add($MDEAHLogs_AliasAttrib) + + # Create a dynamic parameter object with the attributes already assigned: Name, Type, and Attributes Collection + [System.Management.Automation.RuntimeDefinedParameter]$MDEAHLogs = New-Object -TypeName System.Management.Automation.RuntimeDefinedParameter('MDEAHLogs', [System.IO.FileInfo[]], $MDEAHLogs_AttributesCollection) + + # Add the dynamic parameter object to the dictionary + $ParamDictionary.Add('MDEAHLogs', $MDEAHLogs) + } + + #Region-KernelModeOnly-Parameter + + # Create a parameter attribute collection + $KernelModeOnly_AttributesCollection = New-Object -TypeName System.Collections.ObjectModel.Collection[System.Attribute] + + # Create a mandatory attribute and add it to the collection + [System.Management.Automation.ParameterAttribute]$KernelModeOnly_MandatoryAttrib = New-Object -TypeName System.Management.Automation.ParameterAttribute + $KernelModeOnly_MandatoryAttrib.Mandatory = $false + $KernelModeOnly_AttributesCollection.Add($KernelModeOnly_MandatoryAttrib) + + # Create an alias attribute and add it to the collection + $KernelModeOnly_AliasAttrib = New-Object -TypeName System.Management.Automation.AliasAttribute -ArgumentList 'KMode' + $KernelModeOnly_AttributesCollection.Add($KernelModeOnly_AliasAttrib) + + # Create a dynamic parameter object with the attributes already assigned: Name, Type, and Attributes Collection + [System.Management.Automation.RuntimeDefinedParameter]$KernelModeOnly = New-Object -TypeName System.Management.Automation.RuntimeDefinedParameter('KernelModeOnly', [System.Management.Automation.SwitchParameter], $KernelModeOnly_AttributesCollection) + + # Add the dynamic parameter object to the dictionary + $ParamDictionary.Add('KernelModeOnly', $KernelModeOnly) + + #Endregion-KernelModeOnly-Parameter + + #Region-LogType-Parameter + + # Create a parameter attribute collection + $LogType_AttributesCollection = New-Object -TypeName System.Collections.ObjectModel.Collection[System.Attribute] + + # Create a mandatory attribute and add it to the collection + [System.Management.Automation.ParameterAttribute]$LogType_MandatoryAttrib = New-Object -TypeName System.Management.Automation.ParameterAttribute + $LogType_MandatoryAttrib.Mandatory = $false + $LogType_AttributesCollection.Add($LogType_MandatoryAttrib) + + # Create a ValidateSet attribute with the allowed values + [System.Management.Automation.ValidateSetAttribute]$LogType_ValidateSetAttrib = New-Object -TypeName System.Management.Automation.ValidateSetAttribute('Audit', 'Blocked') + $LogType_AttributesCollection.Add($LogType_ValidateSetAttrib) + + # Create an alias attribute and add it to the collection + $LogType_AliasAttrib = New-Object -TypeName System.Management.Automation.AliasAttribute -ArgumentList 'LogKind' + $LogType_AttributesCollection.Add($LogType_AliasAttrib) + + # Create a dynamic parameter object with the attributes already assigned: Name, Type, and Attributes Collection + [System.Management.Automation.RuntimeDefinedParameter]$LogType = New-Object -TypeName System.Management.Automation.RuntimeDefinedParameter('LogType', [System.String], $LogType_AttributesCollection) + + # Add the dynamic parameter object to the dictionary + $ParamDictionary.Add('LogType', $LogType) + + #Endregion-LogType-Parameter + + #Region-Deploy-Parameter + + # Create a parameter attribute collection + $Deploy_AttributesCollection = New-Object -TypeName System.Collections.ObjectModel.Collection[System.Attribute] + + # Create a mandatory attribute and add it to the collection + [System.Management.Automation.ParameterAttribute]$Deploy_MandatoryAttrib = New-Object -TypeName System.Management.Automation.ParameterAttribute + $Deploy_MandatoryAttrib.Mandatory = $false + $Deploy_AttributesCollection.Add($Deploy_MandatoryAttrib) + + # Create an alias attribute and add it to the collection + $Deploy_AliasAttrib = New-Object -TypeName System.Management.Automation.AliasAttribute -ArgumentList 'Up' + $Deploy_AttributesCollection.Add($Deploy_AliasAttrib) + + # Create a dynamic parameter object with the attributes already assigned: Name, Type, and Attributes Collection + [System.Management.Automation.RuntimeDefinedParameter]$Deploy = New-Object -TypeName System.Management.Automation.RuntimeDefinedParameter('Deploy', [System.Management.Automation.SwitchParameter], $Deploy_AttributesCollection) + + # Add the dynamic parameter object to the dictionary + $ParamDictionary.Add('Deploy', $Deploy) + + #Endregion-Deploy-Parameter + + #Region-ExtremeVisibility-Parameter + + # Create a parameter attribute collection + $ExtremeVisibility_AttributesCollection = New-Object -TypeName System.Collections.ObjectModel.Collection[System.Attribute] + + # Create a mandatory attribute and add it to the collection + [System.Management.Automation.ParameterAttribute]$ExtremeVisibility_MandatoryAttrib = New-Object -TypeName System.Management.Automation.ParameterAttribute + $ExtremeVisibility_MandatoryAttrib.Mandatory = $false + $ExtremeVisibility_AttributesCollection.Add($ExtremeVisibility_MandatoryAttrib) + + $ExtremeVisibility_AliasAttrib = New-Object -TypeName System.Management.Automation.AliasAttribute -ArgumentList 'XVis' + $ExtremeVisibility_AttributesCollection.Add($ExtremeVisibility_AliasAttrib) + + # Create a dynamic parameter object with the attributes already assigned: Name, Type, and Attributes Collection + [System.Management.Automation.RuntimeDefinedParameter]$ExtremeVisibility = New-Object -TypeName System.Management.Automation.RuntimeDefinedParameter('ExtremeVisibility', [System.Management.Automation.SwitchParameter], $ExtremeVisibility_AttributesCollection) + + # Add the dynamic parameter object to the dictionary + $ParamDictionary.Add('ExtremeVisibility', $ExtremeVisibility) + + #Endregion-ExtremeVisibility-Parameter + + #Region-SkipVersionCheck-Parameter + + # Create a parameter attribute collection + $SkipVersionCheck_AttributesCollection = New-Object -TypeName System.Collections.ObjectModel.Collection[System.Attribute] + + # Create a mandatory attribute and add it to the collection + [System.Management.Automation.ParameterAttribute]$SkipVersionCheck_MandatoryAttrib = New-Object -TypeName System.Management.Automation.ParameterAttribute + $SkipVersionCheck_MandatoryAttrib.Mandatory = $false + $SkipVersionCheck_AttributesCollection.Add($SkipVersionCheck_MandatoryAttrib) + + # Create a dynamic parameter object with the attributes already assigned: Name, Type, and Attributes Collection + [System.Management.Automation.RuntimeDefinedParameter]$SkipVersionCheck = New-Object -TypeName System.Management.Automation.RuntimeDefinedParameter('SkipVersionCheck', [System.Management.Automation.SwitchParameter], $SkipVersionCheck_AttributesCollection) + + # Add the dynamic parameter object to the dictionary + $ParamDictionary.Add('SkipVersionCheck', $SkipVersionCheck) + + #Endregion-SkipVersionCheck-Parameter + + return $ParamDictionary + } begin { # Detecting if Verbose switch is used @@ -90,396 +245,675 @@ Function ConvertTo-WDACPolicy { . "$ModuleRootPath\CoreExt\PSDefaultParameterValues.ps1" Write-Verbose -Message 'ConvertTo-WDACPolicy: Importing the required sub-modules' - Import-Module -FullyQualifiedName "$ModuleRootPath\Shared\Update-Self.psm1" -Force - Import-Module -FullyQualifiedName "$ModuleRootPath\Shared\Write-ColorfulText.psm1" -Force - Import-Module -FullyQualifiedName "$ModuleRootPath\Shared\Receive-CodeIntegrityLogs.psm1" -Force - Import-Module -FullyQualifiedName "$ModuleRootPath\Shared\Edit-CiPolicyRuleOptions.psm1" -Force - Import-Module -FullyQualifiedName "$ModuleRootPath\Shared\New-AppxPackageCiPolicy.psm1" -Force - Import-Module -FullyQualifiedName "$ModuleRootPath\Shared\New-EmptyPolicy.psm1" -Force - Import-Module -FullyQualifiedName "$ModuleRootPath\Shared\Get-RuleRefs.psm1" -Force - Import-Module -FullyQualifiedName "$ModuleRootPath\Shared\Get-FileRules.psm1" -Force - Import-Module -FullyQualifiedName "$ModuleRootPath\Shared\New-StagingArea.psm1" -Force + # Defining list of generic modules required for this cmdlet to import + [System.String[]]$ModulesToImport = @( + "$ModuleRootPath\Shared\Update-Self.psm1", + "$ModuleRootPath\Shared\Write-ColorfulText.psm1", + "$ModuleRootPath\Shared\Receive-CodeIntegrityLogs.psm1", + "$ModuleRootPath\Shared\Edit-CiPolicyRuleOptions.psm1", + "$ModuleRootPath\Shared\New-AppxPackageCiPolicy.psm1", + "$ModuleRootPath\Shared\New-EmptyPolicy.psm1", + "$ModuleRootPath\Shared\Get-RuleRefs.psm1", + "$ModuleRootPath\Shared\Get-FileRules.psm1", + "$ModuleRootPath\Shared\New-StagingArea.psm1" + ) + # Add XML Ops module to the list of modules to import + $ModulesToImport += (Get-ChildItem -File -Filter '*.psm1' -LiteralPath "$ModuleRootPath\XMLOps").FullName + Import-Module -FullyQualifiedName $ModulesToImport -Force + + # Since Dynamic parameters are only available in the parameter dictionary, we have to access them using $PSBoundParameters or assign them manually to another variable in the function's scope + New-Variable -Name 'TimeSpanAgo' -Value $PSBoundParameters['TimeSpanAgo'] -Force + New-Variable -Name 'MDEAHLogs' -Value $PSBoundParameters['MDEAHLogs'] -Force + New-Variable -Name 'KernelModeOnly' -Value $PSBoundParameters['KernelModeOnly'] -Force + New-Variable -Name 'LogType' -Value ($PSBoundParameters['LogType'] ?? 'Audit') -Force + New-Variable -Name 'Deploy' -Value $PSBoundParameters['Deploy'] -Force + New-Variable -Name 'ExtremeVisibility' -Value $PSBoundParameters['ExtremeVisibility'] -Force + New-Variable -Name 'SkipVersionCheck' -Value $PSBoundParameters['SkipVersionCheck'] -Force # if -SkipVersionCheck wasn't passed, run the updater if (-NOT $SkipVersionCheck) { Update-Self -InvocationStatement $MyInvocation.Statement } + # Defining a staging area for the current [System.IO.DirectoryInfo]$StagingArea = New-StagingArea -CmdletName 'ConvertTo-WDACPolicy' - if ($MinutesAgo -or $HoursAgo -or $DaysAgo) { - # Convert MinutesAgo, HoursAgo, and DaysAgo to DateTime objects - [System.DateTime]$CurrentDateTime = Get-Date - [System.DateTime]$StartTime = $CurrentDateTime.AddMinutes(-$MinutesAgo) -as [System.DateTime] - [System.DateTime]$StartTime = $StartTime.AddHours(-$HoursAgo) -as [System.DateTime] - [System.DateTime]$StartTime = $StartTime.AddDays(-$DaysAgo) -as [System.DateTime] - } - - # To store the logs that user selects using GUI - [PSCustomObject[]]$SelectedLogs = @() + # If TimeSpan parameter was selected + if ($TimeSpan) { - # The paths to the policy files to be merged together to produce the final Supplemental policy - [System.IO.FileInfo[]]$PolicyFilesToMerge = @() + # Get the current time + [System.DateTime]$CurrentDateTime = Get-Date - # Initializing some flags - [System.Boolean]$HasKernelFiles = $false - [System.Boolean]$HasNormalFiles = $false + # Create the $StartTime variable based on the user input TimeSpanAgo parameter + switch ($TimeSpan) { + 'Minutes' { [System.DateTime]$StartTime = $CurrentDateTime.AddMinutes(-$TimeSpanAgo) -as [System.DateTime] } + 'Hours' { [System.DateTime]$StartTime = $CurrentDateTime.AddHours(-$TimeSpanAgo) -as [System.DateTime] } + 'Days' { [System.DateTime]$StartTime = $CurrentDateTime.AddDays(-$TimeSpanAgo) -as [System.DateTime] } + } + } # Save the current date in a variable as string [System.String]$CurrentDate = $(Get-Date -Format "MM-dd-yyyy 'at' HH-mm-ss") - - # The total number of the main steps for the progress bar to render - [System.UInt16]$TotalSteps = 5 - [System.UInt16]$CurrentStep = 0 } Process { Try { - $CurrentStep++ - Write-Progress -Id 30 -Activity "Collecting $LogType events" -Status "Step $CurrentStep/$TotalSteps" -PercentComplete ($CurrentStep / $TotalSteps * 100) - - [PSCustomObject[]]$EventsToDisplay = Receive-CodeIntegrityLogs -PostProcessing OnlyExisting -PolicyName:$FilterByPolicyNames -Date:$StartTime -Type:$LogType | - Select-Object -Property @{ - Label = 'File Name' - Expression = { - # Can't use Get-Item or Get-ChildItem because the file might not exist on the disk - # Can't use Split-Path -LiteralPath with -Leaf parameter because not supported - [System.String]$TempPath = Split-Path -LiteralPath $_.'File Name' - $_.'File Name'.Replace($TempPath, '').TrimStart('\') + Switch ($Source) { + + 'LocalEventLogs' { + + # To store the logs that user selects using GUI + [PSCustomObject[]]$SelectedLogs = @() + + # The paths to the policy files to be merged together to produce the final Supplemental policy + [System.IO.FileInfo[]]$PolicyFilesToMerge = @() + + # Initializing some flags + [System.Boolean]$HasKernelFiles = $false + [System.Boolean]$HasNormalFiles = $false + + # The total number of the main steps for the progress bar to render + [System.UInt16]$TotalSteps = 5 + [System.UInt16]$CurrentStep = 0 + + $CurrentStep++ + Write-Progress -Id 30 -Activity "Collecting $LogType events" -Status "Step $CurrentStep/$TotalSteps" -PercentComplete ($CurrentStep / $TotalSteps * 100) + + [PSCustomObject[]]$EventsToDisplay = Receive-CodeIntegrityLogs -PostProcessing OnlyExisting -PolicyName:$FilterByPolicyNames -Date:$StartTime -Type:$LogType | + Select-Object -Property @{ + Label = 'File Name' + Expression = { + # Can't use Get-Item or Get-ChildItem because the file might not exist on the disk + # Can't use Split-Path -LiteralPath with -Leaf parameter because not supported + [System.String]$TempPath = Split-Path -LiteralPath $_.'File Name' + $_.'File Name'.Replace($TempPath, '').TrimStart('\') + } + }, + 'TimeCreated', + 'PolicyName', + 'ProductName', + 'FileVersion', + 'OriginalFileName', + 'FileDescription', + 'InternalName', + 'PackageFamilyName', + @{ + Label = 'Full Path' + Expression = { $_.'File Name' } + }, + 'Validated Signing Level', + 'Requested Signing Level', + 'SI Signing Scenario', + 'UserId', + @{ + Label = 'Publishers' + Expression = { [System.String[]]$_.'Publishers' } + }, + 'SHA256 Hash', + 'SHA256 Flat Hash', + 'SHA1 Hash', + 'SHA1 Flat Hash', + 'PolicyGUID', + 'PolicyHash', + 'ActivityId', + 'Process Name', + 'UserWriteable', + 'PolicyID', + 'Status', + 'USN', + 'SignerInfo' + + # If the KernelModeOnly switch is used, then filter the events by the 'Requested Signing Level' property + if ($KernelModeOnly) { + $EventsToDisplay = $EventsToDisplay | Where-Object -FilterScript { $_.'SI Signing Scenario' -eq 'Kernel-Mode' } + } + + # Sort the events by TimeCreated in descending order + [PSCustomObject[]]$EventsToDisplay = $EventsToDisplay | Sort-Object -Property TimeCreated -Descending + + if (($null -eq $EventsToDisplay) -and ($EventsToDisplay.Count -eq 0)) { + Write-ColorfulText -Color HotPink -InputText 'No logs were found to display based on the current filters. Exiting...' + return + } + + #Region Out-GridView properties visibility settings + + # If the ExtremeVisibility switch is used, then display all the properties of the logs without any filtering + if (-NOT $ExtremeVisibility) { + + [System.String[]]$PropertiesToDisplay = @('File Name', 'TimeCreated', 'PolicyName', 'ProductName', 'FileVersion', 'OriginalFileName', 'FileDescription', 'InternalName', 'PackageFamilyName', 'Full Path', 'SI Signing Scenario', 'UserId', 'Publishers') + + # Create a PSPropertySet object that contains the names of the properties to be visible + # Used for Out-GridView display + # https://learn.microsoft.com/en-us/dotnet/api/system.management.automation.pspropertyset + # https://learn.microsoft.com/en-us/powershell/scripting/learn/deep-dives/everything-about-pscustomobject#using-defaultpropertyset-the-long-way + $Visible = [System.Management.Automation.PSPropertySet]::new( + 'DefaultDisplayPropertySet', # the name of the property set + $PropertiesToDisplay # the names of the properties to be visible + ) + + # Add the PSPropertySet object to the PSStandardMembers member set of each element of the $EventsToDisplay array + foreach ($Element in $EventsToDisplay) { + $Element | Add-Member -MemberType 'MemberSet' -Name 'PSStandardMembers' -Value $Visible + } + } + + #Endregion Out-GridView properties visibility settings + + <# + Will enable this section once this issue has been fixed: https://github.com/PowerShell/GraphicalTools/issues/235 + + if ($AlternateDisplay) { + + if (-NOT (Get-InstalledModule -Name Microsoft.PowerShell.ConsoleGuiTools -ErrorAction SilentlyContinue)) { + Write-Verbose -Message 'ConvertTo-WDACPolicy: Installing the Microsoft.PowerShell.ConsoleGuiTools module' + Install-Module -Name Microsoft.PowerShell.ConsoleGuiTools -Force + } + + # Display the logs in a console grid view using outside module + $SelectedLogs = $EventsToDisplay | Out-ConsoleGridView -Title "$($EventsToDisplay.count) $LogType Code Integrity Logs" -OutputMode Multiple } - }, - 'TimeCreated', - 'PolicyName', - 'ProductName', - 'FileVersion', - 'OriginalFileName', - 'FileDescription', - 'InternalName', - 'PackageFamilyName', - @{ - Label = 'Full Path' - Expression = { $_.'File Name' } - }, - 'Validated Signing Level', - 'Requested Signing Level', - 'SI Signing Scenario', - 'UserId', - 'Publishers', - 'SHA256 Hash', - 'SHA256 Flat Hash', - 'SHA1 Hash', - 'SHA1 Flat Hash', - 'PolicyGUID', - 'PolicyHash', - 'ActivityId', - 'Process Name', - 'UserWriteable', - 'PolicyID', - 'Status', - 'USN', - 'SignerInfo' - - # If the KernelModeOnly switch is used, then filter the events by the 'Requested Signing Level' property - if ($KernelModeOnly) { - $EventsToDisplay = $EventsToDisplay | Where-Object -FilterScript { $_.'SI Signing Scenario' -eq 'Kernel-Mode' } - } + #> - # Sort the events by TimeCreated in descending order - [PSCustomObject[]]$EventsToDisplay = $EventsToDisplay | Sort-Object -Property TimeCreated -Descending + $CurrentStep++ + Write-Progress -Id 30 -Activity 'Displaying the logs' -Status "Step $CurrentStep/$TotalSteps" -PercentComplete ($CurrentStep / $TotalSteps * 100) - if (($null -eq $EventsToDisplay) -and ($EventsToDisplay.Count -eq 0)) { - Write-ColorfulText -Color HotPink -InputText 'No logs were found to display based on the current filters. Exiting...' - return - } + # Display the logs in a grid view using the build-in cmdlet + $SelectedLogs = $EventsToDisplay | Out-GridView -OutputMode Multiple -Title "Displaying $($EventsToDisplay.count) $LogType Code Integrity Logs" - #Region Out-GridView properties visibility settings + Write-Verbose -Message "ConvertTo-WDACPolicy: Selected logs count: $($SelectedLogs.count)" - # If the ExtremeVisibility switch is used, then display all the properties of the logs without any filtering - if (-NOT $ExtremeVisibility) { + if (!$BasePolicyGUID -and !$BasePolicyFile -and !$PolicyToAddLogsTo) { + Write-ColorfulText -Color HotPink -InputText 'A more specific parameter was not provided to define what to do with the selected logs. Exiting...' + return + } - [System.String[]]$PropertiesToDisplay = @('File Name', 'TimeCreated', 'PolicyName', 'ProductName', 'FileVersion', 'OriginalFileName', 'FileDescription', 'InternalName', 'PackageFamilyName', 'Full Path', 'SI Signing Scenario', 'UserId', 'Publishers') + # If the user has selected any logs, then create a WDAC policy for them, otherwise return + if ($null -eq $SelectedLogs) { + return + } - # Create a PSPropertySet object that contains the names of the properties to be visible - # Used for Out-GridView display - # https://learn.microsoft.com/en-us/dotnet/api/system.management.automation.pspropertyset - # https://learn.microsoft.com/en-us/powershell/scripting/learn/deep-dives/everything-about-pscustomobject#using-defaultpropertyset-the-long-way - $Visible = [System.Management.Automation.PSPropertySet]::new( - 'DefaultDisplayPropertySet', # the name of the property set - $PropertiesToDisplay # the names of the properties to be visible - ) + Write-Verbose -Message 'ConvertTo-WDACPolicy: Creating a temporary folder to store the symbolic links to the files and for WDAC polices' + [System.IO.DirectoryInfo]$SymLinksStorage = New-Item -Path (Join-Path -Path $StagingArea 'SymLinkStorage') -ItemType Directory -Force - # Add the PSPropertySet object to the PSStandardMembers member set of each element of the $EventsToDisplay array - foreach ($Element in $EventsToDisplay) { - $Element | Add-Member -MemberType 'MemberSet' -Name 'PSStandardMembers' -Value $Visible - } - } + # The path to the TEMP Supplemental WDAC Policy file + [System.IO.FileInfo]$WDACPolicyPathTemp = Join-Path -Path $StagingArea -ChildPath 'TEMP CiPolicy From Logs.xml' - #Endregion Out-GridView properties visibility settings + # The path to the final Supplemental WDAC Policy file + [System.IO.FileInfo]$WDACPolicyPath = Join-Path -Path $StagingArea -ChildPath "CiPolicy From Logs $CurrentDate.xml" - <# - Will enable this section once this issue has been fixed: https://github.com/PowerShell/GraphicalTools/issues/235 + # The path to the Kernel protected file hashes WDAC Policy file + [System.IO.FileInfo]$WDACPolicyKernelProtectedPath = Join-Path -Path $StagingArea -ChildPath "Kernel Protected Files Hashes $CurrentDate.xml" - if ($AlternateDisplay) { + #Region Kernel-protected-files-automatic-detection-and-allow-rule-creation + # This part takes care of Kernel protected files such as the main executable of the games installed through Xbox app + # For these files, only Kernel can get their hashes, it passes them to event viewer and we take them from event viewer logs + # Any other attempts such as "Get-FileHash" or "Get-AuthenticodeSignature" fail and ConfigCI Module cmdlets totally ignore these files and do not create allow rules for them - if (-NOT (Get-InstalledModule -Name Microsoft.PowerShell.ConsoleGuiTools -ErrorAction SilentlyContinue)) { - Write-Verbose -Message 'ConvertTo-WDACPolicy: Installing the Microsoft.PowerShell.ConsoleGuiTools module' - Install-Module -Name Microsoft.PowerShell.ConsoleGuiTools -Force - } + $CurrentStep++ + Write-Progress -Id 30 -Activity 'Checking for Kernel protected files' -Status "Step $CurrentStep/$TotalSteps" -PercentComplete ($CurrentStep / $TotalSteps * 100) - # Display the logs in a console grid view using outside module - $SelectedLogs = $EventsToDisplay | Out-ConsoleGridView -Title "$($EventsToDisplay.count) $LogType Code Integrity Logs" -OutputMode Multiple - } - #> + Write-Verbose -Message 'ConvertTo-WDACPolicy: Checking for Kernel protected files in the selected logs' - $CurrentStep++ - Write-Progress -Id 30 -Activity 'Displaying the logs' -Status "Step $CurrentStep/$TotalSteps" -PercentComplete ($CurrentStep / $TotalSteps * 100) + # Storing the logs of the kernel protected files + [PSCustomObject[]]$KernelProtectedFileLogs = @() - # Display the logs in a grid view using the build-in cmdlet - $SelectedLogs = $EventsToDisplay | Out-GridView -OutputMode Multiple -Title "$($EventsToDisplay.count) $LogType Code Integrity Logs" + # Looping through every file with .exe and .dll extensions to check if they are kernel protected regardless of whether the file exists or not + foreach ($Log in $SelectedLogs | Where-Object -FilterScript { [System.IO.Path]::GetExtension($_.'Full Path') -in @('.exe', '.dll') }) { - Write-Verbose -Message "ConvertTo-WDACPolicy: Selected logs count: $($SelectedLogs.count)" + try { + # Testing each file to find the protected ones + Get-FileHash -Path $Log.'Full Path' -ErrorAction Stop | Out-Null + } + # If the file is protected, it will throw an exception and the module will continue to the next one + # Making sure only the right file is captured by narrowing down the error type. + # E.g., when get-filehash can't get a file's hash because its open by another program, the exception is different: System.IO.IOException + catch [System.UnauthorizedAccessException] { + $KernelProtectedFileLogs += $Log + } + catch { + Write-Verbose -Message "ConvertTo-WDACPolicy: An unexpected error occurred while checking the file: $($Log.'Full Path')" + } + } - if (!$BasePolicyGUID -and !$BasePolicyFile -and !$PolicyToAddLogsTo) { - Write-ColorfulText -Color HotPink -InputText 'A more specific parameter was not provided to define what to do with the selected logs. Exiting...' - return - } + # Only proceed if any kernel protected file(s) were found in any of the selected logs + if (($null -ne $KernelProtectedFileLogs) -and ($KernelProtectedFileLogs.count -gt 0)) { - # If the user has selected any logs, then create a WDAC policy for them, otherwise return - if ($null -eq $SelectedLogs) { - return - } + Write-Verbose -Message 'ConvertTo-WDACPolicy: The following Kernel protected files were detected, creating allow rules for them:' + $KernelProtectedFileLogs | ForEach-Object -Process { Write-Verbose -Message $($_.'File Name') } - Write-Verbose -Message 'ConvertTo-WDACPolicy: Creating a temporary folder to store the symbolic links to the files and for WDAC polices' - [System.IO.DirectoryInfo]$SymLinksStorage = New-Item -Path (Join-Path -Path $StagingArea 'SymLinkStorage') -ItemType Directory -Force + # Check if any of the kernel-protected files can be allowed by FamilyPackageName + [PSCustomObject]$AppxOutput = New-AppxPackageCiPolicy -Logs $KernelProtectedFileLogs -directoryPath $SymLinksStorage + + if ($null -ne $AppxOutput.PolicyPath) { + # Add the path of the Appx package policy file to the array of policy files to merge + $PolicyFilesToMerge += $AppxOutput.PolicyPath + + # Set the flag indicating that there are kernel-protected files in the selected logs + [System.Boolean]$HasKernelFiles = $true + } + + # If the New-AppxPackageCiPolicy function returned remaining logs then create allow rules for them using Hash level + if (($null -ne $AppxOutput.RemainingLogs) -and ($AppxOutput.RemainingLogs.count -gt 0)) { - # The path to the TEMP Supplemental WDAC Policy file - [System.IO.FileInfo]$WDACPolicyPathTemp = Join-Path -Path $StagingArea -ChildPath 'TEMP CiPolicy From Logs.xml' + # Put the Rules and RulesRefs in an empty policy file by extracting the hashes from the logs + Write-Verbose -Message "ConvertTo-WDACPolicy: $($AppxOutput.RemainingLogs.count) Kernel protected files were found in the selected logs that did not have the PackageFamilyName property or the app is not installed on the system, creating allow rules for them using Hash level" - # The path to the final Supplemental WDAC Policy file - [System.IO.FileInfo]$WDACPolicyPath = Join-Path -Path $StagingArea -ChildPath "CiPolicy From Logs $CurrentDate.xml" + New-EmptyPolicy -RulesContent (Get-FileRules -HashesArray $AppxOutput.RemainingLogs) -RuleRefsContent (Get-RuleRefs -HashesArray $AppxOutput.RemainingLogs) | Out-File -FilePath $WDACPolicyKernelProtectedPath -Force + + # Add the path of the Kernel protected files Hashes policy file to the array of policy files to merge + $PolicyFilesToMerge += $WDACPolicyKernelProtectedPath - # The path to the Kernel protected file hashes WDAC Policy file - [System.IO.FileInfo]$WDACPolicyKernelProtectedPath = Join-Path -Path $StagingArea -ChildPath "Kernel Protected Files Hashes $CurrentDate.xml" + # Set the flag indicating that there are kernel-protected files in the selected logs + [System.Boolean]$HasKernelFiles = $true + } + else { + Write-Verbose -Message 'ConvertTo-WDACPolicy: All kernel protected files were allowed using their PackageFamilyName property' + } + } + else { + Write-Verbose -Message 'ConvertTo-WDACPolicy: No Kernel protected files were found in any of the selected logs' + } - #Region Kernel-protected-files-automatic-detection-and-allow-rule-creation - # This part takes care of Kernel protected files such as the main executable of the games installed through Xbox app - # For these files, only Kernel can get their hashes, it passes them to event viewer and we take them from event viewer logs - # Any other attempts such as "Get-FileHash" or "Get-AuthenticodeSignature" fail and ConfigCI Module cmdlets totally ignore these files and do not create allow rules for them + [System.UInt64]$LogCountBeforeRemovingKernelProtectedFiles = $SelectedLogs.Count - $CurrentStep++ - Write-Progress -Id 30 -Activity 'Checking for Kernel protected files' -Status "Step $CurrentStep/$TotalSteps" -PercentComplete ($CurrentStep / $TotalSteps * 100) + # Remove the logs of the Kernel protected files from the selected logs since they cannot be scanned with New-CIPolicy cmdlet + [PSCustomObject[]]$SelectedLogs = $SelectedLogs | Where-Object -FilterScript { $KernelProtectedFileLogs -notcontains $_ } + + Write-Verbose -Message "ConvertTo-WDACPolicy: The number of logs before removing the Kernel protected files: $LogCountBeforeRemovingKernelProtectedFiles and after: $($SelectedLogs.Count). There were $($KernelProtectedFileLogs.Count) Kernel protected files." + #Endregion Kernel-protected-files-automatic-detection-and-allow-rule-creation + + $CurrentStep++ + Write-Progress -Id 30 -Activity 'Processing the logs' -Status "Step $CurrentStep/$TotalSteps" -PercentComplete ($CurrentStep / $TotalSteps * 100) + + # If there are still logs after removing the kernel protected files, then scan them + if (($null -ne $SelectedLogs) -and ($SelectedLogs.Count -gt 0)) { + + #Region Main Policy Creation - Write-Verbose -Message 'ConvertTo-WDACPolicy: Checking for Kernel protected files in the selected logs' + Write-Verbose -Message 'ConvertTo-WDACPolicy: Creating symbolic links to the non-kernel-protected files in the logs' + Foreach ($File in $SelectedLogs) { + New-Item -ItemType SymbolicLink -Path (Join-Path -Path $SymLinksStorage -ChildPath $File.'File Name') -Target $File.'Full Path' -Force | Out-Null + } - # Storing the logs of the kernel protected files - [PSCustomObject[]]$KernelProtectedFileLogs = @() + # Creating a hash table to dynamically add parameters based on user input and pass them to New-Cipolicy cmdlet + [System.Collections.Hashtable]$CiPolicyScanHashTable = @{ + FilePath = $WDACPolicyPathTemp + ScanPath = $SymLinksStorage + Level = 'WHQLFilePublisher' + MultiplePolicyFormat = $true + UserWriteablePaths = $true + AllowFileNameFallbacks = $true + } + # Only scan UserPEs if the KernelModeOnly switch is not used + if (!$KernelModeOnly) { $CiPolicyScanHashTable['UserPEs'] = $true } - # Looping through every file with .exe and .dll extensions to check if they are kernel protected regardless of whether the file exists or not - foreach ($Log in $SelectedLogs | Where-Object -FilterScript { [System.IO.Path]::GetExtension($_.'Full Path') -in @('.exe', '.dll') }) { + # Set the Fallback property to 'None' if the KernelModeOnly switch is used, otherwise set it to 'FilePublisher' and 'Hash' + $CiPolicyScanHashTable['Fallback'] = $KernelModeOnly ? 'None' : ('FilePublisher', 'Hash') - try { - # Testing each file to find the protected ones - Get-FileHash -Path $Log.'Full Path' -ErrorAction Stop | Out-Null - } - # If the file is protected, it will throw an exception and the module will continue to the next one - # Making sure only the right file is captured by narrowing down the error type. - # E.g., when get-filehash can't get a file's hash because its open by another program, the exception is different: System.IO.IOException - catch [System.UnauthorizedAccessException] { - $KernelProtectedFileLogs += $Log - } - catch { - Write-Verbose -Message "ConvertTo-WDACPolicy: An unexpected error occurred while checking the file: $($Log.'Full Path')" - } - } + Write-Verbose -Message 'ConvertTo-WDACPolicy: Scanning the files in the selected event logs with the following parameters:' + if ($Verbose) { $CiPolicyScanHashTable } - # Only proceed if any kernel protected file(s) were found in any of the selected logs - if (($null -ne $KernelProtectedFileLogs) -and ($KernelProtectedFileLogs.count -gt 0)) { + New-CIPolicy @CiPolicyScanHashTable - Write-Verbose -Message 'ConvertTo-WDACPolicy: The following Kernel protected files were detected, creating allow rules for them:' - $KernelProtectedFileLogs | ForEach-Object -Process { Write-Verbose -Message $($_.'File Name') } + # Add the path of the TEMP WDAC Policy file as the 1st element to the policy files to merge array + $PolicyFilesToMerge = @($WDACPolicyPathTemp) + $PolicyFilesToMerge - # Check if any of the kernel-protected files can be allowed by FamilyPackageName - [PSCustomObject]$AppxOutput = New-AppxPackageCiPolicy -Logs $KernelProtectedFileLogs -directoryPath $SymLinksStorage + # Set the flag indicating that there are normal files in the selected logs + [System.Boolean]$HasNormalFiles = $true - if ($null -ne $AppxOutput.PolicyPath) { - # Add the path of the Appx package policy file to the array of policy files to merge - $PolicyFilesToMerge += $AppxOutput.PolicyPath + #Endregion Main Policy Creation + } - # Set the flag indicating that there are kernel-protected files in the selected logs - [System.Boolean]$HasKernelFiles = $true - } + $CurrentStep++ + Write-Progress -Id 30 -Activity 'Generating the policy' -Status "Step $CurrentStep/$TotalSteps" -PercentComplete ($CurrentStep / $TotalSteps * 100) - # If the New-AppxPackageCiPolicy function returned remaining logs then create allow rules for them using Hash level - if (($null -ne $AppxOutput.RemainingLogs) -and ($AppxOutput.RemainingLogs.count -gt 0)) { + # If there are only kernel-protected files in the selected logs + if (($HasKernelFiles -eq $true) -and ($HasNormalFiles -eq $false)) { + Write-Verbose -Message 'ConvertTo-WDACPolicy: There are only kernel-protected files in the selected logs' + Merge-CIPolicy -PolicyPaths $PolicyFilesToMerge -OutputFilePath $WDACPolicyPath | Out-Null + } + # If there are only normal files in the selected logs + elseif (($HasKernelFiles -eq $false) -and ($HasNormalFiles -eq $true)) { + Write-Verbose -Message 'ConvertTo-WDACPolicy: There are only normal files in the selected logs' - # Put the Rules and RulesRefs in an empty policy file by extracting the hashes from the logs - Write-Verbose -Message "ConvertTo-WDACPolicy: $($AppxOutput.RemainingLogs.count) Kernel protected files were found in the selected logs that did not have the PackageFamilyName property or the app is not installed on the system, creating allow rules for them using Hash level" + # Using merge on a single policy takes care of any possible orphaned rules or file attributes + Merge-CIPolicy -PolicyPaths $WDACPolicyPathTemp -OutputFilePath $WDACPolicyPath | Out-Null + } + # If there are both kernel-protected and normal files in the selected logs + elseif (($HasKernelFiles -eq $true) -and ($HasNormalFiles -eq $true)) { + Write-Verbose -Message 'ConvertTo-WDACPolicy: There are both kernel-protected and normal files in the selected logs' + Merge-CIPolicy -PolicyPaths $PolicyFilesToMerge -OutputFilePath $WDACPolicyPath | Out-Null + } + # If there are no files in the selected logs + else { + Write-ColorfulText -Color HotPink -InputText 'No logs were selected to create a WDAC policy from. Exiting...' + return + } - New-EmptyPolicy -RulesContent (Get-FileRules -HashesArray $AppxOutput.RemainingLogs) -RuleRefsContent (Get-RuleRefs -HashesArray $AppxOutput.RemainingLogs) | Out-File -FilePath $WDACPolicyKernelProtectedPath -Force + #Region Base To Supplemental Policy Association and Deployment - # Add the path of the Kernel protected files Hashes policy file to the array of policy files to merge - $PolicyFilesToMerge += $WDACPolicyKernelProtectedPath + # If -BasePolicyFile parameter was used then associate the supplemental policy with the user input base policy + if ($null -ne $BasePolicyFile) { - # Set the flag indicating that there are kernel-protected files in the selected logs - [System.Boolean]$HasKernelFiles = $true - } - else { - Write-Verbose -Message 'ConvertTo-WDACPolicy: All kernel protected files were allowed using their PackageFamilyName property' - } - } - else { - Write-Verbose -Message 'ConvertTo-WDACPolicy: No Kernel protected files were found in any of the selected logs' - } + # Objectify the user input base policy file to extract its Base policy ID + $InputXMLObj = [System.Xml.XmlDocument](Get-Content -Path $BasePolicyFile) - [System.UInt64]$LogCountBeforeRemovingKernelProtectedFiles = $SelectedLogs.Count + [System.String]$SupplementalPolicyID = Set-CIPolicyIdInfo -FilePath $WDACPolicyPath -PolicyName "Supplemental Policy from event logs - $(Get-Date -Format 'MM-dd-yyyy')" -SupplementsBasePolicyID $InputXMLObj.SiPolicy.BasePolicyID -ResetPolicyID + [System.String]$SupplementalPolicyID = $SupplementalPolicyID.Substring(11) - # Remove the logs of the Kernel protected files from the selected logs since they cannot be scanned with New-CIPolicy cmdlet - [PSCustomObject[]]$SelectedLogs = $SelectedLogs | Where-Object -FilterScript { $KernelProtectedFileLogs -notcontains $_ } + # Configure policy rule options + Edit-CiPolicyRuleOptions -Action Supplemental -XMLFile $WDACPolicyPath - Write-Verbose -Message "ConvertTo-WDACPolicy: The number of logs before removing the Kernel protected files: $LogCountBeforeRemovingKernelProtectedFiles and after: $($SelectedLogs.Count). There were $($KernelProtectedFileLogs.Count) Kernel protected files." - #Endregion Kernel-protected-files-automatic-detection-and-allow-rule-creation + Write-Verbose -Message 'ConvertTo-WDACPolicy: Copying the policy file to the User Config directory' + Copy-Item -Path $WDACPolicyPath -Destination $UserConfigDir -Force - $CurrentStep++ - Write-Progress -Id 30 -Activity 'Processing the logs' -Status "Step $CurrentStep/$TotalSteps" -PercentComplete ($CurrentStep / $TotalSteps * 100) + if ($Deploy) { + ConvertFrom-CIPolicy -XmlFilePath $WDACPolicyPath -BinaryFilePath (Join-Path -Path $StagingArea -ChildPath "$SupplementalPolicyID.cip") | Out-Null - # If there are still logs after removing the kernel protected files, then scan them - if (($null -ne $SelectedLogs) -and ($SelectedLogs.Count -gt 0)) { + Write-Verbose -Message 'ConvertTo-WDACPolicy: Deploying the Supplemental policy' - #Region Main Policy Creation + &'C:\Windows\System32\CiTool.exe' --update-policy (Join-Path -Path $StagingArea -ChildPath "$SupplementalPolicyID.cip") -json | Out-Null + } + } + # If -BasePolicyGUID parameter was used then use it by setting it as the Base policy ID in the supplemental policy + elseif ($null -ne $BasePolicyGUID) { + [System.String]$SupplementalPolicyID = Set-CIPolicyIdInfo -FilePath $WDACPolicyPath -PolicyName "Supplemental Policy from event logs - $(Get-Date -Format 'MM-dd-yyyy')" -SupplementsBasePolicyID $BasePolicyGUID -ResetPolicyID + [System.String]$SupplementalPolicyID = $SupplementalPolicyID.Substring(11) - Write-Verbose -Message 'ConvertTo-WDACPolicy: Creating symbolic links to the non-kernel-protected files in the logs' - Foreach ($File in $SelectedLogs) { - New-Item -ItemType SymbolicLink -Path (Join-Path -Path $SymLinksStorage -ChildPath $File.'File Name') -Target $File.'Full Path' -Force | Out-Null - } + # Configure policy rule options + Edit-CiPolicyRuleOptions -Action Supplemental -XMLFile $WDACPolicyPath + + Write-Verbose -Message 'ConvertTo-WDACPolicy: Copying the policy file to the User Config directory' + Copy-Item -Path $WDACPolicyPath -Destination $UserConfigDir -Force + + if ($Deploy) { + ConvertFrom-CIPolicy -XmlFilePath $WDACPolicyPath -BinaryFilePath (Join-Path -Path $StagingArea -ChildPath "$SupplementalPolicyID.cip") | Out-Null + + Write-Verbose -Message 'ConvertTo-WDACPolicy: Deploying the Supplemental policy' + + &'C:\Windows\System32\CiTool.exe' --update-policy (Join-Path -Path $StagingArea -ChildPath "$SupplementalPolicyID.cip") -json | Out-Null + } + } + # If -PolicyToAddLogsTo parameter was used then merge the supplemental policy with the user input policy + elseif ($null -ne $PolicyToAddLogsTo) { + + # Objectify the user input policy file to extract its policy ID + $InputXMLObj = [System.Xml.XmlDocument](Get-Content -Path $PolicyToAddLogsTo) + + Set-CIPolicyIdInfo -FilePath $WDACPolicyPath -PolicyName "Supplemental Policy from event logs - $(Get-Date -Format 'MM-dd-yyyy')" -ResetPolicyID | Out-Null + + # Remove all policy rule options prior to merging the policies since we don't need to add/remove any policy rule options to/from the user input policy + Edit-CiPolicyRuleOptions -Action RemoveAll -XMLFile $WDACPolicyPath + + Merge-CIPolicy -PolicyPaths $PolicyToAddLogsTo, $WDACPolicyPath -OutputFilePath $PolicyToAddLogsTo | Out-Null - # Creating a hash table to dynamically add parameters based on user input and pass them to New-Cipolicy cmdlet - [System.Collections.Hashtable]$CiPolicyScanHashTable = @{ - FilePath = $WDACPolicyPathTemp - ScanPath = $SymLinksStorage - Level = 'WHQLFilePublisher' - MultiplePolicyFormat = $true - UserWriteablePaths = $true - AllowFileNameFallbacks = $true + # Set HVCI to Strict + Set-HVCIOptions -Strict -FilePath $PolicyToAddLogsTo + + if ($Deploy) { + ConvertFrom-CIPolicy -XmlFilePath $PolicyToAddLogsTo -BinaryFilePath (Join-Path -Path $StagingArea -ChildPath "$($InputXMLObj.SiPolicy.PolicyID).cip") | Out-Null + + Write-Verbose -Message 'ConvertTo-WDACPolicy: Deploying the policy that user selected to add the logs to' + + &'C:\Windows\System32\CiTool.exe' --update-policy (Join-Path -Path $StagingArea -ChildPath "$($InputXMLObj.SiPolicy.PolicyID).cip") -json | Out-Null + } + } + + #Endregion Base To Supplemental Policy Association and Deployment } - # Only scan UserPEs if the KernelModeOnly switch is not used - if (!$KernelModeOnly) { $CiPolicyScanHashTable['UserPEs'] = $true } + 'MDEAdvancedHunting' { - # Set the Fallback property to 'None' if the KernelModeOnly switch is used, otherwise set it to 'FilePublisher' and 'Hash' - $CiPolicyScanHashTable['Fallback'] = $KernelModeOnly ? 'None' : ('FilePublisher', 'Hash') + <# + ALL OF THE FUNCTIONS THAT PERFORM DATA MERGING ARE CREATED TO HANDLE MDE ADVANCED HUNTING DATA ONLY + SO NO DENIED SIGNERS OR DENY RULES WHATSOEVER + FOR MERGING WITH OTHER POLICIES, MERGE-CIPOLICY CMDLET SHOULD BE USED + AT LEAST UNTIL THE NECESSARY FUNCTIONALITY IS ADDED TO THE MERGER FUNCTIONS + #> - Write-Verbose -Message 'ConvertTo-WDACPolicy: Scanning the files in the selected event logs with the following parameters:' - if ($Verbose) { $CiPolicyScanHashTable } + # The total number of the main steps for the progress bar to render + [System.UInt16]$TotalSteps = 9 + [System.UInt16]$CurrentStep = 0 - New-CIPolicy @CiPolicyScanHashTable + $CurrentStep++ + Write-Progress -Id 31 -Activity 'Optimizing the MDE CSV data' -Status "Step $CurrentStep/$TotalSteps" -PercentComplete ($CurrentStep / $TotalSteps * 100) + + Write-Verbose -Message 'Optimizing the MDE CSV data' + [System.Collections.Hashtable[]]$OptimizedCSVData = Optimize-MDECSVData -CSVPath $MDEAHLogs -StagingArea $StagingArea - # Add the path of the TEMP WDAC Policy file as the 1st element to the policy files to merge array - $PolicyFilesToMerge = @($WDACPolicyPathTemp) + $PolicyFilesToMerge + $CurrentStep++ + Write-Progress -Id 31 -Activity 'Identifying the correlated data in the MDE CSV data' -Status "Step $CurrentStep/$TotalSteps" -PercentComplete ($CurrentStep / $TotalSteps * 100) - # Set the flag indicating that there are normal files in the selected logs - [System.Boolean]$HasNormalFiles = $true + Write-Verbose -Message 'Identifying the correlated data in the MDE CSV data' - #Endregion Main Policy Creation - } + if (($null -eq $OptimizedCSVData) -or ($OptimizedCSVData.Count -eq 0)) { + Write-ColorfulText -Color HotPink -InputText 'No valid MDE Advanced Hunting logs available. Exiting...' + return + } - $CurrentStep++ - Write-Progress -Id 30 -Activity 'Generating the policy' -Status "Step $CurrentStep/$TotalSteps" -PercentComplete ($CurrentStep / $TotalSteps * 100) + if ($TimeSpan) { + [System.Collections.Hashtable]$EventPackageCollections = Compare-CorrelatedData -OptimizedCSVData $OptimizedCSVData -StagingArea $StagingArea -StartTime $StartTime -PolicyNamesToFilter:$FilterByPolicyNames -LogType:$LogType + } + else { + [System.Collections.Hashtable]$EventPackageCollections = Compare-CorrelatedData -OptimizedCSVData $OptimizedCSVData -StagingArea $StagingArea -PolicyNamesToFilter:$FilterByPolicyNames -LogType:$LogType + } - # If there are only kernel-protected files in the selected logs - if (($HasKernelFiles -eq $true) -and ($HasNormalFiles -eq $false)) { - Write-Verbose -Message 'ConvertTo-WDACPolicy: There are only kernel-protected files in the selected logs' - Merge-CIPolicy -PolicyPaths $PolicyFilesToMerge -OutputFilePath $WDACPolicyPath | Out-Null - } - # If there are only normal files in the selected logs - elseif (($HasKernelFiles -eq $false) -and ($HasNormalFiles -eq $true)) { - Write-Verbose -Message 'ConvertTo-WDACPolicy: There are only normal files in the selected logs' + # Selecting all of the properties of each log to be displayed + $MDEAHLogsToDisplay = $EventPackageCollections.Values -as [PSCustomObject] | Select-Object -Property * - # Using merge on a single policy takes care of any possible orphaned rules or file attributes - Merge-CIPolicy -PolicyPaths $WDACPolicyPathTemp -OutputFilePath $WDACPolicyPath | Out-Null - } - # If there are both kernel-protected and normal files in the selected logs - elseif (($HasKernelFiles -eq $true) -and ($HasNormalFiles -eq $true)) { - Write-Verbose -Message 'ConvertTo-WDACPolicy: There are both kernel-protected and normal files in the selected logs' - Merge-CIPolicy -PolicyPaths $PolicyFilesToMerge -OutputFilePath $WDACPolicyPath | Out-Null - } - # If there are no files in the selected logs - else { - Write-ColorfulText -Color HotPink -InputText 'No logs were selected to create a WDAC policy from. Exiting...' - return - } + # If the KernelModeOnly switch is used, then filter the logs by the 'SiSigningScenario' property + if ($KernelModeOnly) { + $MDEAHLogsToDisplay = $MDEAHLogsToDisplay | Where-Object -FilterScript { $_.'SiSigningScenario' -eq '0' } + } - #Region Base To Supplemental Policy Association and Deployment + if (($null -eq $MDEAHLogsToDisplay) -or ($MDEAHLogsToDisplay.Count -eq 0)) { + Write-ColorfulText -Color HotPink -InputText 'No MDE Advanced Hunting logs available based on the selected filters. Exiting...' + return + } - # If -BasePolicyFile parameter was used then associate the supplemental policy with the user input base policy - if ($null -ne $BasePolicyFile) { + #Region Out-GridView properties visibility settings - # Objectify the user input base policy file to extract its Base policy ID - $InputXMLObj = [System.Xml.XmlDocument](Get-Content -Path $BasePolicyFile) + # If the ExtremeVisibility switch is used, then display all the properties of the logs without any filtering + if (-NOT $ExtremeVisibility) { - [System.String]$SupplementalPolicyID = Set-CIPolicyIdInfo -FilePath $WDACPolicyPath -PolicyName "Supplemental Policy from event logs - $(Get-Date -Format 'MM-dd-yyyy')" -SupplementsBasePolicyID $InputXMLObj.SiPolicy.BasePolicyID -ResetPolicyID - [System.String]$SupplementalPolicyID = $SupplementalPolicyID.Substring(11) + [System.String[]]$PropertiesToDisplay = @('TimeStamp', 'DeviceName', 'Type', 'SHA256', 'FileName', 'FolderPath', 'InitiatingProcessFileName', 'SignatureStatus', 'PolicyName', 'OriginalFileName', 'InternalName', 'PackageFamilyName', 'FileVersion', 'SISigningScenario') - # Configure policy rule options - Edit-CiPolicyRuleOptions -Action Supplemental -XMLFile $WDACPolicyPath + # Create a PSPropertySet object that contains the names of the properties to be visible + # Used for Out-GridView display + # https://learn.microsoft.com/en-us/dotnet/api/system.management.automation.pspropertyset + # https://learn.microsoft.com/en-us/powershell/scripting/learn/deep-dives/everything-about-pscustomobject#using-defaultpropertyset-the-long-way + $Visible = [System.Management.Automation.PSPropertySet]::new( + 'DefaultDisplayPropertySet', # the name of the property set + $PropertiesToDisplay # the names of the properties to be visible + ) - Write-Verbose -Message 'ConvertTo-WDACPolicy: Copying the policy file to the current directory' - Copy-Item -Path $WDACPolicyPath -Destination $UserConfigDir -Force + # Add the PSPropertySet object to the PSStandardMembers member set of each element of the $EventsToDisplay array + foreach ($Element in $MDEAHLogsToDisplay) { + $Element | Add-Member -MemberType 'MemberSet' -Name 'PSStandardMembers' -Value $Visible + } + } - if ($Deploy) { - ConvertFrom-CIPolicy -XmlFilePath $WDACPolicyPath -BinaryFilePath (Join-Path -Path $StagingArea -ChildPath "$SupplementalPolicyID.cip") | Out-Null + #Endregion Out-GridView properties visibility settings - Write-Verbose -Message 'ConvertTo-WDACPolicy: Deploying the Supplemental policy' + $CurrentStep++ + Write-Progress -Id 31 -Activity 'Displaying the MDE Advanced Hunting logs in a GUI' -Status "Step $CurrentStep/$TotalSteps" -PercentComplete ($CurrentStep / $TotalSteps * 100) - &'C:\Windows\System32\CiTool.exe' --update-policy (Join-Path -Path $StagingArea -ChildPath "$SupplementalPolicyID.cip") -json | Out-Null - } - } - # If -BasePolicyGUID parameter was used then use it by setting it as the Base policy ID in the supplemental policy - elseif ($null -ne $BasePolicyGUID) { - [System.String]$SupplementalPolicyID = Set-CIPolicyIdInfo -FilePath $WDACPolicyPath -PolicyName "Supplemental Policy from event logs - $(Get-Date -Format 'MM-dd-yyyy')" -SupplementsBasePolicyID $BasePolicyGUID -ResetPolicyID - [System.String]$SupplementalPolicyID = $SupplementalPolicyID.Substring(11) + Write-Verbose -Message 'Displaying the MDE Advanced Hunting logs in a GUI' + [PSCustomObject[]]$SelectMDEAHLogs = $MDEAHLogsToDisplay | Out-GridView -OutputMode Multiple -Title "Displaying $($MDEAHLogsToDisplay.count) Microsoft Defender for Endpoint Advanced Hunting Logs" - # Configure policy rule options - Edit-CiPolicyRuleOptions -Action Supplemental -XMLFile $WDACPolicyPath + if (($null -eq $SelectMDEAHLogs) -or ($SelectMDEAHLogs.Count -eq 0)) { + Write-ColorfulText -Color HotPink -InputText 'No MDE Advanced Hunting logs were selected to create a WDAC policy from. Exiting...' + return + } - Write-Verbose -Message 'ConvertTo-WDACPolicy: Copying the policy file to the current directory' - Copy-Item -Path $WDACPolicyPath -Destination $UserConfigDir -Force + $CurrentStep++ + Write-Progress -Id 31 -Activity 'Preparing an empty policy to save the logs to' -Status "Step $CurrentStep/$TotalSteps" -PercentComplete ($CurrentStep / $TotalSteps * 100) - if ($Deploy) { - ConvertFrom-CIPolicy -XmlFilePath $WDACPolicyPath -BinaryFilePath (Join-Path -Path $StagingArea -ChildPath "$SupplementalPolicyID.cip") | Out-Null + # Define the path where the final MDE AH XML policy file will be saved + [System.IO.FileInfo]$OutputPolicyPathMDEAH = Join-Path -Path $StagingArea -ChildPath "MDE Advanced Hunting Policy $CurrentDate.xml" - Write-Verbose -Message 'ConvertTo-WDACPolicy: Deploying the Supplemental policy' + Write-Verbose -Message 'Copying the template policy to the staging area' + Copy-Item -LiteralPath 'C:\Windows\schemas\CodeIntegrity\ExamplePolicies\AllowAll.xml' -Destination $OutputPolicyPathMDEAH -Force - &'C:\Windows\System32\CiTool.exe' --update-policy (Join-Path -Path $StagingArea -ChildPath "$SupplementalPolicyID.cip") -json | Out-Null - } - } - # If -PolicyToAddLogsTo parameter was used then merge the supplemental policy with the user input policy - elseif ($null -ne $PolicyToAddLogsTo) { + Write-Verbose -Message 'Emptying the policy file in preparation for the new data insertion' + Clear-CiPolicy_Semantic -Path $OutputPolicyPathMDEAH + + $CurrentStep++ + Write-Progress -Id 31 -Activity 'Building the Signer and Hash objects from the selected MDE AH logs' -Status "Step $CurrentStep/$TotalSteps" -PercentComplete ($CurrentStep / $TotalSteps * 100) + + Write-Verbose -Message 'Building the Signer and Hash objects from the selected MDE AH logs' + [PSCustomObject]$DataToUseForBuilding = Build-SignerAndHashObjects -Data $SelectMDEAHLogs + + $CurrentStep++ + Write-Progress -Id 31 -Activity 'Creating rules for different levels' -Status "Step $CurrentStep/$TotalSteps" -PercentComplete ($CurrentStep / $TotalSteps * 100) + + if ($Null -ne $DataToUseForBuilding.FilePublisherSigners -and $DataToUseForBuilding.FilePublisherSigners.Count -gt 0) { + Write-Verbose -Message 'Creating File Publisher Level rules' + New-FilePublisherLevelRules -FilePublisherSigners $DataToUseForBuilding.FilePublisherSigners -XmlFilePath $OutputPolicyPathMDEAH + } + if ($Null -ne $DataToUseForBuilding.PublisherSigners -and $DataToUseForBuilding.PublisherSigners.Count -gt 0) { + Write-Verbose -Message 'Creating Publisher Level rules' + New-PublisherLevelRules -PublisherSigners $DataToUseForBuilding.PublisherSigners -XmlFilePath $OutputPolicyPathMDEAH + } + if ($Null -ne $DataToUseForBuilding.CompleteHashes -and $DataToUseForBuilding.CompleteHashes.Count -gt 0) { + Write-Verbose -Message 'Creating Hash Level rules' + New-HashLevelRules -Hashes $DataToUseForBuilding.CompleteHashes -XmlFilePath $OutputPolicyPathMDEAH + } + + # MERGERS + + $CurrentStep++ + Write-Progress -Id 31 -Activity 'Merging the Hash Level rules' -Status "Step $CurrentStep/$TotalSteps" -PercentComplete ($CurrentStep / $TotalSteps * 100) + + Write-Verbose -Message 'Merging the Hash Level rules' + Remove-AllowElements_Semantic -Path $OutputPolicyPathMDEAH + Close-EmptyXmlNodes_Semantic -XmlFilePath $OutputPolicyPathMDEAH + + # Remove-UnreferencedFileRuleRefs -xmlFilePath $OutputPolicyPathMDEAH - # Objectify the user input policy file to extract its policy ID - $InputXMLObj = [System.Xml.XmlDocument](Get-Content -Path $PolicyToAddLogsTo) + $CurrentStep++ + Write-Progress -Id 31 -Activity 'Merging the Signer Level rules' -Status "Step $CurrentStep/$TotalSteps" -PercentComplete ($CurrentStep / $TotalSteps * 100) - Set-CIPolicyIdInfo -FilePath $WDACPolicyPath -PolicyName "Supplemental Policy from event logs - $(Get-Date -Format 'MM-dd-yyyy')" -ResetPolicyID | Out-Null + Write-Verbose -Message 'Merging the Signer Level rules' + Remove-DuplicateFileAttrib_Semantic -XmlFilePath $OutputPolicyPathMDEAH - # Remove all policy rule option prior to merging the policies since we don't need to add/remove any policy rule options to/from the user input policy - Edit-CiPolicyRuleOptions -Action RemoveAll -XMLFile $WDACPolicyPath + $CurrentStep++ + Write-Progress -Id 31 -Activity 'Finishing up the merge operation' -Status "Step $CurrentStep/$TotalSteps" -PercentComplete ($CurrentStep / $TotalSteps * 100) - Merge-CIPolicy -PolicyPaths $PolicyToAddLogsTo, $WDACPolicyPath -OutputFilePath $PolicyToAddLogsTo | Out-Null + <# + Improvement suggestion for the Merge-Signers_Semantic function + When an orphan CiSigner is found, it is currently being removed from the node - # Set HVCI to Strict - Set-HVCIOptions -Strict -FilePath $PolicyToAddLogsTo + Suggestion: + Implement an extra check to go through all User-Mode Signers and make sure they each have a corresponding CiSigner + They already get a CiSigner automatically during build operations, but this check is just extra in case the policy was intentionally modified by the user! - if ($Deploy) { - ConvertFrom-CIPolicy -XmlFilePath $PolicyToAddLogsTo -BinaryFilePath (Join-Path -Path $StagingArea -ChildPath "$($InputXMLObj.SiPolicy.PolicyID).cip") | Out-Null + Use Case: + User intentionally modifies one of the IDs of the CiSigners, but forgets to update the corresponding User-Mode Signer ID, AllowedSigner ID and more. + #> - Write-Verbose -Message 'ConvertTo-WDACPolicy: Deploying the policy that user selected to add the logs to' + # 2 passes are necessary + Merge-Signers_Semantic -XmlFilePath $OutputPolicyPathMDEAH + Merge-Signers_Semantic -XmlFilePath $OutputPolicyPathMDEAH - &'C:\Windows\System32\CiTool.exe' --update-policy (Join-Path -Path $StagingArea -ChildPath "$($InputXMLObj.SiPolicy.PolicyID).cip") -json | Out-Null + # This function runs twice, once for signed data and once for unsigned data + Close-EmptyXmlNodes_Semantic -XmlFilePath $OutputPolicyPathMDEAH + + # UNUSED FUNCTIONS - Their jobs have been replaced by semantic functions + # Keeping them here for reference + + # Remove-OrphanAllowedSignersAndCiSigners_IDBased -Path $OutputPolicyPathMDEAH + # Remove-DuplicateAllowedSignersAndCiSigners_IDBased -Path $OutputPolicyPathMDEAH + # Remove-DuplicateFileAttrib_IDBased -XmlFilePath $OutputPolicyPathMDEAH + # Remove-DuplicateAllowAndFileRuleRefElements_IDBased -XmlFilePath $OutputPolicyPathMDEAH + # Remove-DuplicateFileAttrib_Semantic -XmlFilePath $OutputPolicyPathMDEAH + # Remove-DuplicateFileAttribRef_IDBased -XmlFilePath $OutputPolicyPathMDEAH -Verbose + + #Region Base To Supplemental Policy Association and Deployment + + # If -BasePolicyFile parameter was used then associate the supplemental policy with the user input base policy + if ($null -ne $BasePolicyFile) { + + # Objectify the user input base policy file to extract its Base policy ID + $InputXMLObj = [System.Xml.XmlDocument](Get-Content -Path $BasePolicyFile) + + [System.String]$SupplementalPolicyID = Set-CIPolicyIdInfo -FilePath $OutputPolicyPathMDEAH -PolicyName "Supplemental Policy from MDE Advanced Hunting - $(Get-Date -Format 'MM-dd-yyyy')" -SupplementsBasePolicyID $InputXMLObj.SiPolicy.BasePolicyID -ResetPolicyID + [System.String]$SupplementalPolicyID = $SupplementalPolicyID.Substring(11) + + # Configure policy rule options + Edit-CiPolicyRuleOptions -Action Supplemental -XMLFile $OutputPolicyPathMDEAH + + Write-Verbose -Message 'ConvertTo-WDACPolicy: Copying the policy file to the User Config directory' + Copy-Item -Path $OutputPolicyPathMDEAH -Destination $UserConfigDir -Force + + if ($Deploy) { + ConvertFrom-CIPolicy -XmlFilePath $OutputPolicyPathMDEAH -BinaryFilePath (Join-Path -Path $StagingArea -ChildPath "$SupplementalPolicyID.cip") | Out-Null + + Write-Verbose -Message 'ConvertTo-WDACPolicy: Deploying the Supplemental policy' + + &'C:\Windows\System32\CiTool.exe' --update-policy (Join-Path -Path $StagingArea -ChildPath "$SupplementalPolicyID.cip") -json | Out-Null + } + } + # If -BasePolicyGUID parameter was used then use it by setting it as the Base policy ID in the supplemental policy + elseif ($null -ne $BasePolicyGUID) { + [System.String]$SupplementalPolicyID = Set-CIPolicyIdInfo -FilePath $OutputPolicyPathMDEAH -PolicyName "Supplemental Policy from MDE Advanced Hunting - $(Get-Date -Format 'MM-dd-yyyy')" -SupplementsBasePolicyID $BasePolicyGUID -ResetPolicyID + [System.String]$SupplementalPolicyID = $SupplementalPolicyID.Substring(11) + + # Configure policy rule options + Edit-CiPolicyRuleOptions -Action Supplemental -XMLFile $OutputPolicyPathMDEAH + + Write-Verbose -Message 'ConvertTo-WDACPolicy: Copying the policy file to the User Config directory' + Copy-Item -Path $OutputPolicyPathMDEAH -Destination $UserConfigDir -Force + + if ($Deploy) { + ConvertFrom-CIPolicy -XmlFilePath $OutputPolicyPathMDEAH -BinaryFilePath (Join-Path -Path $StagingArea -ChildPath "$SupplementalPolicyID.cip") | Out-Null + + Write-Verbose -Message 'ConvertTo-WDACPolicy: Deploying the Supplemental policy' + + &'C:\Windows\System32\CiTool.exe' --update-policy (Join-Path -Path $StagingArea -ChildPath "$SupplementalPolicyID.cip") -json | Out-Null + } + } + # If -PolicyToAddLogsTo parameter was used then merge the supplemental policy with the user input policy + elseif ($null -ne $PolicyToAddLogsTo) { + + # Objectify the user input policy file to extract its policy ID + $InputXMLObj = [System.Xml.XmlDocument](Get-Content -Path $PolicyToAddLogsTo) + + Set-CIPolicyIdInfo -FilePath $OutputPolicyPathMDEAH -PolicyName "Supplemental Policy from MDE Advanced Hunting - $(Get-Date -Format 'MM-dd-yyyy')" -ResetPolicyID | Out-Null + + # Remove all policy rule options prior to merging the policies since we don't need to add/remove any policy rule options to/from the user input policy + Edit-CiPolicyRuleOptions -Action RemoveAll -XMLFile $OutputPolicyPathMDEAH + + Merge-CIPolicy -PolicyPaths $PolicyToAddLogsTo, $OutputPolicyPathMDEAH -OutputFilePath $PolicyToAddLogsTo | Out-Null + + # Set HVCI to Strict + Set-HVCIOptions -Strict -FilePath $PolicyToAddLogsTo + + if ($Deploy) { + ConvertFrom-CIPolicy -XmlFilePath $PolicyToAddLogsTo -BinaryFilePath (Join-Path -Path $StagingArea -ChildPath "$($InputXMLObj.SiPolicy.PolicyID).cip") | Out-Null + + Write-Verbose -Message 'ConvertTo-WDACPolicy: Deploying the policy that user selected to add the MDE AH logs to' + + &'C:\Windows\System32\CiTool.exe' --update-policy (Join-Path -Path $StagingArea -ChildPath "$($InputXMLObj.SiPolicy.PolicyID).cip") -json | Out-Null + } + } + else { + Write-Verbose -Message 'ConvertTo-WDACPolicy: Copying the policy file to the User Config directory' + + Edit-CiPolicyRuleOptions -Action Supplemental -XMLFile $OutputPolicyPathMDEAH + + Set-HVCIOptions -Strict -FilePath $OutputPolicyPathMDEAH + + Copy-Item -Path $OutputPolicyPathMDEAH -Destination $UserConfigDir -Force + } + + #Endregion Base To Supplemental Policy Association and Deployment } } - - #Endregion Base To Supplemental Policy Association and Deployment } Finally { Write-Progress -Id 30 -Activity 'Complete.' -Completed + Write-Progress -Id 31 -Activity 'Complete.' -Completed if (-NOT $Debug) { Remove-Item -Path $StagingArea -Recurse -Force @@ -489,65 +923,94 @@ Function ConvertTo-WDACPolicy { <# .SYNOPSIS - Displays the Code Integrity logs in a GUI and allows the user to select the logs to convert to a Supplemental WDAC policy - It's a multi-purpose cmdlet that offers a wide range of functionalities that can either be used separately or mixed together for very detailed and specific tasks + This is a multi-purpose cmdlet that offers a wide range of functionalities that can either be used separately or mixed together for very detailed and specific tasks. + It currently supports Code Integrity and AppLocker logs from the following sources: Local Event logs and Microsoft Defender for Endpoint Advanced Hunting results. + + The cmdlet displays the logs in a GUI and allows the user to select the logs to be processed further. + + The logs can be filtered based on many criteria using the available parameters. + + The output of this cmdlet is a Supplemental Application Control (WDAC) policy. + Based on the input parameters, it can be associated with a base policy or merged with an existing Base or Supplemental policy. .DESCRIPTION - You can filter the logs by the policy name and the time - You can add the logs to an existing WDAC policy or create a new one -.PARAMETER FilterByPolicyNames - The names of the policies to filter the logs by. - Supports auto-completion, press TAB key to view the list of the deployed base policy names to choose from. - It will not display the policies that are already selected on the command line. - You can manually enter the name of the policies that are no longer available on the system. + The cmdlet can be used for local and remote systems. You can utilize this cmdlet to create Application Control for Business policies from MDE Advanced Hunting and then deploy them using Microsoft Intune to your endpoints. + This offers scalability and flexibility in managing your security policies. .PARAMETER PolicyToAddLogsTo The policy to add the selected logs to, it can either be a base or supplemental policy. .PARAMETER BasePolicyFile The base policy file to associate the supplemental policy with .PARAMETER BasePolicyGUID The GUID of the base policy to associate the supplemental policy with -.PARAMETER MinutesAgo - The number of minutes ago from the current time to filter the logs by -.PARAMETER HoursAgo - The number of hours ago from the current time to filter the logs by -.PARAMETER DaysAgo - The number of days ago from the current time to filter the logs by +.PARAMETER FilterByPolicyNames + The names of the policies to filter the logs by. + Supports auto-completion, press TAB key to view the list of the deployed base policy names to choose from. + It will not display the policies that are already selected on the command line. + You can manually enter the name of the policies that are no longer available on the system or are from remote systems in case of MDE Advanced Hunting logs. +.PARAMETER Source + The source of the logs: Local Event logs (LocalEventLogs) or Microsoft Defender for Endpoint Advanced Hunting results (MDEAdvancedHunting) + Supports validate set. +.PARAMETER MDEAHLogs + The path(s) to use MDE AH CSV files. + This is a dynamic parameter and will only be available if the Source parameter is set to MDEAdvancedHunting. .PARAMETER KernelModeOnly - If used, will filter the logs by including only the Kernel-Mode logs + If used, will filter the logs by including only the Kernel-Mode logs. You can use this parameter to easily create Supplemental policies for Strict Kernel-Mode WDAC policy. + More info available here: https://github.com/HotCakeX/Harden-Windows-Security/wiki/WDAC-policy-for-BYOVD-Kernel-mode-only-protection .PARAMETER LogType - The type of logs to display: Audit or Blocked + The type of logs to display: Audit or Blocked, the default is Audit. +.PARAMETER TimeSpan + The unit of time to use when filtering the logs by the time. + The allowed values are: Minutes, Hours, Days +.PARAMETER TimeSpanAgo + The number of the selected time unit to go back in time from the current time. .PARAMETER Deploy If used, will deploy the policy on the system .PARAMETER ExtremeVisibility If used, will display all the properties of the logs without any filtering. .PARAMETER SkipVersionCheck Can be used with any parameter to bypass the online version check - only to be used in rare cases -.NOTES - The biggest specified time unit is used for filtering the logs if more than one time unit is specified. +.LINK + https://github.com/HotCakeX/Harden-Windows-Security/wiki/ConvertTo-WDACPolicy +.INPUTS + System.IO.FileInfo + System.Guid + System.String + System.String[] + System.UInt64 + System.Management.Automation.SwitchParameter +.OUTPUTS + System.String .EXAMPLE ConvertTo-WDACPolicy -PolicyToAddLogsTo "C:\Users\Admin\AllowMicrosoftPlusBlockRules.xml" -Verbose - This example will display the Code Integrity logs in a GUI and allow the user to select the logs to add to the specified policy file. + This example will display the Code Integrity and AppLocker logs in a GUI and allow the user to select the logs to add to the specified policy file. .EXAMPLE ConvertTo-WDACPolicy -Verbose -BasePolicyGUID '{ACE9058C-8A24-47F4-86F0-A33FAB5073E3}' - This example will display the Code Integrity logs in a GUI and allow the user to select the logs to create a new supplemental policy and associate it with the specified base policy GUID. + This example will display the Code Integrity and AppLocker logs in a GUI and allow the user to select the logs to create a new supplemental policy and associate it with the specified base policy GUID. .EXAMPLE ConvertTo-WDACPolicy -BasePolicyFile "C:\Users\Admin\AllowMicrosoftPlusBlockRules.xml" - This example will display the Code Integrity logs in a GUI and allow the user to select the logs to create a new supplemental policy and associate it with the specified base policy file. + This example will display the Code Integrity and AppLocker logs in a GUI and allow the user to select the logs to create a new supplemental policy and associate it with the specified base policy file. .EXAMPLE ConvertTo-WDACPolicy - This example will display the Code Integrity logs in a GUI and takes no further action. + This example will display the Code Integrity and AppLocker logs in a GUI and takes no further action. .EXAMPLE ConvertTo-WDACPolicy -FilterByPolicyNames 'VerifiedAndReputableDesktopFlightSupplemental','WindowsE_Lockdown_Flight_Policy_Supplemental' -Verbose - This example will filter the Code Integrity logs by the specified policy names and display them in a GUI. It will also display verbose messages on the console. + This example will filter the Code Integrity and AppLocker logs by the specified policy names and display them in a GUI. It will also display verbose messages on the console. .EXAMPLE - ConvertTo-WDACPolicy -FilterByPolicyNames 'Microsoft Windows Driver Policy - Enforced' -MinutesAgo 10 + ConvertTo-WDACPolicy -FilterByPolicyNames 'Microsoft Windows Driver Policy - Enforced' -TimeSpan Minutes -TimeSpanAgo 10 - This example will filter the Code Integrity logs by the specified policy name and the number of minutes ago from the current time and display them in a GUI. + This example will filter the local Code Integrity and AppLocker logs by the specified policy name and the number of minutes ago from the current time and display them in a GUI. So, it will display the logs that are 10 minutes old and are associated with the specified policy name. +.EXAMPLE + ConvertTo-WDACPolicy -BasePolicyFile "C:\Program Files\WDACConfig\DefaultWindowsPlusBlockRules.xml" -Source MDEAdvancedHunting -MDEAHLogs "C:\Users\Admin\Downloads\New query.csv" -Deploy -TimeSpan Days -TimeSpanAgo 2 + + This example will create a new supplemental policy from the selected MDE Advanced Hunting logs and associate it with the specified base policy file and it will deploy it on the system. + The displayed logs will be from the last 2 days. You will be able to select the logs to create the policy from in the GUI. + +.EXTERNALHELP ..\Help\ConvertTo-WDACPolicy.xml #> } @@ -560,8 +1023,8 @@ Register-ArgumentCompleter -CommandName 'ConvertTo-WDACPolicy' -ParameterName 'B # SIG # Begin signature block # MIILkgYJKoZIhvcNAQcCoIILgzCCC38CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG -# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCD4HJypadSyR78y -# 86vxRiB9QqEHtGsGww1ktgERFqI2NKCCB9AwggfMMIIFtKADAgECAhMeAAAABI80 +# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBr0mA4RXu2oBhc +# U9urauPhn2HuYI3URrI5fyvTPvJg7qCCB9AwggfMMIIFtKADAgECAhMeAAAABI80 # LDQz/68TAAAAAAAEMA0GCSqGSIb3DQEBDQUAME8xEzARBgoJkiaJk/IsZAEZFgNj # b20xIjAgBgoJkiaJk/IsZAEZFhJIT1RDQUtFWC1DQS1Eb21haW4xFDASBgNVBAMT # C0hPVENBS0VYLUNBMCAXDTIzMTIyNzExMjkyOVoYDzIyMDgxMTEyMTEyOTI5WjB5 @@ -608,16 +1071,16 @@ Register-ArgumentCompleter -CommandName 'ConvertTo-WDACPolicy' -ParameterName 'B # Q0FLRVgtQ0ECEx4AAAAEjzQsNDP/rxMAAAAAAAQwDQYJYIZIAWUDBAIBBQCggYQw # GAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGC # NwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQx -# IgQgljYyT46uy4YCV+Bdtdulj4DdBN9vht6+CgFWliXsZE0wDQYJKoZIhvcNAQEB -# BQAEggIAFslKjSdqBdIj84qKOYDLRAKx5x3uiGxQ9f8tgT+lI5Oac7tg/od5JzuO -# r9G0ZNzBPKg7jWioLaZE2tetbs08JZ0SgGUwEFANnrbi6evp5MVMLojsMcNKCgac -# b8jZuZJ8FCtRIu00Wa5XZuPsZVT3ZNiT5N/OrOA4BygJ/qaTdk/kkHXANnagsaGL -# aPl24s9wadL8T10A5ZMqBMdOFHe7tM7pnrRpK39Ovfc6ZKuBqBvHIPr7OZXxq3hG -# RUVZbuniT90xoYNjhua6ZbvwoFIbxzrbyMLHxS1tLL0NAC95BnEB9ZzuxrMVNLFM -# +cx67HldXFaU7tUWQIFotrLlR0uTukmFuLvP7bhgw21qAcLIwr5UcG0u8fwMUeeo -# LuW7fYyNlFnbhk1qwhHLPvTwfHpjacdy5PgEkUo9+CLpoPrvhBSxxtjEeHp7ZIhB -# 1ODC9LkWJH+YKmjpo3g+Zt+43XTO8dHMWOW1+H8gNS1o7yULgrjf0jlsywU0FtKK -# qYYgxN7WyesOoRNlpAIMcGIgJKm3mfzbJPMziwgNPw241+qSmIsD5iItlVEiNUiI -# gzP/JEZ+Pa7j07FpV48f8tEY4QXosBJ1ny2GCGeFsJZQVZOqG6cTz1kCeFFpPOX8 -# 6i4QKhSYlfAiiBNDRZDbWrMniItz/ZBjimtlWE21WBVbmXuoYxk= +# IgQgFYeb/hSoHlhmH6WMB2vyr6Kv6OPjuvnd2OAXzibMSQswDQYJKoZIhvcNAQEB +# BQAEggIAREFBZ2psLttbizakjXZCoYKVyMbpT6aq/fiB15sRowYAf1fjJxt8HY9B +# vRKOjEofB+Pz+HLuUNUarZoh1hIkUVRpxEJbzge+jpXGjOAhzohNHOzT4fnw0ZUr +# o9EyZnA19PZUWHGhs5S/PEq8vMvPQvzLErFzc2jJHKNVIfn/zx2sxsw/P9Nx1vv1 +# Zn8QZc0cwQrdAAsGVrWzTrzzSSaGjXu0YkBbn+FqxLL+ikmZwH9x6Fhv8y0J6LSg +# enAzoJyfDKlNOUIBizSgIjJEH+nHlhEZjS0IDEp+Vp28hU7o+730WRKwPBp3hDh2 +# J+fYyHFNf/fewU7bzY47o5QIy5snhi19XUKtsGQNoTT6OskYbQGhGTGEXJeaHSU7 +# vjJ9g41Lt/MfpDCx2raoBimikQ03GXSwYAlKyOE1LILxle886izUER9BYsuTSzzO +# i59bzLVbKFWnqFvQ23HhlBd+pSp4gx8Gq9KlIPwGSXMN1maoF+/1bSvhS+jtgZJx +# 38qfFLFzg1F2hI8OA2P9duyZqeE3A0olRQBLrto3qgI2sjiV1YzzXGN9lNV10PyC +# LPGIdU8se8ch1tv1dEBvUMbZfLokVhzEjaAit9KwKWtZ+O8mc//PGonwU0f8Dw6J +# eL3EhV0YM2U05cUGuQ9YJeBVpXrhx1XSopG2bXcB2DB5zOEcfPg= # SIG # End signature block diff --git a/WDACConfig/WDACConfig Module Files/Core/Edit-SignedWDACConfig.psm1 b/WDACConfig/WDACConfig Module Files/Core/Edit-SignedWDACConfig.psm1 index 97b8cacd4..e2217f815 100644 --- a/WDACConfig/WDACConfig Module Files/Core/Edit-SignedWDACConfig.psm1 +++ b/WDACConfig/WDACConfig Module Files/Core/Edit-SignedWDACConfig.psm1 @@ -648,20 +648,20 @@ Function Edit-SignedWDACConfig { # Only create policy for files that are on longer available on the disk if there are any and # if user chose to include deleted files in the final supplemental policy - if ($AuditEventLogsProcessingResults.DeletedFileHashes -and $IncludeDeletedFiles) { + if ($AuditEventLogsProcessingResults.DeletedFileHashes.Values -and $IncludeDeletedFiles) { Write-Verbose -Message 'Attempting to create a policy for files that are no longer available on the disk but were detected in event viewer logs' # Displaying the unique values and count. Even though the DeletedFileHashesEventsPolicy.xml will have many duplicates, the final supplemental policy that will be deployed on the system won't have any duplicates # Because Merge-CiPolicy will automatically take care of removing them - Write-Verbose -Message "$(($AuditEventLogsProcessingResults.DeletedFileHashes.'File Name' | Select-Object -Unique).count) file(s) have been found in event viewer logs that were run during Audit phase but are no longer on the disk, they are as follows:" - $AuditEventLogsProcessingResults.DeletedFileHashes.'File Name' | Select-Object -Unique | ForEach-Object -Process { + Write-Verbose -Message "$(($AuditEventLogsProcessingResults.DeletedFileHashes.Values.'File Name' | Select-Object -Unique).count) file(s) have been found in event viewer logs that were run during Audit phase but are no longer on the disk, they are as follows:" + $AuditEventLogsProcessingResults.DeletedFileHashes.Values.'File Name' | Select-Object -Unique | ForEach-Object -Process { Write-Verbose -Message "$_" } Write-Verbose -Message 'Creating FileRules and RuleRefs for files that are no longer available on the disk but were detected in event viewer logs' - [System.String]$FileRulesHashesResults = Get-FileRules -HashesArray $AuditEventLogsProcessingResults.DeletedFileHashes - [System.String]$RuleRefsHashesResults = (Get-RuleRefs -HashesArray $AuditEventLogsProcessingResults.DeletedFileHashes).Trim() + [System.String]$FileRulesHashesResults = Get-FileRules -HashesArray $AuditEventLogsProcessingResults.DeletedFileHashes.Values + [System.String]$RuleRefsHashesResults = (Get-RuleRefs -HashesArray $AuditEventLogsProcessingResults.DeletedFileHashes.Values).Trim() Write-Verbose -Message 'Saving the File Rules and File Rule Refs in the FileRulesAndFileRefs.txt in the Staging Area for debugging purposes' $FileRulesHashesResults + $RuleRefsHashesResults | Out-File -FilePath (Join-Path -Path $StagingArea -ChildPath 'FileRulesAndFileRefs.txt') -Force @@ -1283,8 +1283,8 @@ Register-ArgumentCompleter -CommandName 'Edit-SignedWDACConfig' -ParameterName ' # SIG # Begin signature block # MIILkgYJKoZIhvcNAQcCoIILgzCCC38CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG -# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDIuZ4SPwMthG/k -# vhcVWrat0mml0N+dMtlsAukb1hQY1aCCB9AwggfMMIIFtKADAgECAhMeAAAABI80 +# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDjbFXavbUf3nB4 +# AVd0vtSfzEJJm2SvUDveBqvNAJEPh6CCB9AwggfMMIIFtKADAgECAhMeAAAABI80 # LDQz/68TAAAAAAAEMA0GCSqGSIb3DQEBDQUAME8xEzARBgoJkiaJk/IsZAEZFgNj # b20xIjAgBgoJkiaJk/IsZAEZFhJIT1RDQUtFWC1DQS1Eb21haW4xFDASBgNVBAMT # C0hPVENBS0VYLUNBMCAXDTIzMTIyNzExMjkyOVoYDzIyMDgxMTEyMTEyOTI5WjB5 @@ -1331,16 +1331,16 @@ Register-ArgumentCompleter -CommandName 'Edit-SignedWDACConfig' -ParameterName ' # Q0FLRVgtQ0ECEx4AAAAEjzQsNDP/rxMAAAAAAAQwDQYJYIZIAWUDBAIBBQCggYQw # GAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGC # NwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQx -# IgQgBeebsY9IebD1hM/zADKHJlKH57GRX+nzV3P7ojSqR3cwDQYJKoZIhvcNAQEB -# BQAEggIAP+Vde9p5rNrqt6yRrCZ0SZ1k2t9vYWSvdZrY5CChHSYkq9f0p+j4EOmw -# i6JxQ0K08YPz2g7TGVZoinm/iFB/VUscEVGrvNxgmW8SfsIPn+pXvMw3RIpSjPxz -# dBSf8qJkf++zTzRLgVj2tGG79bRWNWxtxjvF615F5sdimu4QfEok8b9Jx6RjTbj8 -# lwSESMp6kwI9WVw0posRqAzP+QrnoFSG+uOY6xiYqoJeZxwyEpKH4ZNHiECO8Xl7 -# HzOWeugZG4hbC8zxPlS3npgBjRdVmHSxx6sccYknaMhZBjIq4fIJ2GLqagbcTOR/ -# G3sLTptqhIfU6VLgv05lheaSRBGxknxIBCWBEpOrRt1JrP+PO60TOabwcBR1dEzg -# XtvzGPdLxzCUSRMB82FK3zSWtytXQXq03kAOulecwW+BuljaEv+VLuObZU8ME/nY -# z/X/gCalEiRWUDMh0O4Ub1xsw6KJgNr1Jye0i3ZMQGawuKAIW2wI0YuVPZ2jKV6r -# Qda5AYI074KHEdDAUAy6zOAfLZcUZTjm1hOZQmWxFSUm++kIznN8ZJg7V3MVxhMn -# hYZH+w89ywuYz0KaIk1jKWwBbAtfpmWWEvIL71EdBt07w/un4uzFY8G0sC++KPzk -# l+NANi1AAzvAmRKaNuDFksoLAI9gZq9BkV/LYRaDJBcGEeAYXPg= +# IgQgkIcYUSR+F7OG4W2Klw/jZsfpd2H63mR5sxO/IIiFtq0wDQYJKoZIhvcNAQEB +# BQAEggIAK6Qshoq5Lco+WP4JZ8MPXsBNmBZDmO0mUhCNcd+mBI+/x7lrqrioi0By +# J/C7KBPSUEIHQTbfvpUCFNwPfb/fgIak01U/eXweDpC1nZu1YPQPYd1PIYzc4BJe +# 7BftrnF+FmbrqttEZ4XHhuEtGJ710Bi7B/T3+i8ZN4LC+VeOXp0nJ70R+CuNzz1B +# rpqa8BUa9Q11nAPm//Boy26PRxnDkEQXBIWkoP+l1cc4YoQxqq9PK1xNwtwCzCRE +# jjTBDOsiIpyUaSPE/KzkuQJOexKI0KHHa+LyO90KhQkU/Vov104bVViHl/I+Ur4q +# BUNnsrPVVl/5kWJ+usBiM0qBlMp1zz/e955ncaT87NaDEJNezKjR0vX7fgjSiXHH +# ZEBs65eH/GT0iU6roX1GF0Bvcr4B31mWqTpkOhJ/7OpfhEst7H9erJ60IRjeD/w/ +# W+eRy08XCGR3VpsrI1d9i3YGLPeAnFb1wni+AABfTYCTWRTlu+yg58MUE0zZVoaK +# e1s1g7HNpymUKKAfoJd5LvEPwwfYvO+QlVX/APF7LvGO4EaxpRS9y1Fi3yewVIu/ +# Rwygq/DJvqE1AvTAWL/8sN+/Yy8K3U4BjVnAq8pTfh2M1aHT3K8kZDCaLss9RR9D +# CPGYDrXi17uMatlQNkVoQyxFSVwX6x5LLXoG2oCTXKIXR3kOecE= # SIG # End signature block diff --git a/WDACConfig/WDACConfig Module Files/Core/Edit-WDACConfig.psm1 b/WDACConfig/WDACConfig Module Files/Core/Edit-WDACConfig.psm1 index 0161086c2..26aea66c5 100644 --- a/WDACConfig/WDACConfig Module Files/Core/Edit-WDACConfig.psm1 +++ b/WDACConfig/WDACConfig Module Files/Core/Edit-WDACConfig.psm1 @@ -527,20 +527,20 @@ Function Edit-WDACConfig { # Only create policy for files that are on longer available on the disk if there are any and # if user chose to include deleted files in the final supplemental policy - if ($AuditEventLogsProcessingResults.DeletedFileHashes -and $IncludeDeletedFiles) { + if ($AuditEventLogsProcessingResults.DeletedFileHashes.Values -and $IncludeDeletedFiles) { Write-Verbose -Message 'Attempting to create a policy for files that are no longer available on the disk but were detected in event viewer logs' # Displaying the unique values and count. Even though the DeletedFileHashesEventsPolicy.xml will have many duplicates, the final supplemental policy that will be deployed on the system won't have any duplicates # Because Merge-CiPolicy will automatically take care of removing them - Write-Verbose -Message "$(($AuditEventLogsProcessingResults.DeletedFileHashes.'File Name' | Select-Object -Unique).count) file(s) have been found in event viewer logs that were run during Audit phase but are no longer on the disk, they are as follows:" - $AuditEventLogsProcessingResults.DeletedFileHashes.'File Name' | Select-Object -Unique | ForEach-Object -Process { + Write-Verbose -Message "$(($AuditEventLogsProcessingResults.DeletedFileHashes.Values.'File Name' | Select-Object -Unique).count) file(s) have been found in event viewer logs that were run during Audit phase but are no longer on the disk, they are as follows:" + $AuditEventLogsProcessingResults.DeletedFileHashes.Values.'File Name' | Select-Object -Unique | ForEach-Object -Process { Write-Verbose -Message "$_" } Write-Verbose -Message 'Creating FileRules and RuleRefs for files that are no longer available on the disk but were detected in event viewer logs' - [System.String]$FileRulesHashesResults = Get-FileRules -HashesArray $AuditEventLogsProcessingResults.DeletedFileHashes - [System.String]$RuleRefsHashesResults = (Get-RuleRefs -HashesArray $AuditEventLogsProcessingResults.DeletedFileHashes).Trim() + [System.String]$FileRulesHashesResults = Get-FileRules -HashesArray $AuditEventLogsProcessingResults.DeletedFileHashes.Values + [System.String]$RuleRefsHashesResults = (Get-RuleRefs -HashesArray $AuditEventLogsProcessingResults.DeletedFileHashes.Values).Trim() Write-Verbose -Message 'Saving the File Rules and File Rule Refs in the FileRulesAndFileRefs.txt for debugging purposes' $FileRulesHashesResults + $RuleRefsHashesResults | Out-File -FilePath (Join-Path -Path $StagingArea -ChildPath 'FileRulesAndFileRefs.txt') -Force @@ -1052,8 +1052,8 @@ Register-ArgumentCompleter -CommandName 'Edit-WDACConfig' -ParameterName 'SuppPo # SIG # Begin signature block # MIILkgYJKoZIhvcNAQcCoIILgzCCC38CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG -# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCfDCsZImooZFMI -# 6Kff/z0peBPZRoQvv28sDUySBYSIsaCCB9AwggfMMIIFtKADAgECAhMeAAAABI80 +# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDvOCdhDkt54HVg +# f60AUdJ4lsl17cLAmOkYoeQ+OupZ3KCCB9AwggfMMIIFtKADAgECAhMeAAAABI80 # LDQz/68TAAAAAAAEMA0GCSqGSIb3DQEBDQUAME8xEzARBgoJkiaJk/IsZAEZFgNj # b20xIjAgBgoJkiaJk/IsZAEZFhJIT1RDQUtFWC1DQS1Eb21haW4xFDASBgNVBAMT # C0hPVENBS0VYLUNBMCAXDTIzMTIyNzExMjkyOVoYDzIyMDgxMTEyMTEyOTI5WjB5 @@ -1100,16 +1100,16 @@ Register-ArgumentCompleter -CommandName 'Edit-WDACConfig' -ParameterName 'SuppPo # Q0FLRVgtQ0ECEx4AAAAEjzQsNDP/rxMAAAAAAAQwDQYJYIZIAWUDBAIBBQCggYQw # GAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGC # NwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQx -# IgQgCeZPNqXa13ZGHaivKielzVLt0/iekmYWl6Tz1IaD9kgwDQYJKoZIhvcNAQEB -# BQAEggIAK7B1Z84Bx9/SgfhJzMFDYuYEcdZV9UGhkpxAjm085JDLzRAmOA7SGmwh -# t9oGB1yYDhv3yfdc7EYWiBmSLMio2V1Z2MmLKpvH4wdYbUULf9Qv8FhM7E9pOkkE -# i3xjaH9e09w0OQ0vIKVcPhzWWaBr64sh6pPGqtzloc2FdJm5S5ANRMwozTJMs2E3 -# pZgbj6ZIm7QcOGw7BPXWZl6aNsDFle0ScIZHCU7PWVSPVv5mZ8PNozZOENo/f56V -# PH32CmpeAJxhR7JIUE7VrWzR98RZnwYOZZpPn/lDzIuVsjDJpCFgJd3gQcG9exqp -# 75G2BHKFPs71H3MhWRudtwHPtrVsX1/Vbdjy5i4TUkK7sENVimrBYM5Gwmv/SSPd -# iDKDQMcEpQxZPPOAFqLLZ8rYUE5xWiOL8Ee3bmyZ3jiOs5atY+Vj41kJbd3C6C62 -# UVWRIDXEplHpPwgWjcZHJhxUEleX18+B2LiLYqdHrPuQevsbMIC0xY7wZh2VwgDR -# 0GkKhpwfVEvfdMxMlGhf4zPAXIrnOPA6Vtg6dOpVnXbLmx6VVWpPsvIsLsCxkpUN -# SDEMd9C+RNN8d9vhoMPdp+77rc2x5x68uu6kX4Ssrw7OdSyCo0q7425MH7QfCiBJ -# pzNJ1kU0JOnnp6KL4HFTyPANnh6oIJFmp0jBOULVqQHuX/05CYY= +# IgQgV9gBwVzaa1aQwyQsVcf7ssrsCvjWDQ428nL6MNx3s0AwDQYJKoZIhvcNAQEB +# BQAEggIAijOZA+AntMOvFArHh8IfQl9Cgk57iRvRp+VHQpRbgeLoTxOChP5zoIRJ +# rr85ZXF1AdCe3T2YH2mYJJ3hBBG//SxI/OUENTbEq7/XFGJwIdmEAVWtBZfllfkf +# nFhllDnVNOwn++GlMWsTYKr1wPPyGdKy4ob573GXerY/Rg07KUWlx0awtz1kwC1U +# 5bmcS5iIb/ZAnEswi2qwwJcOJ/RcrGnXUUXt54eHISdGXme8TtZ8oJHP6cj8rh3Y +# 93PQllD2vMbCgmF+4U4ohBVQNYjL46Gyyrw6sNwhzg2kEcYMS+y3HowI0X15Nt9w +# MM3MYHdGos3krk81uHZX7k/vEwV6UAqZr1eI1/W4KWv8JTC8E5n7wmfEGNhh4O4e +# Jf0hjn+ljLQCZOcT3n1l0pWUV/SMzNWuyVCOsT+DVqaAJW7by5oFd7POlfYBUZTY +# M5B7Tu5O8rpIIxdQcqeiCRjE4CFJCfScWOtX6isALZRSWlgAsZDix9bWFMLlCtgX +# 79mQ5irSgmHXiNs2qHTZ3m8ZvCm0pJMGfqZJK9Tw/DwO14FXyY1ecFVYXLuOODh5 +# FY+uPD/LAQCPLgIjZcC/H2L02hSf0Rv9x4n5IPrRc/2eOazUkvM10lmoGRq3cVxE +# LS7yHP2+K+Vv3LEBv9Nl2qYe/HHVrF7ptuOuUZAC5W/u2n+bL9o= # SIG # End signature block diff --git a/WDACConfig/WDACConfig Module Files/Core/Invoke-WDACSimulation.psm1 b/WDACConfig/WDACConfig Module Files/Core/Invoke-WDACSimulation.psm1 index e34c4e3f6..a018387dc 100644 --- a/WDACConfig/WDACConfig Module Files/Core/Invoke-WDACSimulation.psm1 +++ b/WDACConfig/WDACConfig Module Files/Core/Invoke-WDACSimulation.psm1 @@ -141,6 +141,9 @@ Function Invoke-WDACSimulation { # Store the paths of Signed files with EKU mismatch [System.IO.FileInfo[]]$SignedButEKUMismatch = @() + # Store the paths of the signed files that are inaccessible (Kernel-Protected files, Microsoft Defender files etc.) + [System.IO.FileInfo[]]$InAccessibleFilePaths = @() + # Store the paths of Signed files that are not allowed [System.IO.FileInfo[]]$SignedButNotAllowed = @() @@ -277,9 +280,16 @@ Function Invoke-WDACSimulation { # If the file is signed and valid { $_.Status -eq 'valid' } { - # Use the Compare-SignerAndCertificate function to process it - $ComparisonResult = Compare-SignerAndCertificate -XmlFilePath $XmlFilePath -SignedFilePath $CurrentFilePath + try { + # Use the Compare-SignerAndCertificate function to process it + $ComparisonResult = Compare-SignerAndCertificate -XmlFilePath $XmlFilePath -SignedFilePath $CurrentFilePath + } + catch [ExceptionFailedToGetCertificateCollection] { + # If the file's certificate collections could not be fetched due to lack of necessary permissions, place it in a different array of file path + $InAccessibleFilePaths += $CurrentFilePath + break MainSwitchLabel + } # If there is no comparison result then the file is not allowed by the policy if (([System.String]::IsNullOrWhiteSpace($ComparisonResult))) { @@ -584,6 +594,22 @@ Function Invoke-WDACSimulation { $MegaOutputObject += New-Object -TypeName PSObject -Property $Object } } + + if ($InAccessibleFilePaths) { + Write-Verbose -Message 'Looping through the array of signed files that are not processed due to lack of permission' + Write-Verbose -Message "$($InAccessibleFilePaths.count) File(s) are signed but not processed due to lack of permission." -Verbose + foreach ($Path in $InAccessibleFilePaths) { + # Create a hash table with the file path and source properties + [System.Collections.Hashtable]$Object = @{ + FilePath = $Path + Source = 'Signer' + Permission = 'Not processed, No permission' + IsAuthorized = $false + } + # Convert the hash table to a PSObject and add it to the output array + $MegaOutputObject += New-Object -TypeName PSObject -Property $Object + } + } } end { @@ -755,8 +781,8 @@ Register-ArgumentCompleter -CommandName 'Invoke-WDACSimulation' -ParameterName ' # SIG # Begin signature block # MIILkgYJKoZIhvcNAQcCoIILgzCCC38CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG -# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCn8E3ZeFPqV8VL -# WajIcDXn1bbq39TVTDOlwyfHMz9haaCCB9AwggfMMIIFtKADAgECAhMeAAAABI80 +# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDw7Dg/E7IqvgHr +# e2+EDr7FqVMqM5xvt+/JEcb5p91B9KCCB9AwggfMMIIFtKADAgECAhMeAAAABI80 # LDQz/68TAAAAAAAEMA0GCSqGSIb3DQEBDQUAME8xEzARBgoJkiaJk/IsZAEZFgNj # b20xIjAgBgoJkiaJk/IsZAEZFhJIT1RDQUtFWC1DQS1Eb21haW4xFDASBgNVBAMT # C0hPVENBS0VYLUNBMCAXDTIzMTIyNzExMjkyOVoYDzIyMDgxMTEyMTEyOTI5WjB5 @@ -803,16 +829,16 @@ Register-ArgumentCompleter -CommandName 'Invoke-WDACSimulation' -ParameterName ' # Q0FLRVgtQ0ECEx4AAAAEjzQsNDP/rxMAAAAAAAQwDQYJYIZIAWUDBAIBBQCggYQw # GAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGC # NwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQx -# IgQgszcX8IhVikY7ngQcwU5MRcSCFQJV34ynFBARiRCAP0kwDQYJKoZIhvcNAQEB -# BQAEggIASj/eja7u640ip1kUwFnKuuLPKk4rBrtrDSwtmfsWysocgaP8nOOGKVAh -# S3SSNgavBaauLKjJ7PFM2aJ6dk03mCIQYwNnUlb6FwXsVwzSiHyvBAV+NkPz/nEz -# KcqK26APAuOjhOAk1lhDs+wwr1J6Ik0BgUXgy+CCgvlrb47XQ2PykshlLZGynPXa -# KSZ39gthepEfkLOnX2ig4X4hWzXLEOEqti8XTdjB9/u6nlBF8RDQeDzCjToX1zbE -# RDHxVIoNRMH+s6hDJR0lOowUrTP2yamhswlJpUrtkRFKsR0HCMBkkwt0GFYh+yGZ -# ZMZpkgcz5EhVieZwIwKr21sVOXK4M2HkHIRf24urcqTHjYBZzb4evmoyylZ6yw40 -# ouy9YId2/0eyF3JoRflZbESzGqpvlXUH51vQv/5efKgdU2h8zrxldWiDWpt+CjHm -# a5z+Dnh8ZGhFML5nLeCrtwyE+zQdqEg9nef19wm8KpBZ03n4eytNGqUaInFK5GwX -# oO2zpVHaZE11i9ZtfSXzL6Y3z/Olse6AAlK8zQXDC4xoidv/gVFFtpjeKPvqbGuN -# 8f5jGOVg5NtomelkXp6eOeoyC+lS2XlpfKBmzdn+tvyPJm+oUnbn9esPrfZ6s+sb -# gkvddRDDakhm/OWYmftyHuKGHJNz1ITnwTBglGhz1l5gXnJ8D4I= +# IgQgUZX1J1xaIWC9ZOJoF/jPAxpoJ7BbI99W9WxCgIj9Cx0wDQYJKoZIhvcNAQEB +# BQAEggIAD79kNnFWrAT1sA6ryRQZ3UUEcjjZbRDnTZ1zov3ipjQJ1DviypEabdcS +# QNBf/uHXH9d2cFLwmTCej98J0DGxmA4N17u7WOdXip34keS2HRItvpJmpjnsPrVr +# SdpA1hShbyTYnzG91qIwF4+1U7fcXN4quyRH63+B6WSw9qI0+pEU8b32EFfQoN+V +# uZdnAtngssjdLzlx13HMlO8A6ww7reP3EGbLNtyjA5gRPUdNWYY+1brkLID9X4hD +# N0OpovjZe0DNibqZgnQipUqT8oJGWQXmLfI6kL+OaTSSD+BCOQxHWlA6jE+Ruuom +# zOvGlFs1r9eHPo4LscEu4QKUyJCT2eJZloqt8d7dQdnhqPTyOK1FP/lepdL7btYy +# y06O6F82/KIHmr2SU+lT3sn4ceUiFHsvZVCMSk8/QMO64MJiBJqCZ8ZLR73kaDOL +# j/PkHpaALRo2IlfzBJan7g0GWvMEw2ZR+rexxDctwG6IooLVcHD3J3/WiWpo27eT +# f/JzPslSrCsWJzMXqbNMiCqCNCzPMMWsSjJJLH6J5T189XtkaGROA75efsZSQn+s +# mmPpEZWK6gOCnf3R54npwyN6CFGG8g5iq91789cvYmSFNetmfEgtVtbSggecX2gx +# xZXPL5Lxk9on42Lfp8EHq/9t4hEGWQzuDHKa5ztTUTrjQ3bkts8= # SIG # End signature block diff --git a/WDACConfig/WDACConfig Module Files/CoreExt/Classes.psm1 b/WDACConfig/WDACConfig Module Files/CoreExt/Classes.psm1 index 498a68d03..7de22e199 100644 --- a/WDACConfig/WDACConfig Module Files/CoreExt/Classes.psm1 +++ b/WDACConfig/WDACConfig Module Files/CoreExt/Classes.psm1 @@ -1,7 +1,7 @@ # Since the quasi-exported classes will be available process-wide # and therefore also in other runspaces, defining them with the [NoRunspaceAffinity()] attribute. # https://stackoverflow.com/a/78078461/21243735 -# https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_classes?view=powershell-7.4#exporting-classes-with-type-accelerators +# https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_classes#exporting-classes-with-type-accelerators # argument tab auto-completion and ValidateSet for Levels and Fallbacks parameters in the entire module [NoRunspaceAffinity()] @@ -45,11 +45,22 @@ Class CertCNz : System.Management.Automation.IValidateSetValuesGenerator { } } +# a class to throw a custom exception when the certificate collection cannot be obtained during WDAC Simulation +[NoRunspaceAffinity()] +class ExceptionFailedToGetCertificateCollection : System.Exception { + [System.String]$AdditionalData + + ExceptionFailedToGetCertificateCollection([System.String]$Message, [System.String]$AdditionalData) : base($Message) { + $This.additionalData = $AdditionalData + } +} + # Define the types to export with type accelerators. [System.Reflection.TypeInfo[]]$ExportableTypes = @( [ScanLevelz] [CertCNz] [BasePolicyNamez] + [ExceptionFailedToGetCertificateCollection] ) # Get the non-public TypeAccelerators class for defining new accelerators. @@ -73,8 +84,8 @@ foreach ($Type in $ExportableTypes) { # SIG # Begin signature block # MIILkgYJKoZIhvcNAQcCoIILgzCCC38CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG -# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCB4k/BsJwtR9x6g -# bVrZ5j4AcemhaBo+X0XaAn9yhi+wH6CCB9AwggfMMIIFtKADAgECAhMeAAAABI80 +# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCD9KheC+QwZeAmF +# g1xWldilCpuou3ANAFzVdOww8fsLfqCCB9AwggfMMIIFtKADAgECAhMeAAAABI80 # LDQz/68TAAAAAAAEMA0GCSqGSIb3DQEBDQUAME8xEzARBgoJkiaJk/IsZAEZFgNj # b20xIjAgBgoJkiaJk/IsZAEZFhJIT1RDQUtFWC1DQS1Eb21haW4xFDASBgNVBAMT # C0hPVENBS0VYLUNBMCAXDTIzMTIyNzExMjkyOVoYDzIyMDgxMTEyMTEyOTI5WjB5 @@ -121,16 +132,16 @@ foreach ($Type in $ExportableTypes) { # Q0FLRVgtQ0ECEx4AAAAEjzQsNDP/rxMAAAAAAAQwDQYJYIZIAWUDBAIBBQCggYQw # GAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGC # NwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQx -# IgQg5XdgEu3VPKwgiA+EBJKUZFPQWhN2el9Zck+VYvDH5O8wDQYJKoZIhvcNAQEB -# BQAEggIAkWnlsQ98tkQpuBo579x9cBfjUnqHJSFhu4rm74hRJ/dsK0wT4Aoo5Jqd -# X5QKdlvGyUkBQF5EHg9xGjae4+sGqztsOM+87Hj5vA+9lFdRuZLLUOOMEiSrT2Mc -# WvtphT/yKfAOpoKG4Y6AS3H2zXbFjqbLH2X1agqr8WHRDZz9p0rcUj+s7oRV5Zws -# JhV5QbCHb1MMbG05PqGvkJm2YC6/IbTKZVA37K3zLtXzyAmoAvgtbo4VKkYrlHlo -# awN8/aAUJzhyCCevCe3JwHGZnKGwNDjwLfKJK5GrflP2xA4mXr1guGFX92hFHzv2 -# IYrhDqXDToYOWgNFAeUEbdqWt4Dj60LPKsWGw8+MYYXdti6al8f+EvabLMiKJc4g -# OWAPFNU3TNITc1yjtDrIkhtnlt6c3qlbQSBut+otS0epS+9oZbgZyqAEimBJ2jt+ -# 0Etmf/CFrsS9qh8wM6dxnCMvbumSHWraNHl1nqjZNwoE9p9QnU489u6rwQAfkG6k -# 7r1bMNz47yMSuMndSNKwY35uNllSU4J+07urpB9iMlONzFqJ6OxC+z2Etl9NhH1F -# WVLROZ4kYimqJD7kZ4s5Pmg01j8eqAPqbI/OGLd8dLw4O2xDN57tlg/VEROdkaTk -# b5zOEMrks88Cqrusok9MKHDRYerDfZgGpyA3Y3rrWEXRBM9BFlw= +# IgQgZTNa0z12p9az8IOjAJiT4XpvYLnXqt8aiW9MKW2WVWcwDQYJKoZIhvcNAQEB +# BQAEggIAC8es3LEoc9OZMX7URFE6PSF3M5df1l8BL0Qz5T9mWyexJdgUmA7hxBKL +# 9lseS1K8PBxx3vgrdCT5DyGwRrDSOfHumUxQWTkMna339bFalBgV0LXMb7bzqtaQ +# s91nEa7zyhIOPbKvi4cREoAi32qCjW9/YeJIEfrkp7aaGuzX45UsRi6pLfqPWS1F +# vt3mhhsbLLfe607O9jJpNTc3B277AcQMojrYVBVaAmpc+YyCf14tx/RQ1XzRvMKl +# c3QbenLoMUXPuN0tJvbxPZy1YTf7CdgCAUxsiI31DyaGUo7Yk4w+Qld3ltI+LIb8 +# Ggj9eUYxZYPnRuwJNg6Hkub26M2cnO0RGASkVhg2w85UObYMS5w1yBWRuxFj2mnC +# MA68wPL+gnTAfrginETHUmIuAisRvuvB4CgeTkYTF2ANCIJbUNlR60LHYf8nqq91 +# fxv7FnFu99K12bQba7BBR1aFia1ZipANcwfqJvA7/7l5/gkQNWDxr4PG82MqbjIo +# LD8L2Fu6Qit+MtWq/mdE47nXiABTUehadm9CkvCUuA0Eoa3jxpknpz0g3GJEs/Pu +# 4xYmbzJpfwgA7wkRhxOvMwU3lx1Jkf66pQKGnIo4tCkQULE1YVr59Dgx4QvvF0ug +# Cp/Ucn88TkGO3Sk50bMmrCO4SCUUNrot65olABvbcwuKOwEZnZU= # SIG # End signature block diff --git a/WDACConfig/WDACConfig Module Files/CoreExt/PSDefaultParameterValues.ps1 b/WDACConfig/WDACConfig Module Files/CoreExt/PSDefaultParameterValues.ps1 index 940a612ec..cf76e79d6 100644 --- a/WDACConfig/WDACConfig Module Files/CoreExt/PSDefaultParameterValues.ps1 +++ b/WDACConfig/WDACConfig Module Files/CoreExt/PSDefaultParameterValues.ps1 @@ -1,51 +1,87 @@ # $PSDefaultParameterValues only get read from scope where invocation occurs # This is why this file is dot-sourced in every other component of the WDACConfig module at the beginning $PSDefaultParameterValues = @{ - 'Invoke-WebRequest:HttpVersion' = '3.0' - 'Invoke-WebRequest:SslProtocol' = 'Tls12,Tls13' - 'Invoke-RestMethod:HttpVersion' = '3.0' - 'Invoke-RestMethod:SslProtocol' = 'Tls12,Tls13' - 'Import-Module:Verbose' = $false - 'Remove-Module:Verbose' = $false - 'Export-ModuleMember:Verbose' = $false - 'Add-Type:Verbose' = $false - 'Get-WinEvent:Verbose' = $false - 'Test-Path:ErrorAction' = 'SilentlyContinue' - 'Receive-CodeIntegrityLogs:Verbose' = $Verbose - 'Get-FileRules:Verbose' = $Verbose - 'Get-BlockRulesMeta:Verbose' = $Verbose - 'Get-GlobalRootDrives:Verbose' = $Verbose - 'Get-RuleRefs:Verbose' = $Verbose - 'Get-SignTool:Verbose' = $Verbose - 'Move-UserModeToKernelMode:Verbose' = $Verbose - 'New-EmptyPolicy:Verbose' = $Verbose - 'Set-LogSize:Verbose' = $Verbose - 'Test-FilePath:Verbose' = $Verbose - 'Update-Self:Verbose' = $Verbose - 'Write-ColorfulText:Verbose' = $Verbose - 'New-SnapBackGuarantee:Verbose' = $Verbose - 'Compare-SecureStrings:Verbose' = $Verbose - 'Get-KernelModeDriversAudit:Verbose' = $Verbose - 'Copy-CiRules:Verbose' = $Verbose - 'Get-TBSCertificate:Verbose' = $Verbose - 'Get-SignerInfo:Verbose' = $Verbose - 'Get-SignedFileCertificates:Verbose' = $Verbose - 'Get-FileRuleOutput:Verbose' = $Verbose - 'Get-CertificateDetails:Verbose' = $Verbose - 'Get-NestedSignerSignature:Verbose' = $Verbose - 'Compare-SignerAndCertificate:Verbose' = $Verbose - 'Remove-SupplementalSigners:Verbose' = $Verbose - 'Get-ExtendedFileInfo:Verbose' = $Verbose - 'Edit-CiPolicyRuleOptions:Verbose' = $Verbose - 'New-AppxPackageCiPolicy:Verbose' = $Verbose - 'New-StagingArea:Verbose' = $Verbose + 'Invoke-WebRequest:HttpVersion' = '3.0' + 'Invoke-WebRequest:SslProtocol' = 'Tls12,Tls13' + 'Invoke-RestMethod:HttpVersion' = '3.0' + 'Invoke-RestMethod:SslProtocol' = 'Tls12,Tls13' + 'Import-Module:Verbose' = $false + 'Remove-Module:Verbose' = $false + 'Export-ModuleMember:Verbose' = $false + 'Add-Type:Verbose' = $false + 'Get-WinEvent:Verbose' = $false + 'Test-Path:ErrorAction' = 'SilentlyContinue' + 'Receive-CodeIntegrityLogs:Verbose' = $Verbose + 'Get-FileRules:Verbose' = $Verbose + 'Get-BlockRulesMeta:Verbose' = $Verbose + 'Get-GlobalRootDrives:Verbose' = $Verbose + 'Get-RuleRefs:Verbose' = $Verbose + 'Get-SignTool:Verbose' = $Verbose + 'Move-UserModeToKernelMode:Verbose' = $Verbose + 'New-EmptyPolicy:Verbose' = $Verbose + 'Set-LogSize:Verbose' = $Verbose + 'Test-FilePath:Verbose' = $Verbose + 'Update-Self:Verbose' = $Verbose + 'Write-ColorfulText:Verbose' = $Verbose + 'New-SnapBackGuarantee:Verbose' = $Verbose + 'Compare-SecureStrings:Verbose' = $Verbose + 'Get-KernelModeDriversAudit:Verbose' = $Verbose + 'Copy-CiRules:Verbose' = $Verbose + 'Get-TBSCertificate:Verbose' = $Verbose + 'Get-SignerInfo:Verbose' = $Verbose + 'Get-SignedFileCertificates:Verbose' = $Verbose + 'Get-FileRuleOutput:Verbose' = $Verbose + 'Get-CertificateDetails:Verbose' = $Verbose + 'Get-NestedSignerSignature:Verbose' = $Verbose + 'Compare-SignerAndCertificate:Verbose' = $Verbose + 'Remove-SupplementalSigners:Verbose' = $Verbose + 'Get-ExtendedFileInfo:Verbose' = $Verbose + 'Edit-CiPolicyRuleOptions:Verbose' = $Verbose + 'New-AppxPackageCiPolicy:Verbose' = $Verbose + 'New-StagingArea:Verbose' = $Verbose + + 'Build-SignerAndHashObjects:Verbose' = $Verbose + 'Clear-CiPolicy_Semantic:Verbose' = $Verbose + 'Close-EmptyXmlNodes_Semantic:Verbose' = $Verbose + 'Compare-CorrelatedData:Verbose' = $Verbose + 'Merge-Signers_Semantic:Verbose' = $Verbose + 'New-FilePublisherLevelRules:Verbose' = $Verbose + 'New-HashLevelRules:Verbose' = $Verbose + 'New-PublisherLevelRules:Verbose' = $Verbose + 'Optimize-MDECSVData:Verbose' = $Verbose + 'Remove-AllowElements_Semantic:Verbose' = $Verbose + 'Remove-DuplicateAllowAndFileRuleRefElements_IDBased:Verbose' = $Verbose + 'Remove-DuplicateAllowedSignersAndCiSigners_IDBased:Verbose' = $Verbose + 'Remove-DuplicateFileAttrib_IDBased:Verbose' = $Verbose + 'Remove-DuplicateFileAttrib_Semantic:Verbose' = $Verbose + 'Remove-DuplicateFileAttribRef_IDBased:Verbose' = $Verbose + 'Remove-OrphanAllowedSignersAndCiSigners_IDBased:Verbose' = $Verbose + 'Remove-UnreferencedFileRuleRefs:Verbose' = $Verbose + + 'Build-SignerAndHashObjects:Debug' = $Debug + 'Clear-CiPolicy_Semantic:Debug' = $Debug + 'Close-EmptyXmlNodes_Semantic:Debug' = $Debug + 'Compare-CorrelatedData:Debug' = $Debug + 'Merge-Signers_Semantic:Debug' = $Debug + 'New-FilePublisherLevelRules:Debug' = $Debug + 'New-HashLevelRules:Debug' = $Debug + 'New-PublisherLevelRules:Debug' = $Debug + 'Optimize-MDECSVData:Debug' = $Debug + 'Remove-AllowElements_Semantic:Debug' = $Debug + 'Remove-DuplicateAllowAndFileRuleRefElements_IDBased:Debug' = $Debug + 'Remove-DuplicateAllowedSignersAndCiSigners_IDBased:Debug' = $Debug + 'Remove-DuplicateFileAttrib_IDBased:Debug' = $Debug + 'Remove-DuplicateFileAttrib_Semantic:Debug' = $Debug + 'Remove-DuplicateFileAttribRef_IDBased:Debug' = $Debug + 'Remove-OrphanAllowedSignersAndCiSigners_IDBased:Debug' = $Debug + 'Remove-UnreferencedFileRuleRefs:Debug' = $Debug } # SIG # Begin signature block # MIILkgYJKoZIhvcNAQcCoIILgzCCC38CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG -# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCqYVA6Zd0TTgfG -# twEjuYlwv6iubUUgT46kEc1Fa9IXGqCCB9AwggfMMIIFtKADAgECAhMeAAAABI80 +# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBpBEWPRYD4kzMn +# 2iNMMIN5cqDeJtPsQ150cgbRRGJbQaCCB9AwggfMMIIFtKADAgECAhMeAAAABI80 # LDQz/68TAAAAAAAEMA0GCSqGSIb3DQEBDQUAME8xEzARBgoJkiaJk/IsZAEZFgNj # b20xIjAgBgoJkiaJk/IsZAEZFhJIT1RDQUtFWC1DQS1Eb21haW4xFDASBgNVBAMT # C0hPVENBS0VYLUNBMCAXDTIzMTIyNzExMjkyOVoYDzIyMDgxMTEyMTEyOTI5WjB5 @@ -92,16 +128,16 @@ $PSDefaultParameterValues = @{ # Q0FLRVgtQ0ECEx4AAAAEjzQsNDP/rxMAAAAAAAQwDQYJYIZIAWUDBAIBBQCggYQw # GAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGC # NwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQx -# IgQgXCI5qzhzLrTsNdbjFIpMi7yrXdH6NitIg7+26mu7CxQwDQYJKoZIhvcNAQEB -# BQAEggIAEm76QwtGGReBQ2V2PLgnNWE9nVerOUU4pwOKwSDm36gqF80KWdlAaXW8 -# Xx8YwZvnCuy0UIlKPSmQFPfmRqCYQrycLJloosdNqFCZv/ao9L/GQxPYSZeRyVox -# u5lcaCidCvi9NRi5UeO71lpTRjjiCQgkMeverwIh9c+wScdhup531Rbj3lpcqiwQ -# BWYXrBVs4Gni9/XxkeHAT8X3M5DqISbSQdgJK4OIru/TaWoI+3Sh6rJD0E5zaZre -# 7E7mInZUX3PquWzWCK2+6aNEdyEcqLzd4q8cLek/cudilbiav9XL+hSz6oBbwAwC -# sgcol8EPz7/HlzD76RAKXrbJ672xY5gKUAjp2fiyzHIAYU+QLV2+KGbtYVcm28TV -# qAqYW9MkOao6MCDsn44/2vA9+2M7OMAqM2o+Pj6NolSVk/fLhJo5tX0CNObXbLJm -# OamRRifuOV4xM8I4R/O7IHCczu+7jQhCpR9wQ2f2ZGfxoJD8jNb3SkkEYBfjxYaQ -# PNuJX7SiN5N8VVU/RzyZmRNbREj3mXUQT2xW3h7cibrNCWWwae1sKFFkSzsfGbht -# SgIBJV9+RPUbVn0pxRYPobkyCm4o2yBYshMZQPFaY+MOCBT5ZIStw2gLewIjngLX -# e0qgni8Zn+Tr1Fw+FLf2qs/o6nYFLkuVffAT0iult42lpI1pp9c= +# IgQgkwWcJPjK/anc7EdOyGLZXdJUUiL/raV2lFkzSywghYgwDQYJKoZIhvcNAQEB +# BQAEggIAAGg/7xzOpboT7M4YRZP5eydytbmUEh74s6k2uIZcdi0T/+WnJleqTAg+ +# Bsnx17zmEjXWb2/RatvkE9wR7uHKwNMZU3CvLVLdOmZU+KDQNgluL3p1x8ADfReM +# rm49F8uxHUeF8OwoAqAtJgFGKwg8uFwPoHmLZcMOnHTBOINw1suVnFBP02x2n725 +# KuMiKBpO2apj1mW0IYnQQH+UmIOp1dgfL2tCbSZKMRrRaJZz9b0aZeA8BhaukUbY +# J6lqNfR4QX7WqBQHglmhjGrXabXJgI6BfKqNNjE6aZDKci9L+WkDZt2NnBtIdt2i +# B/9o/GF7K/BpiYxc+pCtLBKGUlYvZGOeKqJMu/8Zi0PMSdrf1F3abf9BkdInWmH6 +# 3lFHhG7I4258RY5tw73mgez7dz4o+aOF99Tgga5mwRp7jZ6hBYlJHL8qwOfgmi1t +# tHg6GfH9Ht1pTRAV8XAsJtnNxZbkEVA6bWbRm6S+9QQXgRUAy3w7NIW/CebSU2fv +# /woaE+qd1vM0VWmtHCdX5NTo3wHPvEimunYFUKGR3aSBNGMeY0PJYBdHGRbm/U0b +# sUGSPrQ6Ki2JHYWQWbqMwkWOTIynGMzpmK5turADVEu2Tv26rbYUrkZgR52ErRd9 +# YN1J/pA9PD6o8BYeE4czZGPhnFn1Le/EGtE1ZXl6YrmqIXKgvrA= # SIG # End signature block diff --git a/WDACConfig/WDACConfig Module Files/Help/ConvertTo-WDACPolicy.md b/WDACConfig/WDACConfig Module Files/Help/ConvertTo-WDACPolicy.md new file mode 100644 index 000000000..f2b4c6dbf --- /dev/null +++ b/WDACConfig/WDACConfig Module Files/Help/ConvertTo-WDACPolicy.md @@ -0,0 +1,369 @@ +--- +external help file: ConvertTo-WDACPolicy.xml +Module Name: WDACConfig +online version: https://github.com/HotCakeX/Harden-Windows-Security/wiki/ConvertTo-WDACPolicy +schema: 2.0.0 +--- + +# ConvertTo-WDACPolicy + +## SYNOPSIS +This is a multi-purpose cmdlet that offers a wide range of functionalities that can either be used separately or mixed together for very detailed and specific tasks. + +It currently supports Code Integrity and AppLocker logs from the following sources: Local Event logs and Microsoft Defender for Endpoint Advanced Hunting results. + +The cmdlet displays the logs in a GUI and allows the user to select the logs to be processed further. + +The logs can be filtered based on many criteria using the available parameters. + +The output of this cmdlet is a Supplemental Application Control (WDAC) policy. +Based on the input parameters, it can be associated with a base policy or merged with an existing Base or Supplemental policy. + +## SYNTAX + +### In-Place Upgrade +``` +ConvertTo-WDACPolicy + [-PolicyToAddLogsTo ] + [-Source ] + [-MDEAHLogs ] + [-FilterByPolicyNames ] + [-TimeSpan ] + [-TimeSpanAgo ] + [-KernelModeOnly] + [-LogType ] + [-Deploy] + [-ExtremeVisibility] + [] +``` + +### Base-Policy File Association +``` +ConvertTo-WDACPolicy + [-BasePolicyFile ] + [-Source ] + [-MDEAHLogs ] + [-FilterByPolicyNames ] + [-TimeSpan ] + [-TimeSpanAgo ] + [-KernelModeOnly] + [-LogType ] + [-Deploy] + [-ExtremeVisibility] + [] +``` + +### Base-Policy GUID Association +``` +ConvertTo-WDACPolicy + [-BasePolicyGUID ] + [-Source ] + [-MDEAHLogs ] + [-FilterByPolicyNames ] + [-TimeSpan ] + [-TimeSpanAgo ] + [-KernelModeOnly] + [-LogType ] + [-Deploy] + [-ExtremeVisibility] + [] +``` + +## DESCRIPTION +The cmdlet can be used for local and remote systems. You can utilize this cmdlet to create Application Control for Business policies from MDE Advanced Hunting and then deploy them using Microsoft Intune to your endpoints. +This offers scalability and flexibility in managing your security policies. + +## EXAMPLES + +### EXAMPLE 1 +``` +ConvertTo-WDACPolicy -PolicyToAddLogsTo "C:\Users\Admin\AllowMicrosoftPlusBlockRules.xml" -Verbose +``` + +This example will display the Code Integrity and AppLocker logs in a GUI and allow the user to select the logs to add to the specified policy file. + +### EXAMPLE 2 +``` +ConvertTo-WDACPolicy -Verbose -BasePolicyGUID '{ACE9058C-8A24-47F4-86F0-A33FAB5073E3}' +``` + +This example will display the Code Integrity and AppLocker logs in a GUI and allow the user to select the logs to create a new supplemental policy and associate it with the specified base policy GUID. + +### EXAMPLE 3 +``` +ConvertTo-WDACPolicy -BasePolicyFile "C:\Users\Admin\AllowMicrosoftPlusBlockRules.xml" +``` + +This example will display the Code Integrity and AppLocker logs in a GUI and allow the user to select the logs to create a new supplemental policy and associate it with the specified base policy file. + +### EXAMPLE 4 +``` +ConvertTo-WDACPolicy +``` + +This example will display the Code Integrity and AppLocker logs in a GUI and takes no further action. + +### EXAMPLE 5 +``` +ConvertTo-WDACPolicy -FilterByPolicyNames 'VerifiedAndReputableDesktopFlightSupplemental','WindowsE_Lockdown_Flight_Policy_Supplemental' -Verbose +``` + +This example will filter the Code Integrity and AppLocker logs by the specified policy names and display them in a GUI. It will also display verbose messages on the console. + +### EXAMPLE 6 +``` +ConvertTo-WDACPolicy -FilterByPolicyNames 'Microsoft Windows Driver Policy - Enforced' -TimeSpan Minutes -TimeSpanAgo 10 +``` + +This example will filter the local Code Integrity and AppLocker logs by the specified policy name and the number of minutes ago from the current time and display them in a GUI. +So, it will display the logs that are 10 minutes old and are associated with the specified policy name. + +### EXAMPLE 7 +``` +ConvertTo-WDACPolicy -BasePolicyFile "C:\Program Files\WDACConfig\DefaultWindowsPlusBlockRules.xml" -Source MDEAdvancedHunting -MDEAHLogs "C:\Users\Admin\Downloads\New query.csv" -Deploy -TimeSpan Days -TimeSpanAgo 2 +``` + +This example will create a new supplemental policy from the selected MDE Advanced Hunting logs and associate it with the specified base policy file and it will deploy it on the system. +The displayed logs will be from the last 2 days. You will be able to select the logs to create the policy from in the GUI. + +## PARAMETERS + +### -PolicyToAddLogsTo +The policy to add the selected logs to, it can either be a base or supplemental policy. + +```yaml +Type: FileInfo +Parameter Sets: In-Place Upgrade +Aliases: AddLogs + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -BasePolicyFile +The base policy file to associate the supplemental policy with + +```yaml +Type: FileInfo +Parameter Sets: Base-Policy File Association +Aliases: BaseFile + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -BasePolicyGUID +The GUID of the base policy to associate the supplemental policy with + +```yaml +Type: Guid +Parameter Sets: Base-Policy GUID Association +Aliases: BaseGUID + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Source +The source of the logs: Local Event logs (LocalEventLogs) or Microsoft Defender for Endpoint Advanced Hunting results (MDEAdvancedHunting). +Supports validate set. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: Src + +Required: False +Position: Named +Default value: Event Logs +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -MDEAHLogs +The path(s) to use MDE AH CSV files. This is a dynamic parameter and will only be available if the Source parameter is set to MDEAdvancedHunting. + +```yaml +Type: FileInfo[] +Parameter Sets: (All) +Aliases: MDELogs + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -FilterByPolicyNames +The names of the policies to filter the logs by. +Supports auto-completion, press TAB key to view the list of the deployed base policy names to choose from. +It will not display the policies that are already selected on the command line. +You can manually enter the name of the policies that are no longer available on the system or are from remote systems in case of MDE Advanced Hunting logs. + +```yaml +Type: String[] +Parameter Sets: (All) +Aliases: FilterNames + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -TimeSpan +The unit of time to use when filtering the logs by the time. +The allowed values are: Minutes, Hours, Days + +```yaml +Type: String +Parameter Sets: (All) +Aliases: Duration + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -TimeSpanAgo +The number of the selected time unit to go back in time from the current time. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: Past + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -KernelModeOnly +If used, will filter the logs by including only the Kernel-Mode logs. You can use this parameter to easily create Supplemental policies for Strict Kernel-Mode WDAC policy. + +More info available here: https://github.com/HotCakeX/Harden-Windows-Security/wiki/WDAC-policy-for-BYOVD-Kernel-mode-only-protection + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: KMode + +Required: False +Position: Named +Default value: False +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -LogType +The type of logs to display: Audit or Blocked + +```yaml +Type: String +Parameter Sets: (All) +Aliases: LogKind + +Required: False +Position: Named +Default value: Audit +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Deploy +If used, will deploy the policy on the system + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: Up + +Required: False +Position: Named +Default value: False +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ExtremeVisibility +If used, will display all the properties of the logs without any filtering. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: XVis + +Required: False +Position: Named +Default value: False +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -SkipVersionCheck +Can be used with any parameter to bypass the online version check - only to be used in rare cases + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: False +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ProgressAction +Contains the preference for the progress action + +```yaml +Type: ActionPreference +Parameter Sets: (All) +Aliases: proga + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### System.IO.FileInfo +### System.Guid +### System.String +### System.String[] +### System.UInt64 +### System.Management.Automation.SwitchParameter + +## OUTPUTS + +### System.String + +## NOTES + +## RELATED LINKS + +[Cmdlet Guide](https://github.com/HotCakeX/Harden-Windows-Security/wiki/ConvertTo-WDACPolicy) + diff --git a/WDACConfig/WDACConfig Module Files/Help/ConvertTo-WDACPolicy.xml b/WDACConfig/WDACConfig Module Files/Help/ConvertTo-WDACPolicy.xml new file mode 100644 index 000000000..cfc5e82b5 --- /dev/null +++ b/WDACConfig/WDACConfig Module Files/Help/ConvertTo-WDACPolicy.xml @@ -0,0 +1,747 @@ + + + + + ConvertTo-WDACPolicy + ConvertTo + WDACPolicy + + This is a multi-purpose cmdlet that offers a wide range of functionalities that can either be used separately or mixed together for very detailed and specific tasks. + It currently supports Code Integrity and AppLocker logs from the following sources: Local Event logs and Microsoft Defender for Endpoint Advanced Hunting results. + The cmdlet displays the logs in a GUI and allows the user to select the logs to be processed further. + The logs can be filtered based on many criteria using the available parameters. + The output of this cmdlet is a Supplemental Application Control (WDAC) policy. Based on the input parameters, it can be associated with a base policy or merged with an existing Base or Supplemental policy. + + + + The cmdlet can be used for local and remote systems. You can utilize this cmdlet to create Application Control for Business policies from MDE Advanced Hunting and then deploy them using Microsoft Intune to your endpoints. This offers scalability and flexibility in managing your security policies. + + + + ConvertTo-WDACPolicy + + PolicyToAddLogsTo + + The policy to add the selected logs to, it can either be a base or supplemental policy. + + FileInfo + + FileInfo + + + None + + + Source + + The source of the logs: Local Event logs (LocalEventLogs) or Microsoft Defender for Endpoint Advanced Hunting results (MDEAdvancedHunting). Supports validate set. + + String + + String + + + Event Logs + + + MDEAHLogs + + The path(s) to use MDE AH CSV files. This is a dynamic parameter and will only be available if the Source parameter is set to MDEAdvancedHunting. + + FileInfo[] + + FileInfo[] + + + None + + + FilterByPolicyNames + + The names of the policies to filter the logs by. Supports auto-completion, press TAB key to view the list of the deployed base policy names to choose from. It will not display the policies that are already selected on the command line. You can manually enter the name of the policies that are no longer available on the system or are from remote systems in case of MDE Advanced Hunting logs. + + String[] + + String[] + + + None + + + TimeSpan + + The unit of time to use when filtering the logs by the time. The allowed values are: Minutes, Hours, Days + + String + + String + + + None + + + TimeSpanAgo + + The number of the selected time unit to go back in time from the current time. + + String + + String + + + None + + + KernelModeOnly + + If used, will filter the logs by including only the Kernel-Mode logs. You can use this parameter to easily create Supplemental policies for Strict Kernel-Mode WDAC policy. + More info available here: https://github.com/HotCakeX/Harden-Windows-Security/wiki/WDAC-policy-for-BYOVD-Kernel-mode-only-protection + + + SwitchParameter + + + False + + + LogType + + The type of logs to display: Audit or Blocked + + String + + String + + + Audit + + + Deploy + + If used, will deploy the policy on the system + + + SwitchParameter + + + False + + + ExtremeVisibility + + If used, will display all the properties of the logs without any filtering. + + + SwitchParameter + + + False + + + SkipVersionCheck + + Can be used with any parameter to bypass the online version check - only to be used in rare cases + + + SwitchParameter + + + False + + + ProgressAction + + Contains the preference for the progress action + + ActionPreference + + ActionPreference + + + None + + + + ConvertTo-WDACPolicy + + BasePolicyFile + + The base policy file to associate the supplemental policy with + + FileInfo + + FileInfo + + + None + + + Source + + The source of the logs: Local Event logs (LocalEventLogs) or Microsoft Defender for Endpoint Advanced Hunting results (MDEAdvancedHunting). Supports validate set. + + String + + String + + + Event Logs + + + MDEAHLogs + + The path(s) to use MDE AH CSV files. This is a dynamic parameter and will only be available if the Source parameter is set to MDEAdvancedHunting. + + FileInfo[] + + FileInfo[] + + + None + + + FilterByPolicyNames + + The names of the policies to filter the logs by. Supports auto-completion, press TAB key to view the list of the deployed base policy names to choose from. It will not display the policies that are already selected on the command line. You can manually enter the name of the policies that are no longer available on the system or are from remote systems in case of MDE Advanced Hunting logs. + + String[] + + String[] + + + None + + + TimeSpan + + The unit of time to use when filtering the logs by the time. The allowed values are: Minutes, Hours, Days + + String + + String + + + None + + + TimeSpanAgo + + The number of the selected time unit to go back in time from the current time. + + String + + String + + + None + + + KernelModeOnly + + If used, will filter the logs by including only the Kernel-Mode logs. You can use this parameter to easily create Supplemental policies for Strict Kernel-Mode WDAC policy. + More info available here: https://github.com/HotCakeX/Harden-Windows-Security/wiki/WDAC-policy-for-BYOVD-Kernel-mode-only-protection + + + SwitchParameter + + + False + + + LogType + + The type of logs to display: Audit or Blocked + + String + + String + + + Audit + + + Deploy + + If used, will deploy the policy on the system + + + SwitchParameter + + + False + + + ExtremeVisibility + + If used, will display all the properties of the logs without any filtering. + + + SwitchParameter + + + False + + + SkipVersionCheck + + Can be used with any parameter to bypass the online version check - only to be used in rare cases + + + SwitchParameter + + + False + + + ProgressAction + + Contains the preference for the progress action + + ActionPreference + + ActionPreference + + + None + + + + ConvertTo-WDACPolicy + + BasePolicyGUID + + The GUID of the base policy to associate the supplemental policy with + + Guid + + Guid + + + None + + + Source + + The source of the logs: Local Event logs (LocalEventLogs) or Microsoft Defender for Endpoint Advanced Hunting results (MDEAdvancedHunting). Supports validate set. + + String + + String + + + Event Logs + + + MDEAHLogs + + The path(s) to use MDE AH CSV files. This is a dynamic parameter and will only be available if the Source parameter is set to MDEAdvancedHunting. + + FileInfo[] + + FileInfo[] + + + None + + + FilterByPolicyNames + + The names of the policies to filter the logs by. Supports auto-completion, press TAB key to view the list of the deployed base policy names to choose from. It will not display the policies that are already selected on the command line. You can manually enter the name of the policies that are no longer available on the system or are from remote systems in case of MDE Advanced Hunting logs. + + String[] + + String[] + + + None + + + TimeSpan + + The unit of time to use when filtering the logs by the time. The allowed values are: Minutes, Hours, Days + + String + + String + + + None + + + TimeSpanAgo + + The number of the selected time unit to go back in time from the current time. + + String + + String + + + None + + + KernelModeOnly + + If used, will filter the logs by including only the Kernel-Mode logs. You can use this parameter to easily create Supplemental policies for Strict Kernel-Mode WDAC policy. + More info available here: https://github.com/HotCakeX/Harden-Windows-Security/wiki/WDAC-policy-for-BYOVD-Kernel-mode-only-protection + + + SwitchParameter + + + False + + + LogType + + The type of logs to display: Audit or Blocked + + String + + String + + + Audit + + + Deploy + + If used, will deploy the policy on the system + + + SwitchParameter + + + False + + + ExtremeVisibility + + If used, will display all the properties of the logs without any filtering. + + + SwitchParameter + + + False + + + SkipVersionCheck + + Can be used with any parameter to bypass the online version check - only to be used in rare cases + + + SwitchParameter + + + False + + + ProgressAction + + Contains the preference for the progress action + + ActionPreference + + ActionPreference + + + None + + + + + + PolicyToAddLogsTo + + The policy to add the selected logs to, it can either be a base or supplemental policy. + + FileInfo + + FileInfo + + + None + + + BasePolicyFile + + The base policy file to associate the supplemental policy with + + FileInfo + + FileInfo + + + None + + + BasePolicyGUID + + The GUID of the base policy to associate the supplemental policy with + + Guid + + Guid + + + None + + + Source + + The source of the logs: Local Event logs (LocalEventLogs) or Microsoft Defender for Endpoint Advanced Hunting results (MDEAdvancedHunting). Supports validate set. + + String + + String + + + Event Logs + + + MDEAHLogs + + The path(s) to use MDE AH CSV files. This is a dynamic parameter and will only be available if the Source parameter is set to MDEAdvancedHunting. + + FileInfo[] + + FileInfo[] + + + None + + + FilterByPolicyNames + + The names of the policies to filter the logs by. Supports auto-completion, press TAB key to view the list of the deployed base policy names to choose from. It will not display the policies that are already selected on the command line. You can manually enter the name of the policies that are no longer available on the system or are from remote systems in case of MDE Advanced Hunting logs. + + String[] + + String[] + + + None + + + TimeSpan + + The unit of time to use when filtering the logs by the time. The allowed values are: Minutes, Hours, Days + + String + + String + + + None + + + TimeSpanAgo + + The number of the selected time unit to go back in time from the current time. + + String + + String + + + None + + + KernelModeOnly + + If used, will filter the logs by including only the Kernel-Mode logs. You can use this parameter to easily create Supplemental policies for Strict Kernel-Mode WDAC policy. + More info available here: https://github.com/HotCakeX/Harden-Windows-Security/wiki/WDAC-policy-for-BYOVD-Kernel-mode-only-protection + + SwitchParameter + + SwitchParameter + + + False + + + LogType + + The type of logs to display: Audit or Blocked + + String + + String + + + Audit + + + Deploy + + If used, will deploy the policy on the system + + SwitchParameter + + SwitchParameter + + + False + + + ExtremeVisibility + + If used, will display all the properties of the logs without any filtering. + + SwitchParameter + + SwitchParameter + + + False + + + SkipVersionCheck + + Can be used with any parameter to bypass the online version check - only to be used in rare cases + + SwitchParameter + + SwitchParameter + + + False + + + ProgressAction + + Contains the preference for the progress action + + ActionPreference + + ActionPreference + + + None + + + + + + System.IO.FileInfo + + + + + + + + System.Guid + + + + + + + + System.String + + + + + + + + System.String[] + + + + + + + + System.UInt64 + + + + + + + + System.Management.Automation.SwitchParameter + + + + + + + + + + System.String + + + + + + + + + + + + + + -------------------------- EXAMPLE 1 -------------------------- + ConvertTo-WDACPolicy -PolicyToAddLogsTo "C:\Users\Admin\AllowMicrosoftPlusBlockRules.xml" -Verbose + + This example will display the Code Integrity and AppLocker logs in a GUI and allow the user to select the logs to add to the specified policy file. + + + + -------------------------- EXAMPLE 2 -------------------------- + ConvertTo-WDACPolicy -Verbose -BasePolicyGUID '{ACE9058C-8A24-47F4-86F0-A33FAB5073E3}' + + This example will display the Code Integrity and AppLocker logs in a GUI and allow the user to select the logs to create a new supplemental policy and associate it with the specified base policy GUID. + + + + -------------------------- EXAMPLE 3 -------------------------- + ConvertTo-WDACPolicy -BasePolicyFile "C:\Users\Admin\AllowMicrosoftPlusBlockRules.xml" + + This example will display the Code Integrity and AppLocker logs in a GUI and allow the user to select the logs to create a new supplemental policy and associate it with the specified base policy file. + + + + -------------------------- EXAMPLE 4 -------------------------- + ConvertTo-WDACPolicy + + This example will display the Code Integrity and AppLocker logs in a GUI and takes no further action. + + + + -------------------------- EXAMPLE 5 -------------------------- + ConvertTo-WDACPolicy -FilterByPolicyNames 'VerifiedAndReputableDesktopFlightSupplemental','WindowsE_Lockdown_Flight_Policy_Supplemental' -Verbose + + This example will filter the Code Integrity and AppLocker logs by the specified policy names and display them in a GUI. It will also display verbose messages on the console. + + + + -------------------------- EXAMPLE 6 -------------------------- + ConvertTo-WDACPolicy -FilterByPolicyNames 'Microsoft Windows Driver Policy - Enforced' -TimeSpan Minutes -TimeSpanAgo 10 + + This example will filter the local Code Integrity and AppLocker logs by the specified policy name and the number of minutes ago from the current time and display them in a GUI. So, it will display the logs that are 10 minutes old and are associated with the specified policy name. + + + + -------------------------- EXAMPLE 7 -------------------------- + ConvertTo-WDACPolicy -BasePolicyFile "C:\Program Files\WDACConfig\DefaultWindowsPlusBlockRules.xml" -Source MDEAdvancedHunting -MDEAHLogs "C:\Users\Admin\Downloads\New query.csv" -Deploy -TimeSpan Days -TimeSpanAgo 2 + + This example will create a new supplemental policy from the selected MDE Advanced Hunting logs and associate it with the specified base policy file and it will deploy it on the system. The displayed logs will be from the last 2 days. You will be able to select the logs to create the policy from in the GUI. + + + + + + Cmdlet Guide + https://github.com/HotCakeX/Harden-Windows-Security/wiki/ConvertTo-WDACPolicy + + + + \ No newline at end of file diff --git a/WDACConfig/WDACConfig Module Files/Shared/Receive-CodeIntegrityLogs.psm1 b/WDACConfig/WDACConfig Module Files/Shared/Receive-CodeIntegrityLogs.psm1 index 5c0e8438d..128b4a7a0 100644 --- a/WDACConfig/WDACConfig Module Files/Shared/Receive-CodeIntegrityLogs.psm1 +++ b/WDACConfig/WDACConfig Module Files/Shared/Receive-CodeIntegrityLogs.psm1 @@ -1,14 +1,15 @@ Function Receive-CodeIntegrityLogs { <# .SYNOPSIS - A resilient function that: - Retrieves the Code Integrity Operational logs + A high-performance function that: + Retrieves the Code Integrity Operational logs and App Locker logs Fixes the paths to the files that are being logged Separates events based on their type: Audit or Blocked Separates events based on their file paths: existing or deleted - Finds correlated events with ID 3089 and adds them to the main event (IDs 3076 and 3077) + For Code Integrity logs: Finds correlated events with ID 3089 and adds them to the main event (IDs 3076 and 3077) + For App Locker logs: Finds correlated events with ID 8038 and adds them to the main event (IDs 8028 and 8029) Replaces many numbers in the logs with user-friendly strings - De-duplicates the logs + Performs precise de-duplication of the logs so that the output will always have unique logs Then processes the output based on different criteria .PARAMETER Date The date from which the logs should be collected. If not specified, all logs will be collected. @@ -22,14 +23,16 @@ Function Receive-CodeIntegrityLogs { Separate: Returns the file paths of files that exist on the disk and the hash details of files that do not exist on the disk, separately in a nested object .PARAMETER PolicyNames The names of the policies to filter the logs by + .PARAMETER Category + The category of logs to be collected. Code Integrity, AppLocker, or All. The default value is 'All' .INPUTS System.String System.String[] .OUTPUTS - PSCustomObject + System.Collections.Hashtable #> [CmdletBinding()] - [OutputType([PSCustomObject[]])] + [OutputType([System.Collections.Hashtable])] param( [AllowEmptyString()] [AllowNull()] @@ -47,7 +50,10 @@ Function Receive-CodeIntegrityLogs { [AllowEmptyString()] [AllowNull()] [parameter(mandatory = $false)] - [System.String[]]$PolicyNames + [System.String[]]$PolicyNames, + + [ValidateSet('CodeIntegrity', 'AppLocker', 'All')] + [Parameter(mandatory = $false)][System.String]$Category = 'All' ) Begin { @@ -129,6 +135,7 @@ Function Receive-CodeIntegrityLogs { } } + #Region Global Root Drive Fix Try { # Set a flag indicating that the alternative drive letter mapping method is not necessary unless the primary method fails [System.Boolean]$AlternativeDriveLetterFix = $false @@ -141,38 +148,73 @@ Function Receive-CodeIntegrityLogs { # Set the flag to true indicating the alternative method is being used $AlternativeDriveLetterFix = $true + } + + # Create a hashtable of partition numbers and their associated drive letters + [System.Collections.Hashtable]$DriveLetterMappings = @{} - # Create a hashtable of partition numbers and their associated drive letters - [System.Collections.Hashtable]$DriveLetterMappings = @{} + # Get all partitions and filter out the ones that don't have a drive letter and then add them to the hashtable with the partition number as the key and the drive letter as the value + foreach ($Drive in (Get-Partition | Where-Object -FilterScript { $_.DriveLetter })) { + $DriveLetterMappings[[System.String]$Drive.PartitionNumber] = [System.String]$Drive.DriveLetter + } + #Endregion Global Root Drive Fix - # Get all partitions and filter out the ones that don't have a drive letter and then add them to the hashtable with the partition number as the key and the drive letter as the value - foreach ($Drive in (Get-Partition | Where-Object -FilterScript { $_.DriveLetter })) { - $DriveLetterMappings[[System.String]$Drive.PartitionNumber] = [System.String]$Drive.DriveLetter + if (($Category -eq 'All') -or ($Category -eq 'CodeIntegrity')) { + Try { + Write-Verbose -Message 'Receive-CodeIntegrityLogs: Collecting the Code Integrity Operational logs' + [System.Diagnostics.Eventing.Reader.EventLogRecord[]]$CiRawEventLogs = Get-WinEvent -FilterHashtable @{LogName = 'Microsoft-Windows-CodeIntegrity/Operational' } + } + catch { + Throw "Receive-CodeIntegrityLogs: Could not collect the Code Integrity Operational logs, the number of logs collected is $($CiRawEventLogs.Count)" } + + [Microsoft.PowerShell.Commands.GroupInfo[]]$CiGroupedEvents = $CiRawEventLogs | Group-Object -Property ActivityId + Write-Verbose -Message "Receive-CodeIntegrityLogs: Grouped the Code Integrity logs by ActivityId. The total number of groups is $($CiGroupedEvents.Count) and the total number of logs in the groups is $($CiGroupedEvents.Group.Count)" + } + else { + Write-Verbose -Message 'Receive-CodeIntegrityLogs: Skipping the collection of the Code Integrity logs' } - Try { - Write-Verbose -Message 'Receive-CodeIntegrityLogs: Collecting the Code Integrity Operational logs' - [System.Diagnostics.Eventing.Reader.EventLogRecord[]]$RawEventLogs = Get-WinEvent -FilterHashtable @{LogName = 'Microsoft-Windows-CodeIntegrity/Operational' } + if (($Category -eq 'All') -or ($Category -eq 'AppLocker')) { + Try { + Write-Verbose -Message 'Receive-CodeIntegrityLogs: Collecting the AppLocker logs' + [System.Diagnostics.Eventing.Reader.EventLogRecord[]]$AppLockerRawEventLogs = Get-WinEvent -FilterHashtable @{LogName = 'Microsoft-Windows-AppLocker/MSI and Script' } + } + catch { + Throw "Receive-CodeIntegrityLogs: Could not collect the AppLocker logs, the number of logs collected is $($AppLockerRawEventLogs.Count)" + } + + [Microsoft.PowerShell.Commands.GroupInfo[]]$AppLockerGroupedEvents = $AppLockerRawEventLogs | Group-Object -Property ActivityId + Write-Verbose -Message "Receive-CodeIntegrityLogs: Grouped the AppLocker logs by ActivityId. The total number of groups is $($AppLockerGroupedEvents.Count) and the total number of logs in the groups is $($AppLockerGroupedEvents.Group.Count)" } - catch { - Throw "Receive-CodeIntegrityLogs: Could not collect the Code Integrity Operational logs, the number of logs collected is $($RawEventLogs.Count)" + else { + Write-Verbose -Message 'Receive-CodeIntegrityLogs: Skipping the collection of the AppLocker logs' } - [Microsoft.PowerShell.Commands.GroupInfo[]]$GroupedEvents = $RawEventLogs | Group-Object -Property ActivityId - Write-Verbose -Message "Receive-CodeIntegrityLogs: Grouped the logs by ActivityId. The total number of groups is $($GroupedEvents.Count) and the total number of logs in the groups is $($GroupedEvents.Group.Count)" + # Add Code Integrity and AppLocker logs to a single array based on the selected category + $AccumulatedGroupedEvents = ($Category -eq 'All') ? ($CiGroupedEvents + $AppLockerGroupedEvents) : (($Category -eq 'CodeIntegrity') ? $CiGroupedEvents : $AppLockerGroupedEvents) - # Create a collection to store the packages of logs - [PSCustomObject[]]$EventPackageCollections = @() + # Initialize two separate hashtables - going to split the logs into two hashtables to process them in parallel + [System.Collections.Hashtable]$EventPackageCollections1 = @{} + [System.Collections.Hashtable]$EventPackageCollections2 = @{} + + # A toggle to switch between hashtables + [System.Boolean]$Toggle = $false # Loop over each group of logs - Foreach ($RawLogGroup in $GroupedEvents) { + Foreach ($RawLogGroup in $AccumulatedGroupedEvents) { + + # Toggle the value for the next iteration + [System.Boolean]$Toggle = -NOT $Toggle + + # Select the target hashtable based on the toggle value where the logs will be added, since hashtables are being assigned, they are already referenced and [ref] is not needed + [System.Collections.Hashtable]$TargetHashTable = $Toggle ? $EventPackageCollections1 : $EventPackageCollections2 # Process Audit events - if ($RawLogGroup.Group.Id -contains '3076') { + if (($RawLogGroup.Group.Id -contains '3076') -or ($RawLogGroup.Group.Id -contains '8028')) { # Finding the main event in the group - If there are more than 1, selecting the first one because that means the same event was triggered by multiple deployed policies - [System.Diagnostics.Eventing.Reader.EventLogRecord]$AuditTemp = $RawLogGroup.Group | Where-Object -FilterScript { $_.Id -eq '3076' } | Select-Object -First 1 + [System.Diagnostics.Eventing.Reader.EventLogRecord]$AuditTemp = $RawLogGroup.Group | Where-Object -FilterScript { $_.Id -in '3076', '8028' } | Select-Object -First 1 # If the main event is older than the specified date, skip it if (-NOT ([System.String]::IsNullOrWhiteSpace($Date))) { @@ -181,19 +223,23 @@ Function Receive-CodeIntegrityLogs { } } - # Add the main event along with the correlated events to the collection - $EventPackageCollections += [PSCustomObject]@{ - MainEventData = $AuditTemp - CorrelatedEventsData = $RawLogGroup.Group | Where-Object -FilterScript { $_.Id -eq '3089' } - Type = 'Audit' - } + # Create a local hashtable to store the main event and the correlated events + [System.Collections.Hashtable]$LocalAuditEventPackageCollections = @{} + + $LocalAuditEventPackageCollections['MainEventData'] = $AuditTemp + $LocalAuditEventPackageCollections['CorrelatedEventsData'] = $RawLogGroup.Group | Where-Object -FilterScript { $_.Id -in '3089', '8038' } + $LocalAuditEventPackageCollections['Type'] = 'Audit' + + # Add the main event along with the correlated events as a nested hashtable to the main hashtable + # Using the correlation ID as the key + $TargetHashTable[$RawLogGroup.Name] = $LocalAuditEventPackageCollections } # Process Blocked events - if ($RawLogGroup.Group.Id -contains '3077') { + if (($RawLogGroup.Group.Id -contains '3077') -or ($RawLogGroup.Group.Id -contains '8029')) { # Finding the main event in the group - If there are more than 1, selecting the first one because that means the same event was triggered by multiple deployed policies - [System.Diagnostics.Eventing.Reader.EventLogRecord]$BlockedTemp = $RawLogGroup.Group | Where-Object -FilterScript { $_.Id -eq '3077' } | Select-Object -First 1 + [System.Diagnostics.Eventing.Reader.EventLogRecord]$BlockedTemp = $RawLogGroup.Group | Where-Object -FilterScript { $_.Id -in '3077', '8029' } | Select-Object -First 1 # If the main event is older than the specified date, skip it if (-NOT ([System.String]::IsNullOrWhiteSpace($Date))) { @@ -202,202 +248,311 @@ Function Receive-CodeIntegrityLogs { } } - # Add the main event along with the correlated events to the collection - $EventPackageCollections += [PSCustomObject]@{ - MainEventData = $BlockedTemp - CorrelatedEventsData = $RawLogGroup.Group | Where-Object -FilterScript { $_.Id -eq '3089' } - Type = 'Blocked' + # Create a local hashtable to store the main event and the correlated events + [System.Collections.Hashtable]$LocalBlockedEventPackageCollections = @{} + + $LocalBlockedEventPackageCollections['MainEventData'] = $BlockedTemp + $LocalBlockedEventPackageCollections['CorrelatedEventsData'] = $RawLogGroup.Group | Where-Object -FilterScript { $_.Id -in '3089', '8038' } + $LocalBlockedEventPackageCollections['Type'] = 'Blocked' + + # Add the main event along with the correlated events as a nested hashtable to the main hashtable + # Using the correlation ID as the key + $TargetHashTable[$RawLogGroup.Name] = $LocalBlockedEventPackageCollections + } + } + + # Hashtable that contains the entire output + [System.Collections.Hashtable]$Output = @{ + # all the logs without post-processing + All = @{ + Audit = @{} + Blocked = @{} + } + # only the logs of files that exist on the disk + Existing = @{ + Audit = @{} + Blocked = @{} + } + # only the hash details of files no longer on the disk + Deleted = @{ + Audit = @{} + Blocked = @{} + } + # FilePaths of files on the disk and hash details of files not on the disk + Separated = @{ + Audit = @{ + AvailableFilesPaths = [System.Collections.Generic.HashSet[System.String]] @() + DeletedFileHashes = @{} + } + Blocked = @{ + AvailableFilesPaths = [System.Collections.Generic.HashSet[System.String]] @() + DeletedFileHashes = @{} } } } - #Region Output objects definition based on type - # They return all the logs without post-processing - [PSCustomObject[]]$OutputAudit = @() - [PSCustomObject[]]$OutputBlocked = @() + # Making the hashtable thread-safe by synchronizing it and allowing the Foreach-Object -Parallel to write back data to it safely in real time with $Using scope modifier + # ForEach-Object -Parallel is Thread Session so the scriptblock inside of it can modify parent scope variables since they are references instead of independent copies + # https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_remote_variables#other-situations-where-the-using-scope-modifier-is-needed + # https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_scopes#the-using-scope-modifier + $Output = [System.Collections.Hashtable]::Synchronized($Output) + } + + Process { - # They only return the logs of files that exist on the disk - [PSCustomObject[]]$OutputExistingAudit = @() - [PSCustomObject[]]$OutputExistingBlocked = @() + # Running the main loop in parallel + $EventPackageCollections1, $EventPackageCollections2 | ForEach-Object -Parallel { - # They only return the hash details of files no longer on the disk - [PSCustomObject[]]$OutputDeletedAudit = @() - [PSCustomObject[]]$OutputDeletedBlocked = @() + # Making the parent scope variables available in the parallel child scope as references - # They return FilePaths of files on the disk and hash details of files not on the disk - $OutputSeparatedAudit = [PSCustomObject]@{ - AvailableFilesPaths = [System.IO.FileInfo[]]@() - DeletedFileHashes = [PSCustomObject[]]@() - } - $OutputSeparatedBlocked = [PSCustomObject]@{ - AvailableFilesPaths = [System.IO.FileInfo[]]@() - DeletedFileHashes = [PSCustomObject[]]@() - } - #Endregion Output objects definition based on type - } + # Only variable modified from within the thread session + $Output = $using:Output - Process { + # Variables that are not modified from within the thread session + $DriveLettersGlobalRootFix = $using:DriveLettersGlobalRootFix + $ReqValSigningLevels = $using:ReqValSigningLevels + $SignatureTypeTable = $using:SignatureTypeTable + $VerificationErrorTable = $using:VerificationErrorTable + $AlternativeDriveLetterFix = $using:AlternativeDriveLetterFix + $DriveLetterMappings = $using:DriveLetterMappings - # Loop over each event package in the collection - foreach ($EventPackage in $EventPackageCollections) { + # Set the VerbosePreference to the parent scope's value + $VerbosePreference = $using:VerbosePreference - # Extract the main event data - [System.Diagnostics.Eventing.Reader.EventLogRecord]$Event = $EventPackage.MainEventData + # Loop over each event package in the collection + foreach ($EventPackage in $_.GetEnumerator()) { - # Convert the main event data to XML object - $Xml = [System.Xml.XmlDocument]$Event.ToXml() + # Extract the main event data + [System.Diagnostics.Eventing.Reader.EventLogRecord]$Event = $EventPackage.Value.MainEventData - # Place each event data in a hashtable and repackage it into a custom object at the end for further processing - [PSCustomObject[]]$ProcessedEvents = $Xml.event.EventData.data | ForEach-Object -Begin { $Hash = @{} } -Process { $Hash[$_.name] = $_.'#text' } -End { [pscustomobject]$Hash } + # Convert the main event data to XML object + $Xml = [System.Xml.XmlDocument]$Event.ToXml() - # Loop over each event data object - foreach ($Log in $ProcessedEvents) { + # Place each main event data in a hashtable and return the hashtable at the end + [System.Collections.Hashtable[]]$ProcessedEvents = $Xml.event.EventData.data | ForEach-Object -Begin { [System.Collections.Hashtable]$Hash = @{} } -Process { $Hash[$_.Name] = $_.'#text' } -End { [System.Collections.Hashtable]$Hash } - # Add the TimeCreated property to the $Log object - $Log | Add-Member -NotePropertyName 'TimeCreated' -NotePropertyValue $Event.TimeCreated - # Add the ActivityId property to the $Log object - $Log | Add-Member -NotePropertyName 'ActivityId' -NotePropertyValue $Event.ActivityId - # Add the UserId property to the $Log object - $Log | Add-Member -NotePropertyName 'UserId' -NotePropertyValue $Event.UserId + # Loop over each main event data's hashtable - Main loop + foreach ($Log in $ProcessedEvents) { - # Filter the logs based on the policy that generated them - if (-NOT ([System.String]::IsNullOrWhiteSpace($PolicyNames))) { - if ($Log.PolicyName -notin $PolicyNames) { - continue + # Add the TimeCreated property to the $Log hashtable + $Log['TimeCreated'] = $Event.TimeCreated + # Add the ActivityId property to the $Log hashtable + $Log['ActivityId'] = $Event.ActivityId + # Add the UserId property to the $Log hashtable + $Log['UserId'] = $Event.UserId + + # Filter the logs based on the policy that generated them + if (-NOT ([System.String]::IsNullOrWhiteSpace($PolicyNames))) { + if ($Log.PolicyName -notin $PolicyNames) { + continue + } } - } - # Define the regex pattern for the device path - [System.Text.RegularExpressions.Regex]$Pattern = '\\Device\\HarddiskVolume(?\d+)\\(?.*)$' + # Define the regex pattern for the device path + [System.Text.RegularExpressions.Regex]$Pattern = '\\Device\\HarddiskVolume(?\d+)\\(?.*)$' - # replace the device path with the drive letter if it matches the pattern - if ($Log.'File Name' -match $Pattern) { + # If the File Name property is empty, replace it with the FilePath property and then remove the FilePath property + # Only happens in the AppLocker logs + if ($null -eq $Log['File Name']) { + $Log['File Name'] = $Log['FilePath'] + $Log.Remove('FilePath') + } + + # replace the device path with the drive letter if it matches the pattern + if ($Log['File Name'] -match $Pattern) { - # Use the primary method to fix the drive letter mappings - if ($AlternativeDriveLetterFix -eq $false) { + # Use the primary method to fix the drive letter mappings + if ($AlternativeDriveLetterFix -eq $false) { - [System.UInt32]$HardDiskVolumeNumber = $Matches['HardDiskVolumeNumber'] - [System.String]$RemainingPath = $Matches['RemainingPath'] - [PSCustomObject]$GetLetter = $DriveLettersGlobalRootFix | Where-Object -FilterScript { $_.DevicePath -eq "\Device\HarddiskVolume$HardDiskVolumeNumber" } - [System.IO.FileInfo]$UsablePath = "$($GetLetter.DriveLetter)$RemainingPath" - $Log.'File Name' = $Log.'File Name' -replace $Pattern, $UsablePath + [System.UInt32]$HardDiskVolumeNumber = $Matches['HardDiskVolumeNumber'] + [System.String]$RemainingPath = $Matches['RemainingPath'] + [PSCustomObject]$GetLetter = $DriveLettersGlobalRootFix | Where-Object -FilterScript { $_.DevicePath -eq "\Device\HarddiskVolume$HardDiskVolumeNumber" } + [System.IO.FileInfo]$UsablePath = "$($GetLetter.DriveLetter)$RemainingPath" + $Log['File Name'] = $Log['File Name'] -replace $Pattern, $UsablePath + } + # Use the alternative method to fix the drive letter mappings + else { + $Log['File Name'] = $Log['File Name'] -replace "\\Device\\HarddiskVolume$($Matches['HardDiskVolumeNumber'])", "$($DriveLetterMappings[$Matches['HardDiskVolumeNumber']]):" + } } - # Use the alternative method to fix the drive letter mappings - else { - $Log.'File Name' = $Log.'File Name' -replace "\\Device\\HarddiskVolume$($Matches['HardDiskVolumeNumber'])", "$($DriveLetterMappings[$Matches['HardDiskVolumeNumber']]):" + # sometimes the file name begins with System32 so we prepend the Windows directory to create a full resolvable path + # https://learn.microsoft.com/en-us/dotnet/api/system.string.startswith + elseif ($Log['File Name'].StartsWith('System32', $true, [System.Globalization.CultureInfo]::InvariantCulture)) { + $Log['File Name'] = Join-Path -Path $Env:WinDir -ChildPath ($Log['File Name']) } - } - # sometimes the file name begins with System32 so we prepend the Windows directory to create a full resolvable path - # https://learn.microsoft.com/en-us/dotnet/api/system.string.startswith - elseif ($Log.'File Name'.StartsWith('System32', $true, [System.Globalization.CultureInfo]::InvariantCulture)) { - $Log.'File Name' = Join-Path -Path $Env:WinDir -ChildPath ($Log.'File Name') - } - # Replace these numbers in the logs with user-friendly strings that represent the signature level at which the code was verified - $Log.'Requested Signing Level' = $ReqValSigningLevels[[System.UInt16]$Log.'Requested Signing Level'] - $Log.'Validated Signing Level' = $ReqValSigningLevels[[System.UInt16]$Log.'Validated Signing Level'] + # Replace these numbers in the logs with user-friendly strings that represent the signature level at which the code was verified + $Log['Requested Signing Level'] = $ReqValSigningLevels[[System.UInt16]$Log['Requested Signing Level']] + $Log['Validated Signing Level'] = $ReqValSigningLevels[[System.UInt16]$Log['Validated Signing Level']] - # Replace the SI Signing Scenario numbers with a user-friendly string - $Log.'SI Signing Scenario' = $Log.'SI Signing Scenario' -eq '0' ? 'Kernel-Mode' : 'User-Mode' + # Replace the SI Signing Scenario numbers with a user-friendly string + $Log['SI Signing Scenario'] = $Log['SI Signing Scenario'] -eq '0' ? 'Kernel-Mode' : 'User-Mode' - # Translate the SID to a UserName - Try { - [System.Security.Principal.SecurityIdentifier]$ObjSID = New-Object -TypeName System.Security.Principal.SecurityIdentifier($Log.UserId) - $Log.UserId = [System.String]($ObjSID.Translate([System.Security.Principal.NTAccount])).Value - } - Catch { - Write-Verbose -Message "Receive-CodeIntegrityLogs: Could not translate the SID $($Log.UserId) to a username." - } + # Translate the SID to a UserName + if ($null -ne $Log.UserId) { + Try { + [System.Security.Principal.SecurityIdentifier]$ObjSID = New-Object -TypeName System.Security.Principal.SecurityIdentifier($Log.UserId) + $Log.UserId = [System.String]($ObjSID.Translate([System.Security.Principal.NTAccount])).Value + } + Catch { + Write-Verbose -Message "Receive-CodeIntegrityLogs: Could not translate the SID $($Log.UserId) to a username for the Activity ID $($Log['ActivityId']) for the file $($Log['File Name'])" + } + } + else { + Write-Verbose -Message "Receive-CodeIntegrityLogs: The UserId property is null for the Activity ID $($Log['ActivityId']) for the file $($Log['File Name'])" + } - # If there are correlated events, then process them - if ($null -ne $EventPackage.CorrelatedEventsData) { + # If there are correlated events, then process them + if ($null -ne $EventPackage.Value.CorrelatedEventsData) { - # Store the unique publisher name in an array - [System.String[]]$Publishers = @() + # A hashtable for storing the correlated logs + [System.Collections.Hashtable]$CorrelatedLogs = @{} - # Store the correlated logs in an array - these logs are processed into a custom object - [PSCustomObject[]]$CorrelatedLogs = @() + # Store the unique publisher name in HashSet + $Publishers = [System.Collections.Generic.HashSet[System.String]]@() - foreach ($CorrelatedEvent in $EventPackage.CorrelatedEventsData) { + # Looping over each correlated event data + # There are more than 1 if the file has multiple signers/publishers + foreach ($CorrelatedEvent in $EventPackage.Value.CorrelatedEventsData) { - # Convert the main event data to XML object - $XmlCorrelated = [System.Xml.XmlDocument]$CorrelatedEvent.ToXml() + # Convert the main event data to XML object + $XmlCorrelated = [System.Xml.XmlDocument]$CorrelatedEvent.ToXml() - # Place each event data in a hashtable and repackage it into a custom object at the end for further processing - [PSCustomObject[]]$ProcessedCorrelatedEvents = $XmlCorrelated.event.EventData.data | ForEach-Object -Begin { $Hash = @{} } -Process { $Hash[$_.name] = $_.'#text' } -End { [pscustomobject]$Hash } + # Place each event data in a hashtable and return the hashtable at the end + [System.Collections.Hashtable[]]$ProcessedCorrelatedEvents = $XmlCorrelated.event.EventData.data | ForEach-Object -Begin { [System.Collections.Hashtable]$Hash = @{} } -Process { $Hash[$_.name] = $_.'#text' } -End { [System.Collections.Hashtable]$Hash } - # Loop over each event data object - foreach ($CorrelatedLog in $ProcessedCorrelatedEvents) { + # Loop over each event data object + foreach ($CorrelatedLog in $ProcessedCorrelatedEvents) { - # Replace the properties with their user-friendly strings - $CorrelatedLog.SignatureType = $SignatureTypeTable[[System.UInt16]$CorrelatedLog.SignatureType] - $CorrelatedLog.ValidatedSigningLevel = $ReqValSigningLevels[[System.UInt16]$CorrelatedLog.ValidatedSigningLevel] - $CorrelatedLog.VerificationError = $VerificationErrorTable[[System.UInt16]$CorrelatedLog.VerificationError] + # Replace the properties with their user-friendly strings + $CorrelatedLog.SignatureType = $SignatureTypeTable[[System.UInt16]$CorrelatedLog.SignatureType] + $CorrelatedLog.ValidatedSigningLevel = $ReqValSigningLevels[[System.UInt16]$CorrelatedLog.ValidatedSigningLevel] + $CorrelatedLog.VerificationError = $VerificationErrorTable[[System.UInt16]$CorrelatedLog.VerificationError] - # Add the Correlated Log to the array of Correlated Logs - $CorrelatedLogs += $CorrelatedLog + # Create a unique key for each Publisher + [System.String]$PublisherKey = $CorrelatedLog.PublisherTBSHash + '|' + + $CorrelatedLog.PublisherName + '|' + + $CorrelatedLog.IssuerTBSHash + '|' + + $CorrelatedLog.IssuerName - # Add the unique publisher name to the array of Publishers - if ($CorrelatedLog.PublisherName -notin $Publishers) { - $Publishers += $CorrelatedLog.PublisherName + # Add the Correlated Log to the array of Correlated Logs if it doesn't already exist there + if (-NOT $CorrelatedLogs.ContainsKey($PublisherKey)) { + $CorrelatedLogs[$PublisherKey] = $CorrelatedLog + } + + # Add the unique publisher name to the array of Publishers if it doesn't already exist there + if (-NOT $Publishers.Contains($CorrelatedLog.PublisherName)) { + [System.Void]$Publishers.Add($CorrelatedLog.PublisherName) + } } } + + Write-Debug -Message "Receive-CodeIntegrityLogs: The number of unique publishers in the correlated events is $($Publishers.Count)" + $Log['Publishers'] = $Publishers + + Write-Debug -Message "Receive-CodeIntegrityLogs: The number of correlated events is $($CorrelatedLogs.Count)" + $Log['SignerInfo'] = $CorrelatedLogs } - Write-Debug -Message "Receive-CodeIntegrityLogs: The number of unique publishers in the correlated events is $($Publishers.Count)" - $Log | Add-Member -NotePropertyName 'Publishers' -NotePropertyValue $Publishers + # Add the Type property to the log object + $Log['Type'] = $EventPackage.Value.Type - # De-Duplicate the correlated logs based on specific properties - $CorrelatedLogs = $CorrelatedLogs | Group-Object -Property PublisherTBSHash, PublisherName, IssuerTBSHash, IssuerName | - ForEach-Object -Process { $_.Group[0] } + #Region Post-processing for the logs - Write-Debug -Message "Receive-CodeIntegrityLogs: The number of correlated events is $($CorrelatedLogs.Count)" - $Log | Add-Member -NotePropertyName 'SignerInfo' -NotePropertyValue $CorrelatedLogs - } + # Creating a unique string key for the current log + # The key ending up being too long doesn't matter and doesn't affect the performance + # Since all keys are hashed in a hashtable + [System.String]$UniqueLogKey = $Log['File Name'] + '|' + + $Log.ProductName + '|' + + $Log.FileVersion + '|' + + $Log.OriginalFileName + '|' + + $Log.FileDescription + '|' + + $Log.InternalName + '|' + + $Log.PackageFamilyName + '|' + + $Log.Publishers + '|' + + $Log['SHA256 Hash'] + '|' + + $Log['SHA256 Flat Hash'] - # Add the Type property to the log object - $Log | Add-Member -NotePropertyName 'Type' -NotePropertyValue $EventPackage.Type + # Using the SyncRoot property to lock the $Output hashtable during the check-and-add sequence, making it atomic and thread-safe + # This ensures that only one thread at a time can execute the code within the try block, thus preventing race conditions + [System.Threading.Monitor]::Enter($using:Output.SyncRoot) - #Region Post-processing for the logs + try { - if ($Log.Type -eq 'Audit') { + if ($Log.Type -eq 'Audit') { - # Add the log to the output object if it has Audit type - $OutputAudit += $Log + # Add the log to the output hashtable if it has Audit type and doesn't already exist there + if (-NOT $Output.All.Audit.ContainsKey($UniqueLogKey)) { + $Output.All.Audit[$UniqueLogKey] = $Log + } - # If the file the log is referring to is currently on the disk - if (Test-Path -Path $Log.'File Name') { - $OutputExistingAudit += $Log - $OutputSeparatedAudit.AvailableFilesPaths += $Log.'File Name' - } - # If the file is not currently on the disk, extract its hashes from the log - else { - $TempDeletedOutputAudit = $Log | Select-Object -Property FileVersion, 'File Name', PolicyGUID, 'SHA256 Hash', 'SHA256 Flat Hash', 'SHA1 Hash', 'SHA1 Flat Hash' + # If the file the log is referring to is currently on the disk + if (Test-Path -Path $Log['File Name']) { - $OutputDeletedAudit += $TempDeletedOutputAudit - $OutputSeparatedAudit.DeletedFileHashes += $TempDeletedOutputAudit - } - } - elseif ($Log.Type -eq 'Blocked') { + if (-NOT $Output.Existing.Audit.ContainsKey($UniqueLogKey)) { + $Output.Existing.Audit[$UniqueLogKey] = $Log + } - # Add the log to the output object if it has Blocked type - $OutputBlocked += $Log + if (-NOT $Output.Separated.Audit.AvailableFilesPaths.Contains($Log['File Name'])) { + [System.Void]$Output.Separated.Audit.AvailableFilesPaths.Add($Log['File Name']) + } + } + # If the file is not currently on the disk, extract its hashes from the log + else { + $TempDeletedOutputAudit = $Log | Select-Object -Property FileVersion, 'File Name', PolicyGUID, 'SHA256 Hash', 'SHA256 Flat Hash', 'SHA1 Hash', 'SHA1 Flat Hash' - # If the file the log is referring to is currently on the disk - if (Test-Path -Path $Log.'File Name') { - $OutputExistingBlocked += $Log - $OutputSeparatedBlocked.AvailableFilesPaths += $Log.'File Name' - } - # If the file is not currently on the disk, extract its hashes from the log - else { - $TempDeletedOutputBlocked = $Log | Select-Object -Property FileVersion, 'File Name', PolicyGUID, 'SHA256 Hash', 'SHA256 Flat Hash', 'SHA1 Hash', 'SHA1 Flat Hash' + if (-NOT $Output.Deleted.Audit.ContainsKey($UniqueLogKey)) { + $Output.Deleted.Audit[$UniqueLogKey] = $TempDeletedOutputAudit + } - $OutputDeletedBlocked += $TempDeletedOutputBlocked - $OutputSeparatedBlocked.DeletedFileHashes += $TempDeletedOutputBlocked + if (-NOT $Output.Separated.Audit.DeletedFileHashes.Contains($UniqueLogKey)) { + $Output.Separated.Audit.DeletedFileHashes[$UniqueLogKey] = $TempDeletedOutputAudit + } + } + } + + elseif ($Log.Type -eq 'Blocked') { + + # Add the log to the output hashtable if it has Blocked type and doesn't already exist there + if (-NOT $Output.All.Blocked.ContainsKey($UniqueLogKey)) { + $Output.All.Blocked[$UniqueLogKey] = $Log + } + + # If the file the log is referring to is currently on the disk + if (Test-Path -Path $Log['File Name']) { + + if (-NOT $Output.Existing.Blocked.ContainsKey($UniqueLogKey)) { + $Output.Existing.Blocked[$UniqueLogKey] = $Log + } + + if (-NOT $Output.Separated.Blocked.AvailableFilesPaths.Contains($Log['File Name'])) { + [System.Void]$Output.Separated.Blocked.AvailableFilesPaths.Add($Log['File Name']) + } + } + # If the file is not currently on the disk, extract its hashes from the log + else { + $TempDeletedOutputBlocked = $Log | Select-Object -Property FileVersion, 'File Name', PolicyGUID, 'SHA256 Hash', 'SHA256 Flat Hash', 'SHA1 Hash', 'SHA1 Flat Hash' + + if (-NOT $Output.Deleted.Blocked.ContainsKey($UniqueLogKey)) { + $Output.Deleted.Blocked[$UniqueLogKey] = $TempDeletedOutputBlocked + } + + if (-NOT $Output.Separated.Blocked.DeletedFileHashes.Contains($UniqueLogKey)) { + $Output.Separated.Blocked.DeletedFileHashes[$UniqueLogKey] = $TempDeletedOutputBlocked + } + } + } + #Endregion Post-processing for the logs + + } + catch { + Throw $_ + } + # Always ensures the lock is released + finally { + [System.Threading.Monitor]::Exit($using:Output.SyncRoot) } } - - #Endregion Post-processing for the logs } } } @@ -406,78 +561,42 @@ Function Receive-CodeIntegrityLogs { Switch ($PostProcessing) { 'Separate' { if ($Type -eq 'Audit') { - # De-duplication - $OutputSeparatedAudit.AvailableFilesPaths = $OutputSeparatedAudit.AvailableFilesPaths | Select-Object -Unique - - $OutputSeparatedAudit.DeletedFileHashes = $OutputSeparatedAudit.DeletedFileHashes | Group-Object -Property 'File Name', ProductName, FileVersion, OriginalFileName, FileDescription, InternalName, PackageFamilyName, Publishers, 'SHA256 Hash', 'SHA256 Flat Hash' | - ForEach-Object -Process { $_.Group[0] } - - Write-Verbose -Message "Receive-CodeIntegrityLogs: Returning $($OutputSeparatedAudit.AvailableFilesPaths.Count) Audit Code Integrity logs for files on the disk and $($OutputSeparatedAudit.DeletedFileHashes.Count) for the files not on the disk, in a nested object." - Return $OutputSeparatedAudit + Write-Verbose -Message "Receive-CodeIntegrityLogs: Returning $($Output.Separated.Audit.AvailableFilesPaths.Count) Audit Code Integrity logs for files on the disk and $($Output.Separated.Audit.DeletedFileHashes.Count) for the files not on the disk." + Return $Output.Separated.Audit } else { - # De-duplication - $OutputSeparatedBlocked.AvailableFilesPaths = $OutputSeparatedBlocked.AvailableFilesPaths | Select-Object -Unique - - $OutputSeparatedBlocked.DeletedFileHashes = $OutputSeparatedBlocked.DeletedFileHashes | Group-Object -Property 'File Name', ProductName, FileVersion, OriginalFileName, FileDescription, InternalName, PackageFamilyName, Publishers, 'SHA256 Hash', 'SHA256 Flat Hash' | - ForEach-Object -Process { $_.Group[0] } - - Write-Verbose -Message "Receive-CodeIntegrityLogs: Returning $($OutputSeparatedBlocked.AvailableFilesPaths.Count) Blocked Code Integrity logs for files on the disk and $($OutputSeparatedBlocked.DeletedFileHashes.Count) for the files not on the disk, in a nested object." - Return $OutputSeparatedBlocked + Write-Verbose -Message "Receive-CodeIntegrityLogs: Returning $($Output.Separated.Blocked.AvailableFilesPaths.Count) Blocked Code Integrity logs for files on the disk and $($Output.Separated.Blocked.DeletedFileHashes.Count) for the files not on the disk." + Return $Output.Separated.Blocked } } 'OnlyExisting' { if ($Type -eq 'Audit') { - # De-duplication - $OutputExistingAudit = $OutputExistingAudit | Group-Object -Property 'File Name', ProductName, FileVersion, OriginalFileName, FileDescription, InternalName, PackageFamilyName, Publishers, 'SHA256 Hash', 'SHA256 Flat Hash' | - ForEach-Object -Process { $_.Group[0] } - - Write-Verbose -Message "Receive-CodeIntegrityLogs: Returning $($OutputExistingAudit.Count) Audit Code Integrity logs for files on the disk." - Return $OutputExistingAudit + Write-Verbose -Message "Receive-CodeIntegrityLogs: Returning $($Output.Existing.Audit.Values.Count) Audit Code Integrity logs for files on the disk." + Return $Output.Existing.Audit.Values } else { - # De-duplication - $OutputExistingBlocked = $OutputExistingBlocked | Group-Object -Property 'File Name', ProductName, FileVersion, OriginalFileName, FileDescription, InternalName, PackageFamilyName, Publishers, 'SHA256 Hash', 'SHA256 Flat Hash' | - ForEach-Object -Process { $_.Group[0] } - - Write-Verbose -Message "Receive-CodeIntegrityLogs: Returning $($OutputExistingBlocked.Count) Blocked Code Integrity logs for files on the disk." - Return $OutputExistingBlocked + Write-Verbose -Message "Receive-CodeIntegrityLogs: Returning $($Output.Existing.Blocked.Values.Count) Blocked Code Integrity logs for files on the disk." + Return $Output.Existing.Blocked.Values } } 'OnlyDeleted' { if ($Type -eq 'Audit') { - # De-duplication - $OutputDeletedAudit = $OutputDeletedAudit | Group-Object -Property 'File Name', ProductName, FileVersion, OriginalFileName, FileDescription, InternalName, PackageFamilyName, Publishers, 'SHA256 Hash', 'SHA256 Flat Hash' | - ForEach-Object -Process { $_.Group[0] } - - Write-Verbose -Message "Receive-CodeIntegrityLogs: Returning $($OutputDeletedAudit.Count) Audit Code Integrity logs for files not on the disk." - Return $OutputDeletedAudit + Write-Verbose -Message "Receive-CodeIntegrityLogs: Returning $($Output.Deleted.Audit.Values.Count) Audit Code Integrity logs for files not on the disk." + Return $Output.Deleted.Audit.Values } else { - # De-duplication - $OutputDeletedBlocked = $OutputDeletedBlocked | Group-Object -Property 'File Name', ProductName, FileVersion, OriginalFileName, FileDescription, InternalName, PackageFamilyName, Publishers, 'SHA256 Hash', 'SHA256 Flat Hash' | - ForEach-Object -Process { $_.Group[0] } - - Write-Verbose -Message "Receive-CodeIntegrityLogs: Returning $($OutputDeletedBlocked.Count) Blocked Code Integrity logs for files not on the disk." - Return $OutputDeletedBlocked + Write-Verbose -Message "Receive-CodeIntegrityLogs: Returning $($Output.Deleted.Blocked.Values.Count) Blocked Code Integrity logs for files not on the disk." + Return $Output.Deleted.Blocked.Values } } Default { if ($Type -eq 'Audit') { - # De-duplication - $OutputAudit = $OutputAudit | Group-Object -Property 'File Name', ProductName, FileVersion, OriginalFileName, FileDescription, InternalName, PackageFamilyName, Publishers, 'SHA256 Hash', 'SHA256 Flat Hash' | - ForEach-Object -Process { $_.Group[0] } - - Write-Verbose -Message "Receive-CodeIntegrityLogs: Returning $($OutputAudit.Count) Audit Code Integrity logs." - Return $OutputAudit + Write-Verbose -Message "Receive-CodeIntegrityLogs: Returning $($Output.All.Audit.Values.Count) Audit Code Integrity logs." + Return $Output.All.Audit.Values } else { - # De-duplication - $OutputBlocked = $OutputBlocked | Group-Object -Property 'File Name', ProductName, FileVersion, OriginalFileName, FileDescription, InternalName, PackageFamilyName, Publishers, 'SHA256 Hash', 'SHA256 Flat Hash' | - ForEach-Object -Process { $_.Group[0] } - - Write-Verbose -Message "Receive-CodeIntegrityLogs: Returning $($OutputBlocked.Count) Blocked Code Integrity logs." - Return $OutputBlocked + Write-Verbose -Message "Receive-CodeIntegrityLogs: Returning $($Output.All.Blocked.Values.Count) Blocked Code Integrity logs." + Return $Output.All.Blocked.Values } } } @@ -488,8 +607,8 @@ Export-ModuleMember -Function 'Receive-CodeIntegrityLogs' # SIG # Begin signature block # MIILkgYJKoZIhvcNAQcCoIILgzCCC38CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG -# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBu2Eh9/Y1qc3D1 -# cb59O4vgLzT6zyYfTT2TC3AbuPUvFqCCB9AwggfMMIIFtKADAgECAhMeAAAABI80 +# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDcERx1F7+tkP1p +# qKprRkMwvaXGigd6ufErod7k3ugL3KCCB9AwggfMMIIFtKADAgECAhMeAAAABI80 # LDQz/68TAAAAAAAEMA0GCSqGSIb3DQEBDQUAME8xEzARBgoJkiaJk/IsZAEZFgNj # b20xIjAgBgoJkiaJk/IsZAEZFhJIT1RDQUtFWC1DQS1Eb21haW4xFDASBgNVBAMT # C0hPVENBS0VYLUNBMCAXDTIzMTIyNzExMjkyOVoYDzIyMDgxMTEyMTEyOTI5WjB5 @@ -536,16 +655,16 @@ Export-ModuleMember -Function 'Receive-CodeIntegrityLogs' # Q0FLRVgtQ0ECEx4AAAAEjzQsNDP/rxMAAAAAAAQwDQYJYIZIAWUDBAIBBQCggYQw # GAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGC # NwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQx -# IgQgTu37ssl/EC4xZ6wKqk7BrZktQrhoqasesmbJXGEAwN4wDQYJKoZIhvcNAQEB -# BQAEggIAT5C5GdgsHFLkkr2vJIp6YfeF9pZmhEdYI1fL4gh8/CreA1clL4xloMX+ -# Kf9MmUI0GJ+Aupsh3N1fQp09CS2A3uh2wjXWAFu8UiYApjAD9PV0rTg9+QHWYj7M -# 88ExmHPGgsfbZqM+SJmweWehCNgRkXJ0qWsZokrq6Rr+1CDJFQA3VNDz3ojkKf9+ -# 29nN/Ub2v50h0u4esDhy7eoKiha7MWIDuxFEz8rRIa2AIBIUGdyV4w8SEor9BZxb -# wn/nDXkixFODsXIiI7547zWfYS+hQIXNdkMbJr3Dqq20pJMrR6MhvzIrXM7dsfDQ -# Jo7VaMuMthKuz4jLKExfDHAqK6QFz1/Z1tA26ffoFdd8gOLuT6z/cGgbwY5+kiCB -# 9/UC9EOb7f+IOY7l+sn8/BixEmJNNPJ3lsbXtdeGoejpBKYGXymw5iLpejnWQbPX -# 9eCAs/+9Mu3OpAu2IyZd5V5UghhlewvpJY1DQ8LW4YhcSgGxQ6LmHbMW7hFnn4i/ -# 8FVkLJz1IJU5HUZhztUy3ZlvZMfVC1w9YmjIUOkyDLuYRBzUMx4ZW/aKvW6OTIYm -# yvJLdeGw8hy1+eEopMs6JbUHUB52vKNyGuNWYQZGU7KFs7L0NqtfGbYttuVLql6c -# seOkPaD4eZWnASyQYsnpJDOyr5Xr4+icyfqcr/2ch5mthethJ2M= +# IgQgXhfwKKzGimgBGVVxp/7Mv3wyUgvOqTdRUvabmAQUj0swDQYJKoZIhvcNAQEB +# BQAEggIAdrDkEZ0CGQBnuPmLbmmC2RMavmZC5oDqNQNGUTmoJ/JQK2cLm3dUhey8 +# hQuyY5qiS7+RyRqv0Y9PWJYyQutcw6TyfUvb1f7wpaDExZqXW/m91XJWFQumBe0d +# sO37WEjvqxES2iwaAAku+saQvZ2+6EKQpQ9gtavTLsPi6NZPhMJb4SguvwWw6Xlq +# MPtFnFZm8NlFQRGeg4aIdkNBr32GxHYWYZMGYTQ3BTWQJ7bwQTK3FEm4mWbtdh+P +# AfaMiK+fxV5hcA0pmEifalpRv6TbCULlLXO7myuDKpIpMKqRIrFcYy2VQuADmHKz +# OcNrFqD/YK2dHFNpDr2i1IOiU7M1CJNpinBPUvEwnnbXETcYVxHAoZmwUrjSHNjk +# ytX1RxSNjErH/Rs6Cr6u+g0eq05Xo5g/lB343lYoqgUgUXan37m1wJDNeKBOmHy7 +# cD4zJ64EJ9syjpFgrwDShrB41MrVrt0+gKbPzwsKgswpbqGiQyIJHcng6MaLiKa5 +# OHQvpvmCakQQVVtJ1dix4uCNwzs/XSfFYgT1FjdGpMYz/pdBdCTd7MyEQRNJW/M2 +# ib4gFXMwHtNMHTM0qpP/wLJL1mjnhDMbDsYWT1QFPnCSqGEmxI9uO0zF/wu1Njvi +# o1P1MuyPg5PsbglHD5iSpLycYwrviufyiEc1v0V5I/hjrkBoejI= # SIG # End signature block diff --git a/WDACConfig/WDACConfig Module Files/WDACConfig.psd1 b/WDACConfig/WDACConfig Module Files/WDACConfig.psd1 index e9f3040f8..683744182 100644 --- a/WDACConfig/WDACConfig Module Files/WDACConfig.psd1 +++ b/WDACConfig/WDACConfig Module Files/WDACConfig.psd1 @@ -4,7 +4,7 @@ RootModule = 'WDACConfig.psm1' # Version number of this module. - ModuleVersion = '0.3.5' + ModuleVersion = '0.3.6' # Supported PSEditions CompatiblePSEditions = @('Core') @@ -71,7 +71,7 @@ This is an advanced PowerShell module for WDAC (Windows Defender Application Con '@ # Minimum version of the PowerShell engine required by this module - PowerShellVersion = '7.4.1' + PowerShellVersion = '7.4.2' # Name of the PowerShell host required by this module # PowerShellHostName = '' @@ -235,7 +235,26 @@ This is an advanced PowerShell module for WDAC (Windows Defender Application Con 'C#\Crypt32dll.cs', 'C#\AuthenticodeHashCalc.cs', 'C#\PageHashCalc.cs', - 'C#\Crypt32CertCN.cs' + 'C#\Crypt32CertCN.cs', + 'Help\ConvertTo-WDACPolicy.xml', + 'Help\ConvertTo-WDACPolicy.md', + 'XMLOps\Build-SignerAndHashObjects.psm1', + 'XMLOps\Clear-CiPolicy_Semantic.psm1', + 'XMLOps\Close-EmptyXmlNodes_Semantic.psm1', + 'XMLOps\Compare-CorrelatedData.psm1', + 'XMLOps\Merge-Signers_Semantic.psm1', + 'XMLOps\New-FilePublisherLevelRules.psm1', + 'XMLOps\New-HashLevelRules.psm1', + 'XMLOps\New-PublisherLevelRules.psm1', + 'XMLOps\Optimize-MDECSVData.psm1', + 'XMLOps\Remove-AllowElements_Semantic.psm1', + 'XMLOps\Remove-DuplicateAllowAndFileRuleRefElements_IDBased.psm1', + 'XMLOps\Remove-DuplicateAllowedSignersAndCiSigners_IDBased.psm1', + 'XMLOps\Remove-DuplicateFileAttrib_IDBased.psm1', + 'XMLOps\Remove-DuplicateFileAttrib_Semantic.psm1', + 'XMLOps\Remove-DuplicateFileAttribRef_IDBased.psm1', + 'XMLOps\Remove-OrphanAllowedSignersAndCiSigners_IDBased.psm1', + 'XMLOps\Remove-UnreferencedFileRuleRefs.psm1' ) # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. diff --git a/WDACConfig/WDACConfig Module Files/WDACSimulation/Compare-SignerAndCertificate.psm1 b/WDACConfig/WDACConfig Module Files/WDACSimulation/Compare-SignerAndCertificate.psm1 index 4f91928ab..9d2ad3332 100644 --- a/WDACConfig/WDACConfig Module Files/WDACSimulation/Compare-SignerAndCertificate.psm1 +++ b/WDACConfig/WDACConfig Module Files/WDACSimulation/Compare-SignerAndCertificate.psm1 @@ -303,6 +303,7 @@ Function Compare-SignerAndCertificate { } } } + # If the Signer matched and it doesn't have a FileAttrib, then it's a Publisher level signer else { $CurrentFileInfo.SignerID = $Signer.ID $CurrentFileInfo.SignerName = $Signer.Name @@ -340,6 +341,9 @@ Function Compare-SignerAndCertificate { # Check if the signer's name (Referring to the one in the XML file) matches the same Intermediate certificate's SubjectCN elseif (($Signer.CertRoot -eq $Certificate.TBSValue) -and ($Signer.Name -eq $Certificate.SubjectCN)) { + # If the signer has a FileAttrib indicating it was generated with FilePublisher or SignedVersion level, and it wasn't already matched with those levels above, then do not use it for other levels + if ($Signer.HasFileAttrib) { Continue } + $CurrentFileInfo.SignerID = $Signer.ID $CurrentFileInfo.SignerName = $Signer.Name $CurrentFileInfo.SignerCertRoot = $Signer.CertRoot @@ -373,6 +377,9 @@ Function Compare-SignerAndCertificate { # Check if the signer's name (Referring to the one in the XML file) matches the Leaf certificate's SubjectCN elseif (($Signer.CertRoot -eq $PrimaryCertificateLeafDetails.TBSValue) -and ($Signer.Name -eq $PrimaryCertificateLeafDetails.SubjectCN)) { + # If the signer has a FileAttrib indicating it was generated with FilePublisher or SignedVersion level, and it wasn't already matched with those levels above, then do not use it for other levels + if ($Signer.HasFileAttrib) { Continue } + $CurrentFileInfo.SignerID = $Signer.ID $CurrentFileInfo.SignerName = $Signer.Name $CurrentFileInfo.SignerCertRoot = $Signer.CertRoot @@ -525,6 +532,7 @@ Function Compare-SignerAndCertificate { } } } + # If the Signer matched and it doesn't have a FileAttrib, then it's a Publisher level signer else { $CurrentFileInfo.NestedSignerID = $Signer.ID $CurrentFileInfo.NestedSignerName = $Signer.Name @@ -557,6 +565,9 @@ Function Compare-SignerAndCertificate { # PcaCertificate, RootCertificate levels eligibility check elseif (($Signer.CertRoot -eq $NestedCertificate.TBSValue) -and ($Signer.Name -eq $NestedCertificate.SubjectCN)) { + # If the signer has a FileAttrib indicating it was generated with FilePublisher or SignedVersion level, and it wasn't already matched with those levels above, then do not use it for other levels + if ($Signer.HasFileAttrib) { Continue } + $CurrentFileInfo.NestedSignerID = $Signer.ID $CurrentFileInfo.NestedSignerName = $Signer.Name $CurrentFileInfo.NestedSignerCertRoot = $Signer.CertRoot @@ -585,6 +596,9 @@ Function Compare-SignerAndCertificate { # LeafCertificate level eligibility check elseif (($Signer.CertRoot -eq $NestedCertificateLeafDetails.TBSValue) -and ($Signer.Name -eq $NestedCertificateLeafDetails.SubjectCN)) { + # If the signer has a FileAttrib indicating it was generated with FilePublisher or SignedVersion level, and it wasn't already matched with those levels above, then do not use it for other levels + if ($Signer.HasFileAttrib) { Continue } + $CurrentFileInfo.NestedSignerID = $Signer.ID $CurrentFileInfo.NestedSignerName = $Signer.Name $CurrentFileInfo.NestedSignerCertRoot = $Signer.CertRoot @@ -655,8 +669,8 @@ Export-ModuleMember -Function 'Compare-SignerAndCertificate' # SIG # Begin signature block # MIILkgYJKoZIhvcNAQcCoIILgzCCC38CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG -# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCXHNvEtgWNm6yQ -# qNgkzmpRByZwQDu2goxzPvv1BTokQ6CCB9AwggfMMIIFtKADAgECAhMeAAAABI80 +# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDwkblcNmqFlmKE +# hrG2O1hGeLD1onn2mfdruKIQ0v7td6CCB9AwggfMMIIFtKADAgECAhMeAAAABI80 # LDQz/68TAAAAAAAEMA0GCSqGSIb3DQEBDQUAME8xEzARBgoJkiaJk/IsZAEZFgNj # b20xIjAgBgoJkiaJk/IsZAEZFhJIT1RDQUtFWC1DQS1Eb21haW4xFDASBgNVBAMT # C0hPVENBS0VYLUNBMCAXDTIzMTIyNzExMjkyOVoYDzIyMDgxMTEyMTEyOTI5WjB5 @@ -703,16 +717,16 @@ Export-ModuleMember -Function 'Compare-SignerAndCertificate' # Q0FLRVgtQ0ECEx4AAAAEjzQsNDP/rxMAAAAAAAQwDQYJYIZIAWUDBAIBBQCggYQw # GAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGC # NwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQx -# IgQgpndG7wMZ0sbFxETiPBEL5fwfrcdkJ/hQuL7Xh9SXt/owDQYJKoZIhvcNAQEB -# BQAEggIAX8fWJ+vJ74d+hr10fUZMZzaILZEcUREcWlkTSVkGo2oaTII1ayhCa3QY -# fqCxY7MNC4PwUYShOEbCbpeHFDXwwgBcpw83UygvV0xQnmPv0mxM05NG/5sgidkV -# 7nKa2W82eyGVnVBSaz57CWG2nQduzrdPtWHKrQjDQ2C7aJqrPjIZxFi6/hUmx5/l -# Bz6qIoPXqIAK9RSplRHT3GBAyq2erxVLuqcyEP9X/IzDzoa7Vw77RHxmEUnh2Ytu -# 1nSMkXqCCYt7G2UrJuJ46H5dmI5CzzUHrBUAE2N8YaQJIdOB8/00p8NlxetBMmBo -# eUMFHPPt3gNoqMqAgaQRZHNCXfw+m3okVhP3x78ilvS6r7IjdazyCxZ2uaYVt7Vq -# K3x3Iy9NdAXr5UVg4Uc4fDpFmXVsxxjIwX0mqQxl8Gg0ZIqbpzE8jVW0+gpooKFu -# X5dMyQCEbPMR3xBmlj3AmxAHE/jtBkvsPlivFzZ9micgXDdQpJ1SoxkPRZ9JYLnX -# vCmO9H/jnoHQ3hipXSc2TFLYbUDPNh7gVeJLBbGc62B4Lze5ui5ZDxruRp5HWiYS -# lG9SeaazDhYSwlFy7J5Og141ZbhoJ3ep/qEoxJwVZAV+Hl7lhyZ4Mr0Ow++FOy0W -# ngJWV+fpEeGpwzcwf13ugFKM3LV24JIKrtW32mP2IxpbXzFMoXY= +# IgQgeV/EgDv+lgHhttG8siXgDkD766padyTzGDW9u2Mo6jQwDQYJKoZIhvcNAQEB +# BQAEggIAlUa5hOW7ckG5cSYZI/XvYIFEpLc3z7sz2HA6zwpMG7PsaeS+vFjG/tF8 +# NWBo/r1Mjt+5Ynk2sk7NBH8326NPesmd3n++kNiCflkEjI8kaIFGgQH2A90xYoTh +# cdpDsEaxV5AwObgpfHK+oIXqqT+mkBlzojUL87us2vgIYHUavwCOFr1gVaR1FPka +# oi9nLwqhe05r2hTmDrnxzXX5nJg4k33eoYApSeg4erMbIGOdFRYJFzMl1yQVxZx7 +# oKcFeE/L0vNGSGBP3zi3IYoLGvBo2fIFjpfOJXJWe9leRw2ee6hR/u18lfMawf5Z +# iwOq+Q7GusuvVqFoQAPOCteZDqfP3Ph7GqAgJkODOTL5z3RySEfXmuEILfQmYKAV +# PNUHWDk+9XPAqOg9yhsI8shn0D1bInt50F5ACwUCtbQhjZ8U0FuXTRwtA2Rhnywl +# wK0wlG4dDmDMoBdju3ynFU03eMvX8ZK4Yr4jDqtNinmrFvhlgrbwHDR/p0vRhbq0 +# EBUS27vL/UAom21M9sUyHOOeSbCNZspZYLGfQg90QBtQMt2kmj+da1RTa5QmCLC/ +# gXmTm8yukiqFYlmgcspYplyz4h9RzJyjrGlzaNCUwboTcB/gT5njeYkTBXQEnbl1 +# AnRBwniebLZVLsvlBjCQdDrD7lou5P8XmZCTSOb8+HFXxhJ29VM= # SIG # End signature block diff --git a/WDACConfig/WDACConfig Module Files/WDACSimulation/Get-SignedFileCertificates.psm1 b/WDACConfig/WDACConfig Module Files/WDACSimulation/Get-SignedFileCertificates.psm1 index 1e4bfa669..e7b4e4707 100644 --- a/WDACConfig/WDACConfig Module Files/WDACSimulation/Get-SignedFileCertificates.psm1 +++ b/WDACConfig/WDACConfig Module Files/WDACSimulation/Get-SignedFileCertificates.psm1 @@ -35,8 +35,13 @@ Function Get-SignedFileCertificates { process { # Check which parameter set is used if ($FilePath) { - # If the FilePath parameter is used, import all the certificates from the file - $CertCollection.Import($FilePath, $null, 'DefaultKeySet') + try { + # If the FilePath parameter is used, import all the certificates from the file + $CertCollection.Import($FilePath, $null, 'DefaultKeySet') + } + catch { + throw [ExceptionFailedToGetCertificateCollection]::new('Could not get the certificate collection of the file due to lack of necessary permissions.', 'Get-SignedFileCertificates') + } } elseif ($X509Certificate2) { # If the CertObject parameter is used, add the certificate object to the collection @@ -54,8 +59,8 @@ Export-ModuleMember -Function 'Get-SignedFileCertificates' # SIG # Begin signature block # MIILkgYJKoZIhvcNAQcCoIILgzCCC38CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG -# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDOrDleOeGXEyHo -# vvuN3HYqR0NxLsLbVQb4+2ry+9PXWKCCB9AwggfMMIIFtKADAgECAhMeAAAABI80 +# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDmvRW1JCHoyrOv +# 91Dn2CAz8egQJePpo6Dd6Yd483BCY6CCB9AwggfMMIIFtKADAgECAhMeAAAABI80 # LDQz/68TAAAAAAAEMA0GCSqGSIb3DQEBDQUAME8xEzARBgoJkiaJk/IsZAEZFgNj # b20xIjAgBgoJkiaJk/IsZAEZFhJIT1RDQUtFWC1DQS1Eb21haW4xFDASBgNVBAMT # C0hPVENBS0VYLUNBMCAXDTIzMTIyNzExMjkyOVoYDzIyMDgxMTEyMTEyOTI5WjB5 @@ -102,16 +107,16 @@ Export-ModuleMember -Function 'Get-SignedFileCertificates' # Q0FLRVgtQ0ECEx4AAAAEjzQsNDP/rxMAAAAAAAQwDQYJYIZIAWUDBAIBBQCggYQw # GAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGC # NwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQx -# IgQggyMqz5uOaOB9DSCdRWJ9nSNv6T5y46ZfB3Q2rhUDlJAwDQYJKoZIhvcNAQEB -# BQAEggIAhX6AzdMZ5ApNQxbunttLSA5IJjroUSISOH1PpgHf/ZpQEW0//J5YupQl -# qf2JAKPlY1HzrXtTEyIpqh8oE8/5fGr0kemhIPuIsPsZO97hbwfy9UizsuamrN9c -# vGWBDOO9WpYyu9mJ8CzXDfpE8Hzla3H5+giRUr9EAuuO6XodJ51k4pxpfe965ixi -# vAK8kSDbv8O3SEhX8jFzBqVzNIr2Y5sAhOuY9421vJ1fvXDYYpghAvn2VsX4AkXx -# x4d4DqbyLS69OEIElEdNGZdWKqocPECsJQwcLa7ompbb5iiwCf9UOoR/4Y7/ddbL -# OKCqQInV3KLBe0FDYyphVuNws5idFobAyWnja1li5GiL9oaHUHn+q4ne5CSWay2v -# OjqDqE6OfH7L1NcVcB93ZUEIR6kTdba6q3wXqR82Y6caK0qR6DCReCRcIgtjEUlY -# tEy9a8E1WZoa+xdC2hLChPjEMgUXjdrUDZvEuVPQyBf+kXYoXHKWldZYDOj0691Q -# 2AmppvLqSH4BB0wSb/dVIJN9mF28W2WSg2w7ymv4fQlVtHGQ4z+sD2obN13Mcv2v -# FOxJhBQjkeyl69Ef9WfSHIydKSMMvB0+v+Fsdm6aASLNaXvoQJAi3+DavIj7E49G -# 7GCOKax6VWFw3GZ9q3QQy7SSMV6Y0PRgJBreLi5WjcUTCSeNvyo= +# IgQgiXUhrv4vQ96S7F3u6/44/1f99O3Q/ce9AP9okKHGv2MwDQYJKoZIhvcNAQEB +# BQAEggIAItKzX1GwZo6kXxUjSXr0GopK7V2sSJ2KO4Xg1VTmhd9aLAypnglFXhbP +# K7o6K3yq20bxt09LhGPPkOGnHHCsH7J4+HPb5AtVWjk8L6yaH7wFZcRwEbOky8zJ +# DA3TFDlOIChilXr7TxGmug58xzpDEMR6da5BRN/cOnsHKk27OeR8Y2A+NfXilf3+ +# FuRPJuVbi1CNhRSwNb+HNnBDChUvO3lA0f+DaqkGKf3NT4/Rkuubb+kFIWKaENyC +# uJgfXO60PsYrWWAYibJ+NRkr9Q4GdVuJjy+4uk5CTjc/PZsFDMZazPkiextab8u3 +# 91hiuPwdVYNpJzjFafYfqH0ueH4Y3/74FgOp+ceY5OK0DLbFhu9EhM/9XlFJuefN +# h9Dhv6UsU3CGJXYGgRlketvrBg1JGoU4viYCrHhQWi0kJSWLMbaPl2hrC48xa5vN +# F72x37vSYQJzVeS5GUFYkjXK1c/x6K7c7d+I6FuPiwJOqm3vX0J4OzT7moatO0ju +# bCRL4uJEEupTeoKMjy1pO0t4fUhUnNvtTrf8AfpdLyo16a7jDBbTHDDPiEnbVwyy +# khqFymERTc9mG/egd42yAVr1/f8e4wOuLk6G/reeOm2SpDu6MuMUB2+H/0wvqZYv +# t4djmnfqpPSQEFmGncT2VJGgpoY0K+4JBWPmbNgdAEElQIvF+70= # SIG # End signature block diff --git a/WDACConfig/WDACConfig Module Files/XMLOps/Build-SignerAndHashObjects.psm1 b/WDACConfig/WDACConfig Module Files/XMLOps/Build-SignerAndHashObjects.psm1 new file mode 100644 index 000000000..aed6a7293 --- /dev/null +++ b/WDACConfig/WDACConfig Module Files/XMLOps/Build-SignerAndHashObjects.psm1 @@ -0,0 +1,285 @@ +Function Build-SignerAndHashObjects { + <# + .SYNOPSIS + Creates Signer and Hash objects from the input data + 2 types of Signers are created: FilePublisher and Publisher signers + + FilePublisher Signers are created for files that have the necessary details for a FilePublisher rule + Publisher Signers are created for files that don't have the necessary details for a FilePublisher rule + Hashes are created for the unsigned data + + The output is a single object with nested properties for the Signers and Hashes + + Both Publisher and FilePublisher signers first check if the file has both Issuer and Publisher TBS hashes, if they are present then both of them will be used to create the Signer. + If the file is missing the Issuer TBS hash, then the Publisher certificate will be used for both Publisher and Issuer details (TBS and Name) + This will essentially create the Signers based on LeafCertificate Level. + + The New-FilePublisherLevelRules and New-PublisherLevelRules functions both are able to create rules based on different signer WDAC levels. + + The other way around, where Publisher TBS hash is missing but Issuer TBS is present, would create a PCACertificate level Signer, but that is not implemented yet. + Its use case is not clear yet and there haven't been any files with that condition yet. + .PARAMETER Data + The Data to be processed. These are the logs selected by the user and contain both signed and unsigned data. + They will be separated into 2 arrays. + .INPUTS + PSCustomObject[] + .OUTPUTS + PSCustomObject + #> + [CmdletBinding()] + [OutputType([PSCustomObject])] + param ( + [Parameter(Mandatory = $true)][PSCustomObject[]]$Data + ) + Begin { + # Importing the $PSDefaultParameterValues to the current session, prior to everything else + . "$ModuleRootPath\CoreExt\PSDefaultParameterValues.ps1" + + Class CertificateDetailsCreator { + [System.String]$IntermediateCertTBS + [System.String]$IntermediateCertName + [System.String]$LeafCertTBS + [System.String]$LeafCertName + } + + Class FilePublisherSignerCreator { + [CertificateDetailsCreator[]]$CertificateDetails + [System.Version]$FileVersion + [System.String]$FileDescription + [System.String]$InternalName + [System.String]$OriginalFileName + [System.String]$PackageFamilyName + [System.String]$ProductName + [System.String]$FileName + [System.String]$AuthenticodeSHA256 + [System.String]$AuthenticodeSHA1 + [System.Int32]$SiSigningScenario + } + + Class PublisherSignerCreator { + [CertificateDetailsCreator[]]$CertificateDetails + [System.String]$FileName + [System.String]$AuthenticodeSHA256 + [System.String]$AuthenticodeSHA1 + [System.Int32]$SiSigningScenario + } + + Class HashCreator { + [System.String]$AuthenticodeSHA256 + [System.String]$AuthenticodeSHA1 + [System.String]$FileName + [System.Int32]$SiSigningScenario + } + + # An array to store the Signers created with FilePublisher Level + [FilePublisherSignerCreator[]]$FilePublisherSigners = @() + + # An array to store the Signers created with Publisher Level + [PublisherSignerCreator[]]$PublisherSigners = @() + + # An array to store the FileAttributes created using Hash Level + [HashCreator[]]$CompleteHashes = @() + + # Defining the arrays to store the signed and unsigned data + [PSCustomObject[]]$SignedData = @() + [PSCustomObject[]]$UnsignedData = @() + + # Loop through the data and separate the signed and unsigned data + foreach ($Item in $Data) { + if ($Item.SignatureStatus -eq 'Signed') { + $SignedData += $Item + } + else { + $UnsignedData += $Item + } + } + } + + Process { + + if ($Null -ne $SignedData -and $SignedData.Count -gt 0) { + + # Process the signed data + Foreach ($CurrentData in $SignedData) { + + # Create a new FilePublisherSignerCreator object + [FilePublisherSignerCreator]$CurrentFilePublisherSigner = New-Object -TypeName FilePublisherSignerCreator + # Create a new PublisherSignerCreator object + [PublisherSignerCreator]$CurrentPublisherSigner = New-Object -TypeName PublisherSignerCreator + + # Loop through each correlated event and process the certificate details + foreach ($CorData in $CurrentData.CorrelatedEventsData.Values) { + + # Create a new CertificateDetailsCreator object to store the certificate details + [CertificateDetailsCreator]$CurrentCorData = New-Object -TypeName CertificateDetailsCreator + + # Add the certificate details to the new object + $CurrentCorData.LeafCertTBS = $CorData.PublisherTBSHash + $CurrentCorData.LeafCertName = $CorData.PublisherName + $CurrentCorData.IntermediateCertTBS = $CorData.IssuerTBSHash + $CurrentCorData.IntermediateCertName = $CorData.IssuerName + + # If the file doesn't have Issuer TBS hash (aka Intermediate certificate hash), use the leaf cert's TBS hash and CN instead (aka publisher TBS hash) + # This is according to the ConfigCI's workflow when encountering specific files + # MDE doesn't generate Issuer TBS hash for some files + # For those files, the FilePublisher rule will be created with the file's leaf Certificate details only (Publisher certificate) + if (([System.String]::IsNullOrWhiteSpace($CurrentCorData.IntermediateCertTBS)) -and (-NOT (([System.String]::IsNullOrWhiteSpace($CurrentCorData.LeafCertTBS))))) { + + Write-Warning -Message "Intermediate Certificate TBS hash is empty for the file: $($CurrentData.FileName), using the leaf certificate TBS hash instead" + + $CurrentCorData.IntermediateCertName = $CurrentCorData.LeafCertName + + $CurrentCorData.IntermediateCertTBS = $CurrentCorData.LeafCertTBS + + } + + # Add the Certificate details to both the FilePublisherSignerCreator and PublisherSignerCreator objects + # Because later we will determine if the $CurrentData is suitable for FilePublisher or Publisher level + $CurrentFilePublisherSigner.CertificateDetails += $CurrentCorData + $CurrentPublisherSigner.CertificateDetails += $CurrentCorData + } + + # If the file's version is empty or it has no file attribute, then add it to the Publishers array + # because FilePublisher rule cannot be created for it + if ( + ( + ([System.String]::IsNullOrWhiteSpace($CurrentData.OriginalFileName)) -and + ([System.String]::IsNullOrWhiteSpace($CurrentData.InternalName)) -and + ([System.String]::IsNullOrWhiteSpace($CurrentData.FileDescription)) -and + ([System.String]::IsNullOrWhiteSpace($CurrentData.ProductName)) + ) -or ( + ([System.String]::IsNullOrWhiteSpace($CurrentData.FileVersion)) + ) + ) { + $CurrentPublisherSigner.FileName = $CurrentData.FileName + $CurrentPublisherSigner.AuthenticodeSHA256 = $CurrentData.SHA256 + $CurrentPublisherSigner.AuthenticodeSHA1 = $CurrentData.SHA1 + $CurrentPublisherSigner.SiSigningScenario = $CurrentData.SiSigningScenario + + $PublisherSigners += $CurrentPublisherSigner + } + + # If the file has some of the necessary details for a FilePublisher rule then add it to the FilePublisher array + + else { + $CurrentFilePublisherSigner.FileVersion = $CurrentData.FileVersion + $CurrentFilePublisherSigner.FileDescription = $CurrentData.FileDescription + $CurrentFilePublisherSigner.InternalName = $CurrentData.InternalName + $CurrentFilePublisherSigner.OriginalFileName = $CurrentData.OriginalFileName + $CurrentFilePublisherSigner.ProductName = $CurrentData.ProductName + $CurrentFilePublisherSigner.FileName = $CurrentData.FileName + $CurrentFilePublisherSigner.AuthenticodeSHA256 = $CurrentData.SHA256 + $CurrentFilePublisherSigner.AuthenticodeSHA1 = $CurrentData.SHA1 + $CurrentFilePublisherSigner.SiSigningScenario = $CurrentData.SiSigningScenario + + # Some checks to make sure the necessary details are not empty + if (([System.String]::IsNullOrWhiteSpace($CurrentData.SHA256))) { + Write-Warning "SHA256 is empty for the file: $($CurrentData.FileName)" + } + + if (([System.String]::IsNullOrWhiteSpace($CurrentData.SHA1))) { + Write-Warning "SHA1 is empty for the file: $($CurrentData.FileName)" + } + + # Add the new object to the FilePublisherSigners array + $FilePublisherSigners += $CurrentFilePublisherSigner + } + } + + } + + if ($Null -ne $UnsignedData -and $UnsignedData.Count -gt 0) { + + # Processing the unsigned data + Foreach ($HashData in $UnsignedData) { + + # Create a new HashCreator object + [HashCreator]$CurrentHash = New-Object -TypeName HashCreator + + # Add the hash details to the new object + $CurrentHash.AuthenticodeSHA256 = $HashData.SHA256 + $CurrentHash.AuthenticodeSHA1 = $HashData.SHA1 + $CurrentHash.FileName = $HashData.FileName + $CurrentHash.SiSigningScenario = $HashData.SiSigningScenario + + # Add the new object to the CompleteHashes array + $CompleteHashes += $CurrentHash + } + } + + } + End { + # Return the created objects as nested properties of a single object + Return [PSCustomObject]@{ + FilePublisherSigners = $FilePublisherSigners + PublisherSigners = $PublisherSigners + CompleteHashes = $CompleteHashes + } + } +} +Export-ModuleMember -Function 'Build-SignerAndHashObjects' + +# SIG # Begin signature block +# MIILkgYJKoZIhvcNAQcCoIILgzCCC38CAQExDzANBglghkgBZQMEAgEFADB5Bgor +# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG +# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCBjfSTCPR8lxdR +# D1Pd4OZEQVQpd/fIjlehp65GLp00j6CCB9AwggfMMIIFtKADAgECAhMeAAAABI80 +# LDQz/68TAAAAAAAEMA0GCSqGSIb3DQEBDQUAME8xEzARBgoJkiaJk/IsZAEZFgNj +# b20xIjAgBgoJkiaJk/IsZAEZFhJIT1RDQUtFWC1DQS1Eb21haW4xFDASBgNVBAMT +# C0hPVENBS0VYLUNBMCAXDTIzMTIyNzExMjkyOVoYDzIyMDgxMTEyMTEyOTI5WjB5 +# MQswCQYDVQQGEwJVSzEeMBwGA1UEAxMVSG90Q2FrZVggQ29kZSBTaWduaW5nMSMw +# IQYJKoZIhvcNAQkBFhRob3RjYWtleEBvdXRsb29rLmNvbTElMCMGCSqGSIb3DQEJ +# ARYWU3B5bmV0Z2lybEBvdXRsb29rLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIP +# ADCCAgoCggIBAKb1BJzTrpu1ERiwr7ivp0UuJ1GmNmmZ65eckLpGSF+2r22+7Tgm +# pEifj9NhPw0X60F9HhdSM+2XeuikmaNMvq8XRDUFoenv9P1ZU1wli5WTKHJ5ayDW +# k2NP22G9IPRnIpizkHkQnCwctx0AFJx1qvvd+EFlG6ihM0fKGG+DwMaFqsKCGh+M +# rb1bKKtY7UEnEVAsVi7KYGkkH+ukhyFUAdUbh/3ZjO0xWPYpkf/1ldvGes6pjK6P +# US2PHbe6ukiupqYYG3I5Ad0e20uQfZbz9vMSTiwslLhmsST0XAesEvi+SJYz2xAQ +# x2O4n/PxMRxZ3m5Q0WQxLTGFGjB2Bl+B+QPBzbpwb9JC77zgA8J2ncP2biEguSRJ +# e56Ezx6YpSoRv4d1jS3tpRL+ZFm8yv6We+hodE++0tLsfpUq42Guy3MrGQ2kTIRo +# 7TGLOLpayR8tYmnF0XEHaBiVl7u/Szr7kmOe/CfRG8IZl6UX+/66OqZeyJ12Q3m2 +# fe7ZWnpWT5sVp2sJmiuGb3atFXBWKcwNumNuy4JecjQE+7NF8rfIv94NxbBV/WSM +# pKf6Yv9OgzkjY1nRdIS1FBHa88RR55+7Ikh4FIGPBTAibiCEJMc79+b8cdsQGOo4 +# ymgbKjGeoRNjtegZ7XE/3TUywBBFMf8NfcjF8REs/HIl7u2RHwRaUTJdAgMBAAGj +# ggJzMIICbzA8BgkrBgEEAYI3FQcELzAtBiUrBgEEAYI3FQiG7sUghM++I4HxhQSF +# hqV1htyhDXuG5sF2wOlDAgFkAgEIMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA4GA1Ud +# DwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBsGCSsGAQQBgjcVCgQOMAwwCgYIKwYB +# BQUHAwMwHQYDVR0OBBYEFOlnnQDHNUpYoPqECFP6JAqGDFM6MB8GA1UdIwQYMBaA +# FICT0Mhz5MfqMIi7Xax90DRKYJLSMIHUBgNVHR8EgcwwgckwgcaggcOggcCGgb1s +# ZGFwOi8vL0NOPUhPVENBS0VYLUNBLENOPUhvdENha2VYLENOPUNEUCxDTj1QdWJs +# aWMlMjBLZXklMjBTZXJ2aWNlcyxDTj1TZXJ2aWNlcyxDTj1Db25maWd1cmF0aW9u +# LERDPU5vbkV4aXN0ZW50RG9tYWluLERDPWNvbT9jZXJ0aWZpY2F0ZVJldm9jYXRp +# b25MaXN0P2Jhc2U/b2JqZWN0Q2xhc3M9Y1JMRGlzdHJpYnV0aW9uUG9pbnQwgccG +# CCsGAQUFBwEBBIG6MIG3MIG0BggrBgEFBQcwAoaBp2xkYXA6Ly8vQ049SE9UQ0FL +# RVgtQ0EsQ049QUlBLENOPVB1YmxpYyUyMEtleSUyMFNlcnZpY2VzLENOPVNlcnZp +# Y2VzLENOPUNvbmZpZ3VyYXRpb24sREM9Tm9uRXhpc3RlbnREb21haW4sREM9Y29t +# P2NBQ2VydGlmaWNhdGU/YmFzZT9vYmplY3RDbGFzcz1jZXJ0aWZpY2F0aW9uQXV0 +# aG9yaXR5MA0GCSqGSIb3DQEBDQUAA4ICAQA7JI76Ixy113wNjiJmJmPKfnn7brVI +# IyA3ZudXCheqWTYPyYnwzhCSzKJLejGNAsMlXwoYgXQBBmMiSI4Zv4UhTNc4Umqx +# pZSpqV+3FRFQHOG/X6NMHuFa2z7T2pdj+QJuH5TgPayKAJc+Kbg4C7edL6YoePRu +# HoEhoRffiabEP/yDtZWMa6WFqBsfgiLMlo7DfuhRJ0eRqvJ6+czOVU2bxvESMQVo +# bvFTNDlEcUzBM7QxbnsDyGpoJZTx6M3cUkEazuliPAw3IW1vJn8SR1jFBukKcjWn +# aau+/BE9w77GFz1RbIfH3hJ/CUA0wCavxWcbAHz1YoPTAz6EKjIc5PcHpDO+n8Fh +# t3ULwVjWPMoZzU589IXi+2Ol0IUWAdoQJr/Llhub3SNKZ3LlMUPNt+tXAs/vcUl0 +# 7+Dp5FpUARE2gMYA/XxfU9T6Q3pX3/NRP/ojO9m0JrKv/KMc9sCGmV9sDygCOosU +# 5yGS4Ze/DJw6QR7xT9lMiWsfgL96Qcw4lfu1+5iLr0dnDFsGowGTKPGI0EvzK7H+ +# DuFRg+Fyhn40dOUl8fVDqYHuZJRoWJxCsyobVkrX4rA6xUTswl7xYPYWz88WZDoY +# gI8AwuRkzJyUEA07IYtsbFCYrcUzIHME4uf8jsJhCmb0va1G2WrWuyasv3K/G8Nn +# f60MsDbDH1mLtzGCAxgwggMUAgEBMGYwTzETMBEGCgmSJomT8ixkARkWA2NvbTEi +# MCAGCgmSJomT8ixkARkWEkhPVENBS0VYLUNBLURvbWFpbjEUMBIGA1UEAxMLSE9U +# Q0FLRVgtQ0ECEx4AAAAEjzQsNDP/rxMAAAAAAAQwDQYJYIZIAWUDBAIBBQCggYQw +# GAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGC +# NwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQx +# IgQge9/vjiEobgoKkZ2Fbr9O84FUXZpRh53RBX/FsQl8H9EwDQYJKoZIhvcNAQEB +# BQAEggIATUI4JK8zOb0YNETHq+53Xc6GuMHsc0cTHOEpBsBJFiZEbirpxGzL0gPP +# 0yPBqUksFfo/sGcVOQLijyyRkRO4gSXp8qkQohLxTE7bJE6ql1PMXsilQ316u8Cp +# GIOxhILAxfKfNxbMmxun8xf/FgC9F+G1a0C2CsaB2FHSDCLmEXgIjuUhw8dq+0/5 +# hnFNlr3d6ymiL5iU3g8vk4X19RwiYojKNSLc26rbM6vpIgGnBTCZ6Y87f4GfGNpT +# b//6RpRuy8yxCy2dDY3E5ikj8xyJmJ/Uth8Y0sbVddVkIzkRtl2iQNQ2OmnysLs7 +# KKxFo/0qHMQS4vL2gv7lW41Wr5KIj39NnVPpM0weIlL3GMH3Nc80ar6JOikA8GDc +# IPOdnHzekNunZn3SriwWUNG0j6GHCSgh3I4LJmBFsRlu8BLJ4iCxGv0UYlWsiC9B +# pk/F741tspWe5t+Xp5gefHeyrRMd1OQko51h2jgDL5rkKB2a7XJgUVMHP4D18PEQ +# N3W3n0bm15/y3qHQ5Gwxhfwjj1I6fO+Gf+DotxYXnGBIG1w7pyxqvSLikdaHKCgq +# grvSzL5sF51NQCW2Ccml24m5dLwulAhJTcMSHvfQX9qyWJpxANaaMeZNhJAB9lsJ +# 206pdAvv7oWRUWuxduvYAz6D399csJb5mGeSnq2zWUqhrlvqZKg= +# SIG # End signature block diff --git a/WDACConfig/WDACConfig Module Files/XMLOps/Clear-CiPolicy_Semantic.psm1 b/WDACConfig/WDACConfig Module Files/XMLOps/Clear-CiPolicy_Semantic.psm1 new file mode 100644 index 000000000..5431adff4 --- /dev/null +++ b/WDACConfig/WDACConfig Module Files/XMLOps/Clear-CiPolicy_Semantic.psm1 @@ -0,0 +1,133 @@ +Function Clear-CiPolicy_Semantic { + <# + .SYNOPSIS + Clears the CI Policy XML file from all nodes except the base nodes + According to the CI Schema + It clears any XML file to make it usable as a template for MDE AH CI Policy + .PARAMETER Path + The path to the XML file to be processed + .INPUTS + System.IO.FileInfo + .OUTPUTS + System.Void + #> + + [CmdletBinding()] + [OutputType([System.Void])] + param ( + [Parameter(Mandatory = $true)][System.IO.FileInfo]$Path + ) + + Begin { + # Importing the $PSDefaultParameterValues to the current session, prior to everything else + . "$ModuleRootPath\CoreExt\PSDefaultParameterValues.ps1" + + # Load the XML file + [System.Xml.XmlDocument]$Xml = Get-Content -Path $Path + + [System.Xml.XmlNamespaceManager]$Ns = New-Object -TypeName System.Xml.XmlNamespaceManager -ArgumentList $Xml.NameTable + $Ns.AddNamespace('ns', 'urn:schemas-microsoft-com:sipolicy') + } + + Process { + + # Defining the Nodes to keep and clear, according to the CI Schema + $NodesToClear = [ordered]@{ + EKUs = [System.Xml.XmlElement]$Xml.SelectSingleNode('//ns:EKUs', $Ns) + FileRules = [System.Xml.XmlElement]$Xml.SelectSingleNode('//ns:FileRules', $Ns) + Signers = [System.Xml.XmlElement]$Xml.SelectSingleNode('//ns:Signers', $Ns) + UMCI_ProductSigners_Node = [System.Xml.XmlElement]$Xml.SelectSingleNode('//ns:SigningScenarios/ns:SigningScenario[@Value="12"]/ns:ProductSigners', $Ns) + KMCI_ProductSigners_Node = [System.Xml.XmlElement]$Xml.SelectSingleNode('//ns:SigningScenarios/ns:SigningScenario[@Value="131"]/ns:ProductSigners', $Ns) + UMCI_SigningScenario_ProductSigners_FileRulesRef_Node = [System.Xml.XmlElement]$Xml.SelectSingleNode('//ns:SigningScenarios/ns:SigningScenario[@Value="12"]/ns:ProductSigners/ns:FileRulesRef', $Ns) + KMCI_SigningScenario_ProductSigners_FileRulesRef_Node = [System.Xml.XmlElement]$Xml.SelectSingleNode('//ns:SigningScenarios/ns:SigningScenario[@Value="131"]/ns:ProductSigners/ns:FileRulesRef', $Ns) + UpdatePolicySignersNode = [System.Xml.XmlElement]$Xml.SelectSingleNode('//ns:UpdatePolicySigners', $Ns) + CiSignersNode = [System.Xml.XmlElement]$Xml.SelectSingleNode('//ns:CiSigners', $Ns) + } + + foreach ($MainNode in $NodesToClear.Keys) { + + # Get the current node + [System.Xml.XmlElement]$CurrentNode = $NodesToClear[$MainNode] + + if ($Null -ne $CurrentNode) { + # Remove all child nodes + while ($CurrentNode.HasChildNodes) { + [System.Void]$CurrentNode.RemoveChild($CurrentNode.FirstChild) + } + # Set the node to self close + $CurrentNode.IsEmpty = $true + } + } + } + + End { + # Save the modified XML back to the file + $Xml.Save($Path) + } +} +Export-ModuleMember -Function 'Clear-CiPolicy_Semantic' + +# SIG # Begin signature block +# MIILkgYJKoZIhvcNAQcCoIILgzCCC38CAQExDzANBglghkgBZQMEAgEFADB5Bgor +# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG +# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCA8Tc1hHtLXrBEE +# 0Amb6oowv5FOTWlO/MqUpQpItqFfcqCCB9AwggfMMIIFtKADAgECAhMeAAAABI80 +# LDQz/68TAAAAAAAEMA0GCSqGSIb3DQEBDQUAME8xEzARBgoJkiaJk/IsZAEZFgNj +# b20xIjAgBgoJkiaJk/IsZAEZFhJIT1RDQUtFWC1DQS1Eb21haW4xFDASBgNVBAMT +# C0hPVENBS0VYLUNBMCAXDTIzMTIyNzExMjkyOVoYDzIyMDgxMTEyMTEyOTI5WjB5 +# MQswCQYDVQQGEwJVSzEeMBwGA1UEAxMVSG90Q2FrZVggQ29kZSBTaWduaW5nMSMw +# IQYJKoZIhvcNAQkBFhRob3RjYWtleEBvdXRsb29rLmNvbTElMCMGCSqGSIb3DQEJ +# ARYWU3B5bmV0Z2lybEBvdXRsb29rLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIP +# ADCCAgoCggIBAKb1BJzTrpu1ERiwr7ivp0UuJ1GmNmmZ65eckLpGSF+2r22+7Tgm +# pEifj9NhPw0X60F9HhdSM+2XeuikmaNMvq8XRDUFoenv9P1ZU1wli5WTKHJ5ayDW +# k2NP22G9IPRnIpizkHkQnCwctx0AFJx1qvvd+EFlG6ihM0fKGG+DwMaFqsKCGh+M +# rb1bKKtY7UEnEVAsVi7KYGkkH+ukhyFUAdUbh/3ZjO0xWPYpkf/1ldvGes6pjK6P +# US2PHbe6ukiupqYYG3I5Ad0e20uQfZbz9vMSTiwslLhmsST0XAesEvi+SJYz2xAQ +# x2O4n/PxMRxZ3m5Q0WQxLTGFGjB2Bl+B+QPBzbpwb9JC77zgA8J2ncP2biEguSRJ +# e56Ezx6YpSoRv4d1jS3tpRL+ZFm8yv6We+hodE++0tLsfpUq42Guy3MrGQ2kTIRo +# 7TGLOLpayR8tYmnF0XEHaBiVl7u/Szr7kmOe/CfRG8IZl6UX+/66OqZeyJ12Q3m2 +# fe7ZWnpWT5sVp2sJmiuGb3atFXBWKcwNumNuy4JecjQE+7NF8rfIv94NxbBV/WSM +# pKf6Yv9OgzkjY1nRdIS1FBHa88RR55+7Ikh4FIGPBTAibiCEJMc79+b8cdsQGOo4 +# ymgbKjGeoRNjtegZ7XE/3TUywBBFMf8NfcjF8REs/HIl7u2RHwRaUTJdAgMBAAGj +# ggJzMIICbzA8BgkrBgEEAYI3FQcELzAtBiUrBgEEAYI3FQiG7sUghM++I4HxhQSF +# hqV1htyhDXuG5sF2wOlDAgFkAgEIMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA4GA1Ud +# DwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBsGCSsGAQQBgjcVCgQOMAwwCgYIKwYB +# BQUHAwMwHQYDVR0OBBYEFOlnnQDHNUpYoPqECFP6JAqGDFM6MB8GA1UdIwQYMBaA +# FICT0Mhz5MfqMIi7Xax90DRKYJLSMIHUBgNVHR8EgcwwgckwgcaggcOggcCGgb1s +# ZGFwOi8vL0NOPUhPVENBS0VYLUNBLENOPUhvdENha2VYLENOPUNEUCxDTj1QdWJs +# aWMlMjBLZXklMjBTZXJ2aWNlcyxDTj1TZXJ2aWNlcyxDTj1Db25maWd1cmF0aW9u +# LERDPU5vbkV4aXN0ZW50RG9tYWluLERDPWNvbT9jZXJ0aWZpY2F0ZVJldm9jYXRp +# b25MaXN0P2Jhc2U/b2JqZWN0Q2xhc3M9Y1JMRGlzdHJpYnV0aW9uUG9pbnQwgccG +# CCsGAQUFBwEBBIG6MIG3MIG0BggrBgEFBQcwAoaBp2xkYXA6Ly8vQ049SE9UQ0FL +# RVgtQ0EsQ049QUlBLENOPVB1YmxpYyUyMEtleSUyMFNlcnZpY2VzLENOPVNlcnZp +# Y2VzLENOPUNvbmZpZ3VyYXRpb24sREM9Tm9uRXhpc3RlbnREb21haW4sREM9Y29t +# P2NBQ2VydGlmaWNhdGU/YmFzZT9vYmplY3RDbGFzcz1jZXJ0aWZpY2F0aW9uQXV0 +# aG9yaXR5MA0GCSqGSIb3DQEBDQUAA4ICAQA7JI76Ixy113wNjiJmJmPKfnn7brVI +# IyA3ZudXCheqWTYPyYnwzhCSzKJLejGNAsMlXwoYgXQBBmMiSI4Zv4UhTNc4Umqx +# pZSpqV+3FRFQHOG/X6NMHuFa2z7T2pdj+QJuH5TgPayKAJc+Kbg4C7edL6YoePRu +# HoEhoRffiabEP/yDtZWMa6WFqBsfgiLMlo7DfuhRJ0eRqvJ6+czOVU2bxvESMQVo +# bvFTNDlEcUzBM7QxbnsDyGpoJZTx6M3cUkEazuliPAw3IW1vJn8SR1jFBukKcjWn +# aau+/BE9w77GFz1RbIfH3hJ/CUA0wCavxWcbAHz1YoPTAz6EKjIc5PcHpDO+n8Fh +# t3ULwVjWPMoZzU589IXi+2Ol0IUWAdoQJr/Llhub3SNKZ3LlMUPNt+tXAs/vcUl0 +# 7+Dp5FpUARE2gMYA/XxfU9T6Q3pX3/NRP/ojO9m0JrKv/KMc9sCGmV9sDygCOosU +# 5yGS4Ze/DJw6QR7xT9lMiWsfgL96Qcw4lfu1+5iLr0dnDFsGowGTKPGI0EvzK7H+ +# DuFRg+Fyhn40dOUl8fVDqYHuZJRoWJxCsyobVkrX4rA6xUTswl7xYPYWz88WZDoY +# gI8AwuRkzJyUEA07IYtsbFCYrcUzIHME4uf8jsJhCmb0va1G2WrWuyasv3K/G8Nn +# f60MsDbDH1mLtzGCAxgwggMUAgEBMGYwTzETMBEGCgmSJomT8ixkARkWA2NvbTEi +# MCAGCgmSJomT8ixkARkWEkhPVENBS0VYLUNBLURvbWFpbjEUMBIGA1UEAxMLSE9U +# Q0FLRVgtQ0ECEx4AAAAEjzQsNDP/rxMAAAAAAAQwDQYJYIZIAWUDBAIBBQCggYQw +# GAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGC +# NwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQx +# IgQgS0mcNGKXSm0P1nxnpnvoAF1x3kOk3W28WWGCLuwMYrowDQYJKoZIhvcNAQEB +# BQAEggIAfQL2KP/m4RE6aKeSMwHneQxhtvXywu5Bux3TDPEbOyHmI1YvgWmmYiUU +# QvU/1h2DVBnVCQWRZD7aKOYSh97GFDUChthF7NS3hNbMy4hXboYPeyKFtsCaq8m8 +# cAu9hTq6xM0N4QVaI5Yk6V7qrIZZWrGk/f2McWo9m5KuqCdTpaqi2XNQ7IUSUwrZ +# 481l6EQn11U5a90fVEl6Jt0o4knHl8RtR67ONhIZrvitCdAJT0QPAYwbtkv0J11v +# B+Rd0genRiHlllaIyLeIQBLU5Baw9fIyTzFbTVvDC52AQL43n9GNe5ybewfkgqmc +# k2m0yPnrPP2VNSdK/OjexN9ABetOlnhDfozyvEhSmXtrXFAQAzSprW+1av4Zl9tI +# A5yPYaXASvKIm5Fff8fLpfVMLz1d59fTCQ7yLl2R1cKO/qTo4NqNYMvASOpgIYTP +# XIGG85JBCJyzsU+aUVvxHoaWNPFQtqUKn3rMKb3pG41c5Xxd814j+GvYIW639i+P +# BdQAAy2noRmSgWZ+l35+Cmd/cz+inCUzqUcm8P9S1yIU/F+MCp8idma+o4LcYb6S +# WU5UkDfFJoQ/hM3oMXPbhpHI4jD2pCe7oVLzpvzqdFgP41SEZ68UbAMQk8OJzuBh +# 2uMkBk2ky/I6fIkSbPrtTLcfDjoaqm/4uyXCniDmfUo2vTHsyig= +# SIG # End signature block diff --git a/WDACConfig/WDACConfig Module Files/XMLOps/Close-EmptyXmlNodes_Semantic.psm1 b/WDACConfig/WDACConfig Module Files/XMLOps/Close-EmptyXmlNodes_Semantic.psm1 new file mode 100644 index 000000000..ea4fb3d18 --- /dev/null +++ b/WDACConfig/WDACConfig Module Files/XMLOps/Close-EmptyXmlNodes_Semantic.psm1 @@ -0,0 +1,162 @@ +Function Close-EmptyXmlNodes_Semantic { + <# + .SYNOPSIS + Closes all empty XML nodes and removes empty nodes that are neither base nodes nor 'ProductSigners' nodes + According to the CI Schema + + For example, it converts this + + + + + + + + + Or this + + + + + + + + to this + + + + + + .PARAMETER XmlFilePath + The path to the XML file to be processed + .INPUTS + System.IO.FileInfo + .OUTPUTS + System.Void + #> + [CmdletBinding()] + [OutputType([System.Void])] + param ( + [Parameter(Mandatory = $true)][System.IO.FileInfo]$XmlFilePath + ) + Begin { + # Importing the $PSDefaultParameterValues to the current session, prior to everything else + . "$ModuleRootPath\CoreExt\PSDefaultParameterValues.ps1" + + # Define the base node names that should not be removed even if empty + [System.String[]]$BaseNodeNames = @('SiPolicy', 'Rules', 'EKUs', 'FileRules', 'Signers', 'SigningScenarios', 'UpdatePolicySigners', 'CiSigners', 'HvciOptions', 'BasePolicyID', 'PolicyID') + + Function Close-EmptyNodesRecursively { + <# + .SYNOPSIS + Helper function to recursively close empty XML nodes + #> + param ( + [Parameter(Mandatory = $true)][System.Xml.XmlElement]$XmlNode + ) + + foreach ($ChildNode in $XmlNode.ChildNodes) { + if ($ChildNode -is [System.Xml.XmlElement]) { + # Recursively close empty child nodes + Close-EmptyNodesRecursively -XmlNode $ChildNode + + # Check if the node is empty + if (-not $ChildNode.HasChildNodes -and -not $ChildNode.HasAttributes) { + + # Check if it's a base node + if ($BaseNodeNames -contains $ChildNode.LocalName) { + # self-close it + $ChildNode.IsEmpty = $true + } + # Special case for ProductSigners because it's a required node inside each SigningScenario but can't be empty + elseif ($ChildNode.LocalName -eq 'ProductSigners') { + # self-close it + $ChildNode.IsEmpty = $true + } + else { + # If it's not a base node, remove it + [System.Void]$ChildNode.ParentNode.RemoveChild($ChildNode) + } + } + } + } + } + } + Process { + # Load the XML file + [System.Xml.XmlDocument]$Xml = Get-Content -Path $XmlFilePath + + # Start the recursive function from the root element + Close-EmptyNodesRecursively -XmlNode $Xml.DocumentElement + } + End { + # Save the changes back to the XML file + $Xml.Save($XmlFilePath) + } +} +Export-ModuleMember -Function 'Close-EmptyXmlNodes_Semantic' + +# SIG # Begin signature block +# MIILkgYJKoZIhvcNAQcCoIILgzCCC38CAQExDzANBglghkgBZQMEAgEFADB5Bgor +# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG +# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBy9d1yEPRxPjB+ +# 4VagnR0++Yhp8B1EuWdCzssltcuYcaCCB9AwggfMMIIFtKADAgECAhMeAAAABI80 +# LDQz/68TAAAAAAAEMA0GCSqGSIb3DQEBDQUAME8xEzARBgoJkiaJk/IsZAEZFgNj +# b20xIjAgBgoJkiaJk/IsZAEZFhJIT1RDQUtFWC1DQS1Eb21haW4xFDASBgNVBAMT +# C0hPVENBS0VYLUNBMCAXDTIzMTIyNzExMjkyOVoYDzIyMDgxMTEyMTEyOTI5WjB5 +# MQswCQYDVQQGEwJVSzEeMBwGA1UEAxMVSG90Q2FrZVggQ29kZSBTaWduaW5nMSMw +# IQYJKoZIhvcNAQkBFhRob3RjYWtleEBvdXRsb29rLmNvbTElMCMGCSqGSIb3DQEJ +# ARYWU3B5bmV0Z2lybEBvdXRsb29rLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIP +# ADCCAgoCggIBAKb1BJzTrpu1ERiwr7ivp0UuJ1GmNmmZ65eckLpGSF+2r22+7Tgm +# pEifj9NhPw0X60F9HhdSM+2XeuikmaNMvq8XRDUFoenv9P1ZU1wli5WTKHJ5ayDW +# k2NP22G9IPRnIpizkHkQnCwctx0AFJx1qvvd+EFlG6ihM0fKGG+DwMaFqsKCGh+M +# rb1bKKtY7UEnEVAsVi7KYGkkH+ukhyFUAdUbh/3ZjO0xWPYpkf/1ldvGes6pjK6P +# US2PHbe6ukiupqYYG3I5Ad0e20uQfZbz9vMSTiwslLhmsST0XAesEvi+SJYz2xAQ +# x2O4n/PxMRxZ3m5Q0WQxLTGFGjB2Bl+B+QPBzbpwb9JC77zgA8J2ncP2biEguSRJ +# e56Ezx6YpSoRv4d1jS3tpRL+ZFm8yv6We+hodE++0tLsfpUq42Guy3MrGQ2kTIRo +# 7TGLOLpayR8tYmnF0XEHaBiVl7u/Szr7kmOe/CfRG8IZl6UX+/66OqZeyJ12Q3m2 +# fe7ZWnpWT5sVp2sJmiuGb3atFXBWKcwNumNuy4JecjQE+7NF8rfIv94NxbBV/WSM +# pKf6Yv9OgzkjY1nRdIS1FBHa88RR55+7Ikh4FIGPBTAibiCEJMc79+b8cdsQGOo4 +# ymgbKjGeoRNjtegZ7XE/3TUywBBFMf8NfcjF8REs/HIl7u2RHwRaUTJdAgMBAAGj +# ggJzMIICbzA8BgkrBgEEAYI3FQcELzAtBiUrBgEEAYI3FQiG7sUghM++I4HxhQSF +# hqV1htyhDXuG5sF2wOlDAgFkAgEIMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA4GA1Ud +# DwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBsGCSsGAQQBgjcVCgQOMAwwCgYIKwYB +# BQUHAwMwHQYDVR0OBBYEFOlnnQDHNUpYoPqECFP6JAqGDFM6MB8GA1UdIwQYMBaA +# FICT0Mhz5MfqMIi7Xax90DRKYJLSMIHUBgNVHR8EgcwwgckwgcaggcOggcCGgb1s +# ZGFwOi8vL0NOPUhPVENBS0VYLUNBLENOPUhvdENha2VYLENOPUNEUCxDTj1QdWJs +# aWMlMjBLZXklMjBTZXJ2aWNlcyxDTj1TZXJ2aWNlcyxDTj1Db25maWd1cmF0aW9u +# LERDPU5vbkV4aXN0ZW50RG9tYWluLERDPWNvbT9jZXJ0aWZpY2F0ZVJldm9jYXRp +# b25MaXN0P2Jhc2U/b2JqZWN0Q2xhc3M9Y1JMRGlzdHJpYnV0aW9uUG9pbnQwgccG +# CCsGAQUFBwEBBIG6MIG3MIG0BggrBgEFBQcwAoaBp2xkYXA6Ly8vQ049SE9UQ0FL +# RVgtQ0EsQ049QUlBLENOPVB1YmxpYyUyMEtleSUyMFNlcnZpY2VzLENOPVNlcnZp +# Y2VzLENOPUNvbmZpZ3VyYXRpb24sREM9Tm9uRXhpc3RlbnREb21haW4sREM9Y29t +# P2NBQ2VydGlmaWNhdGU/YmFzZT9vYmplY3RDbGFzcz1jZXJ0aWZpY2F0aW9uQXV0 +# aG9yaXR5MA0GCSqGSIb3DQEBDQUAA4ICAQA7JI76Ixy113wNjiJmJmPKfnn7brVI +# IyA3ZudXCheqWTYPyYnwzhCSzKJLejGNAsMlXwoYgXQBBmMiSI4Zv4UhTNc4Umqx +# pZSpqV+3FRFQHOG/X6NMHuFa2z7T2pdj+QJuH5TgPayKAJc+Kbg4C7edL6YoePRu +# HoEhoRffiabEP/yDtZWMa6WFqBsfgiLMlo7DfuhRJ0eRqvJ6+czOVU2bxvESMQVo +# bvFTNDlEcUzBM7QxbnsDyGpoJZTx6M3cUkEazuliPAw3IW1vJn8SR1jFBukKcjWn +# aau+/BE9w77GFz1RbIfH3hJ/CUA0wCavxWcbAHz1YoPTAz6EKjIc5PcHpDO+n8Fh +# t3ULwVjWPMoZzU589IXi+2Ol0IUWAdoQJr/Llhub3SNKZ3LlMUPNt+tXAs/vcUl0 +# 7+Dp5FpUARE2gMYA/XxfU9T6Q3pX3/NRP/ojO9m0JrKv/KMc9sCGmV9sDygCOosU +# 5yGS4Ze/DJw6QR7xT9lMiWsfgL96Qcw4lfu1+5iLr0dnDFsGowGTKPGI0EvzK7H+ +# DuFRg+Fyhn40dOUl8fVDqYHuZJRoWJxCsyobVkrX4rA6xUTswl7xYPYWz88WZDoY +# gI8AwuRkzJyUEA07IYtsbFCYrcUzIHME4uf8jsJhCmb0va1G2WrWuyasv3K/G8Nn +# f60MsDbDH1mLtzGCAxgwggMUAgEBMGYwTzETMBEGCgmSJomT8ixkARkWA2NvbTEi +# MCAGCgmSJomT8ixkARkWEkhPVENBS0VYLUNBLURvbWFpbjEUMBIGA1UEAxMLSE9U +# Q0FLRVgtQ0ECEx4AAAAEjzQsNDP/rxMAAAAAAAQwDQYJYIZIAWUDBAIBBQCggYQw +# GAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGC +# NwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQx +# IgQglOsb2MUE+Q4pKWCoArxAXcQDcDW9WV2k8AzHnKEtY98wDQYJKoZIhvcNAQEB +# BQAEggIACyomIKprnazACqVF7xLGt2qJjpyOyHDDeVUqmTj2IRIpMKFEg17XsIMA +# xWjyPGvTnn8t+k1zqhBMviWkGJMwrmBuFHUX3FKjOMIAgoci3zCIr2p5nbxyVhZF +# P2ahBr7iyw3KxwkU+J71tFqYZkJGb0ZQ1YZw/piVFsTwL6P2Ri1HdyK9Migipooy +# +/ubp3NrWlJYvFsgzTeNE78j8n320l3OVoWWMh+uj+uOd3ZGyRdlkNxUwY2KBR3v +# +kBH/6bf/m6P+CGGgJQM8s3YAqybg9ys94pwyZx8fvgsYJO2nKUU+KtoxV0wxpH2 +# v1m1SmAXpY0buru2DFy6rrbL0XQ0bpeqYrfSxd5fVJzcZIcGEiqJBJ50kyfBFvmV +# JFOh1OntKtr5Fns53eJdl8fVq3j74EZX+97xpCQv9nATt2ktBr+zYaAlnpk1svUH +# 05DY6mrTpJpsV1T360u3zZncKr5vKSrJjP+vbfgg1rE60ydZKQzm82y7MWCmmsjw +# jtOboRvqqo/fDAfVYXOGe36Aa2F41Ow4vqPi4RQnBwjEGq8XqzNtSfAGihUcL/sC +# VOru2AYVBd0mLuAwmkHDl7TCfpAJFrMU/DkH3FJOC3+Mpg3r4vYAHP1mLKdKnFmN +# z5i5nxV9EODb5LW3AsdGhL+6n6cVJajV3M0NVp9lF+2Q1gsM1/8= +# SIG # End signature block diff --git a/WDACConfig/WDACConfig Module Files/XMLOps/Compare-CorrelatedData.psm1 b/WDACConfig/WDACConfig Module Files/XMLOps/Compare-CorrelatedData.psm1 new file mode 100644 index 000000000..207f24980 --- /dev/null +++ b/WDACConfig/WDACConfig Module Files/XMLOps/Compare-CorrelatedData.psm1 @@ -0,0 +1,318 @@ +Function Compare-CorrelatedData { + <# + .SYNOPSIS + Finds the correlated events in the new CSV data and groups them together based on the EtwActivityId + Ensures that each Audit or Blocked event has its correlated Signing information events grouped together as nested HashTables + + The correlated data of each log is unique based on 4 main properties, Publisher TBS and name, Issuer TBS and name + + The output of this function does not contain duplicates. Neither duplicate files nor duplicate signer information for each file. + + If 2 logs for the same file exist and one of them contains the signing information while the other one doesn't, the one with signing information is kept. + .PARAMETER OptimizedCSVData + The CSV data to be processed, they should be the output of the Optimize-MDECSVData function + .PARAMETER StagingArea + The path to the directory where the debug CSV file will be saved which are the outputs of this function + .PARAMETER Debug + A switch parameter to enable debugging actions such as exporting the correlated event data to a JSON file + .PARAMETER StartTime + A DateTime object that specifies the start time of the logs to be processed. If this parameter is not specified, all logs will be processed. + .PARAMETER PolicyNamesToFilter + An array of strings that specifies the policy names to filter the logs by. If this parameter is not specified, all logs will be processed. + .PARAMETER LogType + A string that specifies the type of logs to process. The only valid values are 'Audit' and 'Blocked' + .INPUTS + System.Collections.Hashtable[] + System.DateTime + System.String[] + System.String + .OUTPUTS + System.Collections.Hashtable + #> + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + Param ( + [Parameter(Mandatory = $true)][System.Collections.Hashtable[]]$OptimizedCSVData, + [Parameter(Mandatory = $true)][System.IO.DirectoryInfo]$StagingArea, + [Parameter(Mandatory = $false)][System.DateTime]$StartTime, + [AllowNull()] + [Parameter(Mandatory = $false)][System.String[]]$PolicyNamesToFilter, + [ValidateSet('Audit', 'Blocked')] + [Parameter(Mandatory = $true)][System.String]$LogType + ) + + Begin { + # Importing the $PSDefaultParameterValues to the current session, prior to everything else + . "$ModuleRootPath\CoreExt\PSDefaultParameterValues.ps1" + + # Detecting if Debug switch is used + $PSBoundParameters.Debug.IsPresent ? ([System.Boolean]$Debug = $true) : ([System.Boolean]$Debug = $false) | Out-Null + + # Group the events based on the EtwActivityId, which is the unique identifier for each group of correlated events + [Microsoft.PowerShell.Commands.GroupInfo[]]$GroupedEvents = $OptimizedCSVData | Group-Object -Property EtwActivityId + + Write-Verbose -Message "Compare-CorrelatedData: Total number of groups: $($GroupedEvents.Count)" + + # Create a collection to store the packages of logs to return at the end + [System.Collections.Hashtable]$EventPackageCollections = @{} + } + + Process { + + # Loop over each group of logs + Foreach ($RawLogGroup in $GroupedEvents) { + + # Store the group data in a HashTable array + [System.Collections.Hashtable[]]$GroupData = $RawLogGroup.Group + + if ($StartTime) { + try { + # Try to prase the TimeStamp string as DateTime type + [System.DateTime]$CurrentEventTimeStamp = [System.DateTime]::Parse((($GroupData.Timestamp) | Select-Object -First 1)) + + # If the current log's TimeStamp is older than the time frame specified by the user then skip this iteration/log completely + if ($CurrentEventTimeStamp -lt $StartTime ) { + Continue + } + } + Catch { + Write-Verbose -Message "Event Timestamp for the file '$($GroupData.FileName)' was invalid" + } + } + + # Detect the Audit events only if the LogType parameter is set to 'Audit' + if ($LogType -eq 'Audit') { + + # Process Audit events for Code Integrity and AppLocker + if (($GroupData.ActionType -contains 'AppControlCodeIntegrityPolicyAudited') -or ($GroupData.ActionType -contains 'AppControlCIScriptAudited')) { + + # Create a temporary HashTable to store the main event, its correlated events and its type + [System.Collections.Hashtable]$TempAuditHashTable = @{ + CorrelatedEventsData = @{} + Type = 'Audit' + SignatureStatus = '' + } + + # Finding the main Audit event in the group + # Only selecting the first event because when multiple Audit policies of the same type are deployed on the system, they same event is generated for each of them + [System.Collections.Hashtable]$AuditTemp = $GroupData | + Where-Object -FilterScript { $_['ActionType'] -in ('AppControlCodeIntegrityPolicyAudited', 'AppControlCIScriptAudited') } | Select-Object -First 1 + + # If the user provided policy names to filter the logs by + if ($null -ne $PolicyNamesToFilter) { + # Skip this iteration if the policy name of the current log is not in the list of policy names to filter by + if ($AuditTemp.PolicyName -notin $PolicyNamesToFilter) { + Continue + } + } + + # Generating a unique key for the hashtable based on file's properties + [System.String]$UniqueAuditMainEventDataKey = $AuditTemp.FileName + '|' + $AuditTemp.SHA256 + '|' + $AuditTemp.SHA1 + '|' + $AuditTemp.FileVersion + + # Adding the main event to the temporary HashTable, each key/value pair + foreach ($Data in $AuditTemp.GetEnumerator()) { + $TempAuditHashTable[$Data.Key] = $Data.Value + } + + # Looping over the signer infos and adding the unique publisher/issuer pairs to the correlated events data + foreach ($SignerInfo in ($GroupData | Where-Object -FilterScript { $_.ActionType -eq 'AppControlCodeIntegritySigningInformation' })) { + + # If the PublisherTBSHash or IssuerTBSHash is null, skip this iteration, usually in these situations the Issuer name and Publisher names are set to 'unknown' + if (($null -eq $SignerInfo.PublisherTBSHash) -or ($null -eq $SignerInfo.IssuerTBSHash)) { + Continue + } + + [System.String]$UniqueAuditSignerKey = $SignerInfo.PublisherTBSHash + '|' + + $SignerInfo.PublisherName + '|' + + $SignerInfo.IssuerName + '|' + + $SignerInfo.IssuerTBSHash + + if (-NOT $TempAuditHashTable['CorrelatedEventsData'].Contains($UniqueAuditSignerKey)) { + $TempAuditHashTable['CorrelatedEventsData'][$UniqueAuditSignerKey] = $SignerInfo + } + } + + # Determining whether this log package is signed or unsigned + $TempAuditHashTable['SignatureStatus'] = $TempAuditHashTable.CorrelatedEventsData.Count -eq 0 ? 'Unsigned' : 'Signed' + + # Check see if the main hashtable already contains the same file (key) + if ($EventPackageCollections.ContainsKey($UniqueAuditMainEventDataKey)) { + + # If it does, check if the current log is signed and the main log is unsigned + if (($EventPackageCollections[$UniqueAuditMainEventDataKey]['SignatureStatus'] -eq 'Unsigned') -and ($TempAuditHashTable['SignatureStatus'] -eq 'Signed')) { + + Write-Debug -Message "The unsigned log of the file $($TempAuditHashTable['FileName']) is being replaced with its signed log." + + # Remove the Unsigned log from the main HashTable + $EventPackageCollections.Remove($UniqueAuditMainEventDataKey) + + # Add the current Audit event with a unique identifiable key to the main HashTable + # This way we are replacing the Unsigned log with the Signed log that has more data, for the same file, providing the ability to create signature based rules for that file instead of hash based rule + $EventPackageCollections[$UniqueAuditMainEventDataKey] += $TempAuditHashTable + } + } + else { + # Add the current Audit event with a unique identifiable key to the main HashTable + $EventPackageCollections[$UniqueAuditMainEventDataKey] += $TempAuditHashTable + } + } + } + + # Detect the blocked events only if the LogType parameter is set to 'Blocked' + if ($LogType -eq 'Blocked') { + + # Process Blocked events for Code Integrity and AppLocker + if (($GroupData.ActionType -contains 'AppControlCodeIntegrityPolicyBlocked') -or ($GroupData.ActionType -contains 'AppControlCIScriptBlocked')) { + + # Create a temporary HashTable to store the main event, its correlated events and its type + [System.Collections.Hashtable]$TempBlockedHashTable = @{ + CorrelatedEventsData = @{} + Type = 'Blocked' + SignatureStatus = '' + } + + # Finding the main block event in the group + # Only selecting the first event because when multiple enforced policies of the same type are deployed on the system, they same event might be generated for each of them + [System.Collections.Hashtable]$BlockedTemp = $GroupData | + Where-Object -FilterScript { $_['ActionType'] -in ('AppControlCodeIntegrityPolicyBlocked', 'AppControlCIScriptBlocked') } | Select-Object -First 1 + + # If the user provided policy names to filter the logs by + if ($null -ne $PolicyNamesToFilter) { + # Skip this iteration if the policy name of the current log is not in the list of policy names to filter by + if ($BlockedTemp.PolicyName -notin $PolicyNamesToFilter) { + Continue + } + } + + # Generating a unique key for the hashtable based on file's properties + [System.String]$UniqueBlockedMainEventDataKey = $BlockedTemp.FileName + '|' + $BlockedTemp.SHA256 + '|' + $BlockedTemp.SHA1 + '|' + $BlockedTemp.FileVersion + + # Adding the main event to the temporary HashTable, each key/value pair + foreach ($Data in $BlockedTemp.GetEnumerator()) { + $TempBlockedHashTable[$Data.Key] = $Data.Value + } + + # Looping over the signer infos and adding the unique publisher/issuer pairs to the correlated events data + foreach ($SignerInfo in ($GroupData | Where-Object -FilterScript { $_.ActionType -eq 'AppControlCodeIntegritySigningInformation' })) { + + # If the PublisherTBSHash or IssuerTBSHash is null, skip this iteration, usually in these situations the Issuer name and Publisher names are set to 'unknown' + if (($null -eq $SignerInfo.PublisherTBSHash) -or ($null -eq $SignerInfo.IssuerTBSHash)) { + Continue + } + + [System.String]$UniqueBlockedSignerKey = $SignerInfo.PublisherTBSHash + '|' + + $SignerInfo.PublisherName + '|' + + $SignerInfo.IssuerName + '|' + + $SignerInfo.IssuerTBSHash + + if (-NOT $TempBlockedHashTable['CorrelatedEventsData'].Contains($UniqueBlockedSignerKey)) { + $TempBlockedHashTable['CorrelatedEventsData'][$UniqueBlockedSignerKey] = $SignerInfo + } + } + + # Determining whether this log package is signed or unsigned + $TempBlockedHashTable['SignatureStatus'] = $TempBlockedHashTable.CorrelatedEventsData.Count -eq 0 ? 'Unsigned' : 'Signed' + + # Check see if the main hashtable already contains the same file (key) + if ($EventPackageCollections.ContainsKey($UniqueBlockedMainEventDataKey)) { + + # If it does, check if the current log is signed and the main log is unsigned + if (($EventPackageCollections[$UniqueBlockedMainEventDataKey]['SignatureStatus'] -eq 'Unsigned') -and ($TempBlockedHashTable['SignatureStatus'] -eq 'Signed')) { + + Write-Debug -Message "The unsigned log of the file $($TempBlockedHashTable['FileName']) is being replaced with its signed log." + + # Remove the Unsigned log from the main HashTable + $EventPackageCollections.Remove($UniqueBlockedMainEventDataKey) + + # Add the current Audit event with a unique identifiable key to the main HashTable + # This way we are replacing the Unsigned log with the Signed log that has more data, for the same file, providing the ability to create signature based rules for that file instead of hash based rule + $EventPackageCollections[$UniqueBlockedMainEventDataKey] += $TempBlockedHashTable + } + } + else { + # Add the current Audit event with a unique identifiable key to the main HashTable + $EventPackageCollections[$UniqueBlockedMainEventDataKey] += $TempBlockedHashTable + } + } + } + } + } + + End { + + if ($Debug) { + Write-Verbose -Message 'Compare-CorrelatedData: Debug parameter was used, exporting data to Json...' + + # Outputs the entire data to a JSON file for debugging purposes with max details + $EventPackageCollections | ConvertTo-Json -Depth 100 | Set-Content -Path (Join-Path -Path $StagingArea -ChildPath 'Pass2.Json') -Force + } + + Return $EventPackageCollections + } +} +Export-ModuleMember -Function 'Compare-CorrelatedData' + +# SIG # Begin signature block +# MIILkgYJKoZIhvcNAQcCoIILgzCCC38CAQExDzANBglghkgBZQMEAgEFADB5Bgor +# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG +# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCC4U8how5q7ZEkr +# agzaF3Sfxg7+UTeCemF9BEQIH+diD6CCB9AwggfMMIIFtKADAgECAhMeAAAABI80 +# LDQz/68TAAAAAAAEMA0GCSqGSIb3DQEBDQUAME8xEzARBgoJkiaJk/IsZAEZFgNj +# b20xIjAgBgoJkiaJk/IsZAEZFhJIT1RDQUtFWC1DQS1Eb21haW4xFDASBgNVBAMT +# C0hPVENBS0VYLUNBMCAXDTIzMTIyNzExMjkyOVoYDzIyMDgxMTEyMTEyOTI5WjB5 +# MQswCQYDVQQGEwJVSzEeMBwGA1UEAxMVSG90Q2FrZVggQ29kZSBTaWduaW5nMSMw +# IQYJKoZIhvcNAQkBFhRob3RjYWtleEBvdXRsb29rLmNvbTElMCMGCSqGSIb3DQEJ +# ARYWU3B5bmV0Z2lybEBvdXRsb29rLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIP +# ADCCAgoCggIBAKb1BJzTrpu1ERiwr7ivp0UuJ1GmNmmZ65eckLpGSF+2r22+7Tgm +# pEifj9NhPw0X60F9HhdSM+2XeuikmaNMvq8XRDUFoenv9P1ZU1wli5WTKHJ5ayDW +# k2NP22G9IPRnIpizkHkQnCwctx0AFJx1qvvd+EFlG6ihM0fKGG+DwMaFqsKCGh+M +# rb1bKKtY7UEnEVAsVi7KYGkkH+ukhyFUAdUbh/3ZjO0xWPYpkf/1ldvGes6pjK6P +# US2PHbe6ukiupqYYG3I5Ad0e20uQfZbz9vMSTiwslLhmsST0XAesEvi+SJYz2xAQ +# x2O4n/PxMRxZ3m5Q0WQxLTGFGjB2Bl+B+QPBzbpwb9JC77zgA8J2ncP2biEguSRJ +# e56Ezx6YpSoRv4d1jS3tpRL+ZFm8yv6We+hodE++0tLsfpUq42Guy3MrGQ2kTIRo +# 7TGLOLpayR8tYmnF0XEHaBiVl7u/Szr7kmOe/CfRG8IZl6UX+/66OqZeyJ12Q3m2 +# fe7ZWnpWT5sVp2sJmiuGb3atFXBWKcwNumNuy4JecjQE+7NF8rfIv94NxbBV/WSM +# pKf6Yv9OgzkjY1nRdIS1FBHa88RR55+7Ikh4FIGPBTAibiCEJMc79+b8cdsQGOo4 +# ymgbKjGeoRNjtegZ7XE/3TUywBBFMf8NfcjF8REs/HIl7u2RHwRaUTJdAgMBAAGj +# ggJzMIICbzA8BgkrBgEEAYI3FQcELzAtBiUrBgEEAYI3FQiG7sUghM++I4HxhQSF +# hqV1htyhDXuG5sF2wOlDAgFkAgEIMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA4GA1Ud +# DwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBsGCSsGAQQBgjcVCgQOMAwwCgYIKwYB +# BQUHAwMwHQYDVR0OBBYEFOlnnQDHNUpYoPqECFP6JAqGDFM6MB8GA1UdIwQYMBaA +# FICT0Mhz5MfqMIi7Xax90DRKYJLSMIHUBgNVHR8EgcwwgckwgcaggcOggcCGgb1s +# ZGFwOi8vL0NOPUhPVENBS0VYLUNBLENOPUhvdENha2VYLENOPUNEUCxDTj1QdWJs +# aWMlMjBLZXklMjBTZXJ2aWNlcyxDTj1TZXJ2aWNlcyxDTj1Db25maWd1cmF0aW9u +# LERDPU5vbkV4aXN0ZW50RG9tYWluLERDPWNvbT9jZXJ0aWZpY2F0ZVJldm9jYXRp +# b25MaXN0P2Jhc2U/b2JqZWN0Q2xhc3M9Y1JMRGlzdHJpYnV0aW9uUG9pbnQwgccG +# CCsGAQUFBwEBBIG6MIG3MIG0BggrBgEFBQcwAoaBp2xkYXA6Ly8vQ049SE9UQ0FL +# RVgtQ0EsQ049QUlBLENOPVB1YmxpYyUyMEtleSUyMFNlcnZpY2VzLENOPVNlcnZp +# Y2VzLENOPUNvbmZpZ3VyYXRpb24sREM9Tm9uRXhpc3RlbnREb21haW4sREM9Y29t +# P2NBQ2VydGlmaWNhdGU/YmFzZT9vYmplY3RDbGFzcz1jZXJ0aWZpY2F0aW9uQXV0 +# aG9yaXR5MA0GCSqGSIb3DQEBDQUAA4ICAQA7JI76Ixy113wNjiJmJmPKfnn7brVI +# IyA3ZudXCheqWTYPyYnwzhCSzKJLejGNAsMlXwoYgXQBBmMiSI4Zv4UhTNc4Umqx +# pZSpqV+3FRFQHOG/X6NMHuFa2z7T2pdj+QJuH5TgPayKAJc+Kbg4C7edL6YoePRu +# HoEhoRffiabEP/yDtZWMa6WFqBsfgiLMlo7DfuhRJ0eRqvJ6+czOVU2bxvESMQVo +# bvFTNDlEcUzBM7QxbnsDyGpoJZTx6M3cUkEazuliPAw3IW1vJn8SR1jFBukKcjWn +# aau+/BE9w77GFz1RbIfH3hJ/CUA0wCavxWcbAHz1YoPTAz6EKjIc5PcHpDO+n8Fh +# t3ULwVjWPMoZzU589IXi+2Ol0IUWAdoQJr/Llhub3SNKZ3LlMUPNt+tXAs/vcUl0 +# 7+Dp5FpUARE2gMYA/XxfU9T6Q3pX3/NRP/ojO9m0JrKv/KMc9sCGmV9sDygCOosU +# 5yGS4Ze/DJw6QR7xT9lMiWsfgL96Qcw4lfu1+5iLr0dnDFsGowGTKPGI0EvzK7H+ +# DuFRg+Fyhn40dOUl8fVDqYHuZJRoWJxCsyobVkrX4rA6xUTswl7xYPYWz88WZDoY +# gI8AwuRkzJyUEA07IYtsbFCYrcUzIHME4uf8jsJhCmb0va1G2WrWuyasv3K/G8Nn +# f60MsDbDH1mLtzGCAxgwggMUAgEBMGYwTzETMBEGCgmSJomT8ixkARkWA2NvbTEi +# MCAGCgmSJomT8ixkARkWEkhPVENBS0VYLUNBLURvbWFpbjEUMBIGA1UEAxMLSE9U +# Q0FLRVgtQ0ECEx4AAAAEjzQsNDP/rxMAAAAAAAQwDQYJYIZIAWUDBAIBBQCggYQw +# GAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGC +# NwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQx +# IgQgL+mDBytP9xDjH4CUX3CFufyVJmEZqt2BOv63SEo59JkwDQYJKoZIhvcNAQEB +# BQAEggIAiTJwhm+hnrevcV21mYV5qqiKmKSOH0pCWjMA9CVQ/lsSgpVaiWimQPLb +# YbRg8JG6IZqVE6C7CY5GuAFVyMVF8C6PbZcAiLIxkdnxt3SRbIbplb3lcE+OsGvM +# EzMQfcfBdIMzOCX4ieif/uqq+zIp97SXlBQd9lmZeafbqNIpvr4E3uVtLURlxkLz +# guF0jaqjzn7PDItkiJGYwn5fmB4VP7hDPtILi8K2aEhx7x2d+ECMlZgU2s/hBfq1 +# dR3O5qQLVZ89qy8ObVWh77V4j6CFb8gXq4f8+YZntEgw9dXONwaepZgS7El5rB1M +# ZRhONWppQrscSSmZNfSPSAvF7bIZ/QsFLwziyLgKD5zbQJdE0M75i6VggFwYMckf +# z/ACki0tCLuMNf0y9622Y2MWKNfMHhEL4gCfAwnjuI67sOl1isg2OcvbMsrErAhK +# 0JS7yJPwvGoMhusTc5rEq32vAJjyL8odmD5Npk+ww2l6ZZFz2bi2TyMJFgbaq3kp +# Ixp8tcLWZH6VFZMlZsgH4sdC1ajwxcGOdynfpZdSJRL4flBZSph1+JhJuHBWtwkS +# nmDvekGrbtiQCCZrhUDs+ztPwTMACGuRVBDbiQw7iwT5/h2T4iaGxMjnhIFyIcr7 +# PJ5DSSdsxs5p0e8y7EA+XuKAw+2VWKqfTKOS4v/zwB8iGbj9WwY= +# SIG # End signature block diff --git a/WDACConfig/WDACConfig Module Files/XMLOps/Merge-Signers_Semantic.psm1 b/WDACConfig/WDACConfig Module Files/XMLOps/Merge-Signers_Semantic.psm1 new file mode 100644 index 000000000..be3c534e2 --- /dev/null +++ b/WDACConfig/WDACConfig Module Files/XMLOps/Merge-Signers_Semantic.psm1 @@ -0,0 +1,491 @@ +Function Merge-Signers_Semantic { + <# + .SYNOPSIS + Merges the FilePublisher and Publisher Signers in an XML file based on their TBS, Name, and CertPublisher values + For each FilePublisher signer, if two signers are found with the same TBS, Name, and CertPublisher, only one of them will be kept, and their FileAttribRefs are merged + For each Publisher signer, if two signers are found with the same TBS, Name, and CertPublisher, only one of them will be kept + + If two signers have the same TBS, Name, and CertPublisher but only one of them has FileAttribRefs, then they are not the same. This function makes the distinction between FilePublisher and Publisher signers. + This function makes the distinction between the Signers that are part of Signing Scenario 131 and the ones that are part of Signing Scenario 12. + So there are 4 different Signer types to consider. + + At the end, the XML file will have unique FilePublisher and Publisher signers for Signing Scenario 131 and 12, unique elements in the and + + Also, each Signer will have unique and valid FileAttribRef elements with IDs that point to an existing FileAttrib element in the node + .PARAMETER XmlFilePath + The path to the XML file to be modified + .INPUTS + System.IO.FileInfo + .OUTPUTS + System.Void + #> + [CmdletBinding()] + [OutputType([System.Void])] + Param( + [Parameter(Mandatory = $true)][System.IO.FileInfo]$XmlFilePath + ) + + Begin { + # Importing the $PSDefaultParameterValues to the current session, prior to everything else + . "$ModuleRootPath\CoreExt\PSDefaultParameterValues.ps1" + + # Load the XML file + [System.Xml.XmlDocument]$Xml = Get-Content -Path $XmlFilePath + } + + Process { + + # Define the namespace manager + [System.Xml.XmlNamespaceManager]$Ns = New-Object -TypeName System.Xml.XmlNamespaceManager -ArgumentList $Xml.NameTable + $Ns.AddNamespace('ns', 'urn:schemas-microsoft-com:sipolicy') + + # Get the User Mode Signing Scenario node + [System.Xml.XmlNodeList]$AllowedSigners12 = $Xml.SelectNodes('//ns:SigningScenarios/ns:SigningScenario[@Value="12"]/ns:ProductSigners/ns:AllowedSigners', $Ns) + + # Get the Kernel Mode Signing Scenario node + [System.Xml.XmlNodeList]$AllowedSigners131 = $Xml.SelectNodes('//ns:SigningScenarios/ns:SigningScenario[@Value="131"]/ns:ProductSigners/ns:AllowedSigners', $Ns) + + # Get the CiSigners node + [System.Xml.XmlNodeList]$CiSigners = $Xml.SelectNodes('//ns:CiSigners', $Ns) + + # Find all Signer nodes + [System.Xml.XmlNodeList]$SignerNodes = $Xml.SelectNodes('//ns:Signers/ns:Signer', $Ns) + + # Create a hashtable to track unique FilePublisher signers for Signing Scenario 131 - Signers that have at least one FileAttribRef + [System.Collections.Hashtable]$UniqueFilePublisherSigners131 = @{} + + # Create a hashtable to track unique Publisher signers for Signing Scenario 131 - Signers that have no FileAttribRef + [System.Collections.Hashtable]$UniquePublisherSigners131 = @{} + + # Create a hashtable to track unique FilePublisher signers for Signing Scenario 12 - Signers that have at least one FileAttribRef + [System.Collections.Hashtable]$UniqueFilePublisherSigners12 = @{} + + # Create a hashtable to track unique Publisher signers for Signing Scenario 12 - Signers that have no FileAttribRef + [System.Collections.Hashtable]$UniquePublisherSigners12 = @{} + + # XPath expression to select the Signing Scenario with value 131 + [System.String]$SigningScenario131XPath = '//ns:SigningScenarios/ns:SigningScenario[@Value="131"]' + + # Select the Signing Scenario with value 131 + [System.Xml.XmlNode]$SigningScenario131Node = $Xml.SelectSingleNode($SigningScenario131XPath, $Ns) + + # Find all of the elements in the node + [System.Xml.XmlNodeList]$FileRulesElements = $Xml.SelectNodes('//ns:FileRules/ns:FileAttrib', $Ns) + + $FileRulesValidID_HashSet = [System.Collections.Generic.HashSet[System.String]]$FileRulesElements.ID + + if ($SignerNodes.Count -eq 0) { + Write-Verbose -Message 'Merge-Signers: No Signer nodes found in the XML file. Exiting the function.' + Return + } + + # Iterate over each Signer node + foreach ($Signer in $SignerNodes) { + + # If the signer has FileAttribRefs, it is a FilePublisher signer + if ($Signer.SelectNodes('ns:FileAttribRef', $Ns).Count -gt 0) { + + # Making sure that each FilePublisher Signer has valid and unique FileAttribRef elements with IDs that point to an existing FileAttrib element in the node + $ContentToReplaceWith = $Signer.FileAttribRef | Where-Object -FilterScript { $FileRulesValidID_HashSet -contains $_.RuleID } | Group-Object -Property RuleID | ForEach-Object -Process { $_.Group[0] } + + [System.Int64]$Before = $Signer.FileAttribRef.count + + # Remove all FileAttribRef elements from the Signer, whether they are valid or not + $Signer.FileAttribRef | ForEach-Object -Process { + [System.Void]$_.ParentNode.RemoveChild($_) + } + + # Add the valid FileAttribRef elements back to the Signer + $ContentToReplaceWith | ForEach-Object -Process { + [System.Void]$Signer.AppendChild($Xml.ImportNode($_, $true)) + } + + [System.Int64]$After = $Signer.FileAttribRef.count + + if ($Before -ne $After) { + Write-Verbose -Message "Merge-Signers: Removed $($Before - $After) FileAttribRef elements from Signer with ID $($Signer.ID)." + } + + # Determine the Signing Scenario based on the AllowedSigners + $SigningScenario = $SigningScenario131Node.SelectSingleNode("./ns:ProductSigners/ns:AllowedSigners/ns:AllowedSigner[@SignerId='$($Signer.GetAttribute('ID'))']", $Ns) + + # If the signer is part of Signing Scenario 131 + if ($SigningScenario) { + + # Create a unique key for each FilePublisher signer based on TBS, Name, and CertPublisher + [System.String]$FilePublisherKey = $Signer.SelectSingleNode('ns:CertRoot', $Ns).GetAttribute('Value') + '|' + + $Signer.GetAttribute('Name') + '|' + + ($Signer.SelectSingleNode('ns:CertPublisher', $Ns) ? $Signer.SelectSingleNode('ns:CertPublisher', $Ns).GetAttribute('Value') : $Null) + + # If the signer is not in the hashtable, add it with its necessary details + if (-not $UniqueFilePublisherSigners131.ContainsKey($FilePublisherKey)) { + + # Create a temp hashtable to store the signer and its details + [System.Collections.Hashtable]$FilePublisherKeyTemp = @{} + $FilePublisherKeyTemp['Signer'] = @($Signer.Clone()) + $FilePublisherKeyTemp['AllowedSigner'] = $AllowedSigners131.SelectNodes("//ns:AllowedSigner[@SignerId='$($Signer.GetAttribute('ID'))']", $Ns) + + # Add the temp signer hashtable to the main hashtable + $UniqueFilePublisherSigners131[$FilePublisherKey] = $FilePublisherKeyTemp + } + + # If the signer is already in the hashtable + else { + + # Get the FileAttribRefs of the existing signer + [System.Xml.XmlNodeList]$FileAttribRefs = $Signer.SelectNodes('ns:FileAttribRef', $Ns) + + # add each of its FileAttribRefs to the existing signer + foreach ($FileAttribRef in $FileAttribRefs) { + [System.Void]$UniqueFilePublisherSigners131[$FilePublisherKey]['Signer'].AppendChild($Xml.ImportNode($FileAttribRef, $true)) + } + + Write-Verbose -Message "Merge-Signers: Merged FilePublisher signer for Signing Scenario 131 with IDs: $($UniqueFilePublisherSigners131[$FilePublisherKey].ID) and $($Signer.ID). Their FileAttribRefs are merged." + } + } + # If the signer is part of Signing Scenario 12 + else { + + # Create a unique key for each FilePublisher signer based on TBS, Name, and CertPublisher + [System.String]$FilePublisherKey = $Signer.SelectSingleNode('ns:CertRoot', $Ns).GetAttribute('Value') + '|' + + $Signer.GetAttribute('Name') + '|' + + ($Signer.SelectSingleNode('ns:CertPublisher', $Ns) ? $Signer.SelectSingleNode('ns:CertPublisher', $Ns).GetAttribute('Value') : $Null) + + # If the signer is not in the hashtable, add it with its FileAttribRefs + if (-not $UniqueFilePublisherSigners12.ContainsKey($FilePublisherKey)) { + + # Create a temp hashtable to store the signer and its details + [System.Collections.Hashtable]$FilePublisherKeyTemp = @{} + $FilePublisherKeyTemp['Signer'] = @($Signer.Clone()) + $FilePublisherKeyTemp['AllowedSigner'] = $AllowedSigners12.SelectNodes("//ns:AllowedSigner[@SignerId='$($Signer.GetAttribute('ID'))']", $Ns) + $FilePublisherKeyTemp['CiSigners'] = $CiSigners.SelectNodes("//ns:CiSigner[@SignerId='$($Signer.GetAttribute('ID'))']", $Ns) + + # Add the temp signer hashtable to the main hashtable + $UniqueFilePublisherSigners12[$FilePublisherKey] = $FilePublisherKeyTemp + } + + # If the signer is already in the hashtable + else { + + # Get the FileAttribRefs of the existing signer + [System.Xml.XmlNodeList]$FileAttribRefs = $Signer.SelectNodes('ns:FileAttribRef', $Ns) + + # add each of its FileAttribRefs to the existing signer + foreach ($FileAttribRef in $FileAttribRefs) { + [System.Void]$UniqueFilePublisherSigners12[$FilePublisherKey]['Signer'].AppendChild($Xml.ImportNode($FileAttribRef, $true)) + } + + Write-Verbose -Message "Merge-Signers: Merged FilePublisher signer for Signing Scenario 12 with IDs: $($UniqueFilePublisherSigners12[$FilePublisherKey].ID) and $($Signer.ID). Their FileAttribRefs are merged." + } + } + } + + # If the signer has no FileAttribRefs, it is a Publisher or PCA signer + elseif ($Signer.SelectNodes('ns:FileAttribRef', $Ns).Count -eq 0) { + + # Determine the Signing Scenario based on the AllowedSigners + $SigningScenario = $SigningScenario131Node.SelectSingleNode("./ns:ProductSigners/ns:AllowedSigners/ns:AllowedSigner[@SignerId='$($Signer.GetAttribute('ID'))']", $Ns) + + # If the signer is part of Signing Scenario 131 + if ($SigningScenario) { + + # Create a unique key for each Publisher signer based on TBS, Name, and CertPublisher + [System.String]$PublisherKey = $Signer.SelectSingleNode('ns:CertRoot', $Ns).GetAttribute('Value') + '|' + + $Signer.GetAttribute('Name') + '|' + + ($Signer.SelectSingleNode('ns:CertPublisher', $Ns) ? $Signer.SelectSingleNode('ns:CertPublisher', $Ns).GetAttribute('Value') : $Null) + + # If the signer is not in the hashtable, add it with its FileAttribRefs + if (-not $UniquePublisherSigners131.ContainsKey($PublisherKey)) { + + # Create a temp hashtable to store the signer and its details + [System.Collections.Hashtable]$PublisherKeyTemp = @{} + $PublisherKeyTemp['Signer'] = @($Signer.Clone()) + $PublisherKeyTemp['AllowedSigner'] = $AllowedSigners131.SelectNodes("//ns:AllowedSigner[@SignerId='$($Signer.GetAttribute('ID'))']", $Ns) + + # Add the temp signer hashtable to the main hashtable + $UniquePublisherSigners131[$PublisherKey] = $PublisherKeyTemp + } + else { + Write-Verbose -Message "Merge-Signers: Excluded Publisher signer for Signing Scenario 131 with ID: $($Signer.ID). Only one Publisher signer is allowed per TBS, Name, and CertPublisher." + } + } + # If the signer is part of Signing Scenario 12 + else { + + # Create a unique key for each Publisher signer based on TBS, Name, and CertPublisher + [System.String]$PublisherKey = $Signer.SelectSingleNode('ns:CertRoot', $Ns).GetAttribute('Value') + '|' + + $Signer.GetAttribute('Name') + '|' + + ($Signer.SelectSingleNode('ns:CertPublisher', $Ns) ? $Signer.SelectSingleNode('ns:CertPublisher', $Ns).GetAttribute('Value') : $Null) + + # If the signer is not in the hashtable, add it with its FileAttribRefs + if (-not $UniquePublisherSigners12.ContainsKey($PublisherKey)) { + + # Create a temp hashtable to store the signer and its details + [System.Collections.Hashtable]$PublisherKeyTemp = @{} + $PublisherKeyTemp['Signer'] = @($Signer.Clone()) + $PublisherKeyTemp['AllowedSigner'] = $AllowedSigners12.SelectNodes("//ns:AllowedSigner[@SignerId='$($Signer.GetAttribute('ID'))']", $Ns) + $PublisherKeyTemp['CiSigners'] = $CiSigners.SelectNodes("//ns:CiSigner[@SignerId='$($Signer.GetAttribute('ID'))']", $Ns) + + # Add the temp signer hashtable to the main hashtable + $UniquePublisherSigners12[$PublisherKey] = $PublisherKeyTemp + } + else { + Write-Verbose -Message "Merge-Signers: Excluded Publisher signer for Signing Scenario 12 with ID: $($Signer.ID). Only one Publisher signer is allowed per TBS, Name, and CertPublisher." + } + } + } + } + + $UniqueFilePublisherSigners12.Values | ForEach-Object -Process { + + # Create a unique ID for each signer + [System.String]$Guid = [System.Guid]::NewGuid().ToString().replace('-', '').ToUpper() + $Guid = "ID_SIGNER_A_$Guid" + + # Set the ID attribute of the Signer node to the unique ID + foreach ($Signer in $_['Signer']) { + $Signer.SetAttribute('ID', $Guid) + } + # Set the SignerId attribute of the AllowedSigner node to the unique ID + foreach ($AllowedSigner in $_['AllowedSigner']) { + $AllowedSigner.SetAttribute('SignerId', $Guid) + } + # Set the SignerId attribute of the CiSigner node to the unique ID + foreach ($CiSigner in $_['CiSigners']) { + $CiSigner.SetAttribute('SignerId', $Guid) + } + } + + $UniquePublisherSigners12.Values | ForEach-Object -Process { + + # Create a unique ID for each signer + [System.String]$Guid = [System.Guid]::NewGuid().ToString().replace('-', '').ToUpper() + $Guid = "ID_SIGNER_B_$Guid" + + # Set the ID attribute of the Signer node to the unique ID + foreach ($Signer in $_['Signer']) { + $Signer.SetAttribute('ID', $Guid) + } + # Set the SignerId attribute of the AllowedSigner node to the unique ID + foreach ($AllowedSigner in $_['AllowedSigner']) { + $AllowedSigner.SetAttribute('SignerId', $Guid) + } + # Set the SignerId attribute of the CiSigner node to the unique ID + foreach ($CiSigner in $_['CiSigners']) { + $CiSigner.SetAttribute('SignerId', $Guid) + } + } + + $UniquePublisherSigners131.Values | ForEach-Object -Process { + + # Create a unique ID for each signer + [System.String]$Guid = [System.Guid]::NewGuid().ToString().replace('-', '').ToUpper() + $Guid = "ID_SIGNER_B_$Guid" + + # Set the ID attribute of the Signer node to the unique ID + foreach ($Signer in $_['Signer']) { + $Signer.SetAttribute('ID', $Guid) + } + # Set the SignerId attribute of the AllowedSigner node to the unique ID + foreach ($AllowedSigner in $_['AllowedSigner']) { + $AllowedSigner.SetAttribute('SignerId', $Guid) + } + } + + $UniqueFilePublisherSigners131.Values | ForEach-Object -Process { + + # Create a unique ID for each signer + [System.String]$Guid = [System.Guid]::NewGuid().ToString().replace('-', '').ToUpper() + $Guid = "ID_SIGNER_A_$Guid" + + # Set the ID attribute of the Signer node to the unique ID + foreach ($Signer in $_['Signer']) { + $Signer.SetAttribute('ID', $Guid) + } + + # Set the SignerId attribute of the AllowedSigner node to the unique ID + foreach ($AllowedSigner in $_['AllowedSigner']) { + $AllowedSigner.SetAttribute('SignerId', $Guid) + } + } + + # Clear the existing Signers node from any type of Signer + [System.Xml.XmlElement]$SignersNode = $Xml.SelectSingleNode('//ns:Signers', $Ns) + + if ($null -ne $SignersNode) { + $SignersNode.RemoveAll() + } + + # Clear the existing AllowedSigners and CiSigners nodes from any type of Signer + [System.Xml.XmlElement]$AllowedSigners12ToClear = $Xml.SelectSingleNode('//ns:SigningScenarios/ns:SigningScenario[@Value="12"]/ns:ProductSigners/ns:AllowedSigners', $Ns) + [System.Xml.XmlElement]$AllowedSigners131ToClear = $Xml.SelectSingleNode('//ns:SigningScenarios/ns:SigningScenario[@Value="131"]/ns:ProductSigners/ns:AllowedSigners', $Ns) + [System.Xml.XmlElement]$CiSignersToClear = $Xml.SelectSingleNode('//ns:CiSigners', $Ns) + + # Making sure the nodes are not null meaning they are not empty, before attempting to remove all of their elements + if ($null -ne $AllowedSigners12ToClear) { + $AllowedSigners12ToClear.RemoveAll() + } + + if ($null -ne $AllowedSigners131ToClear) { + $AllowedSigners131ToClear.RemoveAll() + } + + if ($null -ne $CiSignersToClear) { + $CiSignersToClear.RemoveAll() + } + + # Repopulate the Signers, AllowedSigners and CiSigners nodes with the unique values + + # Add the unique FilePublisher signers for Signing Scenario 131 back to the Signers node + foreach ($Signer in $UniqueFilePublisherSigners131.Values) { + + # index 0 is used because otherwise it would throw an error about array not being able to be appended to the XML + # first index makes sure it is XmlElement type + [System.Void]$SignersNode.AppendChild($Signer['Signer'][0]) + + # A warning message to catch any edge cases that shouldn't happen + if (($Signer['AllowedSigner'].SignerId | Select-Object -Unique).count -gt 1) { + Write-Warning -Message "Multiple AllowedSigners found for FilePublisher signer for Signing Scenario 131 with ID $($Signer['Signer'][0].ID)." + } + + [System.Void]$AllowedSigners131.AppendChild($Signer['AllowedSigner'].Count -gt 1 ? $Signer['AllowedSigner'][0] : $Signer['AllowedSigner']) + } + + # Add the unique Publisher signers for Signing Scenario 131 back to the Signers node + foreach ($Signer in $UniquePublisherSigners131.Values) { + + # A warning message to catch any edge cases that shouldn't happen + if (($Signer['AllowedSigner'].SignerId | Select-Object -Unique).count -gt 1) { + Write-Warning -Message "Multiple AllowedSigners found for Publisher signer for Signing Scenario 131 with ID $($Signer['Signer'][0].ID)." + } + + # Add the element to the node + [System.Void]$SignersNode.AppendChild($Signer['Signer'][0]) + + # Add the element to the node + [System.Void]$AllowedSigners131.AppendChild($Signer['AllowedSigner'].Count -gt 1 ? $Signer['AllowedSigner'][0] : $Signer['AllowedSigner']) + } + + # Add the unique FilePublisher signers for Signing Scenario 12 back to the Signers node + foreach ($Signer in $UniqueFilePublisherSigners12.Values) { + + # A warning message to catch any edge cases that shouldn't happen + if (($Signer['AllowedSigner'].SignerId | Select-Object -Unique).count -gt 1) { + Write-Warning -Message "Multiple AllowedSigners found for FilePublisher signer for Signing Scenario 12 with ID $($Signer['Signer'][0].ID)." + } + + # A warning message to catch any edge cases that shouldn't happen + if (($Signer['CiSigners'].SignerId | Select-Object -Unique).count -gt 1) { + Write-Warning -Message "Multiple CiSigners found for FilePublisher signer for Signing Scenario 12 with ID $($Signer['Signer'][0].ID)." + } + + # Add the element to the node + [System.Void]$SignersNode.AppendChild($Signer['Signer'][0]) + + # Add the element to the node + [System.Void]$AllowedSigners12.AppendChild($Signer['AllowedSigner'].Count -gt 1 ? $Signer['AllowedSigner'][0] : $Signer['AllowedSigner']) + + if ($Null -ne $Signer['CiSigners']) { + # Add the element to the node + [System.Void]$CiSigners.AppendChild($Signer['CiSigners'].Count -gt 1 ? $Signer['CiSigners'][0] : $Signer['CiSigners']) + } + } + + # Add the unique Publisher signers for Signing Scenario 12 back to the Signers node + foreach ($Signer in $UniquePublisherSigners12.Values) { + + # A warning message to catch any edge cases that shouldn't happen + if (($Signer['AllowedSigner'].SignerId | Select-Object -Unique).count -gt 1) { + Write-Warning -Message "Multiple AllowedSigners found for Publisher signer for Signing Scenario 12 with ID $($Signer['Signer'][0].ID)." + } + + # A warning message to catch any edge cases that shouldn't happen + if (($Signer['CiSigners'].SignerId | Select-Object -Unique).count -gt 1) { + Write-Warning -Message "Multiple CiSigners found for Publisher signer for Signing Scenario 12 with ID $($Signer['Signer'][0].ID)." + } + + # Add the element to the node + [System.Void]$SignersNode.AppendChild($Signer['Signer'][0]) + + # Add the element to the node + [System.Void]$AllowedSigners12.AppendChild($Signer['AllowedSigner'].Count -gt 1 ? $Signer['AllowedSigner'][0] : $Signer['AllowedSigner']) + + if ($Null -ne $Signer['CiSigners']) { + # Add the element to the node + [System.Void]$CiSigners.AppendChild($Signer['CiSigners'].Count -gt 1 ? $Signer['CiSigners'][0] : $Signer['CiSigners']) + } + } + + } + + End { + # Save the changes back to the XML file + $Xml.Save($XmlFilePath) + } +} +Export-ModuleMember -Function 'Merge-Signers_Semantic' + +# SIG # Begin signature block +# MIILkgYJKoZIhvcNAQcCoIILgzCCC38CAQExDzANBglghkgBZQMEAgEFADB5Bgor +# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG +# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCA0g2E/bWZSWqR +# nDu6aV9KLFaH0IpspGoGAw0UQEQs0aCCB9AwggfMMIIFtKADAgECAhMeAAAABI80 +# LDQz/68TAAAAAAAEMA0GCSqGSIb3DQEBDQUAME8xEzARBgoJkiaJk/IsZAEZFgNj +# b20xIjAgBgoJkiaJk/IsZAEZFhJIT1RDQUtFWC1DQS1Eb21haW4xFDASBgNVBAMT +# C0hPVENBS0VYLUNBMCAXDTIzMTIyNzExMjkyOVoYDzIyMDgxMTEyMTEyOTI5WjB5 +# MQswCQYDVQQGEwJVSzEeMBwGA1UEAxMVSG90Q2FrZVggQ29kZSBTaWduaW5nMSMw +# IQYJKoZIhvcNAQkBFhRob3RjYWtleEBvdXRsb29rLmNvbTElMCMGCSqGSIb3DQEJ +# ARYWU3B5bmV0Z2lybEBvdXRsb29rLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIP +# ADCCAgoCggIBAKb1BJzTrpu1ERiwr7ivp0UuJ1GmNmmZ65eckLpGSF+2r22+7Tgm +# pEifj9NhPw0X60F9HhdSM+2XeuikmaNMvq8XRDUFoenv9P1ZU1wli5WTKHJ5ayDW +# k2NP22G9IPRnIpizkHkQnCwctx0AFJx1qvvd+EFlG6ihM0fKGG+DwMaFqsKCGh+M +# rb1bKKtY7UEnEVAsVi7KYGkkH+ukhyFUAdUbh/3ZjO0xWPYpkf/1ldvGes6pjK6P +# US2PHbe6ukiupqYYG3I5Ad0e20uQfZbz9vMSTiwslLhmsST0XAesEvi+SJYz2xAQ +# x2O4n/PxMRxZ3m5Q0WQxLTGFGjB2Bl+B+QPBzbpwb9JC77zgA8J2ncP2biEguSRJ +# e56Ezx6YpSoRv4d1jS3tpRL+ZFm8yv6We+hodE++0tLsfpUq42Guy3MrGQ2kTIRo +# 7TGLOLpayR8tYmnF0XEHaBiVl7u/Szr7kmOe/CfRG8IZl6UX+/66OqZeyJ12Q3m2 +# fe7ZWnpWT5sVp2sJmiuGb3atFXBWKcwNumNuy4JecjQE+7NF8rfIv94NxbBV/WSM +# pKf6Yv9OgzkjY1nRdIS1FBHa88RR55+7Ikh4FIGPBTAibiCEJMc79+b8cdsQGOo4 +# ymgbKjGeoRNjtegZ7XE/3TUywBBFMf8NfcjF8REs/HIl7u2RHwRaUTJdAgMBAAGj +# ggJzMIICbzA8BgkrBgEEAYI3FQcELzAtBiUrBgEEAYI3FQiG7sUghM++I4HxhQSF +# hqV1htyhDXuG5sF2wOlDAgFkAgEIMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA4GA1Ud +# DwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBsGCSsGAQQBgjcVCgQOMAwwCgYIKwYB +# BQUHAwMwHQYDVR0OBBYEFOlnnQDHNUpYoPqECFP6JAqGDFM6MB8GA1UdIwQYMBaA +# FICT0Mhz5MfqMIi7Xax90DRKYJLSMIHUBgNVHR8EgcwwgckwgcaggcOggcCGgb1s +# ZGFwOi8vL0NOPUhPVENBS0VYLUNBLENOPUhvdENha2VYLENOPUNEUCxDTj1QdWJs +# aWMlMjBLZXklMjBTZXJ2aWNlcyxDTj1TZXJ2aWNlcyxDTj1Db25maWd1cmF0aW9u +# LERDPU5vbkV4aXN0ZW50RG9tYWluLERDPWNvbT9jZXJ0aWZpY2F0ZVJldm9jYXRp +# b25MaXN0P2Jhc2U/b2JqZWN0Q2xhc3M9Y1JMRGlzdHJpYnV0aW9uUG9pbnQwgccG +# CCsGAQUFBwEBBIG6MIG3MIG0BggrBgEFBQcwAoaBp2xkYXA6Ly8vQ049SE9UQ0FL +# RVgtQ0EsQ049QUlBLENOPVB1YmxpYyUyMEtleSUyMFNlcnZpY2VzLENOPVNlcnZp +# Y2VzLENOPUNvbmZpZ3VyYXRpb24sREM9Tm9uRXhpc3RlbnREb21haW4sREM9Y29t +# P2NBQ2VydGlmaWNhdGU/YmFzZT9vYmplY3RDbGFzcz1jZXJ0aWZpY2F0aW9uQXV0 +# aG9yaXR5MA0GCSqGSIb3DQEBDQUAA4ICAQA7JI76Ixy113wNjiJmJmPKfnn7brVI +# IyA3ZudXCheqWTYPyYnwzhCSzKJLejGNAsMlXwoYgXQBBmMiSI4Zv4UhTNc4Umqx +# pZSpqV+3FRFQHOG/X6NMHuFa2z7T2pdj+QJuH5TgPayKAJc+Kbg4C7edL6YoePRu +# HoEhoRffiabEP/yDtZWMa6WFqBsfgiLMlo7DfuhRJ0eRqvJ6+czOVU2bxvESMQVo +# bvFTNDlEcUzBM7QxbnsDyGpoJZTx6M3cUkEazuliPAw3IW1vJn8SR1jFBukKcjWn +# aau+/BE9w77GFz1RbIfH3hJ/CUA0wCavxWcbAHz1YoPTAz6EKjIc5PcHpDO+n8Fh +# t3ULwVjWPMoZzU589IXi+2Ol0IUWAdoQJr/Llhub3SNKZ3LlMUPNt+tXAs/vcUl0 +# 7+Dp5FpUARE2gMYA/XxfU9T6Q3pX3/NRP/ojO9m0JrKv/KMc9sCGmV9sDygCOosU +# 5yGS4Ze/DJw6QR7xT9lMiWsfgL96Qcw4lfu1+5iLr0dnDFsGowGTKPGI0EvzK7H+ +# DuFRg+Fyhn40dOUl8fVDqYHuZJRoWJxCsyobVkrX4rA6xUTswl7xYPYWz88WZDoY +# gI8AwuRkzJyUEA07IYtsbFCYrcUzIHME4uf8jsJhCmb0va1G2WrWuyasv3K/G8Nn +# f60MsDbDH1mLtzGCAxgwggMUAgEBMGYwTzETMBEGCgmSJomT8ixkARkWA2NvbTEi +# MCAGCgmSJomT8ixkARkWEkhPVENBS0VYLUNBLURvbWFpbjEUMBIGA1UEAxMLSE9U +# Q0FLRVgtQ0ECEx4AAAAEjzQsNDP/rxMAAAAAAAQwDQYJYIZIAWUDBAIBBQCggYQw +# GAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGC +# NwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQx +# IgQg2lYv809+vqxv2OvkzBPf4jdx7scSVUsXDnSy5NVJWGcwDQYJKoZIhvcNAQEB +# BQAEggIAiA6OpujCZ7kLCC8F3eHZQC1qbjoX4Ay64hPyBThEpP16on3UwU/JjTzz +# Q1vc0onCqWt9AAYrUprJywnJwQramXI8MKgsoxlBq5Lq33ohkTv3f4PzuwOobqa6 +# RJFeQWW1Mc09yU56B02+gUvnN88h7WnCGfcyFNo4B8EByIjG/AMXeSNRP3xZG2lH +# yT1E/+voqtm1OQGESHxiNi5P+TxJbzXmL5PTUlWOy1Wwh9J6sWn/n8hM/LEZdbC0 +# hJROVCay3hSQQNgxZqOp3Z6MCZ11pztaekNkSPKb9mqr/Y8lNG7lOmq9BLj1SjsH +# fbBhgNZVP3dYW+tbNfNov8iWbNNG0IZpJsBqTWdVMhK9HjezYGRGudL3csgvWdjX +# aY5JIGiSTJEMpyf3E/XULmZWx4n5b/b/b9lSyi025EPNwpX50ouh5y1LEqoExaY0 +# TFOXn3PBdJDS5JskedFAR4rPF+63jUh991h8G+bUYva0WfsX9rlXwCU55igBd8xx +# mo1jT+Yr5JIlYdiIto0q0RD7f4ond2SjkrIoUTUTIFch1n6Pw/dkGHW2wL1ItTFj +# j0Ffdns/8/MBVV+LpJKsXsg6BcdzqXfkKnGkL+MJ8v5JzcmNPPbuVgWzADdyl/9N +# UMdivq9YPODBc1v/LvSQ0DpUBqR1kf/qM5rY5AEXecLghsusxDo= +# SIG # End signature block diff --git a/WDACConfig/WDACConfig Module Files/XMLOps/New-FilePublisherLevelRules.psm1 b/WDACConfig/WDACConfig Module Files/XMLOps/New-FilePublisherLevelRules.psm1 new file mode 100644 index 000000000..b4c4167b2 --- /dev/null +++ b/WDACConfig/WDACConfig Module Files/XMLOps/New-FilePublisherLevelRules.psm1 @@ -0,0 +1,247 @@ +Function New-FilePublisherLevelRules { + <# + .SYNOPSIS + Creates new FilePublisher level rules in an XML file + Each rules includes the FileAttribs, Signers, AllowedSigners, and CiSigners (depending on kernel/user mode) + .PARAMETER FilePublisherSigners + The FilePublisherSigners to be used for creating the rules, they are the output of the Build-SignerAndHashObjects function + .PARAMETER XmlFilePath + The path to the XML file to be modified + .INPUTS + PSCustomObject[] + System.IO.FileInfo + .OUTPUTS + System.Void + #> + [CmdletBinding()] + [OutputType([System.Void])] + Param ( + [Parameter(Mandatory = $true)][PSCustomObject[]]$FilePublisherSigners, + [Parameter(Mandatory = $true)][System.IO.FileInfo]$XmlFilePath + ) + + Begin { + # Importing the $PSDefaultParameterValues to the current session, prior to everything else + . "$ModuleRootPath\CoreExt\PSDefaultParameterValues.ps1" + + Write-Verbose -Message "New-FilePublisherLevelRules: There are $($FilePublisherSigners.Count) File Publisher Signers to be added to the XML file" + + # Load the XML file + [System.Xml.XmlDocument]$Xml = Get-Content -Path $XmlFilePath + + # Define the namespace manager + [System.Xml.XmlNamespaceManager]$Ns = New-Object -TypeName System.Xml.XmlNamespaceManager -ArgumentList $Xml.NameTable + $Ns.AddNamespace('ns', 'urn:schemas-microsoft-com:sipolicy') + + # Find the FileRules node + [System.Xml.XmlElement]$FileRulesNode = $Xml.SelectSingleNode('//ns:FileRules', $Ns) + + # Find the Signers Node + [System.Xml.XmlElement]$SignersNode = $Xml.SelectSingleNode('//ns:Signers', $Ns) + + # Find the SigningScenarios Nodes + # [System.Xml.XmlElement]$UMCI_SigningScenario = $Xml.SelectSingleNode('//ns:SigningScenarios/ns:SigningScenario[@Value="12"]', $Ns) + # [System.Xml.XmlElement]$KMCI_SigningScenario = $Xml.SelectSingleNode('//ns:SigningScenarios/ns:SigningScenario[@Value="131"]', $Ns) + + # Find the ProductSigners Nodes + [System.Xml.XmlElement]$UMCI_ProductSigners_Node = $Xml.SelectSingleNode('//ns:SigningScenarios/ns:SigningScenario[@Value="12"]/ns:ProductSigners', $Ns) + [System.Xml.XmlElement]$KMCI_ProductSigners_Node = $Xml.SelectSingleNode('//ns:SigningScenarios/ns:SigningScenario[@Value="131"]/ns:ProductSigners', $Ns) + + # Find the CiSigners Node + [System.Xml.XmlElement]$CiSignersNode = $Xml.SelectSingleNode('//ns:CiSigners', $Ns) + } + + Process { + + foreach ($FileAttrib in $FilePublisherSigners) { + + #Region Creating File Attributes + + [System.String]$GuidFileAttribID = [System.Guid]::NewGuid().ToString().replace('-', '').ToUpper() + + [System.String]$FileAttribID = "ID_FILEATTRIB_A_$GuidFileAttribID" + + [System.Xml.XmlElement]$NewFileAttribNode = $Xml.CreateElement('FileAttrib', $FileRulesNode.NamespaceURI) + $NewFileAttribNode.SetAttribute('ID', $FileAttribID) + $NewFileAttribNode.SetAttribute('FriendlyName', $FileAttrib.FileName) + + #Region Creating File Attributes with automatic fallback + if (-NOT ([System.String]::IsNullOrWhiteSpace($FileAttrib.OriginalFileName))) { + $NewFileAttribNode.SetAttribute('FileName', $FileAttrib.OriginalFileName) + } + elseif (-NOT ([System.String]::IsNullOrWhiteSpace($FileAttrib.InternalName))) { + $NewFileAttribNode.SetAttribute('InternalName', $FileAttrib.InternalName) + } + elseif (-NOT ([System.String]::IsNullOrWhiteSpace($FileAttrib.FileDescription))) { + $NewFileAttribNode.SetAttribute('FileDescription', $FileAttrib.FileDescription) + } + elseif (-NOT ([System.String]::IsNullOrWhiteSpace($FileAttrib.ProductName))) { + $NewFileAttribNode.SetAttribute('ProductName', $FileAttrib.ProductName) + } + #Endregion Creating File Attributes with automatic fallback + + $NewFileAttribNode.SetAttribute('MinimumFileVersion', $FileAttrib.FileVersion) + + # Add the new node to the FileRules node + [System.Void]$FileRulesNode.AppendChild($NewFileAttribNode) + + #Endregion Creating File Attributes + + #Region Creating Signers + + # Create signer for each certificate details in the FilePublisherSigners + # Some files are signed by multiple signers + foreach ($SignerData in $FileAttrib.CertificateDetails) { + + [System.String]$GuidSignerID = [System.Guid]::NewGuid().ToString().replace('-', '').ToUpper() + + [System.String]$SignerID = "ID_SIGNER_A_$GuidSignerID" + + # Create the new Signer element + [System.Xml.XmlElement]$NewSignerNode = $Xml.CreateElement('Signer', $SignersNode.NamespaceURI) + $NewSignerNode.SetAttribute('ID', $SignerID) + $NewSignerNode.SetAttribute('Name', $SignerData.IntermediateCertName) + + # Create the CertRoot element and add it to the Signer element + [System.Xml.XmlElement]$CertRootNode = $Xml.CreateElement('CertRoot', $SignersNode.NamespaceURI) + $CertRootNode.SetAttribute('Type', 'TBS') + $CertRootNode.SetAttribute('Value', $SignerData.IntermediateCertTBS) + [System.Void]$NewSignerNode.AppendChild($CertRootNode) + + # Create the CertPublisher element and add it to the Signer element + [System.Xml.XmlElement]$CertPublisherNode = $Xml.CreateElement('CertPublisher', $SignersNode.NamespaceURI) + $CertPublisherNode.SetAttribute('Value', $SignerData.LeafCertName) + [System.Void]$NewSignerNode.AppendChild($CertPublisherNode) + + # Create the FileAttribRef element and add it to the Signer element + [System.Xml.XmlElement]$FileAttribRefNode = $Xml.CreateElement('FileAttribRef', $SignersNode.NamespaceURI) + $FileAttribRefNode.SetAttribute('RuleID', $FileAttribID) + [System.Void]$NewSignerNode.AppendChild($FileAttribRefNode) + + # Add the new Signer element to the Signers node + [System.Void]$SignersNode.AppendChild($NewSignerNode) + + #Region Adding signer to the Signer Scenario and CiSigners + + # For User-Mode files + if ($FileAttrib.SiSigningScenario -eq '1') { + + # Check if AllowedSigners node exists, if not, create it + $UMCI_Temp_AllowedSignersNode = $UMCI_ProductSigners_Node.SelectSingleNode('ns:AllowedSigners', $Ns) + + if ($Null -eq $UMCI_Temp_AllowedSignersNode) { + + [System.Xml.XmlElement]$UMCI_Temp_AllowedSignersNode = $Xml.CreateElement('AllowedSigners', $Ns.LookupNamespace('ns')) + [System.Void]$UMCI_ProductSigners_Node.AppendChild($UMCI_Temp_AllowedSignersNode) + + } + + # Create Allowed Signers inside the -> -> + [System.Xml.XmlElement]$NewUMCIAllowedSignerNode = $Xml.CreateElement('AllowedSigner', $UMCI_Temp_AllowedSignersNode.NamespaceURI) + $NewUMCIAllowedSignerNode.SetAttribute('SignerId', $SignerID) + [System.Void]$UMCI_Temp_AllowedSignersNode.AppendChild($NewUMCIAllowedSignerNode) + + # Create a CI Signer for the User Mode Signer + [System.Xml.XmlElement]$NewCiSignerNode = $Xml.CreateElement('CiSigner', $CiSignersNode.NamespaceURI) + $NewCiSignerNode.SetAttribute('SignerId', $SignerID) + [System.Void]$CiSignersNode.AppendChild($NewCiSignerNode) + } + + # For Kernel-Mode files + elseif ($FileAttrib.SiSigningScenario -eq '0') { + + # Check if AllowedSigners node exists, if not, create it + $KMCI_Temp_AllowedSignersNode = $KMCI_ProductSigners_Node.SelectSingleNode('ns:AllowedSigners', $Ns) + + if ($Null -eq $KMCI_Temp_AllowedSignersNode) { + + [System.Xml.XmlElement]$KMCI_Temp_AllowedSignersNode = $Xml.CreateElement('AllowedSigners', $Ns.LookupNamespace('ns')) + [System.Void]$KMCI_ProductSigners_Node.AppendChild($KMCI_Temp_AllowedSignersNode) + + } + + # Create Allowed Signers inside the -> -> + [System.Xml.XmlElement]$NewKMCIAllowedSignerNode = $Xml.CreateElement('AllowedSigner', $KMCI_Temp_AllowedSignersNode.NamespaceURI) + $NewKMCIAllowedSignerNode.SetAttribute('SignerId', $SignerID) + [System.Void]$KMCI_Temp_AllowedSignersNode.AppendChild($NewKMCIAllowedSignerNode) + + # Kernel-Mode signers don't need CI Signers + } + + #Endregion Adding signer to the Signer Scenario and CiSigners + } + #Endregion Creating Signers + } + } + + End { + # Save the modified XML back to the file + $Xml.Save($XmlFilePath) + } +} +Export-ModuleMember -Function 'New-FilePublisherLevelRules' + +# SIG # Begin signature block +# MIILkgYJKoZIhvcNAQcCoIILgzCCC38CAQExDzANBglghkgBZQMEAgEFADB5Bgor +# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG +# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCq+qgSjY5yJZig +# cqlQEor76F6dQQHsX45Psc6ghFBKpaCCB9AwggfMMIIFtKADAgECAhMeAAAABI80 +# LDQz/68TAAAAAAAEMA0GCSqGSIb3DQEBDQUAME8xEzARBgoJkiaJk/IsZAEZFgNj +# b20xIjAgBgoJkiaJk/IsZAEZFhJIT1RDQUtFWC1DQS1Eb21haW4xFDASBgNVBAMT +# C0hPVENBS0VYLUNBMCAXDTIzMTIyNzExMjkyOVoYDzIyMDgxMTEyMTEyOTI5WjB5 +# MQswCQYDVQQGEwJVSzEeMBwGA1UEAxMVSG90Q2FrZVggQ29kZSBTaWduaW5nMSMw +# IQYJKoZIhvcNAQkBFhRob3RjYWtleEBvdXRsb29rLmNvbTElMCMGCSqGSIb3DQEJ +# ARYWU3B5bmV0Z2lybEBvdXRsb29rLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIP +# ADCCAgoCggIBAKb1BJzTrpu1ERiwr7ivp0UuJ1GmNmmZ65eckLpGSF+2r22+7Tgm +# pEifj9NhPw0X60F9HhdSM+2XeuikmaNMvq8XRDUFoenv9P1ZU1wli5WTKHJ5ayDW +# k2NP22G9IPRnIpizkHkQnCwctx0AFJx1qvvd+EFlG6ihM0fKGG+DwMaFqsKCGh+M +# rb1bKKtY7UEnEVAsVi7KYGkkH+ukhyFUAdUbh/3ZjO0xWPYpkf/1ldvGes6pjK6P +# US2PHbe6ukiupqYYG3I5Ad0e20uQfZbz9vMSTiwslLhmsST0XAesEvi+SJYz2xAQ +# x2O4n/PxMRxZ3m5Q0WQxLTGFGjB2Bl+B+QPBzbpwb9JC77zgA8J2ncP2biEguSRJ +# e56Ezx6YpSoRv4d1jS3tpRL+ZFm8yv6We+hodE++0tLsfpUq42Guy3MrGQ2kTIRo +# 7TGLOLpayR8tYmnF0XEHaBiVl7u/Szr7kmOe/CfRG8IZl6UX+/66OqZeyJ12Q3m2 +# fe7ZWnpWT5sVp2sJmiuGb3atFXBWKcwNumNuy4JecjQE+7NF8rfIv94NxbBV/WSM +# pKf6Yv9OgzkjY1nRdIS1FBHa88RR55+7Ikh4FIGPBTAibiCEJMc79+b8cdsQGOo4 +# ymgbKjGeoRNjtegZ7XE/3TUywBBFMf8NfcjF8REs/HIl7u2RHwRaUTJdAgMBAAGj +# ggJzMIICbzA8BgkrBgEEAYI3FQcELzAtBiUrBgEEAYI3FQiG7sUghM++I4HxhQSF +# hqV1htyhDXuG5sF2wOlDAgFkAgEIMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA4GA1Ud +# DwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBsGCSsGAQQBgjcVCgQOMAwwCgYIKwYB +# BQUHAwMwHQYDVR0OBBYEFOlnnQDHNUpYoPqECFP6JAqGDFM6MB8GA1UdIwQYMBaA +# FICT0Mhz5MfqMIi7Xax90DRKYJLSMIHUBgNVHR8EgcwwgckwgcaggcOggcCGgb1s +# ZGFwOi8vL0NOPUhPVENBS0VYLUNBLENOPUhvdENha2VYLENOPUNEUCxDTj1QdWJs +# aWMlMjBLZXklMjBTZXJ2aWNlcyxDTj1TZXJ2aWNlcyxDTj1Db25maWd1cmF0aW9u +# LERDPU5vbkV4aXN0ZW50RG9tYWluLERDPWNvbT9jZXJ0aWZpY2F0ZVJldm9jYXRp +# b25MaXN0P2Jhc2U/b2JqZWN0Q2xhc3M9Y1JMRGlzdHJpYnV0aW9uUG9pbnQwgccG +# CCsGAQUFBwEBBIG6MIG3MIG0BggrBgEFBQcwAoaBp2xkYXA6Ly8vQ049SE9UQ0FL +# RVgtQ0EsQ049QUlBLENOPVB1YmxpYyUyMEtleSUyMFNlcnZpY2VzLENOPVNlcnZp +# Y2VzLENOPUNvbmZpZ3VyYXRpb24sREM9Tm9uRXhpc3RlbnREb21haW4sREM9Y29t +# P2NBQ2VydGlmaWNhdGU/YmFzZT9vYmplY3RDbGFzcz1jZXJ0aWZpY2F0aW9uQXV0 +# aG9yaXR5MA0GCSqGSIb3DQEBDQUAA4ICAQA7JI76Ixy113wNjiJmJmPKfnn7brVI +# IyA3ZudXCheqWTYPyYnwzhCSzKJLejGNAsMlXwoYgXQBBmMiSI4Zv4UhTNc4Umqx +# pZSpqV+3FRFQHOG/X6NMHuFa2z7T2pdj+QJuH5TgPayKAJc+Kbg4C7edL6YoePRu +# HoEhoRffiabEP/yDtZWMa6WFqBsfgiLMlo7DfuhRJ0eRqvJ6+czOVU2bxvESMQVo +# bvFTNDlEcUzBM7QxbnsDyGpoJZTx6M3cUkEazuliPAw3IW1vJn8SR1jFBukKcjWn +# aau+/BE9w77GFz1RbIfH3hJ/CUA0wCavxWcbAHz1YoPTAz6EKjIc5PcHpDO+n8Fh +# t3ULwVjWPMoZzU589IXi+2Ol0IUWAdoQJr/Llhub3SNKZ3LlMUPNt+tXAs/vcUl0 +# 7+Dp5FpUARE2gMYA/XxfU9T6Q3pX3/NRP/ojO9m0JrKv/KMc9sCGmV9sDygCOosU +# 5yGS4Ze/DJw6QR7xT9lMiWsfgL96Qcw4lfu1+5iLr0dnDFsGowGTKPGI0EvzK7H+ +# DuFRg+Fyhn40dOUl8fVDqYHuZJRoWJxCsyobVkrX4rA6xUTswl7xYPYWz88WZDoY +# gI8AwuRkzJyUEA07IYtsbFCYrcUzIHME4uf8jsJhCmb0va1G2WrWuyasv3K/G8Nn +# f60MsDbDH1mLtzGCAxgwggMUAgEBMGYwTzETMBEGCgmSJomT8ixkARkWA2NvbTEi +# MCAGCgmSJomT8ixkARkWEkhPVENBS0VYLUNBLURvbWFpbjEUMBIGA1UEAxMLSE9U +# Q0FLRVgtQ0ECEx4AAAAEjzQsNDP/rxMAAAAAAAQwDQYJYIZIAWUDBAIBBQCggYQw +# GAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGC +# NwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQx +# IgQgiM+lrlGTzcP0X8EPHF4ANamz1hlMrf9rxDZZzSZYKYQwDQYJKoZIhvcNAQEB +# BQAEggIAWE05WarBs37TPKSGRFOdG+PlAf7croepwNgWnCdzFN7a9WSgujKz4Q2C +# MeY1MIZhbVRAE63HCrVfF1Il3ve8H8CdUONOu5lgPNzsqwQ3kSjh8WVH+XLQsIbz +# XH/dfBXXn/7Lzn7p+pVP+OQTWPxSw92B4bKCcS16yq/ngigWFi2OL40JhBD8SwSz +# 3+TI3Jd7O9r+Hwk1tFFHl9v49pWU88sNTACYfBtBz8Ncd9WT8yTBbC60W1Fw5Cw/ +# mGsggP0KGuLTGCv1b/2NWcZG3drW0yfJfwMRCODws2DMTj7twGtYeLAF78Ev6yQb +# Qun8OpP3obyvxC0RkvFVA7L355TfJ1gxKom+mRi+yg7w6mmOd/CqCBknhb4S907Q +# K3NUjzbqbXXqu3IMvoxx0MhJU1Ip22r02NUAI6ryppcJmux8oDq1pFRw9Wsz5BgK +# rW9sVQPfeKvhF/m5enW/YVnzvTBF6WUVEMIQsB0LcStwG+/7ZxYpjtKhHp3AOjC8 +# 0yJDm1Fgv1LZNXhrx8UQng9s/96E61lgmLB0VoG1FPng+y707Avrwn0B4aTGfKRw +# gThbsOdQDQso2zrq6M6xB+ege48O9DcyX9mIzzjZiJSEWG0AIVeS/1MvHR5ncMId +# GapYCQfIZj3CjHan32ZIQrrjR9oqNCptGJq1It6akAX3ut68tN8= +# SIG # End signature block diff --git a/WDACConfig/WDACConfig Module Files/XMLOps/New-HashLevelRules.psm1 b/WDACConfig/WDACConfig Module Files/XMLOps/New-HashLevelRules.psm1 new file mode 100644 index 000000000..5e152bbe7 --- /dev/null +++ b/WDACConfig/WDACConfig Module Files/XMLOps/New-HashLevelRules.psm1 @@ -0,0 +1,198 @@ +Function New-HashLevelRules { + <# + .SYNOPSIS + Creates new Hash level rules in an XML file + For each hash data, it creates 2 Hash rules, one for Authenticode SHA2-256 and one for SHA1 hash + It also adds the FileRulesRef for each hash to the ProductSigners node of the correct signing scenario (Kernel/User mode) + .PARAMETER Hashes + The Hashes to be used for creating the rules, they are the output of the Build-SignerAndHashObjects function + .PARAMETER XmlFilePath + The path to the XML file to be modified + .INPUTS + PSCustomObject[] + System.IO.FileInfo + .OUTPUTS + System.Void + #> + [CmdletBinding()] + [OutputType([System.Void])] + Param( + [Parameter(Mandatory = $true)][PSCustomObject[]]$Hashes, + [Parameter(Mandatory = $true)][System.IO.FileInfo]$XmlFilePath + ) + + Begin { + # Importing the $PSDefaultParameterValues to the current session, prior to everything else + . "$ModuleRootPath\CoreExt\PSDefaultParameterValues.ps1" + + Write-Verbose -Message "New-HashLevelRules: There are $($Hashes.Count) Hash rules to be added to the XML file" + + # Load the XML file + [System.Xml.XmlDocument]$Xml = Get-Content -Path $XmlFilePath + + # Define the namespace manager + [System.Xml.XmlNamespaceManager]$Ns = New-Object -TypeName System.Xml.XmlNamespaceManager -ArgumentList $Xml.NameTable + $Ns.AddNamespace('ns', 'urn:schemas-microsoft-com:sipolicy') + + # Find the ProductSigners Nodes + [System.Xml.XmlElement]$UMCI_ProductSigners_Node = $Xml.SelectSingleNode('//ns:SigningScenarios/ns:SigningScenario[@Value="12"]/ns:ProductSigners', $Ns) + [System.Xml.XmlElement]$KMCI_ProductSigners_Node = $Xml.SelectSingleNode('//ns:SigningScenarios/ns:SigningScenario[@Value="131"]/ns:ProductSigners', $Ns) + } + + Process { + + # Find the FileRules node + [System.Xml.XmlElement]$FileRulesNode = $Xml.SelectSingleNode('//ns:FileRules', $Ns) + + # Loop through each hash and create a new rule for it + Foreach ($Hash in $Hashes) { + + [System.String]$Guid = [System.Guid]::NewGuid().ToString().replace('-', '').ToUpper() + + # Create a unique ID for the rule + [System.String]$HashSHA256RuleID = "ID_ALLOW_A_$Guid" + [System.String]$HashSHA1RuleID = "ID_ALLOW_B_$Guid" + + # Create new Allow Hash rule for Authenticode SHA256D + [System.Xml.XmlElement]$NewAuth256HashNode = $Xml.CreateElement('Allow', $FileRulesNode.NamespaceURI) + $NewAuth256HashNode.SetAttribute('ID', $HashSHA256RuleID) + $NewAuth256HashNode.SetAttribute('FriendlyName', "$($Hash.FileName) Hash Sha256") + $NewAuth256HashNode.SetAttribute('Hash', $Hash.AuthenticodeSHA256) + # Add the new node to the FileRules node + [System.Void]$FileRulesNode.AppendChild($NewAuth256HashNode) + + # Create new Allow Hash rule for Authenticode SHA1 + [System.Xml.XmlElement]$NewAuth1HashNode = $Xml.CreateElement('Allow', $FileRulesNode.NamespaceURI) + $NewAuth1HashNode.SetAttribute('ID', $HashSHA1RuleID) + $NewAuth1HashNode.SetAttribute('FriendlyName', "$($Hash.FileName) Hash Sha1") + $NewAuth1HashNode.SetAttribute('Hash', $Hash.AuthenticodeSHA1) + # Add the new node to the FileRules node + [System.Void]$FileRulesNode.AppendChild($NewAuth1HashNode) + + # For User-Mode files + if ($Hash.SiSigningScenario -eq '1') { + + # Check if FileRulesRef node exists, if not, create it + $UMCI_Temp_FileRulesRefNode = $UMCI_ProductSigners_Node.SelectSingleNode('ns:FileRulesRef', $Ns) + + if ($Null -eq $UMCI_Temp_FileRulesRefNode) { + + [System.Xml.XmlElement]$UMCI_Temp_FileRulesRefNode = $Xml.CreateElement('FileRulesRef', $Ns.LookupNamespace('ns')) + [System.Void]$UMCI_ProductSigners_Node.AppendChild($UMCI_Temp_FileRulesRefNode) + + } + + # Create FileRuleRef for Authenticode SHA256 Hash inside the -> -> + [System.Xml.XmlElement]$NewUMCIFileRuleRefNode = $Xml.CreateElement('FileRuleRef', $UMCI_Temp_FileRulesRefNode.NamespaceURI) + $NewUMCIFileRuleRefNode.SetAttribute('RuleID', $HashSHA256RuleID) + [System.Void]$UMCI_Temp_FileRulesRefNode.AppendChild($NewUMCIFileRuleRefNode) + + # Create FileRuleRef for Authenticode SHA1 Hash inside the -> -> + [System.Xml.XmlElement]$NewUMCIFileRuleRefNode = $Xml.CreateElement('FileRuleRef', $UMCI_Temp_FileRulesRefNode.NamespaceURI) + $NewUMCIFileRuleRefNode.SetAttribute('RuleID', $HashSHA1RuleID) + [System.Void]$UMCI_Temp_FileRulesRefNode.AppendChild($NewUMCIFileRuleRefNode) + + } + + # For Kernel-Mode files + elseif ($Hash.SiSigningScenario -eq '0') { + + # Display a warning if a hash rule for a kernel-mode file is being created and the file is not an MSI + # Since MDE does not record the Signing information events (Id 8038) for MSI files so we must create Hash based rules for them + if (-NOT $Hash.FileName.EndsWith('.msi')) { + Write-Warning -Message "Creating Hash rule for Kernel-Mode file: $($Hash.FileName). Kernel-Mode file should be signed!" + } + + # Check if FileRulesRef node exists, if not, create it + $KMCI_Temp_FileRulesRefNode = $KMCI_ProductSigners_Node.SelectSingleNode('ns:FileRulesRef', $Ns) + + if ($Null -eq $KMCI_Temp_FileRulesRefNode) { + + [System.Xml.XmlElement]$KMCI_Temp_FileRulesRefNode = $Xml.CreateElement('FileRulesRef', $Ns.LookupNamespace('ns')) + [System.Void]$KMCI_ProductSigners_Node.AppendChild($KMCI_Temp_FileRulesRefNode) + } + + # Create FileRuleRef for Authenticode SHA256 Hash inside the -> -> + [System.Xml.XmlElement]$NewKMCIFileRuleRefNode = $Xml.CreateElement('FileRuleRef', $KMCI_Temp_FileRulesRefNode.NamespaceURI) + $NewKMCIFileRuleRefNode.SetAttribute('RuleID', $HashSHA256RuleID) + [System.Void]$KMCI_Temp_FileRulesRefNode.AppendChild($NewKMCIFileRuleRefNode) + + # Create FileRuleRef for Authenticode SHA1 Hash inside the -> -> + [System.Xml.XmlElement]$NewKMCIFileRuleRefNode = $Xml.CreateElement('FileRuleRef', $KMCI_Temp_FileRulesRefNode.NamespaceURI) + $NewKMCIFileRuleRefNode.SetAttribute('RuleID', $HashSHA1RuleID) + [System.Void]$KMCI_Temp_FileRulesRefNode.AppendChild($NewKMCIFileRuleRefNode) + } + } + + } + + End { + $Xml.Save($XmlFilePath) + } +} +Export-ModuleMember -Function 'New-HashLevelRules' + +# SIG # Begin signature block +# MIILkgYJKoZIhvcNAQcCoIILgzCCC38CAQExDzANBglghkgBZQMEAgEFADB5Bgor +# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG +# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDt+zZFVPdtiJ5n +# RXmlqwWBdVxLnAa2KTp2wE0xIojfN6CCB9AwggfMMIIFtKADAgECAhMeAAAABI80 +# LDQz/68TAAAAAAAEMA0GCSqGSIb3DQEBDQUAME8xEzARBgoJkiaJk/IsZAEZFgNj +# b20xIjAgBgoJkiaJk/IsZAEZFhJIT1RDQUtFWC1DQS1Eb21haW4xFDASBgNVBAMT +# C0hPVENBS0VYLUNBMCAXDTIzMTIyNzExMjkyOVoYDzIyMDgxMTEyMTEyOTI5WjB5 +# MQswCQYDVQQGEwJVSzEeMBwGA1UEAxMVSG90Q2FrZVggQ29kZSBTaWduaW5nMSMw +# IQYJKoZIhvcNAQkBFhRob3RjYWtleEBvdXRsb29rLmNvbTElMCMGCSqGSIb3DQEJ +# ARYWU3B5bmV0Z2lybEBvdXRsb29rLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIP +# ADCCAgoCggIBAKb1BJzTrpu1ERiwr7ivp0UuJ1GmNmmZ65eckLpGSF+2r22+7Tgm +# pEifj9NhPw0X60F9HhdSM+2XeuikmaNMvq8XRDUFoenv9P1ZU1wli5WTKHJ5ayDW +# k2NP22G9IPRnIpizkHkQnCwctx0AFJx1qvvd+EFlG6ihM0fKGG+DwMaFqsKCGh+M +# rb1bKKtY7UEnEVAsVi7KYGkkH+ukhyFUAdUbh/3ZjO0xWPYpkf/1ldvGes6pjK6P +# US2PHbe6ukiupqYYG3I5Ad0e20uQfZbz9vMSTiwslLhmsST0XAesEvi+SJYz2xAQ +# x2O4n/PxMRxZ3m5Q0WQxLTGFGjB2Bl+B+QPBzbpwb9JC77zgA8J2ncP2biEguSRJ +# e56Ezx6YpSoRv4d1jS3tpRL+ZFm8yv6We+hodE++0tLsfpUq42Guy3MrGQ2kTIRo +# 7TGLOLpayR8tYmnF0XEHaBiVl7u/Szr7kmOe/CfRG8IZl6UX+/66OqZeyJ12Q3m2 +# fe7ZWnpWT5sVp2sJmiuGb3atFXBWKcwNumNuy4JecjQE+7NF8rfIv94NxbBV/WSM +# pKf6Yv9OgzkjY1nRdIS1FBHa88RR55+7Ikh4FIGPBTAibiCEJMc79+b8cdsQGOo4 +# ymgbKjGeoRNjtegZ7XE/3TUywBBFMf8NfcjF8REs/HIl7u2RHwRaUTJdAgMBAAGj +# ggJzMIICbzA8BgkrBgEEAYI3FQcELzAtBiUrBgEEAYI3FQiG7sUghM++I4HxhQSF +# hqV1htyhDXuG5sF2wOlDAgFkAgEIMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA4GA1Ud +# DwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBsGCSsGAQQBgjcVCgQOMAwwCgYIKwYB +# BQUHAwMwHQYDVR0OBBYEFOlnnQDHNUpYoPqECFP6JAqGDFM6MB8GA1UdIwQYMBaA +# FICT0Mhz5MfqMIi7Xax90DRKYJLSMIHUBgNVHR8EgcwwgckwgcaggcOggcCGgb1s +# ZGFwOi8vL0NOPUhPVENBS0VYLUNBLENOPUhvdENha2VYLENOPUNEUCxDTj1QdWJs +# aWMlMjBLZXklMjBTZXJ2aWNlcyxDTj1TZXJ2aWNlcyxDTj1Db25maWd1cmF0aW9u +# LERDPU5vbkV4aXN0ZW50RG9tYWluLERDPWNvbT9jZXJ0aWZpY2F0ZVJldm9jYXRp +# b25MaXN0P2Jhc2U/b2JqZWN0Q2xhc3M9Y1JMRGlzdHJpYnV0aW9uUG9pbnQwgccG +# CCsGAQUFBwEBBIG6MIG3MIG0BggrBgEFBQcwAoaBp2xkYXA6Ly8vQ049SE9UQ0FL +# RVgtQ0EsQ049QUlBLENOPVB1YmxpYyUyMEtleSUyMFNlcnZpY2VzLENOPVNlcnZp +# Y2VzLENOPUNvbmZpZ3VyYXRpb24sREM9Tm9uRXhpc3RlbnREb21haW4sREM9Y29t +# P2NBQ2VydGlmaWNhdGU/YmFzZT9vYmplY3RDbGFzcz1jZXJ0aWZpY2F0aW9uQXV0 +# aG9yaXR5MA0GCSqGSIb3DQEBDQUAA4ICAQA7JI76Ixy113wNjiJmJmPKfnn7brVI +# IyA3ZudXCheqWTYPyYnwzhCSzKJLejGNAsMlXwoYgXQBBmMiSI4Zv4UhTNc4Umqx +# pZSpqV+3FRFQHOG/X6NMHuFa2z7T2pdj+QJuH5TgPayKAJc+Kbg4C7edL6YoePRu +# HoEhoRffiabEP/yDtZWMa6WFqBsfgiLMlo7DfuhRJ0eRqvJ6+czOVU2bxvESMQVo +# bvFTNDlEcUzBM7QxbnsDyGpoJZTx6M3cUkEazuliPAw3IW1vJn8SR1jFBukKcjWn +# aau+/BE9w77GFz1RbIfH3hJ/CUA0wCavxWcbAHz1YoPTAz6EKjIc5PcHpDO+n8Fh +# t3ULwVjWPMoZzU589IXi+2Ol0IUWAdoQJr/Llhub3SNKZ3LlMUPNt+tXAs/vcUl0 +# 7+Dp5FpUARE2gMYA/XxfU9T6Q3pX3/NRP/ojO9m0JrKv/KMc9sCGmV9sDygCOosU +# 5yGS4Ze/DJw6QR7xT9lMiWsfgL96Qcw4lfu1+5iLr0dnDFsGowGTKPGI0EvzK7H+ +# DuFRg+Fyhn40dOUl8fVDqYHuZJRoWJxCsyobVkrX4rA6xUTswl7xYPYWz88WZDoY +# gI8AwuRkzJyUEA07IYtsbFCYrcUzIHME4uf8jsJhCmb0va1G2WrWuyasv3K/G8Nn +# f60MsDbDH1mLtzGCAxgwggMUAgEBMGYwTzETMBEGCgmSJomT8ixkARkWA2NvbTEi +# MCAGCgmSJomT8ixkARkWEkhPVENBS0VYLUNBLURvbWFpbjEUMBIGA1UEAxMLSE9U +# Q0FLRVgtQ0ECEx4AAAAEjzQsNDP/rxMAAAAAAAQwDQYJYIZIAWUDBAIBBQCggYQw +# GAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGC +# NwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQx +# IgQgySMwm3DN7/QLo7E4tIHgbEefvPLHltFCS5fLe92arKEwDQYJKoZIhvcNAQEB +# BQAEggIABSwQyhvuJk4d8BfQXQJQXIJbTxYYjw0usU7wxqSgHgkNCCKhP0KiCz6c +# 6XaWqtZk6CfIUZbuAlbaRSlI72rIe+DtzPDE7f5QgvRaN0TKTiOVbiXndYuoLwAy +# yjdfWisLdAI4+9UNO0J3PjbSco80o6aVs4+7gp6U7GbhU3ZsXX1j4wZN00aZXegU +# UfUfNXMBYvzY996yr0oUT6X5a6iz32NhjRsT2XH7qSsMS/8uN4QsWGv2hCZtNN/g +# eQavEvra7iQ4mmSzuoVLMNbuhg0V4RHAt/heEizHTs7KAFN+/ag5Rw9p1xHbBEFs +# L8XjHMUlwyQQjYPB+g+l5d1WmjCgm+QQGVKojOSN5/boy/54IUfaCWbfwqdNOvZl +# Yqh2cS29aZTBAevwQLQMzUQ9tdCkT9s9YO8GsJWG2cWPBzMO43h9kqjIT6pD6+DQ +# mknttEeSFDuljL642SMxBovKX282KrGGw24dRjfIsqaYxhjBDaLJt/3JpuTG4K42 +# s/rRIJhWJUAtcufvxUu3Hjwo3NarB6931Zlh2VFNF4R0gZXttlbQMqpxNoeb8geG +# NuY935i5SG0xW8HBQH2+7dIrmSGxMkqcyMNJn6i8cF5G7uftJ3m3gfqyuFiz6pOM +# vnr4e0lUB8oGkrbX0aKfYkCGEyIHea/poYLL7JEdz0Qmq1ozI5Q= +# SIG # End signature block diff --git a/WDACConfig/WDACConfig Module Files/XMLOps/New-PublisherLevelRules.psm1 b/WDACConfig/WDACConfig Module Files/XMLOps/New-PublisherLevelRules.psm1 new file mode 100644 index 000000000..ddbad2457 --- /dev/null +++ b/WDACConfig/WDACConfig Module Files/XMLOps/New-PublisherLevelRules.psm1 @@ -0,0 +1,204 @@ +Function New-PublisherLevelRules { + <# + .SYNOPSIS + Creates new Publisher level rules in an XML file + Each rules includes the Signers, AllowedSigners, and CiSigners (depending on kernel/user mode) + .PARAMETER PublisherSigners + The PublisherSigners to be used for creating the rules, they are the output of the Build-SignerAndHashObjects function + .PARAMETER XmlFilePath + The path to the XML file to be modified + .INPUTS + PSCustomObject[] + System.IO.FileInfo + .OUTPUTS + System.Void + #> + [CmdletBinding()] + [OutputType([System.Void])] + Param ( + [Parameter(Mandatory = $true)][PSCustomObject[]]$PublisherSigners, + [Parameter(Mandatory = $true)][System.IO.FileInfo]$XmlFilePath + ) + + Begin { + # Importing the $PSDefaultParameterValues to the current session, prior to everything else + . "$ModuleRootPath\CoreExt\PSDefaultParameterValues.ps1" + + Write-Verbose -Message "New-PublisherLevelRules: There are $($PublisherSigners.Count) Publisher Signers to be added to the XML file" + + # Load the XML file + [System.Xml.XmlDocument]$Xml = Get-Content -Path $XmlFilePath + + # Define the namespace manager + [System.Xml.XmlNamespaceManager]$Ns = New-Object -TypeName System.Xml.XmlNamespaceManager -ArgumentList $Xml.NameTable + $Ns.AddNamespace('ns', 'urn:schemas-microsoft-com:sipolicy') + + # Find the Signers Node + [System.Xml.XmlElement]$SignersNode = $Xml.SelectSingleNode('//ns:Signers', $Ns) + + # Find the ProductSigners Nodes + [System.Xml.XmlElement]$UMCI_ProductSigners_Node = $Xml.SelectSingleNode('//ns:SigningScenarios/ns:SigningScenario[@Value="12"]/ns:ProductSigners', $Ns) + [System.Xml.XmlElement]$KMCI_ProductSigners_Node = $Xml.SelectSingleNode('//ns:SigningScenarios/ns:SigningScenario[@Value="131"]/ns:ProductSigners', $Ns) + + # Find the CiSigners Node + [System.Xml.XmlElement]$CiSignersNode = $Xml.SelectSingleNode('//ns:CiSigners', $Ns) + } + + Process { + + foreach ($PublisherData in $PublisherSigners) { + + #Region Creating Signers + + # Create signer for each certificate details in the PublisherSigners + # Some files are signed by multiple signers + foreach ($SignerData in $PublisherData.CertificateDetails) { + + [System.String]$Guid = [System.Guid]::NewGuid().ToString().replace('-', '').ToUpper() + + [System.String]$SignerID = "ID_SIGNER_B_$Guid" + + # Create the new Signer element + [System.Xml.XmlElement]$NewSignerNode = $Xml.CreateElement('Signer', $SignersNode.NamespaceURI) + $NewSignerNode.SetAttribute('ID', $SignerID) + $NewSignerNode.SetAttribute('Name', $SignerData.IntermediateCertName) + + # Create the CertRoot element and add it to the Signer element + [System.Xml.XmlElement]$CertRootNode = $Xml.CreateElement('CertRoot', $SignersNode.NamespaceURI) + $CertRootNode.SetAttribute('Type', 'TBS') + $CertRootNode.SetAttribute('Value', $SignerData.IntermediateCertTBS) + [System.Void]$NewSignerNode.AppendChild($CertRootNode) + + # Create the CertPublisher element and add it to the Signer element + [System.Xml.XmlElement]$CertPublisherNode = $Xml.CreateElement('CertPublisher', $SignersNode.NamespaceURI) + $CertPublisherNode.SetAttribute('Value', $SignerData.LeafCertName) + [System.Void]$NewSignerNode.AppendChild($CertPublisherNode) + + # Add the new Signer element to the Signers node + [System.Void]$SignersNode.AppendChild($NewSignerNode) + + #Region Adding signer to the Signer Scenario and CiSigners + + # For User-Mode files + if ($PublisherData.SiSigningScenario -eq '1') { + + # Check if AllowedSigners node exists, if not, create it + $UMCI_Temp_AllowedSignersNode = $UMCI_ProductSigners_Node.SelectSingleNode('ns:AllowedSigners', $Ns) + + if ($Null -eq $UMCI_Temp_AllowedSignersNode) { + + [System.Xml.XmlElement]$UMCI_Temp_AllowedSignersNode = $Xml.CreateElement('AllowedSigners', $Ns.LookupNamespace('ns')) + [System.Void]$UMCI_ProductSigners_Node.AppendChild($UMCI_Temp_AllowedSignersNode) + + } + + # Create Allowed Signers inside the -> -> + [System.Xml.XmlElement]$NewUMCIAllowedSignerNode = $Xml.CreateElement('AllowedSigner', $UMCI_Temp_AllowedSignersNode.NamespaceURI) + $NewUMCIAllowedSignerNode.SetAttribute('SignerId', $SignerID) + [System.Void]$UMCI_Temp_AllowedSignersNode.AppendChild($NewUMCIAllowedSignerNode) + + # Create a CI Signer for the User Mode Signer + [System.Xml.XmlElement]$NewCiSignerNode = $Xml.CreateElement('CiSigner', $CiSignersNode.NamespaceURI) + $NewCiSignerNode.SetAttribute('SignerId', $SignerID) + [System.Void]$CiSignersNode.AppendChild($NewCiSignerNode) + } + + # For Kernel-Mode files + elseif ($PublisherData.SiSigningScenario -eq '0') { + + # Check if AllowedSigners node exists, if not, create it + $KMCI_Temp_AllowedSignersNode = $KMCI_ProductSigners_Node.SelectSingleNode('ns:AllowedSigners', $Ns) + + if ($Null -eq $KMCI_Temp_AllowedSignersNode) { + + [System.Xml.XmlElement]$KMCI_Temp_AllowedSignersNode = $Xml.CreateElement('AllowedSigners', $Ns.LookupNamespace('ns')) + [System.Void]$KMCI_ProductSigners_Node.AppendChild($KMCI_Temp_AllowedSignersNode) + + } + + # Create Allowed Signers inside the -> -> + [System.Xml.XmlElement]$NewKMCIAllowedSignerNode = $Xml.CreateElement('AllowedSigner', $KMCI_Temp_AllowedSignersNode.NamespaceURI) + $NewKMCIAllowedSignerNode.SetAttribute('SignerId', $SignerID) + [System.Void]$KMCI_Temp_AllowedSignersNode.AppendChild($NewKMCIAllowedSignerNode) + + # Kernel-Mode signers don't need CI Signers + } + + #Endregion Adding signer to the Signer Scenario and CiSigners + + } + #Endregion Creating Signers + } + } + + End { + # Save the modified XML back to the file + $Xml.Save($XmlFilePath) + } +} +Export-ModuleMember -Function 'New-PublisherLevelRules' + +# SIG # Begin signature block +# MIILkgYJKoZIhvcNAQcCoIILgzCCC38CAQExDzANBglghkgBZQMEAgEFADB5Bgor +# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG +# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBVYPSvMHeTsmWF +# Nqi2haZoVUvTkfNWFNFZTPiiPD9gS6CCB9AwggfMMIIFtKADAgECAhMeAAAABI80 +# LDQz/68TAAAAAAAEMA0GCSqGSIb3DQEBDQUAME8xEzARBgoJkiaJk/IsZAEZFgNj +# b20xIjAgBgoJkiaJk/IsZAEZFhJIT1RDQUtFWC1DQS1Eb21haW4xFDASBgNVBAMT +# C0hPVENBS0VYLUNBMCAXDTIzMTIyNzExMjkyOVoYDzIyMDgxMTEyMTEyOTI5WjB5 +# MQswCQYDVQQGEwJVSzEeMBwGA1UEAxMVSG90Q2FrZVggQ29kZSBTaWduaW5nMSMw +# IQYJKoZIhvcNAQkBFhRob3RjYWtleEBvdXRsb29rLmNvbTElMCMGCSqGSIb3DQEJ +# ARYWU3B5bmV0Z2lybEBvdXRsb29rLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIP +# ADCCAgoCggIBAKb1BJzTrpu1ERiwr7ivp0UuJ1GmNmmZ65eckLpGSF+2r22+7Tgm +# pEifj9NhPw0X60F9HhdSM+2XeuikmaNMvq8XRDUFoenv9P1ZU1wli5WTKHJ5ayDW +# k2NP22G9IPRnIpizkHkQnCwctx0AFJx1qvvd+EFlG6ihM0fKGG+DwMaFqsKCGh+M +# rb1bKKtY7UEnEVAsVi7KYGkkH+ukhyFUAdUbh/3ZjO0xWPYpkf/1ldvGes6pjK6P +# US2PHbe6ukiupqYYG3I5Ad0e20uQfZbz9vMSTiwslLhmsST0XAesEvi+SJYz2xAQ +# x2O4n/PxMRxZ3m5Q0WQxLTGFGjB2Bl+B+QPBzbpwb9JC77zgA8J2ncP2biEguSRJ +# e56Ezx6YpSoRv4d1jS3tpRL+ZFm8yv6We+hodE++0tLsfpUq42Guy3MrGQ2kTIRo +# 7TGLOLpayR8tYmnF0XEHaBiVl7u/Szr7kmOe/CfRG8IZl6UX+/66OqZeyJ12Q3m2 +# fe7ZWnpWT5sVp2sJmiuGb3atFXBWKcwNumNuy4JecjQE+7NF8rfIv94NxbBV/WSM +# pKf6Yv9OgzkjY1nRdIS1FBHa88RR55+7Ikh4FIGPBTAibiCEJMc79+b8cdsQGOo4 +# ymgbKjGeoRNjtegZ7XE/3TUywBBFMf8NfcjF8REs/HIl7u2RHwRaUTJdAgMBAAGj +# ggJzMIICbzA8BgkrBgEEAYI3FQcELzAtBiUrBgEEAYI3FQiG7sUghM++I4HxhQSF +# hqV1htyhDXuG5sF2wOlDAgFkAgEIMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA4GA1Ud +# DwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBsGCSsGAQQBgjcVCgQOMAwwCgYIKwYB +# BQUHAwMwHQYDVR0OBBYEFOlnnQDHNUpYoPqECFP6JAqGDFM6MB8GA1UdIwQYMBaA +# FICT0Mhz5MfqMIi7Xax90DRKYJLSMIHUBgNVHR8EgcwwgckwgcaggcOggcCGgb1s +# ZGFwOi8vL0NOPUhPVENBS0VYLUNBLENOPUhvdENha2VYLENOPUNEUCxDTj1QdWJs +# aWMlMjBLZXklMjBTZXJ2aWNlcyxDTj1TZXJ2aWNlcyxDTj1Db25maWd1cmF0aW9u +# LERDPU5vbkV4aXN0ZW50RG9tYWluLERDPWNvbT9jZXJ0aWZpY2F0ZVJldm9jYXRp +# b25MaXN0P2Jhc2U/b2JqZWN0Q2xhc3M9Y1JMRGlzdHJpYnV0aW9uUG9pbnQwgccG +# CCsGAQUFBwEBBIG6MIG3MIG0BggrBgEFBQcwAoaBp2xkYXA6Ly8vQ049SE9UQ0FL +# RVgtQ0EsQ049QUlBLENOPVB1YmxpYyUyMEtleSUyMFNlcnZpY2VzLENOPVNlcnZp +# Y2VzLENOPUNvbmZpZ3VyYXRpb24sREM9Tm9uRXhpc3RlbnREb21haW4sREM9Y29t +# P2NBQ2VydGlmaWNhdGU/YmFzZT9vYmplY3RDbGFzcz1jZXJ0aWZpY2F0aW9uQXV0 +# aG9yaXR5MA0GCSqGSIb3DQEBDQUAA4ICAQA7JI76Ixy113wNjiJmJmPKfnn7brVI +# IyA3ZudXCheqWTYPyYnwzhCSzKJLejGNAsMlXwoYgXQBBmMiSI4Zv4UhTNc4Umqx +# pZSpqV+3FRFQHOG/X6NMHuFa2z7T2pdj+QJuH5TgPayKAJc+Kbg4C7edL6YoePRu +# HoEhoRffiabEP/yDtZWMa6WFqBsfgiLMlo7DfuhRJ0eRqvJ6+czOVU2bxvESMQVo +# bvFTNDlEcUzBM7QxbnsDyGpoJZTx6M3cUkEazuliPAw3IW1vJn8SR1jFBukKcjWn +# aau+/BE9w77GFz1RbIfH3hJ/CUA0wCavxWcbAHz1YoPTAz6EKjIc5PcHpDO+n8Fh +# t3ULwVjWPMoZzU589IXi+2Ol0IUWAdoQJr/Llhub3SNKZ3LlMUPNt+tXAs/vcUl0 +# 7+Dp5FpUARE2gMYA/XxfU9T6Q3pX3/NRP/ojO9m0JrKv/KMc9sCGmV9sDygCOosU +# 5yGS4Ze/DJw6QR7xT9lMiWsfgL96Qcw4lfu1+5iLr0dnDFsGowGTKPGI0EvzK7H+ +# DuFRg+Fyhn40dOUl8fVDqYHuZJRoWJxCsyobVkrX4rA6xUTswl7xYPYWz88WZDoY +# gI8AwuRkzJyUEA07IYtsbFCYrcUzIHME4uf8jsJhCmb0va1G2WrWuyasv3K/G8Nn +# f60MsDbDH1mLtzGCAxgwggMUAgEBMGYwTzETMBEGCgmSJomT8ixkARkWA2NvbTEi +# MCAGCgmSJomT8ixkARkWEkhPVENBS0VYLUNBLURvbWFpbjEUMBIGA1UEAxMLSE9U +# Q0FLRVgtQ0ECEx4AAAAEjzQsNDP/rxMAAAAAAAQwDQYJYIZIAWUDBAIBBQCggYQw +# GAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGC +# NwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQx +# IgQg7h5se0851U5ToouU+UDdH152mLHdp/rKiOrkNDZM0FAwDQYJKoZIhvcNAQEB +# BQAEggIARlTimUWFipKEKKvsZvefdUeuCe2fMpgLpGBdhcYbYOTojjo4GC24+cr/ +# hrbSx7OZjN0BDRRLjdOHRAtnu5xBr61GHTuX71KCZ6UDrIBFb8tguVh9GUvMAPYd +# u16ZRjx2kiouZbVzaFYloR0NllYbye8ymwEXMaLcDP2cvuRRVCsD0S0Iz0Um2MlE +# rXTsr+EUHxOfwq3DDPYFGmlPakb6t018JEyflSDXcLsavAXIDX7xNzZrkug68qqH +# E36lfYYGNKv+Or6RmU7lcX6aeaf0tzjf8Di88BL90WqBEmvaZft0frl+fuF3yamu +# xFpWN+VGJM/RzO/lOKlCqdYmEqR03W/ijGqK7iMovV0HzeM9R+kYZk9AGhXRTIvM +# YswcZ4quokyvbvAaLfbePKW+hEGJ/zvYMQJic2x5Fdpz1G43osJyfhN4QAwywtnA +# DIzNaSitxt/gZjNfYEB+6cwvxDKA/3gmhFvrEg30DNc7RW61cIiNONQIsAO3zbnZ +# VJ4UOilUqSrR39muOd33R5C/kkjq4urSoT2hZ8aM8Efbvrm6kFDU+H39SZoJReA/ +# UXklqp2MgYsW34c8OFDB3njh8QpSxIdTaZoHiTaHmDhWrY470jy0MWg1y4hiysW3 +# 4CvUiTfgwTdehOAtLSBOKLLtPAWwXRoRJcMYPtTqIbmygU3FAds= +# SIG # End signature block diff --git a/WDACConfig/WDACConfig Module Files/XMLOps/Optimize-MDECSVData.psm1 b/WDACConfig/WDACConfig Module Files/XMLOps/Optimize-MDECSVData.psm1 new file mode 100644 index 000000000..e82c3acbf --- /dev/null +++ b/WDACConfig/WDACConfig Module Files/XMLOps/Optimize-MDECSVData.psm1 @@ -0,0 +1,166 @@ +Function Optimize-MDECSVData { + <# + .SYNOPSIS + Optimizes the MDE CSV data by adding the nested properties in the "AdditionalFields" property to the parent record as first-level properties + .DESCRIPTION + The function runs each CSV file in parallel for fast processing based on the number of CPU cores available + .PARAMETER CSVPaths + The path to the CSV file containing the Microsoft Defender for Endpoint Advanced Hunting data + .PARAMETER Debug + A switch parameter to enable debugging actions such as exporting the new array to a CSV file + .PARAMETER StagingArea + The path to the directory where the debug CSV file will be saved which are the outputs of this function + .INPUTS + System.IO.FileInfo[] + .OUTPUTS + System.Collections.Hashtable[] + #> + [CmdletBinding()] + [OutputType([System.Collections.Hashtable[]])] + Param ( + [Parameter(Mandatory = $true)][System.IO.FileInfo[]]$CSVPaths, + [Parameter(Mandatory = $true)][System.IO.DirectoryInfo]$StagingArea + ) + + Begin { + # Importing the $PSDefaultParameterValues to the current session, prior to everything else + . "$ModuleRootPath\CoreExt\PSDefaultParameterValues.ps1" + + # Detecting if Debug switch is used + $PSBoundParameters.Debug.IsPresent ? ([System.Boolean]$Debug = $true) : ([System.Boolean]$Debug = $false) | Out-Null + + Try { + # Get the number of enabled CPU cores + $CPUEnabledCores = [System.Int64](Get-CimInstance -ClassName Win32_Processor -Verbose:$false).NumberOfEnabledCore + } + Catch { + Write-Verbose -Message 'Optimize-MDECSVData: Unable to detect the number of enabled CPU cores, defaulting to 5...' + } + } + + Process { + + # Create a new HashTable array to hold the updated data from the original CSVs + [System.Collections.Hashtable[]]$NewCsvData = $CSVPaths | ForEach-Object -ThrottleLimit ($CPUEnabledCores ?? 5) -Parallel { + + # Read the initial MDE AH CSV export and save them into a variable + [System.Object[]]$CsvData += Import-Csv -Path $_ + + # Add the nested properties in the "AdditionalFields" property to the parent record as first-level properties + foreach ($Row in $CsvData) { + + # Create a new HashTable for the combined data + [System.Collections.Hashtable]$CurrentRowHashTable = @{} + + # For each row in the CSV data, create a new object to hold the updated properties, except for the "AdditionalFields" property + foreach ($Property in $Row.PSObject.Properties) { + if ($Property.Name -ne 'AdditionalFields') { + $CurrentRowHashTable[$Property.Name] = $Property.Value + } + } + + # Convert the AdditionalFields JSON string to a HashTable + [System.Collections.Hashtable]$JsonConverted = $Row.AdditionalFields | ConvertFrom-Json -AsHashtable + + # Add each Key/Value pairs from the additional fields HashTable to the CurrentRow HashTable + foreach ($Item in $JsonConverted.GetEnumerator()) { + $CurrentRowHashTable[$Item.Name] = $Item.Value + } + + # Send the new HashTable to the pipeline to be saved in the HashTable Array + [System.Collections.Hashtable]$CurrentRowHashTable + } + } + } + + End { + + if ($Debug) { + + Write-Verbose -Message 'Optimize-MDECSVData: Debug parameter was used, exporting the new array to a CSV file...' + + # Initialize a HashSet to keep track of all property names (aka keys in the HashTable Array) + $PropertyNames = [System.Collections.Generic.HashSet[System.String]] @() + + # Loop through each HashTable's keys in the new updated CSV data to find and add any new key names to the list that are not already present + # These are the property names from the AdditionalFields + foreach ($Obj in $NewCsvData.Keys) { + if (-NOT $PropertyNames.Contains($Obj)) { + $PropertyNames += $Obj + } + } + + # Export the new array to a CSV file containing all of the original properties and the new properties from the AdditionalFields + # guarantees that no property gets lost during CSV export + $NewCsvData | Select-Object -Property $PropertyNames | Export-Csv -Path (Join-Path -Path $StagingArea -ChildPath 'Pass1.csv') -Force + } + + Return $NewCsvData + } +} +Export-ModuleMember -Function 'Optimize-MDECSVData' + +# SIG # Begin signature block +# MIILkgYJKoZIhvcNAQcCoIILgzCCC38CAQExDzANBglghkgBZQMEAgEFADB5Bgor +# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG +# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCB/tiT/swn4vwFC +# EL9I+T4BtMjEwRUgNSgiT7tVOayX9aCCB9AwggfMMIIFtKADAgECAhMeAAAABI80 +# LDQz/68TAAAAAAAEMA0GCSqGSIb3DQEBDQUAME8xEzARBgoJkiaJk/IsZAEZFgNj +# b20xIjAgBgoJkiaJk/IsZAEZFhJIT1RDQUtFWC1DQS1Eb21haW4xFDASBgNVBAMT +# C0hPVENBS0VYLUNBMCAXDTIzMTIyNzExMjkyOVoYDzIyMDgxMTEyMTEyOTI5WjB5 +# MQswCQYDVQQGEwJVSzEeMBwGA1UEAxMVSG90Q2FrZVggQ29kZSBTaWduaW5nMSMw +# IQYJKoZIhvcNAQkBFhRob3RjYWtleEBvdXRsb29rLmNvbTElMCMGCSqGSIb3DQEJ +# ARYWU3B5bmV0Z2lybEBvdXRsb29rLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIP +# ADCCAgoCggIBAKb1BJzTrpu1ERiwr7ivp0UuJ1GmNmmZ65eckLpGSF+2r22+7Tgm +# pEifj9NhPw0X60F9HhdSM+2XeuikmaNMvq8XRDUFoenv9P1ZU1wli5WTKHJ5ayDW +# k2NP22G9IPRnIpizkHkQnCwctx0AFJx1qvvd+EFlG6ihM0fKGG+DwMaFqsKCGh+M +# rb1bKKtY7UEnEVAsVi7KYGkkH+ukhyFUAdUbh/3ZjO0xWPYpkf/1ldvGes6pjK6P +# US2PHbe6ukiupqYYG3I5Ad0e20uQfZbz9vMSTiwslLhmsST0XAesEvi+SJYz2xAQ +# x2O4n/PxMRxZ3m5Q0WQxLTGFGjB2Bl+B+QPBzbpwb9JC77zgA8J2ncP2biEguSRJ +# e56Ezx6YpSoRv4d1jS3tpRL+ZFm8yv6We+hodE++0tLsfpUq42Guy3MrGQ2kTIRo +# 7TGLOLpayR8tYmnF0XEHaBiVl7u/Szr7kmOe/CfRG8IZl6UX+/66OqZeyJ12Q3m2 +# fe7ZWnpWT5sVp2sJmiuGb3atFXBWKcwNumNuy4JecjQE+7NF8rfIv94NxbBV/WSM +# pKf6Yv9OgzkjY1nRdIS1FBHa88RR55+7Ikh4FIGPBTAibiCEJMc79+b8cdsQGOo4 +# ymgbKjGeoRNjtegZ7XE/3TUywBBFMf8NfcjF8REs/HIl7u2RHwRaUTJdAgMBAAGj +# ggJzMIICbzA8BgkrBgEEAYI3FQcELzAtBiUrBgEEAYI3FQiG7sUghM++I4HxhQSF +# hqV1htyhDXuG5sF2wOlDAgFkAgEIMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA4GA1Ud +# DwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBsGCSsGAQQBgjcVCgQOMAwwCgYIKwYB +# BQUHAwMwHQYDVR0OBBYEFOlnnQDHNUpYoPqECFP6JAqGDFM6MB8GA1UdIwQYMBaA +# FICT0Mhz5MfqMIi7Xax90DRKYJLSMIHUBgNVHR8EgcwwgckwgcaggcOggcCGgb1s +# ZGFwOi8vL0NOPUhPVENBS0VYLUNBLENOPUhvdENha2VYLENOPUNEUCxDTj1QdWJs +# aWMlMjBLZXklMjBTZXJ2aWNlcyxDTj1TZXJ2aWNlcyxDTj1Db25maWd1cmF0aW9u +# LERDPU5vbkV4aXN0ZW50RG9tYWluLERDPWNvbT9jZXJ0aWZpY2F0ZVJldm9jYXRp +# b25MaXN0P2Jhc2U/b2JqZWN0Q2xhc3M9Y1JMRGlzdHJpYnV0aW9uUG9pbnQwgccG +# CCsGAQUFBwEBBIG6MIG3MIG0BggrBgEFBQcwAoaBp2xkYXA6Ly8vQ049SE9UQ0FL +# RVgtQ0EsQ049QUlBLENOPVB1YmxpYyUyMEtleSUyMFNlcnZpY2VzLENOPVNlcnZp +# Y2VzLENOPUNvbmZpZ3VyYXRpb24sREM9Tm9uRXhpc3RlbnREb21haW4sREM9Y29t +# P2NBQ2VydGlmaWNhdGU/YmFzZT9vYmplY3RDbGFzcz1jZXJ0aWZpY2F0aW9uQXV0 +# aG9yaXR5MA0GCSqGSIb3DQEBDQUAA4ICAQA7JI76Ixy113wNjiJmJmPKfnn7brVI +# IyA3ZudXCheqWTYPyYnwzhCSzKJLejGNAsMlXwoYgXQBBmMiSI4Zv4UhTNc4Umqx +# pZSpqV+3FRFQHOG/X6NMHuFa2z7T2pdj+QJuH5TgPayKAJc+Kbg4C7edL6YoePRu +# HoEhoRffiabEP/yDtZWMa6WFqBsfgiLMlo7DfuhRJ0eRqvJ6+czOVU2bxvESMQVo +# bvFTNDlEcUzBM7QxbnsDyGpoJZTx6M3cUkEazuliPAw3IW1vJn8SR1jFBukKcjWn +# aau+/BE9w77GFz1RbIfH3hJ/CUA0wCavxWcbAHz1YoPTAz6EKjIc5PcHpDO+n8Fh +# t3ULwVjWPMoZzU589IXi+2Ol0IUWAdoQJr/Llhub3SNKZ3LlMUPNt+tXAs/vcUl0 +# 7+Dp5FpUARE2gMYA/XxfU9T6Q3pX3/NRP/ojO9m0JrKv/KMc9sCGmV9sDygCOosU +# 5yGS4Ze/DJw6QR7xT9lMiWsfgL96Qcw4lfu1+5iLr0dnDFsGowGTKPGI0EvzK7H+ +# DuFRg+Fyhn40dOUl8fVDqYHuZJRoWJxCsyobVkrX4rA6xUTswl7xYPYWz88WZDoY +# gI8AwuRkzJyUEA07IYtsbFCYrcUzIHME4uf8jsJhCmb0va1G2WrWuyasv3K/G8Nn +# f60MsDbDH1mLtzGCAxgwggMUAgEBMGYwTzETMBEGCgmSJomT8ixkARkWA2NvbTEi +# MCAGCgmSJomT8ixkARkWEkhPVENBS0VYLUNBLURvbWFpbjEUMBIGA1UEAxMLSE9U +# Q0FLRVgtQ0ECEx4AAAAEjzQsNDP/rxMAAAAAAAQwDQYJYIZIAWUDBAIBBQCggYQw +# GAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGC +# NwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQx +# IgQgtq9cZGH15ZkpTRB8W9sDe46uFhRiRNlWaAmgufoNxR8wDQYJKoZIhvcNAQEB +# BQAEggIAEXIEJeLLUvpOuv0j65JM4f6gmGWNOgqZ908P9UUHjXOF0L5ob4/PEbi2 +# vMe40lQXgMoShMcoPp3o2JCoEk/boMvZWk5aeqQah3SFO6hRPPB2QEJtUS2TlqvQ +# 1x2Tk8ipJKMXob1Ej3gSy3NfW3ltcHt78bSYLrz23eA8tOfxm5MC3AywFsp/SwPN +# 1zME61orAvfVRHMFlFXijOmp1GMWUKYgw4nucioa8IaH8pfPRGexFD47Yjm0/vGk +# 25VGdfCIOsFQ/OEfhYPNmECi74nC7zB6uZmn4GJmwF5crzMXvsEKbh3/DThKGm3F +# AHsG7BZMNDRMBtWTgB22YtVywenrFQDoDUAX/HyHJaES/I9FD9OTVFzdbKLMvSmh +# dfCZ5nWXX8ZiqqmJpdXikw7/catBOuzxB1yM6kpf/m5MrcJQAMRQadKkO0qvtEdA +# Cvb9z2jm7vgeKb6gcBwFHI3MM9kxhr3gUyHdnbllJjbqYs8HAdJe3h2v3iRbalGS +# NEHYny8mR6uPEs51uXA9y+PlmcUNEKdJWwOpJ5rUNlNsunx3VIwsDJswrORMw3L+ +# PSSCNdIwGHWHZetKC+YCBZwXuNWTxsF4q4u/YlyEWOc/0daJSaZigjNNd7ZM/zF6 +# fT+10BYMwRnYHX5dsLU0ZyqhwSqk3nZaN9jpL7JVmp9kblz+mO4= +# SIG # End signature block diff --git a/WDACConfig/WDACConfig Module Files/XMLOps/Remove-AllowElements_Semantic.psm1 b/WDACConfig/WDACConfig Module Files/XMLOps/Remove-AllowElements_Semantic.psm1 new file mode 100644 index 000000000..fa4761de5 --- /dev/null +++ b/WDACConfig/WDACConfig Module Files/XMLOps/Remove-AllowElements_Semantic.psm1 @@ -0,0 +1,211 @@ +function Remove-AllowElements_Semantic { + <# + .SYNOPSIS + A high performance function that removes duplicate elements from the node and their corresponding elements from the node of the node under each node + The criteria for removing duplicates is the Hash attribute of the elements. + If there are multiple elements with the same Hash, the function keeps the first element and removes the rest. + The function only considers elements that are part of the same node and have the same Hash attribute as duplicates. + After the function completes its operation, the XML file will not have any duplicate elements, duplicate elements or any orphan elements. + This is according to the CI Schema. + .PARAMETER Path + The path to the XML file to be modified + .INPUTS + System.IO.FileInfo + .OUTPUTS + System.Void + #> + [CmdletBinding()] + [OutputType([System.Void])] + param ( + [Parameter(Mandatory = $true)][System.IO.FileInfo]$Path + ) + + Begin { + # Importing the $PSDefaultParameterValues to the current session, prior to everything else + . "$ModuleRootPath\CoreExt\PSDefaultParameterValues.ps1" + + # Load the XML file + [System.Xml.XmlDocument]$Xml = Get-Content -Path $Path + + # Define the namespace manager + [System.Xml.XmlNamespaceManager]$Ns = New-Object -TypeName System.Xml.XmlNamespaceManager -ArgumentList $Xml.NameTable + $Ns.AddNamespace('ns', 'urn:schemas-microsoft-com:sipolicy') + + # Get all of the elements inside the node + [System.Xml.XmlNodeList]$AllowElements = $Xml.SelectNodes('//ns:FileRules//ns:Allow', $Ns) + + # Get the node + [System.Xml.XmlElement[]]$FileRulesNode = $Xml.SelectSingleNode('//ns:FileRules', $Ns) + + # Find the FileRulesRef Nodes inside the ProductSigners Nodes of each Signing Scenario + [System.Xml.XmlNodeList]$UMCI_SigningScenario_ProductSigners_FileRulesRef_Node = $Xml.SelectNodes('//ns:SigningScenarios/ns:SigningScenario[@Value="12"]/ns:ProductSigners/ns:FileRulesRef/ns:FileRuleRef', $Ns) + [System.Xml.XmlNodeList]$KMCI_SigningScenario_ProductSigners_FileRulesRef_Node = $Xml.SelectNodes('//ns:SigningScenarios/ns:SigningScenario[@Value="131"]/ns:ProductSigners/ns:FileRulesRef/ns:FileRuleRef', $Ns) + + [System.Xml.XmlNodeList]$UserModeFileRefs = $Xml.SelectNodes('//ns:SigningScenarios/ns:SigningScenario[@Value="12"]/ns:ProductSigners/ns:FileRulesRef', $Ns) + [System.Xml.XmlNodeList]$KernelModeFileRefs = $Xml.SelectNodes('//ns:SigningScenarios/ns:SigningScenario[@Value="131"]/ns:ProductSigners/ns:FileRulesRef', $Ns) + + # Save the IDs of the FileRuleRef elements that are part of the User-Mode and Kernel-Mode signing scenarios as HashSets + # These are unique because HashSets don't support duplicate values + $UserMode_FileRulesRefIDs_HashSet = [System.Collections.Generic.HashSet[System.String]]$UMCI_SigningScenario_ProductSigners_FileRulesRef_Node.RuleID + $KernelMode_FileRulesRefIDs_HashSet = [System.Collections.Generic.HashSet[System.String]]$KMCI_SigningScenario_ProductSigners_FileRulesRef_Node.RuleID + + # 2 Hashtables for User and Kernel mode elements and their corresponding FileRuleRef elements together + [System.Collections.Hashtable]$KernelModeHashTable = @{} + [System.Collections.Hashtable]$UserModeHashTable = @{} + + # 2 Arrays to save the elements of User and Kernel modes + [System.Xml.XmlElement[]]$ArrayOfUserModes = @() + [System.Xml.XmlElement[]]$ArrayOfKernelModes = @() + } + + Process { + + # Separating User-Mode and Kernel-Mode elements + foreach ($AllowElement in $AllowElements) { + + # Check if the User-Mode node has any elements + # And then check if the current element ID is part of the User-Mode node + if (($Null -ne $UserMode_FileRulesRefIDs_HashSet) -and ($UserMode_FileRulesRefIDs_HashSet.Contains($AllowElement.ID))) { + $ArrayOfUserModes += $AllowElement + } + + # Check if the Kernel-Mode node has any elements + # And then check if the current element ID is part of the Kernel-Mode node + elseif (($Null -ne $KernelMode_FileRulesRefIDs_HashSet) -and ($KernelMode_FileRulesRefIDs_HashSet.Contains($AllowElement.ID))) { + $ArrayOfKernelModes += $AllowElement + } + else { + Write-Warning -Message "Remove-AllowElements_Semantic: The Allow element with ID $($AllowElement.ID) is not part of any Signing Scenario. It will be ignored." + } + } + + # Grouping the elements by their Hash value, uniquely, So SHA1 and SHA256 hashes + [System.Xml.XmlElement[]]$GroupsUserModes = $ArrayOfUserModes | Group-Object -Property Hash | ForEach-Object -Process { $_.Group[0] } + [System.Xml.XmlElement[]]$GroupsKernelModes = $ArrayOfKernelModes | Group-Object -Property Hash | ForEach-Object -Process { $_.Group[0] } + + # Adding the User-Mode elements and their corresponding elements to the Hashtables + foreach ($UserModeAllowElement in $GroupsUserModes) { + + # If the current element ID is not already in the KernelModeHashTable, add it + if (-NOT $UserModeHashTable.ContainsKey($UserModeAllowElement)) { + + # The key is the element, the value is all of the elements with the same RuleID as the Allow element's ID, without deduplication at this point + # Cloning is necessary because after clearing the nodes, we would lose the reference to the original elements in those nodes + $UserModeHashTable[@($UserModeAllowElement.Clone())] = @($Xml.SelectNodes("//ns:SigningScenarios/ns:SigningScenario[@Value='12']/ns:ProductSigners/ns:FileRulesRef/ns:FileRuleRef[@RuleID=`"$($UserModeAllowElement.ID)`"]", $Ns).Clone()) + } + } + + # Adding the Kernel-Mode elements and their corresponding elements to the Hashtables + foreach ($KernelModeAllowElement in $GroupsKernelModes) { + + # If the current element ID is not already in the KernelModeHashTable, add it + if (-NOT $KernelModeHashTable.ContainsKey($KernelModeAllowElement)) { + + # The key is the element, the value is all of the elements with the same RuleID as the Allow element's ID, without deduplication at this point + $KernelModeHashTable[@($KernelModeAllowElement.Clone())] = @($Xml.SelectNodes("//ns:SigningScenarios/ns:SigningScenario[@Value='131']/ns:ProductSigners/ns:FileRulesRef/ns:FileRuleRef[@RuleID=`"$($KernelModeAllowElement.ID)`"]", $Ns).Clone()) + } + } + + # Select and remove all elements from + [System.Xml.XmlNodeList]$AllowNodes = $Xml.SelectNodes('//ns:FileRules/ns:Allow', $NS) + foreach ($Node in $AllowNodes) { + [System.Void]$Node.ParentNode.RemoveChild($Node) + } + + # Select and remove all elements from in each signing scenario + [System.Xml.XmlNodeList]$SigningScenarios = $Xml.SelectNodes('//ns:SigningScenario/ns:ProductSigners/ns:FileRulesRef', $NS) + foreach ($Scenario in $SigningScenarios) { + [System.Xml.XmlNodeList]$FileRuleRefs = $Scenario.SelectNodes('ns:FileRuleRef', $NS) + foreach ($FileRuleRef in $FileRuleRefs) { + [System.Void]$FileRuleRef.ParentNode.RemoveChild($FileRuleRef) + } + } + + # Add Unique elements and their corresponding elements back to the XML file for the Kernel-Mode files + foreach ($Group in $UserModeHashTable.GetEnumerator()) { + # Add the unique element + [System.Void]$FileRulesNode.AppendChild($Group.Key[0]) + # Add the unique element, using [0] index because the key is an array even though it only has 1 element + [System.Void]$UserModeFileRefs.AppendChild(($Group.Value.GetEnumerator() | Group-Object -Property RuleID | ForEach-Object -Process { $_.Group[0] })) + } + + # Add Unique elements and their corresponding elements back to the XML file for the Kernel-Mode files + foreach ($Group in $KernelModeHashTable.GetEnumerator()) { + # Add the unique element, using [0] index because the key is an array even though it only has 1 element + [System.Void]$FileRulesNode.AppendChild($Group.Key[0]) + # Add the unique element + [System.Void]$KernelModeFileRefs.AppendChild(($Group.Value.GetEnumerator() | Group-Object -Property RuleID | ForEach-Object -Process { $_.Group[0] })) + } + } + + End { + # Save the modified XML file + $Xml.Save($Path) + } +} +Export-ModuleMember -Function 'Remove-AllowElements_Semantic' + +# SIG # Begin signature block +# MIILkgYJKoZIhvcNAQcCoIILgzCCC38CAQExDzANBglghkgBZQMEAgEFADB5Bgor +# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG +# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCA59l3OQREmnazv +# +zRcyLYcYtAaaTbUNumMFpa+2rwdxaCCB9AwggfMMIIFtKADAgECAhMeAAAABI80 +# LDQz/68TAAAAAAAEMA0GCSqGSIb3DQEBDQUAME8xEzARBgoJkiaJk/IsZAEZFgNj +# b20xIjAgBgoJkiaJk/IsZAEZFhJIT1RDQUtFWC1DQS1Eb21haW4xFDASBgNVBAMT +# C0hPVENBS0VYLUNBMCAXDTIzMTIyNzExMjkyOVoYDzIyMDgxMTEyMTEyOTI5WjB5 +# MQswCQYDVQQGEwJVSzEeMBwGA1UEAxMVSG90Q2FrZVggQ29kZSBTaWduaW5nMSMw +# IQYJKoZIhvcNAQkBFhRob3RjYWtleEBvdXRsb29rLmNvbTElMCMGCSqGSIb3DQEJ +# ARYWU3B5bmV0Z2lybEBvdXRsb29rLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIP +# ADCCAgoCggIBAKb1BJzTrpu1ERiwr7ivp0UuJ1GmNmmZ65eckLpGSF+2r22+7Tgm +# pEifj9NhPw0X60F9HhdSM+2XeuikmaNMvq8XRDUFoenv9P1ZU1wli5WTKHJ5ayDW +# k2NP22G9IPRnIpizkHkQnCwctx0AFJx1qvvd+EFlG6ihM0fKGG+DwMaFqsKCGh+M +# rb1bKKtY7UEnEVAsVi7KYGkkH+ukhyFUAdUbh/3ZjO0xWPYpkf/1ldvGes6pjK6P +# US2PHbe6ukiupqYYG3I5Ad0e20uQfZbz9vMSTiwslLhmsST0XAesEvi+SJYz2xAQ +# x2O4n/PxMRxZ3m5Q0WQxLTGFGjB2Bl+B+QPBzbpwb9JC77zgA8J2ncP2biEguSRJ +# e56Ezx6YpSoRv4d1jS3tpRL+ZFm8yv6We+hodE++0tLsfpUq42Guy3MrGQ2kTIRo +# 7TGLOLpayR8tYmnF0XEHaBiVl7u/Szr7kmOe/CfRG8IZl6UX+/66OqZeyJ12Q3m2 +# fe7ZWnpWT5sVp2sJmiuGb3atFXBWKcwNumNuy4JecjQE+7NF8rfIv94NxbBV/WSM +# pKf6Yv9OgzkjY1nRdIS1FBHa88RR55+7Ikh4FIGPBTAibiCEJMc79+b8cdsQGOo4 +# ymgbKjGeoRNjtegZ7XE/3TUywBBFMf8NfcjF8REs/HIl7u2RHwRaUTJdAgMBAAGj +# ggJzMIICbzA8BgkrBgEEAYI3FQcELzAtBiUrBgEEAYI3FQiG7sUghM++I4HxhQSF +# hqV1htyhDXuG5sF2wOlDAgFkAgEIMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA4GA1Ud +# DwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBsGCSsGAQQBgjcVCgQOMAwwCgYIKwYB +# BQUHAwMwHQYDVR0OBBYEFOlnnQDHNUpYoPqECFP6JAqGDFM6MB8GA1UdIwQYMBaA +# FICT0Mhz5MfqMIi7Xax90DRKYJLSMIHUBgNVHR8EgcwwgckwgcaggcOggcCGgb1s +# ZGFwOi8vL0NOPUhPVENBS0VYLUNBLENOPUhvdENha2VYLENOPUNEUCxDTj1QdWJs +# aWMlMjBLZXklMjBTZXJ2aWNlcyxDTj1TZXJ2aWNlcyxDTj1Db25maWd1cmF0aW9u +# LERDPU5vbkV4aXN0ZW50RG9tYWluLERDPWNvbT9jZXJ0aWZpY2F0ZVJldm9jYXRp +# b25MaXN0P2Jhc2U/b2JqZWN0Q2xhc3M9Y1JMRGlzdHJpYnV0aW9uUG9pbnQwgccG +# CCsGAQUFBwEBBIG6MIG3MIG0BggrBgEFBQcwAoaBp2xkYXA6Ly8vQ049SE9UQ0FL +# RVgtQ0EsQ049QUlBLENOPVB1YmxpYyUyMEtleSUyMFNlcnZpY2VzLENOPVNlcnZp +# Y2VzLENOPUNvbmZpZ3VyYXRpb24sREM9Tm9uRXhpc3RlbnREb21haW4sREM9Y29t +# P2NBQ2VydGlmaWNhdGU/YmFzZT9vYmplY3RDbGFzcz1jZXJ0aWZpY2F0aW9uQXV0 +# aG9yaXR5MA0GCSqGSIb3DQEBDQUAA4ICAQA7JI76Ixy113wNjiJmJmPKfnn7brVI +# IyA3ZudXCheqWTYPyYnwzhCSzKJLejGNAsMlXwoYgXQBBmMiSI4Zv4UhTNc4Umqx +# pZSpqV+3FRFQHOG/X6NMHuFa2z7T2pdj+QJuH5TgPayKAJc+Kbg4C7edL6YoePRu +# HoEhoRffiabEP/yDtZWMa6WFqBsfgiLMlo7DfuhRJ0eRqvJ6+czOVU2bxvESMQVo +# bvFTNDlEcUzBM7QxbnsDyGpoJZTx6M3cUkEazuliPAw3IW1vJn8SR1jFBukKcjWn +# aau+/BE9w77GFz1RbIfH3hJ/CUA0wCavxWcbAHz1YoPTAz6EKjIc5PcHpDO+n8Fh +# t3ULwVjWPMoZzU589IXi+2Ol0IUWAdoQJr/Llhub3SNKZ3LlMUPNt+tXAs/vcUl0 +# 7+Dp5FpUARE2gMYA/XxfU9T6Q3pX3/NRP/ojO9m0JrKv/KMc9sCGmV9sDygCOosU +# 5yGS4Ze/DJw6QR7xT9lMiWsfgL96Qcw4lfu1+5iLr0dnDFsGowGTKPGI0EvzK7H+ +# DuFRg+Fyhn40dOUl8fVDqYHuZJRoWJxCsyobVkrX4rA6xUTswl7xYPYWz88WZDoY +# gI8AwuRkzJyUEA07IYtsbFCYrcUzIHME4uf8jsJhCmb0va1G2WrWuyasv3K/G8Nn +# f60MsDbDH1mLtzGCAxgwggMUAgEBMGYwTzETMBEGCgmSJomT8ixkARkWA2NvbTEi +# MCAGCgmSJomT8ixkARkWEkhPVENBS0VYLUNBLURvbWFpbjEUMBIGA1UEAxMLSE9U +# Q0FLRVgtQ0ECEx4AAAAEjzQsNDP/rxMAAAAAAAQwDQYJYIZIAWUDBAIBBQCggYQw +# GAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGC +# NwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQx +# IgQgaPgpAtIoqfAkmVtrkHZHzQ5PXwrj5/1WOL3CM/+9XmkwDQYJKoZIhvcNAQEB +# BQAEggIAGpj9jz6DjQQRRO5H5rHIcrh9QgoBdfDY3WHPex3iI81VKi6m8BCk9AuC +# F362kmMD9Qkmp6V8Uu98ibGdOvFzzkkYMpdRgqdDLexxW6HbQemE5aRecG6Wgkya +# IKmAajDeRXwnRTVRPJz7GGut3Zh6Oclt8g0oGfqTyL2+WvXDT3vWTEr8aRcS2Mll +# Wg55oFaJyweuVaDDgGfRudy6YfjYUjh3pV26Ravtnr4xW9R/qHPh7ATV89oJe8n6 +# L03Clh9/Qear83B8b7jKeKXoPFsFUWSaZHMKFjTtiUDxAYawlhsCGjGI8RpltiiX +# OiOUEbBfNQxLtutIc1hDDOL9nzEeh2tlw12SRl9jJlXN07vCmxdVbJFINQ7Zvkn9 +# 0qBP1Hi0I1+hsADgOifTqJk0F3gpR4/BwGEJBUUVBEr5UB9JdWa2m5ji96x8HErc +# ODcSz6pfbUA1qURkWqgS6K+/aHGjiQQwJRTuF8TV+lvQHN3NjxFCFs5JlvSDe+Bp +# 27kcso35FlFaeybz9b59IACL/4ddXBcbdSbYt3awu1o0jNdELpUW/R8w6IM0T8jo +# 3A+37GMksD/9FNmS2XLATxE32xeWo4rgATB4qq4w2ANxiUW5MRqEkUKVJzXMimOJ +# CpIPdo4mHpyCh7TlJgQIQTKB9sk4q5aaY5dwM6Pmj0m5dTaqzow= +# SIG # End signature block diff --git a/WDACConfig/WDACConfig Module Files/XMLOps/Remove-DuplicateAllowAndFileRuleRefElements_IDBased.psm1 b/WDACConfig/WDACConfig Module Files/XMLOps/Remove-DuplicateAllowAndFileRuleRefElements_IDBased.psm1 new file mode 100644 index 000000000..bc041704a --- /dev/null +++ b/WDACConfig/WDACConfig Module Files/XMLOps/Remove-DuplicateAllowAndFileRuleRefElements_IDBased.psm1 @@ -0,0 +1,155 @@ +Function Remove-DuplicateAllowAndFileRuleRefElements_IDBased { + <# + .SYNOPSIS + Removes duplicates elements from the nodes + and elements from the nodes in every node of each node + + The criteria for removing duplicates is the ID attribute of the elements and the RuleID attribute of the elements + .PARAMETER XmlFilePath + The file path of the XML document to be modified + .INPUTS + System.IO.FileInfo + .OUTPUTS + System.Void + #> + [CmdletBinding()] + [OutputType([System.Void])] + param ( + [Parameter(Mandatory = $true)][System.IO.FileInfo]$XmlFilePath + ) + + Begin { + # Importing the $PSDefaultParameterValues to the current session, prior to everything else + . "$ModuleRootPath\CoreExt\PSDefaultParameterValues.ps1" + + # Load the XML document from the specified file path + [System.Xml.XmlDocument]$XmlDocument = Get-Content -Path $XmlFilePath + + # Create a namespace manager for handling XML namespaces + [System.Xml.XmlNamespaceManager]$NsMgr = New-Object -TypeName System.Xml.XmlNamespaceManager -ArgumentList $XmlDocument.NameTable + $NsMgr.AddNamespace('sip', 'urn:schemas-microsoft-com:sipolicy') + } + + Process { + # Remove duplicate elements within the section + [System.Xml.XmlNodeList]$AllowElements = $XmlDocument.SelectNodes('//sip:FileRules/sip:Allow', $NsMgr) + + [System.Collections.Hashtable]$UniqueAllowIDs = @{} + + foreach ($AllowElement in $AllowElements) { + + [System.String]$AllowID = $AllowElement.ID + + if ($UniqueAllowIDs.ContainsKey($AllowID)) { + + Write-Verbose "Removing duplicate Allow element with ID: $AllowID" + [System.Void]$AllowElement.ParentNode.RemoveChild($AllowElement) + } + else { + $UniqueAllowIDs[$AllowID] = $true + } + } + + # Remove duplicate elements within under nodes + [System.Xml.XmlNodeList]$SigningScenarios = $XmlDocument.SelectNodes('//sip:SigningScenarios/sip:SigningScenario', $NsMgr) + + foreach ($Scenario in $SigningScenarios) { + + $ProductSigners = $Scenario.ProductSigners + + $FileRulesRefs = $ProductSigners.FileRulesRef + + foreach ($FileRulesRef in $FileRulesRefs) { + + [System.Collections.Hashtable]$UniqueFileRuleRefIDs = @{} + + [System.Xml.XmlElement[]]$FileRuleRefs = $FileRulesRef.FileRuleRef + + foreach ($FileRuleRef in $FileRuleRefs) { + + [System.String]$RuleID = $FileRuleRef.RuleID + + if ($UniqueFileRuleRefIDs.ContainsKey($RuleID)) { + + Write-Verbose "Removing duplicate FileRuleRef element with ID: $RuleID" + [System.Void]$FileRulesRef.RemoveChild($FileRuleRef) + } + else { + $UniqueFileRuleRefIDs[$RuleID] = $true + } + } + } + } + } + + End { + # Save the modified XML document back to the original file path + $XmlDocument.Save($XmlFilePath) + } +} +Export-ModuleMember -Function 'Remove-DuplicateAllowAndFileRuleRefElements_IDBased' + +# SIG # Begin signature block +# MIILkgYJKoZIhvcNAQcCoIILgzCCC38CAQExDzANBglghkgBZQMEAgEFADB5Bgor +# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG +# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAUYPSwmBG2kef3 +# xjy5uVppffuxp60cpAJy7zSNj3rfFKCCB9AwggfMMIIFtKADAgECAhMeAAAABI80 +# LDQz/68TAAAAAAAEMA0GCSqGSIb3DQEBDQUAME8xEzARBgoJkiaJk/IsZAEZFgNj +# b20xIjAgBgoJkiaJk/IsZAEZFhJIT1RDQUtFWC1DQS1Eb21haW4xFDASBgNVBAMT +# C0hPVENBS0VYLUNBMCAXDTIzMTIyNzExMjkyOVoYDzIyMDgxMTEyMTEyOTI5WjB5 +# MQswCQYDVQQGEwJVSzEeMBwGA1UEAxMVSG90Q2FrZVggQ29kZSBTaWduaW5nMSMw +# IQYJKoZIhvcNAQkBFhRob3RjYWtleEBvdXRsb29rLmNvbTElMCMGCSqGSIb3DQEJ +# ARYWU3B5bmV0Z2lybEBvdXRsb29rLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIP +# ADCCAgoCggIBAKb1BJzTrpu1ERiwr7ivp0UuJ1GmNmmZ65eckLpGSF+2r22+7Tgm +# pEifj9NhPw0X60F9HhdSM+2XeuikmaNMvq8XRDUFoenv9P1ZU1wli5WTKHJ5ayDW +# k2NP22G9IPRnIpizkHkQnCwctx0AFJx1qvvd+EFlG6ihM0fKGG+DwMaFqsKCGh+M +# rb1bKKtY7UEnEVAsVi7KYGkkH+ukhyFUAdUbh/3ZjO0xWPYpkf/1ldvGes6pjK6P +# US2PHbe6ukiupqYYG3I5Ad0e20uQfZbz9vMSTiwslLhmsST0XAesEvi+SJYz2xAQ +# x2O4n/PxMRxZ3m5Q0WQxLTGFGjB2Bl+B+QPBzbpwb9JC77zgA8J2ncP2biEguSRJ +# e56Ezx6YpSoRv4d1jS3tpRL+ZFm8yv6We+hodE++0tLsfpUq42Guy3MrGQ2kTIRo +# 7TGLOLpayR8tYmnF0XEHaBiVl7u/Szr7kmOe/CfRG8IZl6UX+/66OqZeyJ12Q3m2 +# fe7ZWnpWT5sVp2sJmiuGb3atFXBWKcwNumNuy4JecjQE+7NF8rfIv94NxbBV/WSM +# pKf6Yv9OgzkjY1nRdIS1FBHa88RR55+7Ikh4FIGPBTAibiCEJMc79+b8cdsQGOo4 +# ymgbKjGeoRNjtegZ7XE/3TUywBBFMf8NfcjF8REs/HIl7u2RHwRaUTJdAgMBAAGj +# ggJzMIICbzA8BgkrBgEEAYI3FQcELzAtBiUrBgEEAYI3FQiG7sUghM++I4HxhQSF +# hqV1htyhDXuG5sF2wOlDAgFkAgEIMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA4GA1Ud +# DwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBsGCSsGAQQBgjcVCgQOMAwwCgYIKwYB +# BQUHAwMwHQYDVR0OBBYEFOlnnQDHNUpYoPqECFP6JAqGDFM6MB8GA1UdIwQYMBaA +# FICT0Mhz5MfqMIi7Xax90DRKYJLSMIHUBgNVHR8EgcwwgckwgcaggcOggcCGgb1s +# ZGFwOi8vL0NOPUhPVENBS0VYLUNBLENOPUhvdENha2VYLENOPUNEUCxDTj1QdWJs +# aWMlMjBLZXklMjBTZXJ2aWNlcyxDTj1TZXJ2aWNlcyxDTj1Db25maWd1cmF0aW9u +# LERDPU5vbkV4aXN0ZW50RG9tYWluLERDPWNvbT9jZXJ0aWZpY2F0ZVJldm9jYXRp +# b25MaXN0P2Jhc2U/b2JqZWN0Q2xhc3M9Y1JMRGlzdHJpYnV0aW9uUG9pbnQwgccG +# CCsGAQUFBwEBBIG6MIG3MIG0BggrBgEFBQcwAoaBp2xkYXA6Ly8vQ049SE9UQ0FL +# RVgtQ0EsQ049QUlBLENOPVB1YmxpYyUyMEtleSUyMFNlcnZpY2VzLENOPVNlcnZp +# Y2VzLENOPUNvbmZpZ3VyYXRpb24sREM9Tm9uRXhpc3RlbnREb21haW4sREM9Y29t +# P2NBQ2VydGlmaWNhdGU/YmFzZT9vYmplY3RDbGFzcz1jZXJ0aWZpY2F0aW9uQXV0 +# aG9yaXR5MA0GCSqGSIb3DQEBDQUAA4ICAQA7JI76Ixy113wNjiJmJmPKfnn7brVI +# IyA3ZudXCheqWTYPyYnwzhCSzKJLejGNAsMlXwoYgXQBBmMiSI4Zv4UhTNc4Umqx +# pZSpqV+3FRFQHOG/X6NMHuFa2z7T2pdj+QJuH5TgPayKAJc+Kbg4C7edL6YoePRu +# HoEhoRffiabEP/yDtZWMa6WFqBsfgiLMlo7DfuhRJ0eRqvJ6+czOVU2bxvESMQVo +# bvFTNDlEcUzBM7QxbnsDyGpoJZTx6M3cUkEazuliPAw3IW1vJn8SR1jFBukKcjWn +# aau+/BE9w77GFz1RbIfH3hJ/CUA0wCavxWcbAHz1YoPTAz6EKjIc5PcHpDO+n8Fh +# t3ULwVjWPMoZzU589IXi+2Ol0IUWAdoQJr/Llhub3SNKZ3LlMUPNt+tXAs/vcUl0 +# 7+Dp5FpUARE2gMYA/XxfU9T6Q3pX3/NRP/ojO9m0JrKv/KMc9sCGmV9sDygCOosU +# 5yGS4Ze/DJw6QR7xT9lMiWsfgL96Qcw4lfu1+5iLr0dnDFsGowGTKPGI0EvzK7H+ +# DuFRg+Fyhn40dOUl8fVDqYHuZJRoWJxCsyobVkrX4rA6xUTswl7xYPYWz88WZDoY +# gI8AwuRkzJyUEA07IYtsbFCYrcUzIHME4uf8jsJhCmb0va1G2WrWuyasv3K/G8Nn +# f60MsDbDH1mLtzGCAxgwggMUAgEBMGYwTzETMBEGCgmSJomT8ixkARkWA2NvbTEi +# MCAGCgmSJomT8ixkARkWEkhPVENBS0VYLUNBLURvbWFpbjEUMBIGA1UEAxMLSE9U +# Q0FLRVgtQ0ECEx4AAAAEjzQsNDP/rxMAAAAAAAQwDQYJYIZIAWUDBAIBBQCggYQw +# GAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGC +# NwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQx +# IgQgin9CocmAhQ1RDZoyhF8+yV8bamI2/H7b00WBB0oiu0owDQYJKoZIhvcNAQEB +# BQAEggIAbzSwTiIRWFRX4Oe9y6w6CKVzXJ6vepkKo+vN0R25fL+LplSDU7dg8sf9 +# BV5HBFwcNsdUPx40KmdVC0wZiHErkBQ3Za174BhUarlYHUinSz+3t7r4SW/+pzSc +# a5ldHDrhVgxUJOZYCMWZouoZzVC9ZUBVxIzRCWFmDY7hMCaK+RqYW/WhQ1hO/thT +# bXU7zanT9hv7T6xN3aJA7o3QLdoxg7YgIXmA1Im2l0iRqkiQ4+Thhz2Jwrsv3e41 +# n6xDdqiEsLGRawCDxSa7WFLOLicDkz7EoFyuYc0iSuYuA28iS458/j4M6X2PQn1R +# SRDkFUREpGsp9DCI1viDjy8x4Yxs8S6OEoF9X27tF1uFVvzzDbVIHu7RAH2R/vTt +# FdkNpVWYGRuR0xnTzHA/rfYvkZiJwnJr04ltPY440kZXwObuXWxQhA9zdVozxIAw +# EQdBU2AgVUpvGPy+MmLphMU2yZ8OKOS7TQKK8vBRqRiHCPZfwgUHNZrTdS9qObVg +# UDZ8ncFqNw5rp1vQOeQZLcLBGwji9bg7dnmWIFERuqVHV/noTfkakT8+a0hL15qC +# mEgWk4qr11/jksX0etRFWwriy5KT82IN+i3CXTUpnA/ftDgCtF17DCwhIkypnt+H +# QeXI/10HDiAjQpAfH8S2t9HYx4ck05cX/xsaN/PVFf35RfjPBNA= +# SIG # End signature block diff --git a/WDACConfig/WDACConfig Module Files/XMLOps/Remove-DuplicateAllowedSignersAndCiSigners_IDBased.psm1 b/WDACConfig/WDACConfig Module Files/XMLOps/Remove-DuplicateAllowedSignersAndCiSigners_IDBased.psm1 new file mode 100644 index 000000000..5b1f46043 --- /dev/null +++ b/WDACConfig/WDACConfig Module Files/XMLOps/Remove-DuplicateAllowedSignersAndCiSigners_IDBased.psm1 @@ -0,0 +1,134 @@ +Function Remove-DuplicateAllowedSignersAndCiSigners_IDBased { + <# + .SYNOPSIS + Removes duplicate SignerIds from the CiSigners and AllowedSigners nodes from each Signing Scenario in a CI policy XML file + The criteria for removing duplicates is the SignerId attribute of the CiSigner and AllowedSigner nodes + .PARAMETER Path + The path to the CI policy XML file + .INPUTS + System.IO.FileInfo + .OUTPUTS + System.Void + #> + [CmdletBinding()] + param ( + [Parameter(Mandatory = $true)][System.IO.FileInfo]$Path + ) + + Begin { + # Importing the $PSDefaultParameterValues to the current session, prior to everything else + . "$ModuleRootPath\CoreExt\PSDefaultParameterValues.ps1" + + # Load the XML file + [System.Xml.XmlDocument]$Xml = Get-Content -Path $Path + + # Create an XmlNamespaceManager for namespace resolution + [System.Xml.XmlNamespaceManager]$NsManager = New-Object System.Xml.XmlNamespaceManager -ArgumentList $Xml.NameTable + $NsManager.AddNamespace('ns', 'urn:schemas-microsoft-com:sipolicy') + + Function Remove-DuplicateSignerIds { + <# + .SYNOPSIS + Removes duplicate SignerIds from the given XmlNodeList + #> + Param( + [Parameter(Mandatory = $true)][System.Xml.XmlNodeList]$NodeList + ) + + [System.String[]]$UniqueSignerIds = @() + + foreach ($Node in $NodeList) { + if ($UniqueSignerIds -notcontains $Node.SignerId) { + $UniqueSignerIds += $Node.SignerId + } + else { + [System.Void]$Node.ParentNode.RemoveChild($Node) + } + } + } + } + + Process { + + # Get CiSigners and AllowedSigners nodes + [System.Xml.XmlNodeList]$CiSigners = $Xml.SelectNodes('//ns:CiSigners/ns:CiSigner', $NsManager) + [System.Xml.XmlNodeList]$AllowedSigners12 = $Xml.SelectNodes('//ns:SigningScenarios/ns:SigningScenario[@Value="12"]/ns:ProductSigners/ns:AllowedSigners/ns:AllowedSigner', $NsManager) + [System.Xml.XmlNodeList]$AllowedSigners131 = $Xml.SelectNodes('//ns:SigningScenarios/ns:SigningScenario[@Value="131"]/ns:ProductSigners/ns:AllowedSigners/ns:AllowedSigner', $NsManager) + + # Remove duplicate signer IDs from CiSigners and AllowedSigners + Remove-DuplicateSignerIds $CiSigners + Remove-DuplicateSignerIds $AllowedSigners12 + Remove-DuplicateSignerIds $AllowedSigners131 + } + + End { + # Save the changes to the XML file + $Xml.Save($Path) + } +} +Export-ModuleMember -Function 'Remove-DuplicateAllowedSignersAndCiSigners_IDBased' + +# SIG # Begin signature block +# MIILkgYJKoZIhvcNAQcCoIILgzCCC38CAQExDzANBglghkgBZQMEAgEFADB5Bgor +# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG +# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBD57nzsQAK99cD +# qbvsBQeiQYJUJb2c7gPO6iklfapr+KCCB9AwggfMMIIFtKADAgECAhMeAAAABI80 +# LDQz/68TAAAAAAAEMA0GCSqGSIb3DQEBDQUAME8xEzARBgoJkiaJk/IsZAEZFgNj +# b20xIjAgBgoJkiaJk/IsZAEZFhJIT1RDQUtFWC1DQS1Eb21haW4xFDASBgNVBAMT +# C0hPVENBS0VYLUNBMCAXDTIzMTIyNzExMjkyOVoYDzIyMDgxMTEyMTEyOTI5WjB5 +# MQswCQYDVQQGEwJVSzEeMBwGA1UEAxMVSG90Q2FrZVggQ29kZSBTaWduaW5nMSMw +# IQYJKoZIhvcNAQkBFhRob3RjYWtleEBvdXRsb29rLmNvbTElMCMGCSqGSIb3DQEJ +# ARYWU3B5bmV0Z2lybEBvdXRsb29rLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIP +# ADCCAgoCggIBAKb1BJzTrpu1ERiwr7ivp0UuJ1GmNmmZ65eckLpGSF+2r22+7Tgm +# pEifj9NhPw0X60F9HhdSM+2XeuikmaNMvq8XRDUFoenv9P1ZU1wli5WTKHJ5ayDW +# k2NP22G9IPRnIpizkHkQnCwctx0AFJx1qvvd+EFlG6ihM0fKGG+DwMaFqsKCGh+M +# rb1bKKtY7UEnEVAsVi7KYGkkH+ukhyFUAdUbh/3ZjO0xWPYpkf/1ldvGes6pjK6P +# US2PHbe6ukiupqYYG3I5Ad0e20uQfZbz9vMSTiwslLhmsST0XAesEvi+SJYz2xAQ +# x2O4n/PxMRxZ3m5Q0WQxLTGFGjB2Bl+B+QPBzbpwb9JC77zgA8J2ncP2biEguSRJ +# e56Ezx6YpSoRv4d1jS3tpRL+ZFm8yv6We+hodE++0tLsfpUq42Guy3MrGQ2kTIRo +# 7TGLOLpayR8tYmnF0XEHaBiVl7u/Szr7kmOe/CfRG8IZl6UX+/66OqZeyJ12Q3m2 +# fe7ZWnpWT5sVp2sJmiuGb3atFXBWKcwNumNuy4JecjQE+7NF8rfIv94NxbBV/WSM +# pKf6Yv9OgzkjY1nRdIS1FBHa88RR55+7Ikh4FIGPBTAibiCEJMc79+b8cdsQGOo4 +# ymgbKjGeoRNjtegZ7XE/3TUywBBFMf8NfcjF8REs/HIl7u2RHwRaUTJdAgMBAAGj +# ggJzMIICbzA8BgkrBgEEAYI3FQcELzAtBiUrBgEEAYI3FQiG7sUghM++I4HxhQSF +# hqV1htyhDXuG5sF2wOlDAgFkAgEIMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA4GA1Ud +# DwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBsGCSsGAQQBgjcVCgQOMAwwCgYIKwYB +# BQUHAwMwHQYDVR0OBBYEFOlnnQDHNUpYoPqECFP6JAqGDFM6MB8GA1UdIwQYMBaA +# FICT0Mhz5MfqMIi7Xax90DRKYJLSMIHUBgNVHR8EgcwwgckwgcaggcOggcCGgb1s +# ZGFwOi8vL0NOPUhPVENBS0VYLUNBLENOPUhvdENha2VYLENOPUNEUCxDTj1QdWJs +# aWMlMjBLZXklMjBTZXJ2aWNlcyxDTj1TZXJ2aWNlcyxDTj1Db25maWd1cmF0aW9u +# LERDPU5vbkV4aXN0ZW50RG9tYWluLERDPWNvbT9jZXJ0aWZpY2F0ZVJldm9jYXRp +# b25MaXN0P2Jhc2U/b2JqZWN0Q2xhc3M9Y1JMRGlzdHJpYnV0aW9uUG9pbnQwgccG +# CCsGAQUFBwEBBIG6MIG3MIG0BggrBgEFBQcwAoaBp2xkYXA6Ly8vQ049SE9UQ0FL +# RVgtQ0EsQ049QUlBLENOPVB1YmxpYyUyMEtleSUyMFNlcnZpY2VzLENOPVNlcnZp +# Y2VzLENOPUNvbmZpZ3VyYXRpb24sREM9Tm9uRXhpc3RlbnREb21haW4sREM9Y29t +# P2NBQ2VydGlmaWNhdGU/YmFzZT9vYmplY3RDbGFzcz1jZXJ0aWZpY2F0aW9uQXV0 +# aG9yaXR5MA0GCSqGSIb3DQEBDQUAA4ICAQA7JI76Ixy113wNjiJmJmPKfnn7brVI +# IyA3ZudXCheqWTYPyYnwzhCSzKJLejGNAsMlXwoYgXQBBmMiSI4Zv4UhTNc4Umqx +# pZSpqV+3FRFQHOG/X6NMHuFa2z7T2pdj+QJuH5TgPayKAJc+Kbg4C7edL6YoePRu +# HoEhoRffiabEP/yDtZWMa6WFqBsfgiLMlo7DfuhRJ0eRqvJ6+czOVU2bxvESMQVo +# bvFTNDlEcUzBM7QxbnsDyGpoJZTx6M3cUkEazuliPAw3IW1vJn8SR1jFBukKcjWn +# aau+/BE9w77GFz1RbIfH3hJ/CUA0wCavxWcbAHz1YoPTAz6EKjIc5PcHpDO+n8Fh +# t3ULwVjWPMoZzU589IXi+2Ol0IUWAdoQJr/Llhub3SNKZ3LlMUPNt+tXAs/vcUl0 +# 7+Dp5FpUARE2gMYA/XxfU9T6Q3pX3/NRP/ojO9m0JrKv/KMc9sCGmV9sDygCOosU +# 5yGS4Ze/DJw6QR7xT9lMiWsfgL96Qcw4lfu1+5iLr0dnDFsGowGTKPGI0EvzK7H+ +# DuFRg+Fyhn40dOUl8fVDqYHuZJRoWJxCsyobVkrX4rA6xUTswl7xYPYWz88WZDoY +# gI8AwuRkzJyUEA07IYtsbFCYrcUzIHME4uf8jsJhCmb0va1G2WrWuyasv3K/G8Nn +# f60MsDbDH1mLtzGCAxgwggMUAgEBMGYwTzETMBEGCgmSJomT8ixkARkWA2NvbTEi +# MCAGCgmSJomT8ixkARkWEkhPVENBS0VYLUNBLURvbWFpbjEUMBIGA1UEAxMLSE9U +# Q0FLRVgtQ0ECEx4AAAAEjzQsNDP/rxMAAAAAAAQwDQYJYIZIAWUDBAIBBQCggYQw +# GAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGC +# NwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQx +# IgQg1RN3lv8q51+3dbDfw6RDdO/tMCkQ3qA/13m/BmWHVTwwDQYJKoZIhvcNAQEB +# BQAEggIAJqea3FtWUkQnS6LT4EOvt+vgEbLel8ximpLvK5osc6I7z6mZ45S/ckwh +# tCNa9xWRBAlYZoUFNqvOXRdG2f+T/oDKClwdoQM7iECGJQLoUNb3o5AEFrWbOeqL +# rdJ6t1TgDflHhFzg+9+WlkIKUuDNtrveEWBxsEC+axm/FwBMpr11GXgvIvuWdlpd +# L4RzW0XWZqsQ1FMiQrz3uZ0gUSEiLV8umoGB9tYzyf4kcq5KbeGyW/q/J9E+OsQc +# 1r4DW8IRP+sl1OLLP04wv6CaFbEib+xsU4JtiZ0QXLRZ3/iN1uj/zp6KFR1hL6c8 +# UdbF65plmRNJhX1bgjGFvq5gO7dj3SPvbuGTRsh3Tr36i54KaEHTKwjbpLw4bZ2F +# ia4cAdm+SsPjmtzdWplkGctt7mWtj9pIptJ/K2igeUhgfCiWTxJaGSjvR47frBLT +# gMcw3X/truTLkiR9iOVUuouNuZRq0bFZMgyiNtdlH+90QHMoPeMjTH0JIku5Yb/g +# XVeh7CjBDWKlv9dDRkW9L7pGYbAj0RiQCBnTyoGpTvLD2H/u7qpcwFEUmvBLqXmR +# HRvex/Xzm9XC03N1xDTK/E+nDriFX77LD0Tzb75ghXuWRGdfKRwyft4eMsoIpo+M +# Is7zpbQlEn57yzwnPVSIg157V0mtiUmcUjDjcohRdLgkED27ezI= +# SIG # End signature block diff --git a/WDACConfig/WDACConfig Module Files/XMLOps/Remove-DuplicateFileAttribRef_IDBased.psm1 b/WDACConfig/WDACConfig Module Files/XMLOps/Remove-DuplicateFileAttribRef_IDBased.psm1 new file mode 100644 index 000000000..abeaef8d7 --- /dev/null +++ b/WDACConfig/WDACConfig Module Files/XMLOps/Remove-DuplicateFileAttribRef_IDBased.psm1 @@ -0,0 +1,126 @@ +Function Remove-DuplicateFileAttribRef_IDBased { + <# + .SYNOPSIS + Loops through each Signer element in node in the XML file and removes duplicate FileAttribRef elements inside them + Based on the RuleID attribute + This is according to the ConfigCI Schema + .PARAMETER XmlFilePath + The path to the XML file to be modified + .INPUTS + System.IO.FileInfo + .OUTPUTS + System.Void + #> + [CmdletBinding()] + [OutputType([System.Void])] + Param( + [Parameter(Mandatory = $true)][System.IO.FileInfo]$XmlFilePath + ) + + Begin { + # Importing the $PSDefaultParameterValues to the current session, prior to everything else + . "$ModuleRootPath\CoreExt\PSDefaultParameterValues.ps1" + + # Load the XML file + [System.Xml.XmlDocument]$Xml = Get-Content -Path $XmlFilePath + } + + Process { + + # Iterate through each Signer element + foreach ($Signer in $Xml.SiPolicy.Signers.Signer) { + + # Create a hashtable to track unique FileAttribRef IDs + [System.Collections.Hashtable]$UniqueFileAttribRefs = @{} + + # Iterate through each FileAttribRef element of the current signer + foreach ($FileAttribRef in $Signer.FileAttribRef) { + + # Get the RuleID attribute value of the current FileAttribRef element + [System.String]$FileAttribRefID = $FileAttribRef.RuleID + + # Check if the current FileAttribRef ID already exists in the hashtable + if (-not $UniqueFileAttribRefs.ContainsKey($FileAttribRefID)) { + + # If not, add it to the hashtable and keep the FileAttribRef element + $UniqueFileAttribRefs[$FileAttribRefID] = $true + } + else { + # If it exists, remove the duplicate FileAttribRef element + [System.Void]$Signer.RemoveChild($FileAttribRef) + } + } + } + } + + End { + # Save the modified XML back to the file + $Xml.Save($XmlFilePath) + } +} +Export-ModuleMember -Function 'Remove-DuplicateFileAttribRef_IDBased' + +# SIG # Begin signature block +# MIILkgYJKoZIhvcNAQcCoIILgzCCC38CAQExDzANBglghkgBZQMEAgEFADB5Bgor +# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG +# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCD4nhMa4E+sitTs +# 3B4CBp/7ekdMtKubP7MC/msdVFT9PaCCB9AwggfMMIIFtKADAgECAhMeAAAABI80 +# LDQz/68TAAAAAAAEMA0GCSqGSIb3DQEBDQUAME8xEzARBgoJkiaJk/IsZAEZFgNj +# b20xIjAgBgoJkiaJk/IsZAEZFhJIT1RDQUtFWC1DQS1Eb21haW4xFDASBgNVBAMT +# C0hPVENBS0VYLUNBMCAXDTIzMTIyNzExMjkyOVoYDzIyMDgxMTEyMTEyOTI5WjB5 +# MQswCQYDVQQGEwJVSzEeMBwGA1UEAxMVSG90Q2FrZVggQ29kZSBTaWduaW5nMSMw +# IQYJKoZIhvcNAQkBFhRob3RjYWtleEBvdXRsb29rLmNvbTElMCMGCSqGSIb3DQEJ +# ARYWU3B5bmV0Z2lybEBvdXRsb29rLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIP +# ADCCAgoCggIBAKb1BJzTrpu1ERiwr7ivp0UuJ1GmNmmZ65eckLpGSF+2r22+7Tgm +# pEifj9NhPw0X60F9HhdSM+2XeuikmaNMvq8XRDUFoenv9P1ZU1wli5WTKHJ5ayDW +# k2NP22G9IPRnIpizkHkQnCwctx0AFJx1qvvd+EFlG6ihM0fKGG+DwMaFqsKCGh+M +# rb1bKKtY7UEnEVAsVi7KYGkkH+ukhyFUAdUbh/3ZjO0xWPYpkf/1ldvGes6pjK6P +# US2PHbe6ukiupqYYG3I5Ad0e20uQfZbz9vMSTiwslLhmsST0XAesEvi+SJYz2xAQ +# x2O4n/PxMRxZ3m5Q0WQxLTGFGjB2Bl+B+QPBzbpwb9JC77zgA8J2ncP2biEguSRJ +# e56Ezx6YpSoRv4d1jS3tpRL+ZFm8yv6We+hodE++0tLsfpUq42Guy3MrGQ2kTIRo +# 7TGLOLpayR8tYmnF0XEHaBiVl7u/Szr7kmOe/CfRG8IZl6UX+/66OqZeyJ12Q3m2 +# fe7ZWnpWT5sVp2sJmiuGb3atFXBWKcwNumNuy4JecjQE+7NF8rfIv94NxbBV/WSM +# pKf6Yv9OgzkjY1nRdIS1FBHa88RR55+7Ikh4FIGPBTAibiCEJMc79+b8cdsQGOo4 +# ymgbKjGeoRNjtegZ7XE/3TUywBBFMf8NfcjF8REs/HIl7u2RHwRaUTJdAgMBAAGj +# ggJzMIICbzA8BgkrBgEEAYI3FQcELzAtBiUrBgEEAYI3FQiG7sUghM++I4HxhQSF +# hqV1htyhDXuG5sF2wOlDAgFkAgEIMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA4GA1Ud +# DwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBsGCSsGAQQBgjcVCgQOMAwwCgYIKwYB +# BQUHAwMwHQYDVR0OBBYEFOlnnQDHNUpYoPqECFP6JAqGDFM6MB8GA1UdIwQYMBaA +# FICT0Mhz5MfqMIi7Xax90DRKYJLSMIHUBgNVHR8EgcwwgckwgcaggcOggcCGgb1s +# ZGFwOi8vL0NOPUhPVENBS0VYLUNBLENOPUhvdENha2VYLENOPUNEUCxDTj1QdWJs +# aWMlMjBLZXklMjBTZXJ2aWNlcyxDTj1TZXJ2aWNlcyxDTj1Db25maWd1cmF0aW9u +# LERDPU5vbkV4aXN0ZW50RG9tYWluLERDPWNvbT9jZXJ0aWZpY2F0ZVJldm9jYXRp +# b25MaXN0P2Jhc2U/b2JqZWN0Q2xhc3M9Y1JMRGlzdHJpYnV0aW9uUG9pbnQwgccG +# CCsGAQUFBwEBBIG6MIG3MIG0BggrBgEFBQcwAoaBp2xkYXA6Ly8vQ049SE9UQ0FL +# RVgtQ0EsQ049QUlBLENOPVB1YmxpYyUyMEtleSUyMFNlcnZpY2VzLENOPVNlcnZp +# Y2VzLENOPUNvbmZpZ3VyYXRpb24sREM9Tm9uRXhpc3RlbnREb21haW4sREM9Y29t +# P2NBQ2VydGlmaWNhdGU/YmFzZT9vYmplY3RDbGFzcz1jZXJ0aWZpY2F0aW9uQXV0 +# aG9yaXR5MA0GCSqGSIb3DQEBDQUAA4ICAQA7JI76Ixy113wNjiJmJmPKfnn7brVI +# IyA3ZudXCheqWTYPyYnwzhCSzKJLejGNAsMlXwoYgXQBBmMiSI4Zv4UhTNc4Umqx +# pZSpqV+3FRFQHOG/X6NMHuFa2z7T2pdj+QJuH5TgPayKAJc+Kbg4C7edL6YoePRu +# HoEhoRffiabEP/yDtZWMa6WFqBsfgiLMlo7DfuhRJ0eRqvJ6+czOVU2bxvESMQVo +# bvFTNDlEcUzBM7QxbnsDyGpoJZTx6M3cUkEazuliPAw3IW1vJn8SR1jFBukKcjWn +# aau+/BE9w77GFz1RbIfH3hJ/CUA0wCavxWcbAHz1YoPTAz6EKjIc5PcHpDO+n8Fh +# t3ULwVjWPMoZzU589IXi+2Ol0IUWAdoQJr/Llhub3SNKZ3LlMUPNt+tXAs/vcUl0 +# 7+Dp5FpUARE2gMYA/XxfU9T6Q3pX3/NRP/ojO9m0JrKv/KMc9sCGmV9sDygCOosU +# 5yGS4Ze/DJw6QR7xT9lMiWsfgL96Qcw4lfu1+5iLr0dnDFsGowGTKPGI0EvzK7H+ +# DuFRg+Fyhn40dOUl8fVDqYHuZJRoWJxCsyobVkrX4rA6xUTswl7xYPYWz88WZDoY +# gI8AwuRkzJyUEA07IYtsbFCYrcUzIHME4uf8jsJhCmb0va1G2WrWuyasv3K/G8Nn +# f60MsDbDH1mLtzGCAxgwggMUAgEBMGYwTzETMBEGCgmSJomT8ixkARkWA2NvbTEi +# MCAGCgmSJomT8ixkARkWEkhPVENBS0VYLUNBLURvbWFpbjEUMBIGA1UEAxMLSE9U +# Q0FLRVgtQ0ECEx4AAAAEjzQsNDP/rxMAAAAAAAQwDQYJYIZIAWUDBAIBBQCggYQw +# GAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGC +# NwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQx +# IgQgatJA+Q54vaBmvRt1j3PkTGi7Y9d9U2yfvEN/hKF+xKUwDQYJKoZIhvcNAQEB +# BQAEggIAD7K+2ZkX52zzwEbgFpRByWR1bM2fN1SHCsMfbE8XCxqYGoRvFUo6/UlI +# nn3/OacKaMSj6hFXDgAnonK5cUp0Ig+LR6+WcK988CUIPEf3yRtnen6c92u702lJ +# /yFvutxJtk6NtgAfnmwXRSHyyUr4L54AJ4aNS/5Z6fkwRSM3q8JqcfUsXc1Wlu6I +# x2LLj/XngQM9uUpG7U7UZXJGcw7/Qx2jUCAg6KV49LBPF+PlrEwS5xSism8PKs33 +# pqU+iqx3yayA86/VKbADr3iJ/5XCvgldff7zyiUk6rLiXi3PyDTmvoON2H2tb0XL +# olmT/0eUc2wlqVYAqDLJJoG0NcveqpxVl62h+3oPK3vLpvebnRs51RcTlcjiWqNE +# jCNLulyDiLYw7F5nqQN/xy+oXh2t+G7bhW12PHhF3jFmrIqM7onWAvaZnNqH4ev6 +# KtXuaBaOhPWiP0mc0k/aPJjRs3IqYQc6IkusV8NYp2mkup3xQFggXtTilTVD6YXz +# R55x3oTgCI4Gc8fuxp7luNux20g6tHhiJMiz5iP2gaAL7uNqE2FScAQmZvH1tbYg +# YAwobQD7X+/YoAKxuNQyA7SRj6u9Uke6J2HV11Awnku2AIto6pvdkCywVPEDOc8s +# Lly60FNiCGUIxMqwXa9vk5F5sxg2eUR61CIXF66pnWEjZPEwnG8= +# SIG # End signature block diff --git a/WDACConfig/WDACConfig Module Files/XMLOps/Remove-DuplicateFileAttrib_IDBased.psm1 b/WDACConfig/WDACConfig Module Files/XMLOps/Remove-DuplicateFileAttrib_IDBased.psm1 new file mode 100644 index 000000000..2f885366a --- /dev/null +++ b/WDACConfig/WDACConfig Module Files/XMLOps/Remove-DuplicateFileAttrib_IDBased.psm1 @@ -0,0 +1,188 @@ +Function Remove-DuplicateFileAttrib_IDBased { + <# + .SYNOPSIS + Takes a path to an XML file and removes duplicate FileAttrib elements from the node + and duplicate FileRuleRef elements from the node under each node + and duplicate FileAttribRef elements from the node under each node. + + The criteria for removing duplicates is the ID attribute of the FileAttrib elements and the RuleID attribute of the FileRuleRef elements + .PARAMETER XmlFilePath + The path to the XML file to be modified. + .INPUTS + System.IO.FileInfo + .OUTPUTS + System.Void + #> + [CmdletBinding()] + [OutputType([System.Void])] + param ( + [Parameter(Mandatory = $true)][System.IO.FileInfo]$XmlFilePath + ) + + Begin { + # Load the XML file + [System.Xml.XmlDocument]$Xml = Get-Content -Path $XmlFilePath + + # Define namespace manager + [System.Xml.XmlNamespaceManager]$NsMgr = New-Object -TypeName System.Xml.XmlNamespaceManager -ArgumentList $Xml.NameTable + $NsMgr.AddNamespace('sip', 'urn:schemas-microsoft-com:sipolicy') + } + + Process { + # Importing the $PSDefaultParameterValues to the current session, prior to everything else + . "$ModuleRootPath\CoreExt\PSDefaultParameterValues.ps1" + + # Get all FileAttrib elements + [System.Xml.XmlNodeList]$FileAttribs = $Xml.SelectNodes('//sip:FileRules/sip:FileAttrib', $NsMgr) + + # Track seen FileAttrib IDs + [System.Collections.Hashtable]$SeenFileAttribIDs = @{} + + # Loop through each FileAttrib element + foreach ($FileAttrib in $FileAttribs) { + + [System.String]$FileAttribID = $FileAttrib.ID + + # Check if the FileAttrib ID has been seen before + if ($SeenFileAttribIDs.ContainsKey($FileAttribID)) { + + Write-Verbose -Message "Remove-DuplicateFileAttrib: Removed duplicate FileAttrib with ID: $FileAttribID" + [System.Void]$FileAttrib.ParentNode.RemoveChild($FileAttrib) + } + else { + # If not seen before, add to seen FileAttrib IDs + $SeenFileAttribIDs[$FileAttribID] = $true + } + } + + # Get all ProductSigners under SigningScenarios + [System.Xml.XmlNodeList]$SigningScenarios = $Xml.SelectNodes('//sip:SigningScenarios/sip:SigningScenario', $NsMgr) + + # Loop through each SigningScenario + foreach ($Scenario in $SigningScenarios) { + + # Track seen FileRuleRef IDs + [System.Collections.Hashtable]$SeenFileRuleRefIDs = @{} + + # Get all FileRuleRef elements under ProductSigners + $FileRuleRefs = $Scenario.ProductSigners.FileRulesRef.FileRuleRef + + # Loop through each FileRuleRef element + foreach ($FileRuleRef in $FileRuleRefs) { + + [System.String]$FileRuleRefID = $FileRuleRef.RuleID + + # Check if the FileRuleRef ID has been seen before + if ($SeenFileRuleRefIDs.ContainsKey($FileRuleRefID)) { + + Write-Verbose -Message "Remove-DuplicateFileAttrib: Removed duplicate FileRuleRef with ID: $FileRuleRefID" + [System.Void]$FileRuleRef.ParentNode.RemoveChild($FileRuleRef) + } + else { + # If not seen before, add to seen FileRuleRef IDs + $SeenFileRuleRefIDs[$FileRuleRefID] = $true + } + } + } + + # Get all Signers + [System.Xml.XmlNodeList]$Signers = $Xml.SelectNodes('//sip:Signers/sip:Signer', $NsMgr) + + # Loop through each Signer + foreach ($Signer in $Signers) { + + # Get all FileAttribRef elements under the Signer + [System.Xml.XmlElement[]]$FileAttribRefs = $Signer.ChildNodes | Where-Object -FilterScript { $_.Name -eq 'FileAttribRef' } + + # Track seen FileAttribRef IDs + [System.Collections.Hashtable]$SeenFileAttribRefIDs = @{} + + # Loop through each FileAttribRef element + foreach ($FileAttribRef in $FileAttribRefs) { + + [System.String]$FileAttribRefID = $FileAttribRef.RuleID + + # Check if the FileAttribRef ID has been seen before + if ($SeenFileAttribRefIDs.ContainsKey($FileAttribRefID)) { + + Write-Verbose -Message "Remove-DuplicateFileAttrib: Removed duplicate FileAttribRef with ID: $FileAttribRefID" + [System.Void]$Signer.RemoveChild($FileAttribRef) + } + else { + # If not seen before, add to seen FileAttribRef IDs + $SeenFileAttribRefIDs[$FileAttribRefID] = $true + } + } + } + } + End { + # Save the modified XML + $Xml.Save($XmlFilePath) + } +} +Export-ModuleMember -Function 'Remove-DuplicateFileAttrib_IDBased' + +# SIG # Begin signature block +# MIILkgYJKoZIhvcNAQcCoIILgzCCC38CAQExDzANBglghkgBZQMEAgEFADB5Bgor +# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG +# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDN5OVdlgeByEF5 +# B7pf4E6TucvFwkeoAMopiXBEMKe+N6CCB9AwggfMMIIFtKADAgECAhMeAAAABI80 +# LDQz/68TAAAAAAAEMA0GCSqGSIb3DQEBDQUAME8xEzARBgoJkiaJk/IsZAEZFgNj +# b20xIjAgBgoJkiaJk/IsZAEZFhJIT1RDQUtFWC1DQS1Eb21haW4xFDASBgNVBAMT +# C0hPVENBS0VYLUNBMCAXDTIzMTIyNzExMjkyOVoYDzIyMDgxMTEyMTEyOTI5WjB5 +# MQswCQYDVQQGEwJVSzEeMBwGA1UEAxMVSG90Q2FrZVggQ29kZSBTaWduaW5nMSMw +# IQYJKoZIhvcNAQkBFhRob3RjYWtleEBvdXRsb29rLmNvbTElMCMGCSqGSIb3DQEJ +# ARYWU3B5bmV0Z2lybEBvdXRsb29rLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIP +# ADCCAgoCggIBAKb1BJzTrpu1ERiwr7ivp0UuJ1GmNmmZ65eckLpGSF+2r22+7Tgm +# pEifj9NhPw0X60F9HhdSM+2XeuikmaNMvq8XRDUFoenv9P1ZU1wli5WTKHJ5ayDW +# k2NP22G9IPRnIpizkHkQnCwctx0AFJx1qvvd+EFlG6ihM0fKGG+DwMaFqsKCGh+M +# rb1bKKtY7UEnEVAsVi7KYGkkH+ukhyFUAdUbh/3ZjO0xWPYpkf/1ldvGes6pjK6P +# US2PHbe6ukiupqYYG3I5Ad0e20uQfZbz9vMSTiwslLhmsST0XAesEvi+SJYz2xAQ +# x2O4n/PxMRxZ3m5Q0WQxLTGFGjB2Bl+B+QPBzbpwb9JC77zgA8J2ncP2biEguSRJ +# e56Ezx6YpSoRv4d1jS3tpRL+ZFm8yv6We+hodE++0tLsfpUq42Guy3MrGQ2kTIRo +# 7TGLOLpayR8tYmnF0XEHaBiVl7u/Szr7kmOe/CfRG8IZl6UX+/66OqZeyJ12Q3m2 +# fe7ZWnpWT5sVp2sJmiuGb3atFXBWKcwNumNuy4JecjQE+7NF8rfIv94NxbBV/WSM +# pKf6Yv9OgzkjY1nRdIS1FBHa88RR55+7Ikh4FIGPBTAibiCEJMc79+b8cdsQGOo4 +# ymgbKjGeoRNjtegZ7XE/3TUywBBFMf8NfcjF8REs/HIl7u2RHwRaUTJdAgMBAAGj +# ggJzMIICbzA8BgkrBgEEAYI3FQcELzAtBiUrBgEEAYI3FQiG7sUghM++I4HxhQSF +# hqV1htyhDXuG5sF2wOlDAgFkAgEIMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA4GA1Ud +# DwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBsGCSsGAQQBgjcVCgQOMAwwCgYIKwYB +# BQUHAwMwHQYDVR0OBBYEFOlnnQDHNUpYoPqECFP6JAqGDFM6MB8GA1UdIwQYMBaA +# FICT0Mhz5MfqMIi7Xax90DRKYJLSMIHUBgNVHR8EgcwwgckwgcaggcOggcCGgb1s +# ZGFwOi8vL0NOPUhPVENBS0VYLUNBLENOPUhvdENha2VYLENOPUNEUCxDTj1QdWJs +# aWMlMjBLZXklMjBTZXJ2aWNlcyxDTj1TZXJ2aWNlcyxDTj1Db25maWd1cmF0aW9u +# LERDPU5vbkV4aXN0ZW50RG9tYWluLERDPWNvbT9jZXJ0aWZpY2F0ZVJldm9jYXRp +# b25MaXN0P2Jhc2U/b2JqZWN0Q2xhc3M9Y1JMRGlzdHJpYnV0aW9uUG9pbnQwgccG +# CCsGAQUFBwEBBIG6MIG3MIG0BggrBgEFBQcwAoaBp2xkYXA6Ly8vQ049SE9UQ0FL +# RVgtQ0EsQ049QUlBLENOPVB1YmxpYyUyMEtleSUyMFNlcnZpY2VzLENOPVNlcnZp +# Y2VzLENOPUNvbmZpZ3VyYXRpb24sREM9Tm9uRXhpc3RlbnREb21haW4sREM9Y29t +# P2NBQ2VydGlmaWNhdGU/YmFzZT9vYmplY3RDbGFzcz1jZXJ0aWZpY2F0aW9uQXV0 +# aG9yaXR5MA0GCSqGSIb3DQEBDQUAA4ICAQA7JI76Ixy113wNjiJmJmPKfnn7brVI +# IyA3ZudXCheqWTYPyYnwzhCSzKJLejGNAsMlXwoYgXQBBmMiSI4Zv4UhTNc4Umqx +# pZSpqV+3FRFQHOG/X6NMHuFa2z7T2pdj+QJuH5TgPayKAJc+Kbg4C7edL6YoePRu +# HoEhoRffiabEP/yDtZWMa6WFqBsfgiLMlo7DfuhRJ0eRqvJ6+czOVU2bxvESMQVo +# bvFTNDlEcUzBM7QxbnsDyGpoJZTx6M3cUkEazuliPAw3IW1vJn8SR1jFBukKcjWn +# aau+/BE9w77GFz1RbIfH3hJ/CUA0wCavxWcbAHz1YoPTAz6EKjIc5PcHpDO+n8Fh +# t3ULwVjWPMoZzU589IXi+2Ol0IUWAdoQJr/Llhub3SNKZ3LlMUPNt+tXAs/vcUl0 +# 7+Dp5FpUARE2gMYA/XxfU9T6Q3pX3/NRP/ojO9m0JrKv/KMc9sCGmV9sDygCOosU +# 5yGS4Ze/DJw6QR7xT9lMiWsfgL96Qcw4lfu1+5iLr0dnDFsGowGTKPGI0EvzK7H+ +# DuFRg+Fyhn40dOUl8fVDqYHuZJRoWJxCsyobVkrX4rA6xUTswl7xYPYWz88WZDoY +# gI8AwuRkzJyUEA07IYtsbFCYrcUzIHME4uf8jsJhCmb0va1G2WrWuyasv3K/G8Nn +# f60MsDbDH1mLtzGCAxgwggMUAgEBMGYwTzETMBEGCgmSJomT8ixkARkWA2NvbTEi +# MCAGCgmSJomT8ixkARkWEkhPVENBS0VYLUNBLURvbWFpbjEUMBIGA1UEAxMLSE9U +# Q0FLRVgtQ0ECEx4AAAAEjzQsNDP/rxMAAAAAAAQwDQYJYIZIAWUDBAIBBQCggYQw +# GAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGC +# NwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQx +# IgQg1YjlaZg6ywldl32si2XI4t9tP50x9Wv8R0OLYHj0xn8wDQYJKoZIhvcNAQEB +# BQAEggIAG7BSa+6dzuhxFDxo/y/6LsZrK5y9OyKLmoIU++vU3qMxyZVDDA2vcqsX +# 1jbYKgHSS6lVg3uIaPzgypXHYjYA+vrF0rqO2uPeqGASTUhfYKBtf5BBYVfV3iW5 +# 5L+VoqnCE0xA8G2kcmR7JYZ04W28mgJtu7OME2OBAgl1qAbNPQdYAd8rfHI4Iwgv +# b1JFMIl1m9iB4Lf9EfVRcqfuMaTe7k0Y9/yO4qBrEDXJS/oym0c27wCQgW7wkJWm +# IeWXftqa3SvJBnDS80jeKRhuaF2raGiBvXnRWSFLbhb+e2DQF7yLkxW3z+wIhKLk +# Z6WqpKkdg+TFQYYrn19dGeKJqXhfjhUhynVVYm1FbUz/owy6ABNjg6IHV4CMDj0E +# Ugw82PuW+h3weVruU5BouE6TjcHjbPzU0CpbJ9RuZUaBGJqHCXJQO3KtJBxAxmg3 +# cG2HbyM3NbR+0jxYcu1o7NBVYtLRqG9T+uYdo/PMlGmW4T861oItb4M6Ll9VK4vK +# h7pRuEmGXuvyX2L/A8tE6FRMoJxAPXyOjUScRr2cpADL3r4c9pM400PVsi01Siel +# rChg5vsOA/LdekI+lv55usDRBRMJZSJ5KGDxFVZjGRA+TBjcNZBOxRUgQU0QoP+f +# 962/77rKRJ5NuLbQkKYnj0Bgg0yqHJMVwc7bNWq3XdSDfcc81j0= +# SIG # End signature block diff --git a/WDACConfig/WDACConfig Module Files/XMLOps/Remove-DuplicateFileAttrib_Semantic.psm1 b/WDACConfig/WDACConfig Module Files/XMLOps/Remove-DuplicateFileAttrib_Semantic.psm1 new file mode 100644 index 000000000..a23382f9f --- /dev/null +++ b/WDACConfig/WDACConfig Module Files/XMLOps/Remove-DuplicateFileAttrib_Semantic.psm1 @@ -0,0 +1,231 @@ +function Remove-DuplicateFileAttrib_Semantic { + <# + .SYNOPSIS + A function that deduplicates the elements inside the node. + It successfully detects duplicate elements based on their properties. For example, + if two elements have the same MinimumFileVersion and one of these properties of them are the same (FileName, InternalName, FileDescription, FilePath, and ProductName), they are considered half-duplicates. + In order to be considered fully duplicate, they must also be associated with Signers whose IDs are in the same SigningScenario. + + So for example, if two elements have the same FileName and MinimumFileVersion, but they are associated with 2 different Signers, one in kernel mode and the other in user mode signing scenario, they are not considered duplicates. + + After deduplication, the function updates the FileAttribRef RuleID for associated Signers by setting the RuleID of the removed duplicate FileAttrib elements to the RuleID of the unique remaining FileAttrib element. + + This is according to the CI Schema + .PARAMETER XmlFilePath + The path to the XML file to be processed + .INPUTS + System.IO.FileInfo + .OUTPUTS + System.Void + #> + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)][System.IO.FileInfo]$XmlFilePath + ) + + Begin { + # Importing the $PSDefaultParameterValues to the current session, prior to everything else + . "$ModuleRootPath\CoreExt\PSDefaultParameterValues.ps1" + + # Load the XML file + [System.Xml.XmlDocument]$XmlDoc = New-Object -TypeName System.Xml.XmlDocument + $XmlDoc.Load($XmlFilePath) + + # Create a namespace manager + [System.Xml.XmlNamespaceManager]$NsMgr = New-Object -TypeName System.Xml.XmlNamespaceManager -ArgumentList $XmlDoc.NameTable + $NsMgr.AddNamespace('ns', 'urn:schemas-microsoft-com:sipolicy') + + # Define a hashtable to store FileAttrib elements based on their properties + [System.Collections.Hashtable]$FileAttribHash = @{} + + # Define a hashtable to store FileAttrib elements along with their associated Signer IDs + [System.Collections.Hashtable]$FileAttribSignerHash = @{} + + # Define a hashtable to store Signer IDs and their associated SigningScenario IDs + [System.Collections.Hashtable]$SignerScenarioHash = @{} + + } + + Process { + + # Iterate through each FileAttrib element + foreach ($FileAttrib in $XmlDoc.SelectNodes('//ns:FileAttrib', $NsMgr)) { + + # Get the relevant properties + [System.String]$MinimumFileVersion = $FileAttrib.GetAttribute('MinimumFileVersion') + [System.String]$FileName = $FileAttrib.GetAttribute('FileName') + [System.String]$InternalName = $FileAttrib.GetAttribute('InternalName') + [System.String]$FileDescription = $FileAttrib.GetAttribute('FileDescription') + [System.String]$FilePath = $FileAttrib.GetAttribute('FilePath') + [System.String]$ProductName = $FileAttrib.GetAttribute('ProductName') + + # Generate a unique key based on relevant properties + [System.String]$Key = "$MinimumFileVersion-$FileName-$InternalName-$FileDescription-$FilePath-$ProductName" + + # Check if the key already exists in the hashtable + if (-not $FileAttribHash.ContainsKey($Key)) { + + # If not, add the key and create a new array to store the FileAttrib element + $FileAttribHash[$Key] = @($FileAttrib) + } + else { + # If the key already exists, append the FileAttrib element to the existing array + $FileAttribHash[$Key] += $FileAttrib + } + + # Get the Signer ID associated with this FileAttrib + $SignerID = $XmlDoc.SelectSingleNode("//ns:Signer[ns:FileAttribRef/@RuleID='$($FileAttrib.GetAttribute('ID'))']/@ID", $NsMgr).Value + + # Add the FileAttrib and its associated Signer ID to the hashtable + if (-not $FileAttribSignerHash.ContainsKey($Key)) { + + # If not, add the key and create a new array to store the Signer ID + $FileAttribSignerHash[$Key] = @($SignerID) + } + else { + # If the key already exists, append the Signer ID to the existing array + $FileAttribSignerHash[$Key] += $SignerID + } + + # Get the SigningScenario ID associated with this Signer ID + $SigningScenarioID = $XmlDoc.SelectSingleNode("//ns:SigningScenario[ns:ProductSigners/ns:AllowedSigners/ns:AllowedSigner[@SignerId='$SignerID']]/@ID", $NsMgr).Value + + # Add the Signer ID and its associated SigningScenario ID to the hashtable + if (-not $SignerScenarioHash.ContainsKey($SignerID)) { + + # add the Signer ID and create a new array to store the SigningScenario ID + $SignerScenarioHash[$SignerID] = @($SigningScenarioID) + } + else { + # If the Signer ID already exists, append the SigningScenario ID to the existing array + $SignerScenarioHash[$SignerID] += $SigningScenarioID + } + } + + # Iterate through the hashtable to find and remove duplicates + foreach ($Key in $FileAttribHash.Keys) { + + # If there's more than one FileAttrib element for this key + if ($FileAttribHash[$Key].Count -gt 1) { + + # Get the unique Signer IDs associated with the FileAttrib elements + $SignerIDs = $FileAttribSignerHash[$Key] | Select-Object -Unique + + # Get the unique SigningScenario IDs associated with the Signer IDs + $ScenarioIDs = $SignerIDs | ForEach-Object -Process { $SignerScenarioHash[$_] } | Select-Object -Unique + + # If there are multiple unique SigningScenario IDs associated with this set of Signer IDs + if ($ScenarioIDs.Count -gt 1) { + # Skip deduplication as the Signer IDs are in different Signing scenarios, meaning both User and Kernel modes are involved so it shouldn't be touched + continue + } + else { + # Remove duplicates by keeping only the first FileAttrib element + $FirstFileAttrib = $FileAttribHash[$Key] | Select-Object -First 1 + + # Iterate through the remaining FileAttrib elements + for ($i = 1; $i -lt $FileAttribHash[$Key].Count; $i++) { + + # Get the duplicate FileAttrib element to remove based on the index + $FileAttribToRemove = $FileAttribHash[$Key][$i] + + # Update FileAttribRef RuleID for associated Signers + $SignerIDs | ForEach-Object -Process { + + # Get the Signer element associated with this Signer ID + $Signer = $XmlDoc.SelectSingleNode("//ns:Signer[@ID='$_']", $NsMgr) + + # Get the FileAttribRef element associated with the duplicate FileAttrib element + $FileAttribRef = $Signer.SelectSingleNode("ns:FileAttribRef[@RuleID='$($FileAttribToRemove.GetAttribute('ID'))']", $NsMgr) + + # Updating the RuleID of the duplicate of the Signer before removing it and setting it to the RuleID of the unique remaining FileAttrib element + if ($Null -ne $FileAttribRef) { + + if ($FirstFileAttrib.GetAttribute('ID') -notin $Signer.FileAttribRef.RuleID) { + + + $FileAttribRef.SetAttribute('RuleID', $FirstFileAttrib.GetAttribute('ID')) + } + } + } + # Remove the duplicate FileAttrib element + Write-Verbose -Message "Remove-DuplicateFileAttrib: Removed duplicate FileAttrib with ID: $($FileAttribToRemove.GetAttribute('ID'))" + [System.Void]$FileAttribToRemove.ParentNode.RemoveChild($FileAttribToRemove) + } + } + } + } + + } + + End { + # Save the modified XML back to file + $XmlDoc.Save($XmlFilePath) + } +} +Export-ModuleMember -Function 'Remove-DuplicateFileAttrib_Semantic' + +# SIG # Begin signature block +# MIILkgYJKoZIhvcNAQcCoIILgzCCC38CAQExDzANBglghkgBZQMEAgEFADB5Bgor +# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG +# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCC+k7vhp8c86WbL +# Kwg60udQjt71Llf3NlXi9z9ZX+33K6CCB9AwggfMMIIFtKADAgECAhMeAAAABI80 +# LDQz/68TAAAAAAAEMA0GCSqGSIb3DQEBDQUAME8xEzARBgoJkiaJk/IsZAEZFgNj +# b20xIjAgBgoJkiaJk/IsZAEZFhJIT1RDQUtFWC1DQS1Eb21haW4xFDASBgNVBAMT +# C0hPVENBS0VYLUNBMCAXDTIzMTIyNzExMjkyOVoYDzIyMDgxMTEyMTEyOTI5WjB5 +# MQswCQYDVQQGEwJVSzEeMBwGA1UEAxMVSG90Q2FrZVggQ29kZSBTaWduaW5nMSMw +# IQYJKoZIhvcNAQkBFhRob3RjYWtleEBvdXRsb29rLmNvbTElMCMGCSqGSIb3DQEJ +# ARYWU3B5bmV0Z2lybEBvdXRsb29rLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIP +# ADCCAgoCggIBAKb1BJzTrpu1ERiwr7ivp0UuJ1GmNmmZ65eckLpGSF+2r22+7Tgm +# pEifj9NhPw0X60F9HhdSM+2XeuikmaNMvq8XRDUFoenv9P1ZU1wli5WTKHJ5ayDW +# k2NP22G9IPRnIpizkHkQnCwctx0AFJx1qvvd+EFlG6ihM0fKGG+DwMaFqsKCGh+M +# rb1bKKtY7UEnEVAsVi7KYGkkH+ukhyFUAdUbh/3ZjO0xWPYpkf/1ldvGes6pjK6P +# US2PHbe6ukiupqYYG3I5Ad0e20uQfZbz9vMSTiwslLhmsST0XAesEvi+SJYz2xAQ +# x2O4n/PxMRxZ3m5Q0WQxLTGFGjB2Bl+B+QPBzbpwb9JC77zgA8J2ncP2biEguSRJ +# e56Ezx6YpSoRv4d1jS3tpRL+ZFm8yv6We+hodE++0tLsfpUq42Guy3MrGQ2kTIRo +# 7TGLOLpayR8tYmnF0XEHaBiVl7u/Szr7kmOe/CfRG8IZl6UX+/66OqZeyJ12Q3m2 +# fe7ZWnpWT5sVp2sJmiuGb3atFXBWKcwNumNuy4JecjQE+7NF8rfIv94NxbBV/WSM +# pKf6Yv9OgzkjY1nRdIS1FBHa88RR55+7Ikh4FIGPBTAibiCEJMc79+b8cdsQGOo4 +# ymgbKjGeoRNjtegZ7XE/3TUywBBFMf8NfcjF8REs/HIl7u2RHwRaUTJdAgMBAAGj +# ggJzMIICbzA8BgkrBgEEAYI3FQcELzAtBiUrBgEEAYI3FQiG7sUghM++I4HxhQSF +# hqV1htyhDXuG5sF2wOlDAgFkAgEIMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA4GA1Ud +# DwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBsGCSsGAQQBgjcVCgQOMAwwCgYIKwYB +# BQUHAwMwHQYDVR0OBBYEFOlnnQDHNUpYoPqECFP6JAqGDFM6MB8GA1UdIwQYMBaA +# FICT0Mhz5MfqMIi7Xax90DRKYJLSMIHUBgNVHR8EgcwwgckwgcaggcOggcCGgb1s +# ZGFwOi8vL0NOPUhPVENBS0VYLUNBLENOPUhvdENha2VYLENOPUNEUCxDTj1QdWJs +# aWMlMjBLZXklMjBTZXJ2aWNlcyxDTj1TZXJ2aWNlcyxDTj1Db25maWd1cmF0aW9u +# LERDPU5vbkV4aXN0ZW50RG9tYWluLERDPWNvbT9jZXJ0aWZpY2F0ZVJldm9jYXRp +# b25MaXN0P2Jhc2U/b2JqZWN0Q2xhc3M9Y1JMRGlzdHJpYnV0aW9uUG9pbnQwgccG +# CCsGAQUFBwEBBIG6MIG3MIG0BggrBgEFBQcwAoaBp2xkYXA6Ly8vQ049SE9UQ0FL +# RVgtQ0EsQ049QUlBLENOPVB1YmxpYyUyMEtleSUyMFNlcnZpY2VzLENOPVNlcnZp +# Y2VzLENOPUNvbmZpZ3VyYXRpb24sREM9Tm9uRXhpc3RlbnREb21haW4sREM9Y29t +# P2NBQ2VydGlmaWNhdGU/YmFzZT9vYmplY3RDbGFzcz1jZXJ0aWZpY2F0aW9uQXV0 +# aG9yaXR5MA0GCSqGSIb3DQEBDQUAA4ICAQA7JI76Ixy113wNjiJmJmPKfnn7brVI +# IyA3ZudXCheqWTYPyYnwzhCSzKJLejGNAsMlXwoYgXQBBmMiSI4Zv4UhTNc4Umqx +# pZSpqV+3FRFQHOG/X6NMHuFa2z7T2pdj+QJuH5TgPayKAJc+Kbg4C7edL6YoePRu +# HoEhoRffiabEP/yDtZWMa6WFqBsfgiLMlo7DfuhRJ0eRqvJ6+czOVU2bxvESMQVo +# bvFTNDlEcUzBM7QxbnsDyGpoJZTx6M3cUkEazuliPAw3IW1vJn8SR1jFBukKcjWn +# aau+/BE9w77GFz1RbIfH3hJ/CUA0wCavxWcbAHz1YoPTAz6EKjIc5PcHpDO+n8Fh +# t3ULwVjWPMoZzU589IXi+2Ol0IUWAdoQJr/Llhub3SNKZ3LlMUPNt+tXAs/vcUl0 +# 7+Dp5FpUARE2gMYA/XxfU9T6Q3pX3/NRP/ojO9m0JrKv/KMc9sCGmV9sDygCOosU +# 5yGS4Ze/DJw6QR7xT9lMiWsfgL96Qcw4lfu1+5iLr0dnDFsGowGTKPGI0EvzK7H+ +# DuFRg+Fyhn40dOUl8fVDqYHuZJRoWJxCsyobVkrX4rA6xUTswl7xYPYWz88WZDoY +# gI8AwuRkzJyUEA07IYtsbFCYrcUzIHME4uf8jsJhCmb0va1G2WrWuyasv3K/G8Nn +# f60MsDbDH1mLtzGCAxgwggMUAgEBMGYwTzETMBEGCgmSJomT8ixkARkWA2NvbTEi +# MCAGCgmSJomT8ixkARkWEkhPVENBS0VYLUNBLURvbWFpbjEUMBIGA1UEAxMLSE9U +# Q0FLRVgtQ0ECEx4AAAAEjzQsNDP/rxMAAAAAAAQwDQYJYIZIAWUDBAIBBQCggYQw +# GAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGC +# NwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQx +# IgQgCkEmQA6Bz4ztKSOupfOnuQhd/Bt9MEKu7Xyy114q6qswDQYJKoZIhvcNAQEB +# BQAEggIAPIf9nIuem5E+h461vsYOgvtTCZL6pJgJnL/84ZR1QF8KO+m68u50IvSC +# Oy4+EeN6OttimzwoP+wqYjZMFv/wskyFCZzKPnRpTcm36ixGSoEO7iTgrbcAN2n6 +# MPlOKp5/GGumWzoNu4YxHsKCQNA9X1jyOUjkXhUFbXMcPjryQ60tHW7HsoDCuP9o +# qTHKDWvBd+97HohQzKUDtD3HAf64WSLbDxVKsRlmc1LE5zZGfMgiTYO0phw0gFSK +# G8c4rxfsG7tBo73Dl8JBYLnWYtiOB/Z3lk/N2atMPNgAsowHTjqOTGmSoO0Ckfy9 +# Z03gnTp9Clf0Mcu/0MG/7aVKhUIoafNMNUWqRBBsEHIxc3+TkdUL8hIp2b5KhLTx +# x/2D9a+eBqWT1B2RPiyhktqrmgL/WUWM5hQmNvg38gaDoV/wF9CNFOdcJ/tEObqd +# pUdhWf6rFmaj6+b3vqjqoToqHeB1thdaXaqgGYU/xp1sCyk8Xe2McLdcEm2rgiG4 +# am3kRspavl2BGW8gfMwu4I8/7AbH/wDFIVbkku2EegRR8Hw5RrCZbdQI1Qxs2wNG +# DlTfuAnBFIiAbCBxPsify3SLN06StlYQzzHBJNcs1WwOoZX42PFasNYZr8UR/12+ +# z3xXf/Tm2OGW+u0aBzOW7b8milwL9+CWqN7InbMXFgUOmZMNWZU= +# SIG # End signature block diff --git a/WDACConfig/WDACConfig Module Files/XMLOps/Remove-OrphanAllowedSignersAndCiSigners_IDBased.psm1 b/WDACConfig/WDACConfig Module Files/XMLOps/Remove-OrphanAllowedSignersAndCiSigners_IDBased.psm1 new file mode 100644 index 000000000..4b860b0dc --- /dev/null +++ b/WDACConfig/WDACConfig Module Files/XMLOps/Remove-OrphanAllowedSignersAndCiSigners_IDBased.psm1 @@ -0,0 +1,139 @@ +Function Remove-OrphanAllowedSignersAndCiSigners_IDBased { + <# + .SYNOPSIS + Removes elements with invalid SignerIds from the CiSigners and AllowedSigners nodes in a CI policy XML file + These are elements with SignerIds that are not found in any in the node + .PARAMETER Path + The path to the CI policy XML file + .INPUTS + System.IO.FileInfo + .OUTPUTS + System.Void + #> + [CmdletBinding()] + [OutputType([System.Void])] + param ( + [Parameter(Mandatory = $true)][System.IO.FileInfo]$Path + ) + + Begin { + # Importing the $PSDefaultParameterValues to the current session, prior to everything else + . "$ModuleRootPath\CoreExt\PSDefaultParameterValues.ps1" + + # Load the XML file + [System.Xml.XmlDocument]$Xml = Get-Content -Path $Path + + # Create an XmlNamespaceManager for namespace resolution + [System.Xml.XmlNamespaceManager]$NsManager = New-Object System.Xml.XmlNamespaceManager -ArgumentList $Xml.NameTable + $NsManager.AddNamespace('ns', 'urn:schemas-microsoft-com:sipolicy') + + # Get the list of valid signer IDs from the Signers node + [System.String[]]$ValidSignerIds = $Xml.SelectNodes('//ns:Signers/ns:Signer', $NsManager) | ForEach-Object -Process { $_.ID } + + Function Remove-InvalidSignerIds { + <# + .SYNOPSIS + Removes nodes with invalid SignerIds from the given XmlNodeList + .INPUTS + System.Xml.XmlNodeList + .OUTPUTS + System.Void + .PARAMETER NodeList + The XmlNodeList to remove invalid SignerIds from + #> + Param ( + [Parameter(Mandatory = $true)][System.Xml.XmlNodeList]$NodeList + ) + + foreach ($Node in $NodeList) { + if ($ValidSignerIds -notcontains $Node.SignerId) { + [System.Void]$Node.ParentNode.RemoveChild($Node) + } + } + } + } + + Process { + + # Get CiSigners and AllowedSigners nodes + [System.Xml.XmlNodeList]$CiSigners = $Xml.SelectNodes('//ns:CiSigners/ns:CiSigner', $NsManager) + [System.Xml.XmlNodeList]$AllowedSigners12 = $Xml.SelectNodes('//ns:SigningScenarios/ns:SigningScenario[@Value="12"]/ns:ProductSigners/ns:AllowedSigners/ns:AllowedSigner', $NsManager) + [System.Xml.XmlNodeList]$AllowedSigners131 = $Xml.SelectNodes('//ns:SigningScenarios/ns:SigningScenario[@Value="131"]/ns:ProductSigners/ns:AllowedSigners/ns:AllowedSigner', $NsManager) + + # Remove invalid signer IDs from CiSigners and AllowedSigners + Remove-InvalidSignerIds $CiSigners + Remove-InvalidSignerIds $AllowedSigners12 + Remove-InvalidSignerIds $AllowedSigners131 + + } + End { + # Save the changes to the XML file + $Xml.Save($Path) + } +} +Export-ModuleMember -Function 'Remove-OrphanAllowedSignersAndCiSigners_IDBased' + +# SIG # Begin signature block +# MIILkgYJKoZIhvcNAQcCoIILgzCCC38CAQExDzANBglghkgBZQMEAgEFADB5Bgor +# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG +# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDxCtyqvRx02Cij +# 0iw25QrPxYTS8h82ZJlkaV81jjeC06CCB9AwggfMMIIFtKADAgECAhMeAAAABI80 +# LDQz/68TAAAAAAAEMA0GCSqGSIb3DQEBDQUAME8xEzARBgoJkiaJk/IsZAEZFgNj +# b20xIjAgBgoJkiaJk/IsZAEZFhJIT1RDQUtFWC1DQS1Eb21haW4xFDASBgNVBAMT +# C0hPVENBS0VYLUNBMCAXDTIzMTIyNzExMjkyOVoYDzIyMDgxMTEyMTEyOTI5WjB5 +# MQswCQYDVQQGEwJVSzEeMBwGA1UEAxMVSG90Q2FrZVggQ29kZSBTaWduaW5nMSMw +# IQYJKoZIhvcNAQkBFhRob3RjYWtleEBvdXRsb29rLmNvbTElMCMGCSqGSIb3DQEJ +# ARYWU3B5bmV0Z2lybEBvdXRsb29rLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIP +# ADCCAgoCggIBAKb1BJzTrpu1ERiwr7ivp0UuJ1GmNmmZ65eckLpGSF+2r22+7Tgm +# pEifj9NhPw0X60F9HhdSM+2XeuikmaNMvq8XRDUFoenv9P1ZU1wli5WTKHJ5ayDW +# k2NP22G9IPRnIpizkHkQnCwctx0AFJx1qvvd+EFlG6ihM0fKGG+DwMaFqsKCGh+M +# rb1bKKtY7UEnEVAsVi7KYGkkH+ukhyFUAdUbh/3ZjO0xWPYpkf/1ldvGes6pjK6P +# US2PHbe6ukiupqYYG3I5Ad0e20uQfZbz9vMSTiwslLhmsST0XAesEvi+SJYz2xAQ +# x2O4n/PxMRxZ3m5Q0WQxLTGFGjB2Bl+B+QPBzbpwb9JC77zgA8J2ncP2biEguSRJ +# e56Ezx6YpSoRv4d1jS3tpRL+ZFm8yv6We+hodE++0tLsfpUq42Guy3MrGQ2kTIRo +# 7TGLOLpayR8tYmnF0XEHaBiVl7u/Szr7kmOe/CfRG8IZl6UX+/66OqZeyJ12Q3m2 +# fe7ZWnpWT5sVp2sJmiuGb3atFXBWKcwNumNuy4JecjQE+7NF8rfIv94NxbBV/WSM +# pKf6Yv9OgzkjY1nRdIS1FBHa88RR55+7Ikh4FIGPBTAibiCEJMc79+b8cdsQGOo4 +# ymgbKjGeoRNjtegZ7XE/3TUywBBFMf8NfcjF8REs/HIl7u2RHwRaUTJdAgMBAAGj +# ggJzMIICbzA8BgkrBgEEAYI3FQcELzAtBiUrBgEEAYI3FQiG7sUghM++I4HxhQSF +# hqV1htyhDXuG5sF2wOlDAgFkAgEIMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA4GA1Ud +# DwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBsGCSsGAQQBgjcVCgQOMAwwCgYIKwYB +# BQUHAwMwHQYDVR0OBBYEFOlnnQDHNUpYoPqECFP6JAqGDFM6MB8GA1UdIwQYMBaA +# FICT0Mhz5MfqMIi7Xax90DRKYJLSMIHUBgNVHR8EgcwwgckwgcaggcOggcCGgb1s +# ZGFwOi8vL0NOPUhPVENBS0VYLUNBLENOPUhvdENha2VYLENOPUNEUCxDTj1QdWJs +# aWMlMjBLZXklMjBTZXJ2aWNlcyxDTj1TZXJ2aWNlcyxDTj1Db25maWd1cmF0aW9u +# LERDPU5vbkV4aXN0ZW50RG9tYWluLERDPWNvbT9jZXJ0aWZpY2F0ZVJldm9jYXRp +# b25MaXN0P2Jhc2U/b2JqZWN0Q2xhc3M9Y1JMRGlzdHJpYnV0aW9uUG9pbnQwgccG +# CCsGAQUFBwEBBIG6MIG3MIG0BggrBgEFBQcwAoaBp2xkYXA6Ly8vQ049SE9UQ0FL +# RVgtQ0EsQ049QUlBLENOPVB1YmxpYyUyMEtleSUyMFNlcnZpY2VzLENOPVNlcnZp +# Y2VzLENOPUNvbmZpZ3VyYXRpb24sREM9Tm9uRXhpc3RlbnREb21haW4sREM9Y29t +# P2NBQ2VydGlmaWNhdGU/YmFzZT9vYmplY3RDbGFzcz1jZXJ0aWZpY2F0aW9uQXV0 +# aG9yaXR5MA0GCSqGSIb3DQEBDQUAA4ICAQA7JI76Ixy113wNjiJmJmPKfnn7brVI +# IyA3ZudXCheqWTYPyYnwzhCSzKJLejGNAsMlXwoYgXQBBmMiSI4Zv4UhTNc4Umqx +# pZSpqV+3FRFQHOG/X6NMHuFa2z7T2pdj+QJuH5TgPayKAJc+Kbg4C7edL6YoePRu +# HoEhoRffiabEP/yDtZWMa6WFqBsfgiLMlo7DfuhRJ0eRqvJ6+czOVU2bxvESMQVo +# bvFTNDlEcUzBM7QxbnsDyGpoJZTx6M3cUkEazuliPAw3IW1vJn8SR1jFBukKcjWn +# aau+/BE9w77GFz1RbIfH3hJ/CUA0wCavxWcbAHz1YoPTAz6EKjIc5PcHpDO+n8Fh +# t3ULwVjWPMoZzU589IXi+2Ol0IUWAdoQJr/Llhub3SNKZ3LlMUPNt+tXAs/vcUl0 +# 7+Dp5FpUARE2gMYA/XxfU9T6Q3pX3/NRP/ojO9m0JrKv/KMc9sCGmV9sDygCOosU +# 5yGS4Ze/DJw6QR7xT9lMiWsfgL96Qcw4lfu1+5iLr0dnDFsGowGTKPGI0EvzK7H+ +# DuFRg+Fyhn40dOUl8fVDqYHuZJRoWJxCsyobVkrX4rA6xUTswl7xYPYWz88WZDoY +# gI8AwuRkzJyUEA07IYtsbFCYrcUzIHME4uf8jsJhCmb0va1G2WrWuyasv3K/G8Nn +# f60MsDbDH1mLtzGCAxgwggMUAgEBMGYwTzETMBEGCgmSJomT8ixkARkWA2NvbTEi +# MCAGCgmSJomT8ixkARkWEkhPVENBS0VYLUNBLURvbWFpbjEUMBIGA1UEAxMLSE9U +# Q0FLRVgtQ0ECEx4AAAAEjzQsNDP/rxMAAAAAAAQwDQYJYIZIAWUDBAIBBQCggYQw +# GAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGC +# NwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQx +# IgQgXNqcihUO+cJkmSBignGIEGOKN92AK2Cc2iVAC82b38UwDQYJKoZIhvcNAQEB +# BQAEggIAFl+2r1q4vUEaPND38pvNL3mtp4lXqwLRVfia1XzVGcXWuAenKHWFvDIi +# oJABEa7sXzAZn7HeZp2Np33pLI6upWDSjbsNi4s5RY4Dgq0d6OXKKcONrrq5g0+m +# pErsvVPoQk+P98VLVLjZdZcVc415EwUdYF1ZwpoBzMVB+S4YLUggvLHsc65mkc3e +# nLIdejBbk9shyv2dbdB0NRIKB/ccIcU3X+/Fttjz53Z++EYQA7iWVnUKqEsoYF8/ +# 0k+o8oDcXWOnp+q5Y49QfuPtp0nVxagInPMuf//LZFCFiwU1Bru2rHTOf3bY6Q4u +# bSm6L8SdB1cmX7H3wiAs/BB0pEQwVSIVSHmktq2s0VrN06D4ArZ/hipylYCTCTEc +# IhEp8i+sAKi97OHYcwhv+pB30O4sHscvpujnIMaBfyKNvEU9/0PcdBcg5RhHAi5J +# NLhAigOMAFfwQZamUxM5ffkGYFlfU9HXxlsIwS5KhAzRqtvPBDbrE+RbLgZEtUho +# Gd3v8vsHPzzrSYn+UMNQ2A0Dnt/nqN8ZGbsJm69rxh0WgXepfLXQpttMy8YVcsBm +# QEMhb0Dh/fzZJLQ0xYeB2q21zAftMaqKVSmdPSF+43tpbAzPRiFWElY8xnEpotvQ +# jhZ1cJkvYZcf3k1WrEXba3GRjul9C5M4PZyQZaz6qDy0iwuJZKk= +# SIG # End signature block diff --git a/WDACConfig/WDACConfig Module Files/XMLOps/Remove-UnreferencedFileRuleRefs.psm1 b/WDACConfig/WDACConfig Module Files/XMLOps/Remove-UnreferencedFileRuleRefs.psm1 new file mode 100644 index 000000000..5bb0d86ea --- /dev/null +++ b/WDACConfig/WDACConfig Module Files/XMLOps/Remove-UnreferencedFileRuleRefs.psm1 @@ -0,0 +1,116 @@ +function Remove-UnreferencedFileRuleRefs { + <# + .SYNOPSIS + Removes elements from the node of each Signing Scenario that are not referenced by any element in the node + .PARAMETER xmlFilePath + The path to the XML file to be modified + .INPUTS + System.IO.FileInfo + .OUTPUTS + System.Void + #> + [CmdletBinding()] + [OutputType([System.Void])] + param ( + [Parameter(Mandatory = $true)][System.IO.FileInfo]$XmlFilePath + ) + + Begin { + # Importing the $PSDefaultParameterValues to the current session, prior to everything else + . "$ModuleRootPath\CoreExt\PSDefaultParameterValues.ps1" + + # Load the XML file + [System.Xml.XmlDocument]$XmlContent = Get-Content $XmlFilePath + } + + Process { + # Define the namespace to use with the namespace manager + [System.Xml.XmlNamespaceManager]$NsManager = New-Object -TypeName System.Xml.XmlNamespaceManager -ArgumentList $XmlContent.NameTable + $NsManager.AddNamespace('def', 'urn:schemas-microsoft-com:sipolicy') + + # Find all Allow elements and store their IDs + $AllowedIds = $XmlContent.SelectNodes('//def:Allow', $NsManager) | ForEach-Object -Process { $_.ID } + + # Find all FileRuleRef elements + $fileRuleRefs = $XmlContent.SelectNodes('//def:FileRuleRef', $NsManager) + + foreach ($fileRuleRef in $fileRuleRefs) { + # Check if the RuleID attribute is not in the list of allowed IDs + if ($AllowedIds -notcontains $fileRuleRef.RuleID) { + # Remove the FileRuleRef element if it's not referenced + [System.Void]$fileRuleRef.ParentNode.RemoveChild($fileRuleRef) + } + } + } + + End { + # Save the modified XML back to the file or to a new file + $XmlContent.Save($XmlFilePath) + } +} +Export-ModuleMember -Function 'Remove-UnreferencedFileRuleRefs' + +# SIG # Begin signature block +# MIILkgYJKoZIhvcNAQcCoIILgzCCC38CAQExDzANBglghkgBZQMEAgEFADB5Bgor +# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG +# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAcu5khoRJnO0Ve +# bHSHvJfXe9Li5yOP3zscbsFqUJGCUaCCB9AwggfMMIIFtKADAgECAhMeAAAABI80 +# LDQz/68TAAAAAAAEMA0GCSqGSIb3DQEBDQUAME8xEzARBgoJkiaJk/IsZAEZFgNj +# b20xIjAgBgoJkiaJk/IsZAEZFhJIT1RDQUtFWC1DQS1Eb21haW4xFDASBgNVBAMT +# C0hPVENBS0VYLUNBMCAXDTIzMTIyNzExMjkyOVoYDzIyMDgxMTEyMTEyOTI5WjB5 +# MQswCQYDVQQGEwJVSzEeMBwGA1UEAxMVSG90Q2FrZVggQ29kZSBTaWduaW5nMSMw +# IQYJKoZIhvcNAQkBFhRob3RjYWtleEBvdXRsb29rLmNvbTElMCMGCSqGSIb3DQEJ +# ARYWU3B5bmV0Z2lybEBvdXRsb29rLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIP +# ADCCAgoCggIBAKb1BJzTrpu1ERiwr7ivp0UuJ1GmNmmZ65eckLpGSF+2r22+7Tgm +# pEifj9NhPw0X60F9HhdSM+2XeuikmaNMvq8XRDUFoenv9P1ZU1wli5WTKHJ5ayDW +# k2NP22G9IPRnIpizkHkQnCwctx0AFJx1qvvd+EFlG6ihM0fKGG+DwMaFqsKCGh+M +# rb1bKKtY7UEnEVAsVi7KYGkkH+ukhyFUAdUbh/3ZjO0xWPYpkf/1ldvGes6pjK6P +# US2PHbe6ukiupqYYG3I5Ad0e20uQfZbz9vMSTiwslLhmsST0XAesEvi+SJYz2xAQ +# x2O4n/PxMRxZ3m5Q0WQxLTGFGjB2Bl+B+QPBzbpwb9JC77zgA8J2ncP2biEguSRJ +# e56Ezx6YpSoRv4d1jS3tpRL+ZFm8yv6We+hodE++0tLsfpUq42Guy3MrGQ2kTIRo +# 7TGLOLpayR8tYmnF0XEHaBiVl7u/Szr7kmOe/CfRG8IZl6UX+/66OqZeyJ12Q3m2 +# fe7ZWnpWT5sVp2sJmiuGb3atFXBWKcwNumNuy4JecjQE+7NF8rfIv94NxbBV/WSM +# pKf6Yv9OgzkjY1nRdIS1FBHa88RR55+7Ikh4FIGPBTAibiCEJMc79+b8cdsQGOo4 +# ymgbKjGeoRNjtegZ7XE/3TUywBBFMf8NfcjF8REs/HIl7u2RHwRaUTJdAgMBAAGj +# ggJzMIICbzA8BgkrBgEEAYI3FQcELzAtBiUrBgEEAYI3FQiG7sUghM++I4HxhQSF +# hqV1htyhDXuG5sF2wOlDAgFkAgEIMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA4GA1Ud +# DwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBsGCSsGAQQBgjcVCgQOMAwwCgYIKwYB +# BQUHAwMwHQYDVR0OBBYEFOlnnQDHNUpYoPqECFP6JAqGDFM6MB8GA1UdIwQYMBaA +# FICT0Mhz5MfqMIi7Xax90DRKYJLSMIHUBgNVHR8EgcwwgckwgcaggcOggcCGgb1s +# ZGFwOi8vL0NOPUhPVENBS0VYLUNBLENOPUhvdENha2VYLENOPUNEUCxDTj1QdWJs +# aWMlMjBLZXklMjBTZXJ2aWNlcyxDTj1TZXJ2aWNlcyxDTj1Db25maWd1cmF0aW9u +# LERDPU5vbkV4aXN0ZW50RG9tYWluLERDPWNvbT9jZXJ0aWZpY2F0ZVJldm9jYXRp +# b25MaXN0P2Jhc2U/b2JqZWN0Q2xhc3M9Y1JMRGlzdHJpYnV0aW9uUG9pbnQwgccG +# CCsGAQUFBwEBBIG6MIG3MIG0BggrBgEFBQcwAoaBp2xkYXA6Ly8vQ049SE9UQ0FL +# RVgtQ0EsQ049QUlBLENOPVB1YmxpYyUyMEtleSUyMFNlcnZpY2VzLENOPVNlcnZp +# Y2VzLENOPUNvbmZpZ3VyYXRpb24sREM9Tm9uRXhpc3RlbnREb21haW4sREM9Y29t +# P2NBQ2VydGlmaWNhdGU/YmFzZT9vYmplY3RDbGFzcz1jZXJ0aWZpY2F0aW9uQXV0 +# aG9yaXR5MA0GCSqGSIb3DQEBDQUAA4ICAQA7JI76Ixy113wNjiJmJmPKfnn7brVI +# IyA3ZudXCheqWTYPyYnwzhCSzKJLejGNAsMlXwoYgXQBBmMiSI4Zv4UhTNc4Umqx +# pZSpqV+3FRFQHOG/X6NMHuFa2z7T2pdj+QJuH5TgPayKAJc+Kbg4C7edL6YoePRu +# HoEhoRffiabEP/yDtZWMa6WFqBsfgiLMlo7DfuhRJ0eRqvJ6+czOVU2bxvESMQVo +# bvFTNDlEcUzBM7QxbnsDyGpoJZTx6M3cUkEazuliPAw3IW1vJn8SR1jFBukKcjWn +# aau+/BE9w77GFz1RbIfH3hJ/CUA0wCavxWcbAHz1YoPTAz6EKjIc5PcHpDO+n8Fh +# t3ULwVjWPMoZzU589IXi+2Ol0IUWAdoQJr/Llhub3SNKZ3LlMUPNt+tXAs/vcUl0 +# 7+Dp5FpUARE2gMYA/XxfU9T6Q3pX3/NRP/ojO9m0JrKv/KMc9sCGmV9sDygCOosU +# 5yGS4Ze/DJw6QR7xT9lMiWsfgL96Qcw4lfu1+5iLr0dnDFsGowGTKPGI0EvzK7H+ +# DuFRg+Fyhn40dOUl8fVDqYHuZJRoWJxCsyobVkrX4rA6xUTswl7xYPYWz88WZDoY +# gI8AwuRkzJyUEA07IYtsbFCYrcUzIHME4uf8jsJhCmb0va1G2WrWuyasv3K/G8Nn +# f60MsDbDH1mLtzGCAxgwggMUAgEBMGYwTzETMBEGCgmSJomT8ixkARkWA2NvbTEi +# MCAGCgmSJomT8ixkARkWEkhPVENBS0VYLUNBLURvbWFpbjEUMBIGA1UEAxMLSE9U +# Q0FLRVgtQ0ECEx4AAAAEjzQsNDP/rxMAAAAAAAQwDQYJYIZIAWUDBAIBBQCggYQw +# GAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGC +# NwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQx +# IgQg8gxPJnFjmmFnhOJlgbHgyAsxCdc+tJyrGTU5AhNNiR4wDQYJKoZIhvcNAQEB +# BQAEggIAXRJ5rVmyU8ZkeVqd5jsbqGBUPYEp8uNuJx3IwoQozCKScPAVnZWmmf1i +# nCFmaMoNjSVPfGMyME2Mo05Cc259y8fSsX3tP//53tu7DJ5c6+usGstLohBycDtC +# zXeY5G7MofB2p3nbbTgOIE9AgBXiIcno4PgGuW6e2xz6JgQ5DWPeScmXwshyf3yx +# GlCE8MFsyEeOVrXYo8TtaKuURGQNUUl+6tgashtfzjC873TeJSyGQrFuJX90LTJU +# fBr4IQWRM5KyOJ+iHYvDyyXmbg3dK80yVQEl9YrU2oPUlvco1Ek3IRRFq9/ROiyE +# cXFlkedz1mxoBXvmDgULe7/x3DLnd9jSxutgZNMizEdSm6mHy2A1ewSPpDhEg3jA +# Th7p00GC0Yz6XQUnK4dVIiZuJL7aQ6AEkVvwB7/oapvr6yj6Viw7oelT/4YuWyd4 +# hyFoELviLDL1DKTLZUu+yzjpp2jZbgdG8X6WToK5v9aFDgc7aDhGokv2kCj65wcJ +# YmShZ/F/XRYt4eyTJaSoPk3XP+D/kVu5XYWQemVdeKZWY3Pt74IsqbJdisAqbvRO +# kFvqXsUnpGKx2YbFLo3JZbK/24o34++s/qR/y1LiljIk9DcV+5vkon3Xuxb1Sy6V +# AosnD0X2CXmkHR7g6ow8D/aDO541umtn8GxIem3hpZMAzb9a46E= +# SIG # End signature block diff --git a/WDACConfig/version.txt b/WDACConfig/version.txt index 09e915703..53b61ecf7 100644 --- a/WDACConfig/version.txt +++ b/WDACConfig/version.txt @@ -1 +1 @@ -0.3.5 \ No newline at end of file +0.3.6 \ No newline at end of file diff --git a/Wiki posts/WDACConfig Module Main/ConvertTo-WDACPolicy.md b/Wiki posts/WDACConfig Module Main/ConvertTo-WDACPolicy.md index 08c411e28..40c1f9688 100644 --- a/Wiki posts/WDACConfig Module Main/ConvertTo-WDACPolicy.md +++ b/Wiki posts/WDACConfig Module Main/ConvertTo-WDACPolicy.md @@ -5,10 +5,11 @@ ```powershell ConvertTo-WDACPolicy [-PolicyToAddLogsTo ] + [-Source ] + [-MDEAHLogs ] [-FilterByPolicyNames ] - [-MinutesAgo ] - [-HoursAgo ] - [-DaysAgo ] + [-TimeSpan ] + [-TimeSpanAgo ] [-KernelModeOnly] [-LogType ] [-Deploy] @@ -19,10 +20,11 @@ ConvertTo-WDACPolicy ```powershell ConvertTo-WDACPolicy [-BasePolicyFile ] + [-Source ] + [-MDEAHLogs ] [-FilterByPolicyNames ] - [-MinutesAgo ] - [-HoursAgo ] - [-DaysAgo ] + [-TimeSpan ] + [-TimeSpanAgo ] [-KernelModeOnly] [-LogType ] [-Deploy] @@ -33,10 +35,11 @@ ConvertTo-WDACPolicy ```powershell ConvertTo-WDACPolicy [-BasePolicyGUID ] + [-Source ] + [-MDEAHLogs ] [-FilterByPolicyNames ] - [-MinutesAgo ] - [-HoursAgo ] - [-DaysAgo ] + [-TimeSpan ] + [-TimeSpanAgo ] [-KernelModeOnly] [-LogType ] [-Deploy] @@ -65,6 +68,7 @@ The policy to add the selected logs to, it can either be a base or supplemental | Type: |[FileInfo](https://learn.microsoft.com/en-us/dotnet/api/system.io.fileinfo)| | :-------------: | :-------------: | +| Aliases: AddLogs | | Position: | Named | | Default value: | None | | Required: | False | @@ -86,6 +90,7 @@ The base policy file to associate the supplemental policy with. | Type: |[FileInfo](https://learn.microsoft.com/en-us/dotnet/api/system.io.fileinfo)| | :-------------: | :-------------: | +| Aliases: BaseFile | | Position: | Named | | Default value: | None | | Required: | False | @@ -107,6 +112,7 @@ The GUID of the base policy to associate the supplemental policy with. | Type: |[Guid](https://learn.microsoft.com/en-us/dotnet/api/system.guid)| | :-------------: | :-------------: | +| Aliases: BaseGUID | | Position: | Named | | Default value: | None | | Required: | False | @@ -128,60 +134,7 @@ You can manually enter the name of the policies that are no longer available on | Type: |[String](https://learn.microsoft.com/en-us/dotnet/api/system.string)[]| | :-------------: | :-------------: | -| Position: | Named | -| Default value: | None | -| Required: | False | -| Accept pipeline input: | False | -| Accept wildcard characters: | False | - - - -
- -### -MinutesAgo - -The number of minutes ago from the current time to filter the logs by - -
- -| Type: |[UInt64](https://learn.microsoft.com/en-us/dotnet/api/system.uint64)| -| :-------------: | :-------------: | -| Position: | Named | -| Default value: | None | -| Required: | False | -| Accept pipeline input: | False | -| Accept wildcard characters: | False | - -
- -
- -### -HoursAgo - -The number of hours ago from the current time to filter the logs by - -
- -| Type: |[UInt64](https://learn.microsoft.com/en-us/dotnet/api/system.uint64)| -| :-------------: | :-------------: | -| Position: | Named | -| Default value: | None | -| Required: | False | -| Accept pipeline input: | False | -| Accept wildcard characters: | False | - -
- -
- -### -DaysAgo - -The number of days ago from the current time to filter the logs by - -
- -| Type: |[UInt64](https://learn.microsoft.com/en-us/dotnet/api/system.uint64)| -| :-------------: | :-------------: | +| Aliases: FilterNames | | Position: | Named | | Default value: | None | | Required: | False | @@ -200,6 +153,7 @@ If used, will filter the logs by including only the Kernel-Mode logs. | Type: |[SwitchParameter](https://learn.microsoft.com/en-us/dotnet/api/system.management.automation.switchparameter)| | :-------------: | :-------------: | +| Aliases: KMode | | Position: | Named | | Default value: | None | | Required: | False | @@ -218,6 +172,7 @@ The type of logs to display: Audit or Blocked | Type: |[String](https://learn.microsoft.com/en-us/dotnet/api/system.string)| | :-------------: | :-------------: | +| Aliases: LogKind | | Position: | Named | | Default value: | `Audit` | | Required: | False | @@ -236,6 +191,7 @@ If used, will deploy the policy on the system. | Type: |[SwitchParameter](https://learn.microsoft.com/en-us/dotnet/api/system.management.automation.switchparameter)| | :-------------: | :-------------: | +| Aliases: Up | | Position: | Named | | Default value: | None | | Required: | False | @@ -254,6 +210,7 @@ If used, will display all the properties of the logs without any filtering. | Type: |[SwitchParameter](https://learn.microsoft.com/en-us/dotnet/api/system.management.automation.switchparameter)| | :-------------: | :-------------: | +| Aliases: XVis | | Position: | Named | | Default value: | None | | Required: | False |