Advertisement
Laughing_Mantis

PatchExtract Version 1.15 by Greg Linares (@Laughing_Mantis)

Oct 20th, 2016
1,052
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. <#
  2. ================
  3. PATCHEXTRACT.PS1
  4. =================
  5. Version 1.15 Microsoft MSU Patch Extraction and Patch Organization Utility by Greg Linares (@Laughing_Mantis)
  6.  
  7. This Powershell script will extract a Microsoft MSU update file and then organize the output of extracted files and folders.
  8.  
  9. Organization of the output files is based on the patch's files and will organize them based on their archicture (x86, x64, or wow64)
  10. as well as their content-type, ie: resource and catalog files will be moved to a JUNK subfolder and patch binaries and index files will
  11. goto a PATCH folder.
  12.  
  13. This script was developed in order to aid reverse engineers in quickly organizing patches so they can be binary diffed faster and easier.
  14. This was especially developed with the new bulk Microsoft Kernel patches in mind.
  15.  
  16. Example output folder structure ouput would be similar to this:
  17.  
  18. C:\PATCHES\MS15-XXX\PRE
  19.     -x86
  20.         - x86 Binary patched files
  21.     -x64
  22.         - x64 binary patched files
  23.     -WOW64
  24.         - syswow64 binary patched files
  25.     -JUNK
  26.         - resource, catalog, mum, and other non-binary based patched files
  27.     -PATCH
  28.         - original patch, cabs and xml files from the extraction
  29.     -MSIL
  30.         - MSIL .NET binary patched files ***New in Version 1.1***
  31.  
  32.     Directories will automagically be organized into filename-version to remove garbage filler folder names
  33.        
  34.        
  35. =============
  36. REQUIREMENTS
  37. =============
  38. 'expand.exe' to be present in %WINDIR%\SYSTEM32 (it is by default) - It will execute this file @ the current users permissions
  39. A valid Microsoft MSU patch file to extract (PATCH variable)
  40. Directory and File write/creation permissions to the PATH folder specified
  41.        
  42.    
  43. =======    
  44. USAGE
  45. =======
  46.  
  47. Powershell -ExecutionPolicy Bypass -File PatchExtract.ps1 -Patch C:\Patches\Windows6.1-KB3088195-x64.msu -Path C:\Patches\MS15-XXX\POST\
  48.  
  49.  
  50. This would extract the patch file C:\Patches\Windows6.1-KB3088195-x64.msu to the folder C:\Patches\MS15-XXX\POST\.
  51. It will then create all the sub organization folders within C:\Patches\MS15-XXX\POST\ folder.
  52.  
  53. (Note: the optional Powershell parameters '-ExecutionPolicy Bypass' is necessary in some environments to overcome Powershell execution restrictions)
  54.  
  55. ==========
  56. ARGUMENTS
  57. ==========
  58. -PATCH <STRING:Filename> [REQUIRED] [NO DEFAULT]
  59.     Specifies the MSU file that will be extracted to the specified PATH folder and then organized into the x86, x64, WOW, JUNK, and BIN folders specified
  60.     Extract command will be "expand -F:* <PATCH> <PATH>"
  61.     Non MSU files have not been tested however if the extraction does not generate a CAB file of the same name (indicator of successful extraction of MSU files)
  62.     the script assumes extraction failed.
  63.    
  64. -PATH <STRING:FolderPath> [REQUIRED] [NO DEFAULT]
  65.     Specified the folder that the PATCH file will be extracted and organized into
  66.     If the specified folders does not exist yet, the user will be prompted if they want to create it.
  67.     Relative paths '.\POST' can be used but it has not extensively been tested.
  68.  
  69.     ***New in Version 1.1***
  70.     The -PATH variable may be now omitted to expand to current directory
  71.    
  72.  
  73. -x86 <STRING:Foldername> [OPTIONAL] [DEFAULT='x86']
  74.  
  75.     Specifies the folder name within $PATH to store x86 patch binaries
  76.     example: -x86 32bit
  77.    
  78.    
  79. -x64 <STRING:Foldername> [OPTIONAL] [DEFAULT='x64']
  80.  
  81.     Specifies the folder name within $PATH to store x64 patch binaries
  82.     example: -x64 64bit
  83.    
  84. -WOW <STRING:Foldername> [OPTIONAL] [DEFAULT='WOW64']
  85.  
  86.     Specifies the folder name within $PATH to store wow64 type patch binaries
  87.     example: -WOW sysWOW64
  88.  
  89. -MSIL <STRING:Foldername> [OPTIONAL] [DEFAULT='MSIL']
  90.  
  91.     *** New in Version 1.1***
  92.     Specifies the folder name within $PATH to store .NET type patch binaries
  93.     example: -MSIL DOTNET
  94.    
  95. -JUNK <STRING:Foldername> [OPTIONAL] [DEFAULT='JUNK']
  96.  
  97.     Specifies the folder name within $PATH to store resource, catalog, and other generally useless for diffing patch binaries
  98.     example: -JUNK res
  99.    
  100.    
  101. -BIN <STRING:Foldername> [OPTIONAL] [DEFAULT='PATCH']
  102.  
  103.     Specifies the folder name within $PATH to store extraction xml and original patch msu and cab files
  104.     example: -BIN bin
  105.  
  106.  
  107. ================
  108. VERSION HISTORY
  109. ================
  110. I originally wrote this as an ugly batch file sometime between 2014 and 2015 as a way to organize folders but it was incomplete and buggy
  111.  
  112. Oct 15, 2015 - Initial Public Release 1.0
  113. Oct 20, 2016 - Version 1.1 Released
  114.                 * Bug fixes handling new naming format for patch .cab files
  115.                 * Added the ability to auto-extract to the same directory as current PATCH
  116.                 * filtered output directory name format to aid in bindiffing
  117.  
  118. Oct 20, 2016 - Version 1.2 Released
  119.                 * Bug fixes handling MSIL renaming issues and collisions in renaming patch folders
  120.  
  121. Oct 20, 2016 - Version 1.5 Released
  122.                 * Handles various types of sub arch binaries in x86, amd64, and wow64  during folder renames
  123.  
  124. ==========
  125. LICENSING
  126. ==========
  127. This script is provided free as beer.  It probably has some bugs and coding issues, however if you like it or find it useful please give me a shout out on twitter @Laughing_Mantis.  
  128. Feedback is encouraged and I will be likely releasing new scripts and tools and training in the future if it is welcome.
  129.  
  130.  
  131. -GLin
  132.  
  133. #>
  134.  
  135.  
  136.  
  137.  
  138. Param
  139. (
  140.  
  141.     [Parameter(ValueFromPipelineByPropertyName = $true)]
  142.     [ValidateNotNullOrEmpty()]
  143.     [string]$PATCH = "",
  144.    
  145.     [Parameter(ValueFromPipelineByPropertyName = $true)]
  146.     [string]$PATH = "",
  147.    
  148.     [Parameter(ValueFromPipelineByPropertyName = $true)]
  149.     [string]$x86 = "x86",
  150.    
  151.     [Parameter(ValueFromPipelineByPropertyName = $true)]
  152.     [string]$x64 = "x64",
  153.    
  154.     [Parameter(ValueFromPipelineByPropertyName = $true)]
  155.     [string]$WOW = "WOW64",
  156.  
  157.     [Parameter(ValueFromPipelineByPropertyName = $true)]
  158.     [string]$MSIL = "MSIL",
  159.    
  160.     [Parameter(ValueFromPipelineByPropertyName = $true)]
  161.     [string]$JUNK = "JUNK",
  162.    
  163.     [Parameter(ValueFromPipelineByPropertyName = $true)]
  164.     [string]$BIN = "PATCH"
  165.        
  166. )
  167.  
  168. Clear-Host
  169.  
  170.  
  171.  
  172.  
  173. if ($PATCH -eq "")
  174. {
  175.     Throw ("Error: No PATCH file specified.  Specify a valid Microsoft MSU Patch with the -PATCH argument")
  176.    
  177. }
  178.  
  179. if ((Split-Path $PATCH -Parent) -eq "")
  180. {
  181.     # First look in current working directory for the relative filename
  182.     $CurrentDir = $(get-location).Path;
  183.     $PATCH = $CurrentDir + "\" + $PATCH
  184.  
  185.     # if that doesnt work we look in the current script directory (less likely)
  186.     # but hey we tried
  187.     if (!(Test-Path $PATCH))
  188.     {
  189.         $scriptDir = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
  190.         $PATCH = $scriptDir + "\" + $PATCH
  191.     }
  192. }
  193.  
  194. if (!(Test-Path $PATCH))
  195. {
  196.     Throw ("Error: Specified PATCH file ($PATCH) does not exist.  Specify a valid Microsoft MSU Patch file with the -PATCH argument.")
  197. }
  198.  
  199. if ($PATH -eq "")
  200. {
  201.     $PATH = Split-Path $PATCH -Parent
  202.     write-Host ("PATH = $PATH")
  203.     Write-Host ("No PATH folder specified.  Will extract to $PATH folder.")
  204.    
  205. }
  206.  
  207. #Bug Fix (Resolve-Path Error if invalid path was specified before the path was created)
  208.  
  209.  
  210.  
  211.  
  212. if (!($PATCH.ToUpper().EndsWith(".MSU")))
  213. {
  214.     Do
  215.     {
  216.         $Attempt = Read-Host ("Warning: Specified PATCH file ($PATCH) is not a MSU file type. Do you still want to attempt extraction? [Y] or [N]")
  217.     }
  218.     Until ('Y', 'y', 'n', 'N' -ccontains $Attempt)
  219.     if ($Attempt.ToUpper() -eq 'N')
  220.     {
  221.         Write-Host ("Exiting...")
  222.         Exit
  223.     }
  224. }
  225.  
  226. if (!(Test-Path $PATH))
  227. {
  228.     Do
  229.     {
  230.         $Attempt = Read-Host ("Warning: Specified PATH folder ($PATH) does not exist. Do you want to create it? [Y] or [N]")
  231.     }
  232.     Until ('Y', 'y', 'n', 'N' -ccontains $Attempt)
  233.     if ($Attempt.ToUpper() -eq 'N')
  234.     {
  235.         Write-Host ("Exiting...")
  236.         Exit
  237.     }
  238.     else
  239.     {
  240.         New-Item $PATH -Force -ItemType Directory
  241.         Write-Host "Created $PATH Folder" -ForegroundColor Green
  242.     }
  243. }
  244.  
  245. $PATCH = Resolve-Path $PATCH
  246. $PATH = Resolve-Path $PATH
  247.  
  248. Write-Host "Patch to Extract: $PATCH"
  249. Write-Host "Extraction Path: $PATH"
  250. Write-Host "x86 File Storage Folder Name: $x86"
  251. Write-Host "x64 File Storage Folder Name: $x64"
  252. Write-Host "WOW64 File Storage Folder Name: $WOW"
  253. Write-Host "MSIL File Storage Folder Name: $MSIL"
  254. Write-Host "Junk File Storage Folder Name: $JUNK"
  255. Write-Host "Orignal Patch File Storage Folder Name: $BIN"
  256.  
  257. $SYSPATH = Join-Path -path (get-item env:\windir).Value -ChildPath "system32"
  258.  
  259. $EXPAND = Join-Path -path $SYSPATH -ChildPath "expand.exe"
  260.  
  261.  
  262. if (!(Test-Path $EXPAND))
  263. {
  264.     Throw ("Error: Cannot find 'Expand.exe' in the $SYSPATH folder.")
  265. }
  266.  
  267. $ARG = '-F:* ' + '"' + $PATCH + '" ' + '"' + $PATH + '"'
  268.  
  269. Write-Host "Executing the following command: $EXPAND $ARG" -ForegroundColor Green
  270.  
  271. Start-Process -File $EXPAND -ArgumentList $ARG -Wait
  272.  
  273.  
  274.  
  275. $CAB = Join-Path -path $PATH -ChildPath $((Get-ChildItem $PATCH).Basename + ".CAB")
  276.  
  277. if ((!(Test-Path $CAB)) -or ($CAB -eq ""))
  278. {
  279.     Write-Host "PATH = $PATH"
  280.     Write-Host (Get-ChildItem -Path $PATH -Filter *.cab)
  281.     ### Look for first existing CAB file in the directory as backup
  282.     $CAB = (Get-ChildItem -Path $PATH -Filter *.cab | Select-Object -First 1)
  283.     Write-Host "CAB = $CAB"
  284.     if (!(Test-Path $CAB))
  285.     {
  286.         Throw "Error: Patch .CAB File could not be located.  Patch Extraction failed - please send notification of this error to @Laughing_Mantis."
  287.     }
  288. }
  289. <# Microsoft newer patches do not follow this formula - likely an attempt to prevent
  290. auto extraction so we need to handle scenarios where patch cab does not have the same
  291. name as the container MSP #>
  292.  
  293.  
  294.  
  295. $ARG = '-F:* ' + '"' + $CAB + '" ' + '"' + $PATH + '"'
  296.  
  297. Write-Host "Executing the following command: $EXPAND $ARG" -ForegroundColor Green
  298.  
  299. Start-Process -File $EXPAND -ArgumentList $ARG -Wait
  300.  
  301.  
  302. $PATCHx86 = Join-Path -path $PATH -ChildPath $x86
  303. $PATCHx64 = Join-Path -path $PATH -ChildPath $x64
  304. $PATCHWOW = Join-Path -path $PATH -ChildPath $WOW
  305. $PATCHMSIL = Join-Path -path $PATH -ChildPath $MSIL
  306. $PATCHJUNK = Join-Path -path $PATH -ChildPath $JUNK
  307. $PATCHCAB = Join-Path -path $PATH -ChildPath $BIN
  308.  
  309.  
  310. if (!(Test-Path $PATCHx86 -pathType Container))
  311. {
  312.     New-Item $PATCHx86 -Force -ItemType Directory
  313.     Write-Host "Making $PATCHx86 Folder" -ForegroundColor Green
  314. }
  315.  
  316. if (!(Test-Path $PATCHx64 -pathType Container))
  317. {
  318.     New-Item $PATCHx64 -Force -ItemType Directory
  319.     Write-Host "Making $PATCHx64 Folder" -ForegroundColor Green
  320. }
  321.  
  322. if (!(Test-Path $PATCHWOW -pathType Container))
  323. {
  324.     New-Item $PATCHWOW -Force -ItemType Directory
  325.     Write-Host "Making $PATCHWOW Folder" -ForegroundColor Green
  326. }
  327.  
  328. if (!(Test-Path $PATCHMSIL -pathType Container))
  329. {
  330.     New-Item $PATCHMSIL -Force -ItemType Directory
  331.     Write-Host "Making $PATCHMSIL Folder" -ForegroundColor Green
  332. }
  333.  
  334. if (!(Test-Path $PATCHJUNK -pathType Container))
  335. {
  336.     New-Item $PATCHJUNK -Force -ItemType Directory
  337.     Write-Host "Making $PATCHJUNK Folder" -ForegroundColor Green
  338. }
  339.  
  340. if (!(Test-Path $PATCHCAB -pathType Container))
  341. {
  342.     New-Item $PATCHCAB -Force -ItemType Directory
  343.     Write-Host "Making $PATCHCAB Folder" -ForegroundColor Green
  344. }
  345.  
  346.  
  347. $PATCHFolders = Get-ChildItem -Path $PATH -Force -ErrorAction SilentlyContinue | where {$_.Attributes -eq 'Directory'}
  348.  
  349. foreach ($folder in $PATCHFolders)
  350. {
  351.     if ($folder.Name.Contains(".resources_"))
  352.     {
  353.         Move-Item $folder.FullName $PATCHJUNK -Force
  354.         Write-Host "Moving $folder to $PATCHJUNK" -ForegroundColor Green
  355.         Continue
  356.     }
  357.     else
  358.     {
  359.         if ($folder.Name.StartsWith("x86_"))
  360.         {
  361.             Move-Item $folder.FullName $PATCHx86 -Force
  362.             Write-Host "Moving $folder to $PATCHx86" -ForegroundColor Green
  363.             Continue
  364.         }
  365.        
  366.         if ($folder.Name.StartsWith("amd64_"))
  367.         {
  368.             Move-Item $folder.FullName $PATCHx64 -Force
  369.             Write-Host "Moving $folder to $PATCHx64" -ForegroundColor Green
  370.             Continue
  371.         }
  372.        
  373.         if ($folder.Name.StartsWith("wow64_"))
  374.         {
  375.             Move-Item $folder.FullName $PATCHWOW -Force
  376.             Write-Host "Moving $folder to $PATCHWOW" -ForegroundColor Green
  377.             Continue
  378.         }
  379.  
  380.         if ($folder.Name.StartsWith("msil_"))
  381.         {
  382.             Move-Item $folder.FullName $PATCHMSIL -Force
  383.             Write-Host "Moving $folder to $PATCHMSIL" -ForegroundColor Green
  384.             Continue
  385.         }
  386.     }
  387. }
  388.  
  389. <# PRETTY BINDIFF OUTPUT - changes folder names from x86-microsoft-windows-filename-hash-version-garbage to filename-version #>
  390.  
  391. $PATCHFolders = Get-ChildItem -Path $PATCHx86 -Force -ErrorAction SilentlyContinue | where {$_.Attributes -eq 'Directory'}
  392.  
  393. foreach ($folder in $PATCHFolders)
  394. {
  395.     if ($folder -like "x86_microsoft-windows-*")
  396.     {
  397.         $newfolder = $folder.Name.Replace("x86_microsoft-windows-", "")
  398.         $newname = $newfolder.Split("_")[0]
  399.         $version = $newfolder.Split("_")[2]
  400.         $newname = $newname + "_" + $version
  401.         Write-Host ("Renaming $folder to $newname") -ForegroundColor Green
  402.         Rename-Item -path $folder.FullName -newName ($newname)
  403.     }
  404.     elseif ($folder -like "x86_*")
  405.     {
  406.         $newfolder = $folder.Name.Replace("x86_", "")
  407.         $newname = $newfolder.Split("_")[0]
  408.         $version = $newfolder.Split("_")[2]
  409.         $newname = $newname + "_" + $version
  410.         Write-Host ("Renaming $folder to $newname") -ForegroundColor Green
  411.         Rename-Item -path $folder.FullName -newName ($newname)
  412.     }
  413. }
  414.  
  415. $PATCHFolders = Get-ChildItem -Path $PATCHx64 -Force -ErrorAction SilentlyContinue | where {$_.Attributes -eq 'Directory'}
  416.  
  417. foreach ($folder in $PATCHFolders)
  418. {
  419.     if ($folder -like "amd64_microsoft-windows-*")
  420.     {
  421.         $newfolder = $folder.Name.Replace("amd64_microsoft-windows-", "")
  422.         $newname = $newfolder.Split("_")[0]
  423.         $version = $newfolder.Split("_")[2]
  424.         $newname = $newname + "_" + $version
  425.         Write-Host ("Renaming $folder to $newname") -ForegroundColor Green
  426.         Rename-Item -path $folder.FullName -newName ($newname)
  427.     }
  428.     elseif ($folder -like "amd64_*")
  429.     {
  430.         $newfolder = $folder.Name.Replace("amd64_", "")
  431.         $newname = $newfolder.Split("_")[0]
  432.         $version = $newfolder.Split("_")[2]
  433.         $newname = $newname + "_" + $version
  434.         Write-Host ("Renaming $folder to $newname") -ForegroundColor Green
  435.         Rename-Item -path $folder.FullName -newName ($newname)
  436.     }
  437. }
  438.  
  439. $PATCHFolders = Get-ChildItem -Path $PATCHWOW -Force -ErrorAction SilentlyContinue | where {$_.Attributes -eq 'Directory'}
  440.  
  441. foreach ($folder in $PATCHFolders)
  442. {
  443.     if ($folder -like "wow64_microsoft-windows-*")
  444.     {
  445.         $newfolder = $folder.Name.Replace("wow64_microsoft-windows-", "")
  446.         $newname = $newfolder.Split("_")[0]
  447.         $version = $newfolder.Split("_")[2]
  448.         $newname = $newname + "_" + $version
  449.         Write-Host ("Renaming $folder to $newname") -ForegroundColor Green
  450.         Rename-Item -path $folder.FullName -newName ($newname)
  451.     }
  452.     elseif ($folder -like "wow64_*")
  453.     {
  454.         $newfolder = $folder.Name.Replace("wow64_", "")
  455.         $newname = $newfolder.Split("_")[0]
  456.         $version = $newfolder.Split("_")[2]
  457.         $newname = $newname + "_" + $version
  458.         Write-Host ("Renaming $folder to $newname") -ForegroundColor Green
  459.         Rename-Item -path $folder.FullName -newName ($newname)
  460.     }
  461. }
  462.  
  463. $PATCHFolders = Get-ChildItem -Path $PATCHMSIL -Force -ErrorAction SilentlyContinue | where {$_.Attributes -eq 'Directory'}
  464.  
  465. foreach ($folder in $PATCHFolders)
  466. {
  467.     if ($folder -like "msil_*")
  468.     {
  469.         $newfolder = $folder.Name.Replace("msil_", "")
  470.         $newname = $newfolder.Split("_")[0]
  471.         $version = $newfolder.Split("_")[2]
  472.         $newname = $newname + "_" + $version
  473.         Write-Host ("Renaming $folder to $newname") -ForegroundColor Green
  474.         Rename-Item -path $folder.FullName -newName ($newname)
  475.     }
  476.  
  477. }
  478.  
  479. $Junkfiles = Get-ChildItem -Path $PATH -Force -ErrorAction SilentlyContinue
  480.  
  481.  
  482. foreach ($JunkFile in $Junkfiles)
  483. {
  484.    
  485.     try
  486.     {
  487.         if (($JunkFile.Name.EndsWith(".manifest")) -or ($JunkFile.Name.EndsWith(".cat")) -or ($JunkFile.Name.EndsWith(".mum")))
  488.         {
  489.             Move-Item $JunkFile.FullName $PATCHJUNK -Force -ErrorAction SilentlyContinue
  490.             Write-Host "Moving $JunkFile to $PATCHJUNK" -ForegroundColor Green
  491.             Continue
  492.         }
  493.        
  494.         if (($JunkFile.Name.EndsWith(".cab")) -or ($JunkFile.Name.EndsWith(".xml")) -or ($JunkFile.Name.EndsWith(".msu")) -or ($JunkFile.Name.EndsWith("pkgProperties.txt")))
  495.         {
  496.             Move-Item $JunkFile.FullName $PATCHCAB -Force -ErrorAction SilentlyContinue
  497.             Write-Host "Moving $JunkFile to $PATCHCAB" -ForegroundColor Green
  498.             Continue
  499.         }
  500.         if ($JunkFile.Name -eq "patch")
  501.         {
  502.             Move-Item $JunkFile.FullName $PATCHCAB -Force -ErrorAction SilentlyContinue
  503.             Write-Host "Moving $JunkFile to $PATCHCAB" -ForegroundColor Green
  504.             Continue
  505.         }
  506.     }
  507.     catch
  508.     {
  509.         Write-Host "Error Processing ($JunkFile.Fullname)" -ForegroundColor Red
  510.     }
  511. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement