├── LICENSE ├── README.md ├── powershell ├── Compile-CSProject.psm1 ├── ConfuserEXProj.psm1 └── Update-Vsproject.psm1 └── vars └── psScripts.groovy /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Conor Richard 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # offsecops-shared-library 2 | A collection of scripts used to support an OffSecOps pipeline. 3 | 4 | For more information, refer to the following blog posts: 5 | - [OffSecOps Basic Setup](https://blog.xenoscr.net/OffSecOps-Basic-Setup/) 6 | - [OffSecOps Stage Two](https://blog.xenoscr.net/OffSecOps-Stage-Two/) 7 | 8 | -------------------------------------------------------------------------------- /powershell/Compile-CSProject.psm1: -------------------------------------------------------------------------------- 1 | function Get-CSProjLangVer 2 | { 3 | param( 4 | [Parameter(Mandatory=$true, 5 | Position=0, 6 | HelpMessage="The base path of the Visual Studio project.")] 7 | [ValidateScript({ 8 | if (-Not ($_ | Test-Path)) { 9 | throw "The provided path does not exist." 10 | } 11 | return $true 12 | })] 13 | [System.IO.FileInfo] 14 | $Path 15 | ) 16 | <# 17 | .SYNOPSIS 18 | Get the language version from a visual studio project. 19 | .DESCRIPTION 20 | Get the language version from a visual studio project. 21 | .PARAMETER Path 22 | The base path of the Visual Studio project. 23 | .INPUTS 24 | None. You cannot pipe objects to Get-CSProjLangVer. 25 | .OUTPUTS 26 | The language version is returned as a string. 27 | .EXAMPLE 28 | PS> Get-CSProjLangVer -Path C:\foo\ 29 | #> 30 | 31 | $projFiles = Get-ChildItem $Path -Recurse -Filter *.csproj 32 | $maxLangVersion = 0 33 | foreach($file in $projFiles) 34 | { 35 | $content = New-Object xml 36 | $content.PreserveWhitespace = $true 37 | try { 38 | $content.Load($file.FullName) 39 | } 40 | catch { 41 | Write-Error "Failed to load $file.FullName." 42 | return $false 43 | } 44 | 45 | $langVersionNodes = $content.GetElementsByTagName("LangVersion"); 46 | 47 | If ($langVersionNodes.Count -gt 0) 48 | { 49 | ForEach ($entry in $langVersionNodes) 50 | { 51 | $version = $entry.InnerText; 52 | if($version -gt $maxLangVersion) 53 | { 54 | $maxLangVersion = $version 55 | } 56 | } 57 | } 58 | } 59 | return $maxLangVersion 60 | } 61 | 62 | function Get-CSProjFrameworkVer 63 | { 64 | param( 65 | [Parameter(Mandatory=$true, 66 | Position=0, 67 | HelpMessage="The base path of the Visual Studio project.")] 68 | [ValidateScript({ 69 | if (-Not ($_ | Test-Path)) { 70 | throw "The provided path does not exist." 71 | } 72 | return $true 73 | })] 74 | [System.IO.FileInfo] 75 | $Path 76 | ) 77 | <# 78 | .SYNOPSIS 79 | Get the .NET Framework version from a visual studio project. 80 | .DESCRIPTION 81 | Get the .NET Framework version from a visual studio project. 82 | .PARAMETER Path 83 | The base path of the Visual Studio project. 84 | .INPUTS 85 | None. You cannot pipe objects to Get-CSProjLangVer. 86 | .OUTPUTS 87 | The language version is returned as a string. 88 | .EXAMPLE 89 | PS> Get-CSProjFrameworkVer -Path C:\foo\ 90 | #> 91 | 92 | $projFiles = Get-ChildItem $Path -Recurse -Filter *.csproj 93 | $maxLangVersion = 0 94 | foreach($file in $projFiles) 95 | { 96 | $content = New-Object xml 97 | $content.PreserveWhitespace = $true 98 | try { 99 | $content.Load($file.FullName) 100 | } 101 | catch { 102 | Write-Error "Failed to load $file.FullName." 103 | return $false 104 | } 105 | 106 | $frameworkNodes = $content.GetElementsByTagName("TargetFrameworkVersion"); 107 | 108 | If ($frameworkNodes.Count -gt 0) 109 | { 110 | ForEach ($entry in $frameworkNodes) 111 | { 112 | $version = $entry.InnerText.Trim("v"); 113 | if($version -gt $maxLangVersion) 114 | { 115 | $maxLangVersion = $version 116 | } 117 | } 118 | } 119 | } 120 | return $maxLangVersion 121 | } 122 | 123 | # Language Version information I used is located here 124 | # https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-options/langversion-compiler-option 125 | function Get-MSBuildPath 126 | { 127 | param( 128 | [Parameter(Mandatory=$true, 129 | Position=0, 130 | HelpMessage="The target language version.")] 131 | [string] 132 | $langVersion 133 | ) 134 | <# 135 | .SYNOPSIS 136 | Returns the path of the appropriate MSBuild.exe to use. 137 | .DESCRIPTION 138 | Returns the path of the appropriate MSBuild.exe to use based on the target language version. 139 | .PARAMETER langVersion 140 | The target language version 141 | .INPUT 142 | None. 143 | .OUTPUT 144 | A string containing the full path of the correct MSBuild.exe to use. 145 | .EXAMPLE 146 | PS> Get-MSBuildPath -langVersion 8.0 147 | #> 148 | If (([float]$langVersion -ge 8.0)) 149 | { 150 | return "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\MSBuild.exe" 151 | } 152 | ElseIf (([float]$langVersion -lt 8.0) -and ([float]$langVersion -ge 7.0)) 153 | { 154 | return "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\MSBuild.exe" 155 | } 156 | Else 157 | { 158 | return $false 159 | } 160 | } 161 | 162 | function Invoke-MSBuildEnv 163 | { 164 | param( 165 | [Parameter(Mandatory=$true, 166 | Position=0, 167 | HelpMessage="The target language version.")] 168 | [string] 169 | $langVersion 170 | ) 171 | <# 172 | .SYNOPSIS 173 | Invokes the appropriate VsMsBuildCmd.bat and duplicates the environment variables. 174 | .DESCRIPTION 175 | Invokes the appropriate VsMsBuildCmd.bat and duplicates the environment variables. 176 | .PARAMETER langVersion 177 | The target language version 178 | .INPUT 179 | None. 180 | .OUTPUT 181 | A string containing the full path of the correct MSBuild.exe to use. 182 | .EXAMPLE 183 | PS> Get-MSBuildPath -langVersion 8.0 184 | #> 185 | If (([float]$langVersion -ge 8.0)) 186 | { 187 | Invoke-DevEnvironment "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\Tools\VsMSBuildCmd.bat" 188 | } 189 | ElseIf (([float]$langVersion -lt 8.0) -and ([float]$langVersion -ge 7.0)) 190 | { 191 | Invoke-DevEnvironment "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\Tools\VsMSBuildCmd.bat" 192 | } 193 | } 194 | 195 | # Borrowed from https://github.com/majkinetor/posh/blob/master/MM_Admin/Invoke-Environment.ps1 196 | function Invoke-DevEnvironment { 197 | param 198 | ( 199 | # Any cmd shell command, normally a configuration batch file. 200 | [Parameter(Mandatory=$true)] 201 | [string] $Command 202 | ) 203 | <# 204 | .SYNOPSIS 205 | Invokes a command and imports its environment variables. 206 | 207 | .DESCRIPTION 208 | It invokes any cmd shell command (normally a configuration batch file) and 209 | imports its environment variables to the calling process. Command output is 210 | discarded completely. It fails if the command exit code is not 0. To ignore 211 | the exit code use the 'call' command. 212 | 213 | .EXAMPLE 214 | # Invokes Config.bat in the current directory or the system path 215 | Invoke-Environment Config.bat 216 | 217 | .EXAMPLE 218 | # Visual Studio environment: works even if exit code is not 0 219 | Invoke-Environment 'call "%VS100COMNTOOLS%\vsvars32.bat"' 220 | 221 | .EXAMPLE 222 | # This command fails if vsvars32.bat exit code is not 0 223 | Invoke-Environment '"%VS100COMNTOOLS%\vsvars32.bat"' 224 | #> 225 | 226 | try { 227 | $Command = "`"" + $Command + "`"" 228 | cmd /c "$Command > nul 2>&1 && set" | . { process { 229 | if ($_ -match '^([^=]+)=(.*)') { 230 | [System.Environment]::SetEnvironmentVariable($matches[1], $matches[2]) 231 | } 232 | } 233 | } 234 | } 235 | catch { 236 | Write-Error "Failed to load Dev Environment." 237 | return $false 238 | } 239 | return $true 240 | } 241 | 242 | function Invoke-CSProjectCleanup 243 | { 244 | param( 245 | [Parameter(Mandatory=$true, 246 | Position=0, 247 | HelpMessage="Path to a Visual Studio Solution File")] 248 | [ValidateScript({ 249 | if (-Not ($_ | Test-Path)){ 250 | throw "The provided .sln file path does not exist." 251 | } 252 | if (-Not ($_ | Test-Path -PathType Leaf)) { 253 | throw "The provided argument must be a file. Folder paths are not allowed." 254 | } 255 | if ($_ -NotMatch "(\.sln)") { 256 | throw "The file specified does not have the correct file extention." 257 | } 258 | return $true 259 | })] 260 | [System.IO.FileInfo] 261 | $slnPath, 262 | [Parameter(Mandatory=$true, 263 | Position=1, 264 | HelpMessage="The target configuration.")] 265 | [ValidateSet("Debug", "Release")] 266 | [string] 267 | $targetConfiguration, 268 | [Parameter(Mandatory=$true, 269 | Position=2, 270 | HelpMessage="The target CPU platform.")] 271 | [ValidateSet("x86", "x64", "Any CPU")] 272 | [string] 273 | $targetPlatform, 274 | [Parameter(Mandatory=$false, 275 | Position=3, 276 | HelpMessage="Force delete the `"obj`" and `"bin`" folders.")] 277 | [bool] 278 | $forceDelete = $false 279 | ) 280 | <# 281 | .SYNOPSIS 282 | Clean a target C# solution. 283 | .DESCRIPTION 284 | Clean a target C# solution. 285 | .PARAMETER targetPlatform 286 | The target CPU Architecture. (x86, x64, Any CPU) 287 | .PARAMETER targetConfiguration 288 | The target configuration. (Debug, Release) 289 | .PARAMETER slnPath 290 | The target Visual Studio solution. 291 | .INPUTS 292 | None. You cannot pipe objects to Compile-Solution. 293 | .OUTPUTS 294 | None. There is no output other than status printed to the console. 295 | .EXAMPLE 296 | PS> Invoke-CSProjectCleanup -targetPlatform "x86" -TargetConfiguration "Release" -slnPath "C:\foo\bar.sln" 297 | #> 298 | $langVersion = Get-CSProjLangVer -Path $(Split-Path $slnPath -Parent) 299 | If ($(Invoke-MSBuildEnv -langVersion $langVersion)) { 300 | $msBuildPath = Get-MSBuildPath -langVersion $langVersion 301 | 302 | try { 303 | $buildOutput = (& "$($msBuildPath)" "$($slnPath)" "/t:Clean" "/p:configuration=$($targetConfiguration);platform=$(targetPlatform)" | Write-Output) 304 | } 305 | catch { 306 | Write-Error "Failed to run cleanup." 307 | return $false 308 | } 309 | If ($buildOutput -like "*Build succeeded*") { 310 | Write-Host $buildOutput 311 | return $true 312 | } 313 | else { 314 | Write-Error "Clean Failed" 315 | Write-Host $buildOutput 316 | return $false 317 | } 318 | If ($forceDelete) { 319 | Get-ChildItem "$(Split-Path $slnPath -Parent)\*\bin" | forEach { Remove-Item -Path $_.FullName -Force -Recurse } 320 | Get-ChildItem "$(Split-Path $slnPath -Parent)\*\obj" | forEach { Remove-Item -Path $_.FullName -Force -Recurse } 321 | } 322 | } 323 | else { 324 | return $false 325 | } 326 | } 327 | 328 | function Invoke-CompileCSProject 329 | { 330 | param( 331 | [Parameter(Mandatory=$true, 332 | Position=0, 333 | HelpMessage="The target CPU platform.")] 334 | [ValidateSet("x86", "x64", "Any CPU")] 335 | [string] 336 | $targetPlatform, 337 | [Parameter(Mandatory=$true, 338 | Position=1, 339 | HelpMessage="The target configuration.")] 340 | [ValidateSet("Debug", "Release")] 341 | [string] 342 | $targetConfiguration, 343 | [Parameter(Mandatory=$true, 344 | Position=2, 345 | HelpMessage="Path to a Visual Studio Solution File")] 346 | [ValidateScript({ 347 | if (-Not ($_ | Test-Path)){ 348 | throw "The provided .sln file path does not exist." 349 | } 350 | if (-Not ($_ | Test-Path -PathType Leaf)) { 351 | throw "The provided argument must be a file. Folder paths are not allowed." 352 | } 353 | if ($_ -NotMatch "(\.sln)") { 354 | throw "The file specified does not have the correct file extention." 355 | } 356 | return $true 357 | })] 358 | [System.IO.FileInfo] 359 | $slnPath, 360 | [Parameter(Mandatory=$false, 361 | Position=3, 362 | HelpMessage="The target .NET Framework Version.")] 363 | [String] 364 | $targetFrameworkVersion = $null, 365 | [Parameter(Mandatory=$false, 366 | Position=4, 367 | HelpMessage="Clean up first?")] 368 | $cleanup = $false 369 | ) 370 | <# 371 | .SYNOPSIS 372 | Compile a target C# solution. 373 | .DESCRIPTION 374 | Compile a target C# solution. 375 | .PARAMETER targetPlatform 376 | The target CPU Architecture. (x86, x64, Any CPU) 377 | .PARAMETER targetConfiguration 378 | The target configuration. (Debug, Release) 379 | .PARAMETER slnPath 380 | The target Visual Studio solution. 381 | .INPUTS 382 | None. You cannot pipe objects to Compile-Solution. 383 | .OUTPUTS 384 | None. There is no output other than status printed to the console. 385 | .EXAMPLE 386 | PS> Invoke-CompileCSProject -targetPlatform "x86" -TargetConfiguration "Release" -slnPath "C:\foo\bar.sln" 387 | #> 388 | $slnName = $(Get-Item $slnPath).Basename 389 | $langVersion = Get-CSProjLangVer -Path $(Split-Path $slnPath -Parent) 390 | If (-Not $targetFrameworkVersion) { 391 | $targetFrameworkVersion = Get-CSProjFrameworkVer -Path $(Split-Path $slnPath -Parent) 392 | } 393 | If ($(Invoke-MSBuildEnv -langVersion $langVersion)) { 394 | $msBuildPath = Get-MSBuildPath -langVersion $langVersion 395 | 396 | If ($cleanup) { 397 | try { 398 | If (-Not (Invoke-ProjectCleanup -slnPath $slnPath -targetConfiguration $targetConfiguration -targetPlatform $targetPlatform)) { 399 | Write-Error "Cleanup failed." 400 | return $false 401 | } 402 | } 403 | catch { 404 | Write-Error "Failed to run cleanup." 405 | return $false 406 | } 407 | } 408 | 409 | try { 410 | # Build the soltuion 411 | $buildOutput = (& "$($msBuildPath)" "$($slnPath)" "/t:Build" "/p:configuration=$($targetConfiguration);platform=$($targetPlatform);targetFrameworkVersion=v$($targetFrameworkVersion);OutputPath=bin\$($targetConfiguration)\$($targetFrameworkVersion)\$($targetPlatform.Replace(`" `", `"`"))\;AssemblyName=$($slnName).$($targetFrameworkVersion).$($targetPlatform.Replace(`" `",`"`"))" | Write-Output) 412 | } 413 | catch { 414 | Write-Error "Failed to run build." 415 | return $false 416 | } 417 | If ($buildOutput -like "*Build succeeded*") { 418 | Write-Host "Build Success." 419 | Write-Host $buildOutput 420 | return $true 421 | } 422 | else { 423 | Write-Error "Build Failed" 424 | Write-Host $buildOutput 425 | return $false 426 | } 427 | } 428 | else { 429 | return $false 430 | } 431 | } 432 | 433 | Export-ModuleMember -Function Get-CSProjLangVer 434 | Export-ModuleMember -Function Get-CSProjFrameworkVer 435 | Export-ModuleMember -Function Get-MSBuildPath 436 | Export-ModuleMember -Function Invoke-MSBuildEnv 437 | Export-ModuleMember -Function Invoke-DevEnvironment 438 | Export-ModuleMember -Function Invoke-CSProjectCleanup 439 | Export-ModuleMember -Function Invoke-CompileCSProject 440 | -------------------------------------------------------------------------------- /powershell/ConfuserEXProj.psm1: -------------------------------------------------------------------------------- 1 | function Get-ConfuserExProtections 2 | { 3 | <# 4 | .SYNOPSIS 5 | Returns a PSCustomObject populated with the default ConfuserEX protections settings 6 | .DESCRIPTION 7 | Returns a PSCustomObject populated with the default ConfuserEX protections settings 8 | .EXAMPLE 9 | PS > $protections = Get-ConfuserExProtections 10 | #> 11 | 12 | return $protections = [PSCustomObject]@{ 13 | seed = $null 14 | debug = $null 15 | preset = $null 16 | packer = "compressor" 17 | antiDebug = [PSCustomObject]@{ 18 | id = "anti debug" 19 | preset = "Minimum" 20 | action = "add" 21 | enabled = $false 22 | argument = [PSCustomObject]@{ 23 | mode = "safe" 24 | } 25 | } 26 | antiDump = [PSCustomObject]@{ 27 | id = "anti dump" 28 | preset = "Maximum" 29 | action = "add" 30 | enabled = $false 31 | argument = $null 32 | } 33 | antiILDasm = [PSCustomObject]@{ 34 | id = "anti ildasm" 35 | preset = "Minimum" 36 | action = "add" 37 | enabled = $false 38 | argument = $null 39 | } 40 | antiTamper = [PSCustomObject]@{ 41 | id = "anti tamper" 42 | preset = "Maximum" 43 | action = "add" 44 | enabled = $false 45 | argument = [PSCustomObject]@{ 46 | mode = "normal" 47 | key = "normal" 48 | } 49 | } 50 | constants = [PSCustomObject]@{ 51 | id = "constants" 52 | preset = "Normal" 53 | action = "add" 54 | enabled = $false 55 | argument = [PSCustomObject]@{ 56 | mode = "normal" 57 | decoderCount = 5 58 | elements = "SI" 59 | cfg = "false" 60 | compressor = "lzma" 61 | compress = "Auto" 62 | } 63 | } 64 | ctrlFlow = [PSCustomObject]@{ 65 | id = "ctrl flow" 66 | preset = "Normal" 67 | action = "add" 68 | enabled = $false 69 | argument = [PSCustomObject]@{ 70 | type = "switch" 71 | predicate = "normal" 72 | intensity = 60 73 | depth = 4 74 | junk = "false" 75 | } 76 | } 77 | invalidMetadata = [PSCustomObject]@{ 78 | id = "invalid metadata" 79 | preset = "Maximum" 80 | action = "add" 81 | enabled = $false 82 | argument = $null 83 | } 84 | rename = [PSCustomObject]@{ 85 | id = "rename" 86 | preset = "Minimum" 87 | action = "add" 88 | enabled = $false 89 | argument = [PSCustomObject]@{ 90 | mode = "unicode" 91 | password = $null 92 | renameArgs = "true" 93 | renEnum = "false" 94 | flatten = "true" 95 | forceRen = "false" 96 | renPublic = "false" 97 | renPdb = "false" 98 | renXaml = "true" 99 | } 100 | } 101 | harden = [PSCustomObject]@{ 102 | id = "harden" 103 | preset = "Minimum" 104 | action = "add" 105 | enabled = $false 106 | argument = $null 107 | } 108 | refProxy = [PSCustomObject]@{ 109 | id = "ref proxy" 110 | preset = "Normal" 111 | action = "add" 112 | enabled = $false 113 | argument = [PSCustomObject]@{ 114 | mode = "mild" 115 | encoding = "normal" 116 | internal = "false" 117 | typeErasure = "false" 118 | depth = 3 119 | initCount = 16 120 | } 121 | } 122 | resources = [PSCustomObject]@{ 123 | id = "resources" 124 | preset = "Normal" 125 | action = "add" 126 | enabled = $false 127 | argument = [PSCustomObject]@{ 128 | mode = "normal" 129 | } 130 | } 131 | watermark = [PSCustomObject]@{ 132 | id = "watermark" 133 | preset = "Normal" 134 | action = "remove" 135 | enabled = $true 136 | argument = $null 137 | } 138 | } 139 | } 140 | 141 | function New-ConfuserEXProj { 142 | <# 143 | .SYNOPSIS 144 | Writes a ConfuserEX project file with the provided protections 145 | .DESCRIPTION 146 | Writes a ConfuserEX project file with the provided protections 147 | .EXAMPLE 148 | PS > Write-ConfuserEXProj -Protections $protections -TargetAssembly "C:\foo\bar.exe" 149 | #> 150 | 151 | param( 152 | [Parameter(Mandatory=$true, 153 | Position=0, 154 | HelpMessage="A ConfuserEx Protections PowerShell Object.")] 155 | [System.Object] 156 | $Protections, 157 | [Parameter(Mandatory=$true, 158 | Position=1, 159 | HelpMessage="The path to the Assembly to be protected.")] 160 | [ValidateScript({ 161 | if (-Not ($_ | Test-Path)){ 162 | throw "The provided file path does not exist." 163 | } 164 | if (-Not ($_ | Test-Path -PathType Leaf)) { 165 | throw "The provided argument must be a file. Folder paths are not allowed." 166 | } 167 | if (($_ -NotMatch "(\.exe)") -AND ($_ -NotMatch "(\.dll)")) { 168 | throw "The file specified does not have the correct file extention." 169 | } 170 | return $true 171 | })] 172 | [String] 173 | $TargetAssembly 174 | ) 175 | 176 | # Get the base directory 177 | $cesBaseDir = $targetAssembly | Split-Path -Parent 178 | 179 | # Set the outpuat directory 180 | $cexOutputDir = "$cesBaseDir\Confused" 181 | 182 | # Set the outputFile 183 | $outputFile = "$cesBaseDir\ConfuserEX.crproj" 184 | 185 | # Set target Bin 186 | $targetBin = $targetAssembly | Split-Path -Leaf 187 | 188 | $xmlSettings = New-Object System.Xml.XmlWriterSettings 189 | $xmlSettings.OmitXmlDeclaration = $true 190 | $xmlSettings.Indent = $true 191 | $xmlSettings.IndentChars = " " 192 | 193 | $xmlWriter = [System.XML.XmlWriter]::Create($outputFile, $xmlSettings) 194 | 195 | $xmlWriter.WriteStartElement("project", "http://confuser.codeplex.com") 196 | $xmlWriter.WriteAttributeString("outputDir", $cexOutputDir) 197 | $xmlWriter.WriteAttributeString("baseDir", $cesBaseDir) 198 | 199 | if ($protections.seed) { 200 | $xmlWriter.WriteAttributeString("seed", $protections.seed) 201 | } 202 | 203 | if ($protections.debug) { 204 | $xmlWriter.WriteAttributeString("debug", "true") 205 | } 206 | 207 | # Create the rules element 208 | $xmlWriter.WriteStartElement("rule") 209 | $xmlWriter.WriteAttributeString("pattern", "true") 210 | if ($protections.preset) 211 | { 212 | $presets = "minimum","normal","aggressive","maximum" 213 | if ($presets.Contains($protections.preset.ToLower())) { 214 | $xmlWriter.WriteAttributeString("preset", $protections.preset.ToLower()) 215 | } 216 | } 217 | $xmlWriter.WriteAttributeString("inherit", "false") 218 | 219 | # Anti Debug option 220 | if ($protections.antiDebug.enabled) 221 | { 222 | $xmlWriter.WriteStartElement("protection") 223 | $xmlWriter.WriteAttributeString("id", "anti debug") 224 | 225 | if ($protections.antiDebug.argument.mode.ToLower() -ne "safe") 226 | { 227 | $xmlWriter.WriteStartElement("argument") 228 | $xmlWriter.WriteAttributeString("name", "mode") 229 | $xmlWriter.WriteAttributeString("value", $protections.antiDebug.argument.mode.ToLower()) 230 | $xmlWriter.WriteEndElement() 231 | } 232 | $xmlWriter.WriteEndElement() 233 | } 234 | 235 | # Anti Dump options 236 | if ($protections.antiDump.enabled) 237 | { 238 | $xmlWriter.WriteStartElement("protection") 239 | $xmlWriter.WriteAttributeString("id", "anti dump") 240 | $xmlWriter.WriteEndElement() 241 | } 242 | 243 | # Anti ildasm options 244 | if ($protections.antiILDasm.enabled) 245 | { 246 | $xmlWriter.WriteStartElement("protection") 247 | $xmlWriter.WriteAttributeString("id", "anti ildasm") 248 | $xmlWriter.WriteEndElement() 249 | } 250 | 251 | # Anti Tamper options 252 | if ($protections.antiTamper.enabled) 253 | { 254 | $xmlWriter.WriteStartElement("protection") 255 | $xmlWriter.WriteAttributeString("id", "anti tamper") 256 | 257 | if ($protections.antiTamper.argument.mode.ToLower() -ne "normal") 258 | { 259 | $xmlWriter.WriteStartElement("argument") 260 | $xmlWriter.WriteAttributeString("name", "mode") 261 | $xmlWriter.WriteAttributeString("value", $protections.antiTamper.argument.mode.ToLower()) 262 | $xmlWriter.WriteEndElement() 263 | } 264 | 265 | if ($protections.antiTamper.argument.key.ToLower() -ne "normal") 266 | { 267 | $xmlWriter.WriteStartElement("argument") 268 | $xmlWriter.WriteAttributeString("name", "key") 269 | $xmlWriter.WriteAttributeString("value", $protections.antiTamper.argument.key.ToLower()) 270 | $xmlWriter.WriteEndElement() 271 | } 272 | $xmlWriter.WriteEndElement() 273 | } 274 | 275 | # Constants options 276 | if ($protections.constants.enabled) 277 | { 278 | $xmlWriter.WriteStartElement("protection") 279 | $xmlWriter.WriteAttributeString("id", "constants") 280 | 281 | if ($protections.constants.argument.mode.ToLower() -ne "safe") 282 | { 283 | $xmlWriter.WriteStartElement("argument") 284 | $xmlWriter.WriteAttributeString("name", "mode") 285 | $xmlWriter.WriteAttributeString("value", $protections.constants.argument.mode.ToLower()) 286 | $xmlWriter.WriteEndElement() 287 | } 288 | 289 | if ($protections.constants.argument.decoderCount -ne 5) 290 | { 291 | $xmlWriter.WriteStartElement("argument") 292 | $xmlWriter.WriteAttributeString("name", "decoderCount") 293 | $xmlWriter.WriteAttributeString("value", $protections.constants.argument.decoderCount.ToLower()) 294 | $xmlWriter.WriteEndElement() 295 | } 296 | $xmlWriter.WriteEndElement() 297 | } 298 | 299 | # Ctrl Flow options 300 | if ($protections.ctrlFlow.enabled) 301 | { 302 | $xmlWriter.WriteStartElement("protection") 303 | $xmlWriter.WriteAttributeString("id", "ctrl flow") 304 | 305 | if ($protections.ctrlFlow.argument.type.ToLower() -ne "switch") 306 | { 307 | $xmlWriter.WriteStartElement("argument") 308 | $xmlWriter.WriteAttributeString("name", "type") 309 | $xmlWriter.WriteAttributeString("value", $protections.ctrlFlow.argument.type.ToLower()) 310 | $xmlWriter.WriteEndElement() 311 | } 312 | 313 | if ($protections.ctrlFlow.argument.predicate.ToLower() -ne "normal") 314 | { 315 | $xmlWriter.WriteStartElment("argument") 316 | $xmlWriter.WriteAttributeString("name", "predicate") 317 | $xmlWriter.WriteAttributeString("value", $protections.ctrlFlow.argument.predicate.ToLower()) 318 | $xmlWriter.WriteEndElement() 319 | } 320 | 321 | if ($protections.ctrlFlow.argument.intensity -ne 60) 322 | { 323 | $xmlWriter.WriteStartElement("argument") 324 | $xmlWriter.WriteAttributeString("name", "intensity") 325 | $xmlWriter.WriteAttributeString("valu", $protections.ctrlFlow.argument.intensity.ToLower()) 326 | $xmlWriter.WriteEndElement() 327 | } 328 | 329 | if ($protections.ctrlFlow.argument.depth -ne 4) 330 | { 331 | $xmlWriter.WriteStartElement("argument") 332 | $xmlWriter.WriteAttributeString("name", "depth") 333 | $xmlWriter.WriteAttributeStirng("value", $protections.ctrlFlow.argument.depth.ToLower()) 334 | $xmlWriter.WriteEndElement() 335 | } 336 | 337 | if ($protections.ctrlFlow.argument.junk.ToLower() -ne "false") 338 | { 339 | $xmlWriter.WriteStartElement("argument") 340 | $xmlWriter.WriteAttributeString("name", "junk") 341 | $xmlWriter.WriteAttributeString("value", $protections.ctrlFlow.argument.junk.ToLower()) 342 | $xmlWriter.WriteEndElement() 343 | } 344 | $xmlWriter.WriteEndElement() 345 | } 346 | 347 | # Harden option 348 | if ($protections.harden.enabled) 349 | { 350 | $xmlWriter.WriteStartElement("protection") 351 | $xmlWriter.WriteAttributeString("id", "harden") 352 | $xmlWriter.WriteEndElement() 353 | } 354 | 355 | # Invalid Metadata options 356 | if ($protections.invalidMetadata.enabled) 357 | { 358 | $xmlWriter.WriteStartElement("protection") 359 | $xmlWriter.WriteAttributeString("id", "invalid metadata") 360 | $xmlWriter.WriteEndElement() 361 | } 362 | 363 | # Reference Proxy options 364 | if ($protections.refProxy.enabled) 365 | { 366 | $xmlWriter.WriteStartElement("protection") 367 | $xmlWriter.WriteAttributeString("id", "ref proxy") 368 | 369 | if ($protections.refProxy.argument.mode.ToLower() -ne "mild") 370 | { 371 | $xmlWriter.WriteStartElement("argument") 372 | $xmlWriter.WriteAttributeString("name", "mode") 373 | $xmlWriter.WriteAttributeString("value", $protections.refProxy.argument.mode.ToLower()) 374 | $xmlWriter.WriteEndElement() 375 | } 376 | 377 | if ($protections.refProxy.argument.encoding.ToLower() -ne "normal") 378 | { 379 | $xmlWriter.WriteStartElement("argument") 380 | $xmlWriter.WriteAttributeString("name", "encoding") 381 | $xmlWriter.WriteAttributeString("value", $protections.refProxy.argument.encoding.ToLower()) 382 | $xmlWriter.WriteEndElement() 383 | } 384 | 385 | if ($protections.refProxy.argument.internal.ToLower() -ne "false") 386 | { 387 | $xmlWriter.WriteStartElement("argument") 388 | $xmlWriter.WriteAttributeString("name", "internal") 389 | $xmlWriter.WriteAttributeString("value", $protections.refProxy.argument.internal.ToLower()) 390 | $xmlWriter.WriteEndElement() 391 | } 392 | 393 | if ($protections.refProxy.argument.typeErasure.ToLower() -ne "false") 394 | { 395 | $xmlWriter.WriteStartElement("argument") 396 | $xmlWriter.WriteAttributeString("name", "typeErasure") 397 | $xmlWriter.WriteAttributeString("value", $protections.refProxy.argument.typeErasure.ToLower()) 398 | $xmlWriter.WriteEndElement() 399 | } 400 | 401 | if (($protections.refProxy.argument.depth -ne 3) -and (($protections.refProxy.argument.encoding.ToLower() -eq "expression") -or ($protections.refProxy.argument.encoding.ToLower() -eq "x86"))) 402 | { 403 | $xmlWriter.WriteStartElement("argument") 404 | $xmlWriter.WriteAttributeString("name", "depth") 405 | $xmlWriter.WriteAttributeString("value", $protections.refProxy.argument.depth.ToLower()) 406 | $xmlWriter.WriteEndElement() 407 | } 408 | 409 | if (($protections.refProxy.argument.initCount -ne 16) -and ($protections.refProxy.argument.mode.ToLower() -eq "strong")) 410 | { 411 | $xmlWriter.WriteStartElement("argument") 412 | $xmlWriter.WriteAttributeString("name", "initCount") 413 | $xmlWriter.WriteAttributeString("value", $protections.refProxy.argument.initCount.ToLower()) 414 | $xmlWriter.WriteEndElement() 415 | } 416 | $xmlWriter.WriteEndElement() 417 | } 418 | 419 | # Resource Protection options 420 | if ($protections.resources.enabled) 421 | { 422 | $xmlWriter.WriteStartElement("protection") 423 | $xmlWriter.WriteAttributeString("id", "resources") 424 | 425 | if ($protections.resources.argument.mode.ToLower() -ne "normal") 426 | { 427 | $xmlWriter.WriteStartElement("argument") 428 | $xmlWriter.WriteAttributeString("name", "mode") 429 | $xmlWriter.WriteAttributeString("value", $protections.resources.argument.mode.ToLower()) 430 | $xmlWriter.WriteEndElement() 431 | } 432 | $xmlWriter.WriteEndElement() 433 | } 434 | 435 | # Name Protection options 436 | if ($protections.rename.enabled) 437 | { 438 | $xmlWriter.WriteStartElement("protection") 439 | $xmlWriter.WriteAttributeString("id", "rename") 440 | 441 | if ($protections.rename.argument.mode.ToLower() -ne "unicode") 442 | { 443 | $xmlWriter.StartElement("argument") 444 | $xmlWriter.WriteAttributeString("name", "mode") 445 | $xmlWriter.WriteAttributeString("value", $protections.resources.argument.mode.ToLower()) 446 | $xmlWriter.WriteEndElement() 447 | } 448 | 449 | if ($protections.rename.argument.mode.ToLower() -eq "reversible") 450 | { 451 | $xmlWriter.StartElement("argument") 452 | $xmlWriter.WriteAttributeString("name", "password") 453 | $xmlWriter.WriteAttributeString("value", $protections.resources.argument.password.ToLower()) 454 | $xmlWriter.WriteEndElement() 455 | } 456 | 457 | if ($protections.rename.argument.renameArgs.ToLower() -ne "true") 458 | { 459 | $xmlWriter.StartElement("argument") 460 | $xmlWriter.WriteAttributeString("name", "renameArgs") 461 | $xmlWriter.WriteAttributeString("value", $protections.resources.argument.renameArgs.ToLower()) 462 | $xmlWriter.WriteEndElement() 463 | } 464 | 465 | if ($protections.rename.argument.renEnum.ToLower() -ne "false") 466 | { 467 | $xmlWriter.StartElement("argument") 468 | $xmlWriter.WriteAttributeString("name", "renEnum") 469 | $xmlWriter.WriteAttributeString("value", $protections.resources.argument.renEnum.ToLower()) 470 | $xmlWriter.WriteEndElement() 471 | } 472 | 473 | if ($protections.rename.argument.flatten.ToLower() -ne "true") 474 | { 475 | $xmlWriter.StartElement("argument") 476 | $xmlWriter.WriteAttributeString("name", "flatten") 477 | $xmlWriter.WriteAttributeString("value", $protections.resources.argument.flatten.ToLower()) 478 | $xmlWriter.WriteEndElement() 479 | } 480 | 481 | if ($protections.rename.argument.forceRen.ToLower() -ne "false") 482 | { 483 | $xmlWriter.StartElement("argument") 484 | $xmlWriter.WriteAttributeString("name", "forceRen") 485 | $xmlWriter.WriteAttributeString("value", $protections.resources.argument.forceRen.ToLower()) 486 | $xmlWriter.WriteEndElement() 487 | } 488 | 489 | if ($protections.rename.argument.renPublic.ToLower() -ne "false") 490 | { 491 | $xmlWriter.StartElement("argument") 492 | $xmlWriter.WriteAttributeString("name", "renPublic") 493 | $xmlWriter.WriteAttributeString("value", $protections.resources.argument.renPublic.ToLower()) 494 | $xmlWriter.WriteEndElement() 495 | } 496 | 497 | if ($protections.rename.argument.renPdb.ToLower() -ne "false") 498 | { 499 | $xmlWriter.StartElement("argument") 500 | $xmlWriter.WriteAttributeString("name", "renPdb") 501 | $xmlWriter.WriteAttributeString("value", $protections.resources.argument.renPdb.ToLower()) 502 | $xmlWriter.WriteEndElement() 503 | } 504 | 505 | if ($protections.rename.argument.renXaml.ToLower() -ne "true") 506 | { 507 | $xmlWriter.StartElement("argument") 508 | $xmlWriter.WriteAttributeString("name", "renXaml") 509 | $xmlWriter.WriteAttributeString("value", $protections.resources.argument.renXaml.ToLower()) 510 | $xmlWriter.WriteEndElement() 511 | } 512 | $xmlWriter.WriteEndElement() 513 | } 514 | 515 | # watermark options 516 | if ($protections.watermark.enabled) 517 | { 518 | $xmlWriter.WriteStartElement("protection") 519 | $xmlWriter.WriteAttributeString("id", "watermark") 520 | $xmlWriter.WriteAttributeString("action", $protections.watermark.action.ToLower()) 521 | $xmlWriter.WriteEndElement() 522 | } 523 | 524 | # Close out the rules 525 | $xmlWriter.WriteEndElement() 526 | 527 | # Set Packer option if enabled and the target assembly is an EXE 528 | # DLLs cannot be compressed with ConfuserEX 529 | if (($protections.packer) -AND ($targetBin -match '\.exe$')) { 530 | $xmlWriter.WriteStartElement("packer") 531 | $xmlWriter.WriteAttributeString("id", $protections.packer.ToLower()) 532 | $xmlWriter.WriteEndElement() 533 | } 534 | 535 | # Set the target binary 536 | $xmlWriter.WriteStartElement("module") 537 | $xmlWriter.WriteAttributeString("path", $targetBin) 538 | $xmlWriter.WriteEndElement() 539 | 540 | # Close out the project 541 | $xmlWriter.WriteEndElement() 542 | 543 | # End and finalize the document 544 | $xmlWriter.WriteEndDocument() 545 | $xmlWriter.Flush() 546 | $xmlWriter.Close() 547 | return $true 548 | } 549 | 550 | Export-ModuleMember -Function Get-ConfuserExProtections 551 | Export-ModuleMember -Function New-ConfuserEXProj 552 | -------------------------------------------------------------------------------- /powershell/Update-Vsproject.psm1: -------------------------------------------------------------------------------- 1 | <# .SYNOPSIS 2 | This script can manipulate a Visual Studio C# project an solution to retarget it to use a specific .NET version and output seperate x86 and x64 versions. 3 | .DESCRIPTION 4 | The script will find all .csproj files in a target directory. If the .csproj files it finds does not contain an x86 and x64 targets. If the target .NET version needs to be changed, it will do this too. The script will then use slngen to generate a new solution file. 5 | .NOTES 6 | You will need to install slngen to use this script: https://microsoft.github.io/slngen/ 7 | .LINK 8 | https://github.com/xenoscr/SharpProjMangler 9 | #> 10 | 11 | # Print out formatted XML 12 | function Write-PrettyXml 13 | { 14 | param( 15 | [Parameter(Madatory=$true, 16 | Position=0)] 17 | [xml] 18 | $xml 19 | ) 20 | <# 21 | .SYNOPSIS 22 | Print formatted XML to the console. 23 | .DESCRIPTION 24 | Print formatted XML to the console. This function requires an XML object as an argument. 25 | .PARAMETER xml 26 | An [xml] object that will be formatted and printed to the console. 27 | .INPUTS 28 | None. You cannot pipe objects to Write-PrettyXml. 29 | .OUTPUTS 30 | None. The output is printed to the console. 31 | .EXAMPLE 32 | PS> Write-PrettyXml [xml]$xmlContent 33 | #> 34 | 35 | $StringWriter = New-Object System.IO.StringWriter; 36 | $XmlWriter = New-Object System.Xml.XmlTextWriter $StringWriter; 37 | $XmlWriter.Formatting = "indented"; 38 | $xml.WriteTo($XmlWriter); 39 | $XmlWriter.Flush(); 40 | $StringWriter.Flush(); 41 | Write-Output $StringWriter.ToString(); 42 | } 43 | 44 | # Update the target .NET version 45 | function Update-CSProjDotNetVer 46 | { 47 | param( 48 | [Parameter(Mandatory=$true, 49 | Position=0, 50 | HelpMessage="A system.object[] containing project file details.")] 51 | [System.Object[]] 52 | $projFiles, 53 | [Parameter(Mandatory=$true, 54 | Position=1, 55 | HelpMessage="The target .NET Framework")] 56 | [Version] 57 | $targetFrameworkVersion 58 | ) 59 | <# 60 | .SYNOPSIS 61 | Update the target .NET Framework of C# projects. 62 | .DESCRIPTION 63 | Update the target .NET Framework of all C# projects passed to the function. 64 | .PARAMETER projFiles 65 | A System.Object[] containing C# project file locations. 66 | .PARAMETER targetFrameworkVersion 67 | The target .NET Framework version. 68 | .INPUTS 69 | None. You cannot pipe objects to Update-CSProjDotNetVer. 70 | .OUTPUTS 71 | None. The changes are saved to the C# project files. 72 | .EXAMPLE 73 | PS> Update-CSProjDotNetVer -projFiles $(Get-ChildItem C:\Example\ -Recurse -Filter *.csproj) -Version [Version]"v3.5" 74 | #> 75 | 76 | $projsWithVersion = New-Object Collections.Generic.List[object] 77 | foreach($file in $projFiles) 78 | { 79 | try { 80 | $content = New-Object xml 81 | $content.PreserveWhitespace = $true 82 | $content.Load($file.FullName) 83 | $versionNodes = $content.GetElementsByTagName("TargetFrameworkVersion"); 84 | } 85 | catch { 86 | Write-Error "Failed to load $file.FullName" 87 | return $false 88 | } 89 | 90 | switch($versionNodes.Count) 91 | { 92 | 0 { 93 | Write-Host "The project has no framework version: $file.FullName" 94 | break; 95 | } 96 | 1 { 97 | try { 98 | $version = $versionNodes[0].InnerText; 99 | 100 | $projsWithVersion.Add([PsCustomObject]@{ 101 | File = $file; 102 | XmlContent = $content; 103 | VersionNode = $versionNodes[0]; 104 | VersionRaw = $version; 105 | Version = [Version]::new($version.Replace("v", "")) 106 | }) 107 | } 108 | catch { 109 | Write-Error "Failed to collect .NET Framework Version." 110 | return $false 111 | } 112 | break; 113 | } 114 | default { 115 | Write-Host "The project has multiple elements of TargetFrameworkVersion: $file.FullName" 116 | break; 117 | } 118 | } 119 | } 120 | 121 | foreach($proj in $projsWithVersion) 122 | { 123 | if($targetFrameworkVersion -ne $proj.Version) 124 | { 125 | try { 126 | $proj.VersionNode.set_InnerXML("v$targetFrameworkVersion") 127 | $proj.XmlContent.Save($proj.File.FullName); 128 | #Write-Host "It would have been changed from $($proj.Version) to $targetFrameworkVersion." 129 | #Write-PrettyXml $proj.XmlContent 130 | } 131 | catch { 132 | Write-Error "Failed to modify .NET Framework Version." 133 | return $false 134 | } 135 | } 136 | } 137 | return $true 138 | } 139 | 140 | Function Update-CSProjPlatform 141 | { 142 | param( 143 | [Parameter(Mandatory=$true, 144 | Position=0, 145 | HelpMessage="System.Object[] containing project file paths")] 146 | [System.Object[]] 147 | $projFiles, 148 | [Parameter(Mandatory=$true, 149 | Position=1, 150 | HelpMessage="The target CPU Platform: x86 or x64")] 151 | [ValidateSet("x86", "x64")] 152 | [string] 153 | $targetPlatform 154 | ) 155 | <# 156 | .SYNOPSIS 157 | Update C# project files to include the specified platform configuration. 158 | .DESCRIPTION 159 | Update C# project files to include the specified platform configuration. 160 | .PARAMETER projFiles 161 | A System.Object[] containing C# project file locations. 162 | .PARAMETER targetPlatform 163 | The target CPU architecture. 164 | .INPUTS 165 | None. You cannot pipe objects to Update-CSProjPlatform. 166 | .OUTPUTS 167 | None. The changes are saved to the C# project files. 168 | .EXAMPLE 169 | PS> Update-CSProjPlatform -projFiles $(Get-ChildItem C:\Example\ -Recurse -Filter *.csproj) -targetPlatform "x86" 170 | #> 171 | 172 | foreach($file in $projFiles) 173 | { 174 | $projsWithPlatforms = New-Object Collections.Generic.List[object] 175 | $targetPlatformFound = $False 176 | $content = New-Object xml 177 | $content.PreserveWhitespace = $true 178 | 179 | try { 180 | $content.Load($file.FullName) 181 | } 182 | catch { 183 | Write-Error "Failed to load $file.FullName" 184 | return $false 185 | } 186 | 187 | ForEach ($propGroup in $content.Project.PropertyGroup) 188 | { 189 | If ($propGroup.AssemblyName) 190 | { 191 | try { 192 | $propGroup.AssemblyName = "`$(MSBuildProjectName).`$(TargetFrameworkVersion).`$(Platform)" 193 | } 194 | catch { 195 | Write-Error "Failed to modify AssemblyName value." 196 | return $false 197 | } 198 | } 199 | If ($propGroup.OutputPath) 200 | { 201 | try { 202 | $propGroup.OutputPath = "bin\`$(Configuration)\`$(Platform)\`$(TargetFrameworkVersion)\" 203 | } 204 | catch { 205 | Write-Error "Failed to modify OutputPath value." 206 | return $false 207 | } 208 | } 209 | If ($propGroup.Condition) 210 | { 211 | If ($propGroup.PlatformTarget -eq $targetPlatform) 212 | { 213 | $targetPlatformFound = $True 214 | } 215 | 216 | 217 | $projsWithPlatforms.Add($propGroup) 218 | } 219 | } 220 | If (-Not $targetPlatformFound) 221 | { 222 | $DebugDone = $False 223 | $ReleaseDone = $False 224 | $newReleaseNode = $Null 225 | $newDebugNode = $Null 226 | ForEach ($projEntry in $projsWithPlatforms) 227 | { 228 | If (([regex]::Match($propGroup.Condtion, "Release")) -and ($ReleaseDone -ne $True)) 229 | { 230 | $newReleaseNode = $projEntry.Clone() 231 | $patternMatch = [regex]::Match($newReleaseNode.Condition, "((Debug|Release)\|.*)'").Captures.Groups[1].value 232 | try { 233 | $newReleaseNode.Condition = $newReleaseNode.Condition.Replace($patternMatch, "Release|$targetPlatform") 234 | } 235 | catch { 236 | Write-Error "Failed to modify condition value." 237 | return $false 238 | } 239 | 240 | try { 241 | $newReleaseNode.PlatformTarget = $targetPlatform 242 | } 243 | catch { 244 | Write-Error "Failed to modify PlatformTarget value." 245 | } 246 | 247 | If (($newReleaseNode.Prefer32Bit -eq 'false') -and ($targetPlatform -eq 'x86')) 248 | { 249 | try { 250 | $newReleaseNode.Prefer32Bit = 'true' 251 | } 252 | catch { 253 | Write-Error "Failed to set Prefer32Bit." 254 | return $false 255 | } 256 | } 257 | 258 | try { 259 | $newReleaseNode.OutputPath = "bin\`$(Configuration)\`$(Platform)\`$(TargetFrameworkVersion)\" 260 | } 261 | catch { 262 | Write-Error "Failed to modify OutputPath value." 263 | return $false 264 | } 265 | 266 | Write-Host "DefineConstants = $($newReleaseNode.DefineConstants.GetType())" 267 | If ($newReleaseNode.DefineConstants -is [System.Xml.XmlElement]) 268 | { 269 | try { 270 | $result = $newReleaseNode.DefineConstants.AppendChild($content.CreateTextNode("")) 271 | } 272 | catch { 273 | Write-Error "Failed to modify DefineConstants value." 274 | return $false 275 | } 276 | } 277 | else 278 | { 279 | try { 280 | $newReleaseNode.DefineConstants = [string]"" 281 | } 282 | catch { 283 | Write-Error "Failed to modify DefineConstants value." 284 | return $false 285 | } 286 | } 287 | $ReleaseDone = $True 288 | } 289 | ElseIf (([regex]::Match($propGroup.Condtion, "Debug")) -and ($DebugDone -ne $True)) 290 | { 291 | $newDebugNode = $projEntry.Clone() 292 | $patternMatch = [regex]::Match($newDebugNode.Condition, "((Debug|Release)\|.*)'").Captures.Groups[1].value 293 | try { 294 | $newDebugNode.Condition = $newDebugNode.Condition.Replace($patternMatch, "Debug|$targetPlatform") 295 | } 296 | catch { 297 | Write-Error "Failed to modify Condition value." 298 | return $false 299 | } 300 | 301 | try { 302 | $newDebugNode.PlatformTarget = $targetPlatform 303 | } 304 | catch { 305 | Write-Error "Failed to modify PlatformTarget value." 306 | return $false 307 | } 308 | 309 | If (($newDebugNode.Prefer32Bit -eq 'false') -and ($targetPlatform -eq 'x86')) 310 | { 311 | $newDebugNode.Prefer32Bit = 'true' 312 | } 313 | 314 | try { 315 | $newDebugNode.OutputPath = "bin\`$(Configuration)\`$(Platform)\`$(TargetFrameworkVersion)\" 316 | } 317 | catch { 318 | Write-Error "Failed to modify OutputPath value." 319 | return $false 320 | } 321 | 322 | Write-Host "DefineConstants = $($newDebugNode.DefineConstants.GetType())" 323 | If ($newDebugNode.DefineConstants -is [System.Xml.XmlElement]) 324 | { 325 | try { 326 | $result = $newDebugNode.DefineConstants.AppendChild($content.CreateTextNode("DEBUG;TRACE")) 327 | } 328 | catch { 329 | Write-Error "Failed to add DefineConstansts value." 330 | return $false 331 | } 332 | } 333 | else 334 | { 335 | try { 336 | $newDebugNode.DefineConstants = [string]"DEBUG;TRACE" 337 | } 338 | catch { 339 | Write-Error "Failed to modify DefineConstants value." 340 | return $false 341 | } 342 | } 343 | $DebugDone = $True 344 | } 345 | } 346 | If (($DebugDone) -And ($ReleaseDone)) 347 | { 348 | try { 349 | $content.Project.InsertAfter($newReleaseNode, $content.Project.PropertyGroup[0]) | Out-Null 350 | $content.Project.InsertAfter($newDebugNode, $content.Project.PropertyGroup[0]) | Out-Null 351 | $content.Save($file.FullName); 352 | #Write-PrettyXml $content 353 | Remove-Variable content 354 | } 355 | catch { 356 | Write-Error "Failed to add new PropertyGroups." 357 | return $false 358 | } 359 | } 360 | } 361 | } 362 | return $true 363 | } 364 | 365 | Function Add-SlnConfig 366 | { 367 | param( 368 | [Parameter(Mandatory=$true, 369 | Position=0, 370 | HelpMessage="Full path to the .sln file.")] 371 | [ValidateScript({ 372 | if (-Not ($_ | Test-Path)){ 373 | throw "The provided .sln file path does not exist." 374 | } 375 | if (-Not ($_ | Test-Path -PathType Leaf)) { 376 | throw "The provided argument must be a file. Folder paths are not allowed." 377 | } 378 | if ($_ -NotMatch "(\.sln)") { 379 | throw "The file specified does not have the correct file extention." 380 | } 381 | return $true 382 | })] 383 | [System.IO.FileInfo] 384 | $Path, 385 | [Parameter(Mandatory=$true, 386 | Position=1, 387 | HelpMessage="The CPU architecture to target.")] 388 | [ValidateSet("x86", "x64")] 389 | [string] 390 | $cpuArch 391 | ) 392 | <# 393 | .SYNOPSIS 394 | Update Visual Studio Solution file to include the specified platform configuration. 395 | .DESCRIPTION 396 | Update Visual Studio Solution file to include the specified platform configuration. 397 | .PARAMETER Path 398 | A file path pointing to a Visual Studio solution file (.sln). 399 | .PARAMETER cpuArch 400 | The target CPU architecture. 401 | .INPUTS 402 | None. You cannot pipe objects to Add-SlnConfig. 403 | .OUTPUTS 404 | None. The changes are saved to the solution file. 405 | .EXAMPLE 406 | PS> Add-SlnConfig -Path C:\foo\bar.sln -cpuArch "x86" 407 | #> 408 | 409 | # Get the raw contents of the solution file. 410 | $slnContent = Get-Content -Raw $Path 411 | 412 | # Regex to capture all project configurations 413 | [regex]$regexProjEntry = '(?<=Project)(?ms).*?(?=EndProject)' 414 | 415 | # Regex to capture Solution Platform Configurations 416 | [regex]$regexSolPlatformConf = '(?<=GlobalSection\(SolutionConfigurationPlatforms\) = preSolution)(?ms).*?(?=EndGlobalSection)' 417 | 418 | # Regex to capture Project Configurations 419 | [regex]$regexProjConf = '(?<=GlobalSection\(ProjectConfigurationPlatforms\) = postSolution)(?ms).*?(?=EndGlobalSection)' 420 | 421 | # Regex to capture UUIDs 422 | [regex]$regexUUID = '[a-fA-F0-9]{8}-([a-fA-F0-9]{4}-){3}[a-fA-F0-9]{12}' 423 | 424 | # Get the Platform Configurations from the solution file 425 | $solPlatformConf = $regexSolPlatformConf.Matches($slnContent) 426 | 427 | # Get the Project Entries from the solution file 428 | $allProjEntries = $regexProjEntry.Matches($slnContent) 429 | 430 | # Get current configurations from the solution file 431 | $allConfigurations = $regexProjConf.Matches($slnContent) 432 | 433 | # The list of configurations to add. 434 | $confList = ("Debug|$cpuArch", "Release|$cpuArch") 435 | 436 | # Loop through each configuration 437 | ForEach ($curConf in $confList) 438 | { 439 | $solPlatformConf = $regexSolPlatformConf.Matches($slnContent) 440 | $newSolPlatformConf = "$curConf = $curConf" 441 | [regex]$regexNewSolPlatformConf = [regex]::escape($newSolPlatformConf) 442 | 443 | if ($regexNewSolPlatformConf.Match($solPlatformConf[0].value).Success -eq $false) 444 | { 445 | $whitespace = $solPlatformConf[0].Value.Substring(0, $solPlatformConf[0].Value.Length - $solPlatformConf[0].Value.TrimStart().Length) 446 | $newSolPlat = "$whitespace$curConf = $curConf" 447 | try { 448 | $slnContent = $slnContent.Insert([int]$solPlatformConf[0].Index, $newSolPlat) 449 | } 450 | catch { 451 | Write-Error "Failed to insert solution." 452 | return $false 453 | } 454 | } 455 | 456 | ForEach ($projEntry in $allProjEntries) 457 | { 458 | $uuidMatch = $regexUUID.Matches($projEntry.value)[1] 459 | if ($uuidMatch -eq $null) 460 | { 461 | continue 462 | } 463 | else 464 | { 465 | $curUUID = $uuidMatch.value 466 | [regex]$regexUUIDCheck = $curUUID 467 | [regex]$regexNewConfig = [regex]::escape("{$curUUID}.$curConf.ActiveCfg = $curConf") 468 | $foundUUID = $regexUUIDCheck.Matches($allConfigurations) 469 | $foundMatch = $regexNewConfig.Match($slnContent) 470 | 471 | if (($foundMatch[0].Success -eq $false) -and ($foundUUID[0].Success -eq $true) -and ($curUUID.Length -gt 0)) 472 | { 473 | [regex]$regexFindConfig = "\t*\{$curUUID}\..*\.*ActiveCfg.*" 474 | $profMatch = $regexFindConfig.Match($slnContent) 475 | $whitespace = $profMatch.value.Substring(0, $profMatch.value.Length - $profMatch.value.TrimStart().Length) 476 | $newCfg = "$whitespace{$curUUID}.$curConf.ActiveCfg = $curConf`n$whitespace{$curUUID}.$curConf.Build.0 = $curConf`n" 477 | try { 478 | $slnContent = $slnContent.Insert([int]$profMatch[0].Index, $newCfg) 479 | } 480 | catch { 481 | Write-Error "Failed to insert new solution UUID." 482 | return $false 483 | } 484 | } 485 | else 486 | { 487 | Write-Host "{$curUUID}.$curConf.ActiveCfg = $curConf" 488 | } 489 | } 490 | } 491 | } 492 | try { 493 | # Write the contents and replace all the Windows newlines with unix style new lines, which is the default for a solution file. 494 | [byte[]][char[]]$slnContent.Replace("`r`n", "`n") | Set-Content -NoNewLine -Encoding Byte -Path $Path 495 | } 496 | catch { 497 | Write-Error "Failed to save changes to the solution file." 498 | return $false 499 | } 500 | 501 | return $true 502 | } 503 | 504 | Function Update-CSProjects 505 | { 506 | param( 507 | [Parameter(Mandatory=$true, 508 | Position=0, 509 | HelpMessage="Full path to the .sln file.")] 510 | [ValidateScript({ 511 | if (-Not ($_ | Test-Path)) { 512 | throw "The provided .sln file path does not exist." 513 | } 514 | if (-Not ($_ | Test-Path -PathType Leaf)) { 515 | throw "The provided argument must be a file. Folder paths are not allowed." 516 | } 517 | if ($_ -NotMatch "(\.sln)") { 518 | throw "The file specified does not have the correct file extention." 519 | } 520 | return $true 521 | })] 522 | [System.IO.FileInfo] 523 | $slnPath, 524 | [Parameter(Mandatory=$true, 525 | Position=1, 526 | HelpMessage="Enter the base path of the Visual Studio Project")] 527 | [ValidateScript({ 528 | if (-Not ($_ | Test-Path)) { 529 | throw "The provided path does not exist." 530 | } 531 | return $true 532 | })] 533 | [System.IO.FileInfo] 534 | $Path, 535 | [Parameter(Mandatory=$true, 536 | Position=2, 537 | HelpMessage="Enter the target CPU platform: x86 or x64")] 538 | [ValidateSet("x86", "x64", "all")] 539 | [string] 540 | $TargetPlatform = "all", 541 | [Parameter(Mandatory=$false, 542 | Position=3, 543 | HelpMessage="Enter the target .NET framework version")] 544 | [string] 545 | $TargetFramework 546 | ) 547 | <# 548 | .SYNOPSIS 549 | Update a C# solution and project files to include the specified platform configuration. 550 | .DESCRIPTION 551 | Update a C# solution and project files to include the specified platform configuration. 552 | .PARAMETER slnPath 553 | A file path pointing to a Visual Studio solution file (.sln). 554 | .PARAMETER Path 555 | The base path of the Visual Studio project. 556 | .PARAMETER TargetPlatform 557 | The target platform to be used. Valid options are: all, x86, and x64. 558 | .PARAMETER TargetFramework 559 | The .NET Framework version to target. 560 | .INPUTS 561 | None. You cannot pipe objects to Update-CSProjects. 562 | .OUTPUTS 563 | None. The changes are saved to the solution and project files. 564 | .EXAMPLE 565 | PS> Update-CSProjects -slnPath C:\foo\bar.sln -Path C:\foo -TargetPlatform "x86" -TargetFramework "4.0" 566 | #> 567 | 568 | # Find all the .csproj files in the target path 569 | $projFiles = Get-ChildItem $Path -Recurse -Filter *.csproj 570 | if ($projFiles.Count -lt 1) 571 | { 572 | Write-Host "No .csproj files were found. Did you specify the correct path?" 573 | return $false 574 | } 575 | else 576 | { 577 | if ($targetFramework -ne "") 578 | { 579 | if ($(Update-CSProjDotNetVer $projFiles $targetFramework) -eq $false) { 580 | return $false 581 | } 582 | } 583 | if ($targetPlatform -eq "all") 584 | { 585 | if ($(Add-SlnConfig $slnPath "x86") -eq $false) { 586 | return $false 587 | } 588 | if ($(Add-SlnConfig $slnPath "x64") -eq $false) { 589 | return $false 590 | } 591 | if ($(Update-CSProjPlatform $projFiles "x86") -eq $false) { 592 | return $false 593 | } 594 | if ($(Update-CSProjPlatform $projFiles "x64") -eq $false) { 595 | return $false 596 | } 597 | } 598 | else 599 | { 600 | if ($(Add-SlnConfig $slnPath $targetPlatform) -eq $false) { 601 | return $false 602 | } 603 | if ($(Update-CSProjPlatform $projFiles $targetPlatform) -eq $false) { 604 | return $false 605 | } 606 | } 607 | } 608 | return $true 609 | } 610 | 611 | Export-ModuleMember -Function Update-CSProjects 612 | Export-ModuleMember -Function Update-CSProjDotNetVer 613 | Export-ModuleMember -Function Update-CSProjPlatform 614 | Export-ModuleMember -Function Write-PrettyXml 615 | Export-ModuleMember -Function Add-SlnConfig 616 | -------------------------------------------------------------------------------- /vars/psScripts.groovy: -------------------------------------------------------------------------------- 1 | def modifySolution(psScriptPath, targetPlatform, targetFramework = null) { 2 | if (targetFramework != null) { 3 | def stdout = powershell(returnStdout: true, script: """ 4 | Write-Information "Importing PowerShell module: Update-Vsproject". 5 | Import-Module "${psScriptPath}Update-Vsproject.psm1" 6 | 7 | Write-Information "Gathering *.sln files to modify" 8 | \$slnPath = \$(Get-ChildItem -Filter *.sln)[0].FullName 9 | 10 | Write-Information "Modifying the Solution and Project." 11 | if (\$(Update-CSProjects -slnPath \$slnPath -Path \$(\$slnPath | Split-Path -Parent) -TargetPlatform "${targetPlatform}" -TargetFramework "${targetFramework}")) { 12 | Write-Information "Modifications successful." 13 | Exit 0 14 | } else { 15 | Write-Error "Modifications failed." 16 | Exit 1 17 | } 18 | """) 19 | println stdout 20 | } else { 21 | def stdout = powershell(returnStdout: true, script: """ 22 | Write-Information "Importing PowerShell module: Update-Vsproject". 23 | Import-Module "${psScriptPath}Update-Vsproject.psm1" 24 | 25 | Write-Information "Gathering *.sln files to modify" 26 | \$slnPath = \$(Get-ChildItem -Filter *.sln)[0].FullName 27 | 28 | Write-Information "Modifying the Solution and Project." 29 | if (\$(Update-CSProjects -slnPath \$slnPath -Path \$(\$slnPath | Split-Path -Parent) -TargetPlatform "${targetPlatform}")) { 30 | Write-Information "Modifications successful." 31 | Exit 0 32 | } else { 33 | Write-Error "Modifications failed." 34 | Exit 1 35 | } 36 | """) 37 | println stdout 38 | } 39 | } 40 | 41 | def removeOldAssemblies() { 42 | def stdout = powershell(returnStdout: true, script: """ 43 | Write-Information "Collection Assembly List." 44 | Get-ChildItem "*\\bin" | forEach { Remove-Item -Path \$_.FullName -Force -Recurse } 45 | Get-ChildItem "*\\obj" | forEach { Remove-Item -Path \$_.FullName -Force -Recurse } 46 | Write-Information "Clean-up complete." 47 | Exit 0 48 | """) 49 | println stdout 50 | } 51 | 52 | def compileSolution(psScriptPath, targetPlatform, targetFramework = null) { 53 | if (targetFramework != null) { 54 | def stdout = powershell(returnStdout: true, script: """ 55 | Write-Information "Importing PowerShell module: Compile-CSProject" 56 | Import-Module "${psScriptPath}Compile-CSProject.psm1" 57 | 58 | Write-Information "Collecting *.sln list." 59 | \$slnPath = \$(Get-ChildItem -Filter *.sln)[0].FullName 60 | 61 | Write-Information "Compiling Solution, Platform = ${targetPlatform} Framwork = ${targetFramework}" 62 | if (\$(Invoke-CompileCSProject -targetPlatform "${targetPlatform}" -TargetConfiguration "Release" -slnPath \$slnPath -targetFrameworkVersion ${targetFramework})) { 63 | Write-Information "Compilation successful." 64 | Exit 0 65 | } else { 66 | Write-Error "Compilation failed." 67 | Exit 1 68 | } 69 | """) 70 | println stdout 71 | } else { 72 | def stdout = powershell(returnStdout: true, script: """ 73 | Write-Information "Importing PowerShell module: Compile-CSProject" 74 | Import-Module "${psScriptPath}Compile-CSProject.psm1" 75 | 76 | Write-Information "Collecting *.sln list." 77 | \$slnPath = \$(Get-ChildItem -Filter *.sln)[0].FullName 78 | 79 | Write-Information "Compiling Solution, Platform = ${targetPlatform} Framwork = Default" 80 | if (\$(Invoke-CompileCSProject -targetPlatform "${targetPlatform}" -TargetConfiguration "Release" -slnPath \$slnPath)) { 81 | Write-Information "Compilation successful." 82 | Exit 0 83 | } else { 84 | Write-Error "Compilation failed." 85 | Exit 1 86 | } 87 | """) 88 | println stdout 89 | } 90 | } 91 | 92 | def confuseSolution(psScriptPath, confuserExPath) { 93 | def stdout = powershell(returnStdout: true, script: """ 94 | Write-Information "Importing PowerShell Module: ConfuserExProj" 95 | Import-Module "${psScriptPath}ConfuserEXProj.psm1" 96 | 97 | Write-Information "Configuring ConfuserEx Protections." 98 | \$confuserProperties = Get-ConfuserExProtections 99 | \$confuserProperties.antiTamper.enabled = \$True 100 | \$confuserProperties.constants.enabled = \$true 101 | \$confuserProperties.ctrlFlow.enabled = \$true 102 | \$confuserProperties.refProxy.enabled = \$true 103 | \$confuserProperties.resources.enabled = \$true 104 | \$confuserProperties.antiILDasm.enabled = \$true 105 | \$confuserProperties.watermark.enabled = \$true 106 | \$confuserProperties.watermark.action = "remove" 107 | \$confuserProperties.packer = \$null 108 | 109 | Write-Information "Collecting list of Assemblies." 110 | \$assemblyList = Get-ChildItem -Recurse | Where-Object { \$_.name -match \'((x86|x64|AnyCPU)\\.exe\$|(x86|x64|AnyCPU)\\.dll\$)\' } 111 | 112 | Write-Information "Processing Assemblies." 113 | ForEach (\$assembly in \$assemblyList) { 114 | If (\$assembly.FullName -NotMatch \'\\\\obj\\\\\') { 115 | Write-Information "Creating ConfuserEx Project File." 116 | If (\$(New-ConfuserExProj -Protections \$confuserProperties -TargetAssembly "\$(\$assembly.FullName)")) { 117 | try { 118 | Write-Information "Running ConfuserEx on: \$(\$assembly.FullName)" 119 | . "${confuserExPath}" -n "\$(\$assembly.FullName | Split-Path -Parent)\\ConfuserEx.crproj" 120 | } catch { 121 | Write-Error "ConfuserEx failed." 122 | Exit 1 123 | } 124 | try { 125 | Write-Information "Renaming Confused Assembly." 126 | Move-Item "\$(\$assembly.FullName | Split-Path -Parent)\\Confused\\\$(\$assembly.FullName | Split-Path -Leaf)" -Destination "\$(\$assembly.FullName | Split-Path -Parent)\\Confused\\Confused_\$(\$assembly.FullName | Split-Path -Leaf)" 127 | } catch { 128 | Write-Error "Renaming Confused Assembly failed." 129 | Exit 1 130 | } 131 | } else { 132 | Write-Error "Unable to create ConfuserEx configuration." 133 | Exit 1 134 | } 135 | } 136 | } 137 | """) 138 | println stdout 139 | } --------------------------------------------------------------------------------