Differences between Visual Studio 2003, 2005, 2008, 2010, 2012, 2013, and 2015

If you're interested in knowing when specific Visual Studio compiler options have been introduced, here you go.

2003 to 2005

Option                 Purpose                                                                                             
------                 -------                                                                                             
/analyze               Enable code analysis.                                                                               
/bigobj                Increases the number of addressable sections in an .obj file.                                       
/doc                   Process documentation comments to an XML file.                                                      
/errorReport           Allows you to provide internal compiler error (ICE) information directly to the Visual C++ team.    
/favor                 Produces code that is optimized for a specific x64 architecture or for the specifics of             
                       micro-architectures in both the AMD64 and Extended Memory 64 Technology (EM64T) architectures.      
/FC                    Display full path of source code files passed to cl.exe in diagnostic text.                         
/Fp                    Specifies a precompiled header file name.                                                           
/G1                    Optimize for Itanium processor. Only available in the IPF cross compiler or IPF native compiler.    
/G2                    Optimize for Itanium2 processor (default between /G1 and /G2). Only available in the IPF cross      
                       compiler or IPF native compiler.                                                                    
/GF                    Enables string pooling.                                                                             
/homeparams            Forces parameters passed in registers to be written to their locations on the stack upon function   
                       entry. This compiler option is only for the x64 compilers (native and cross compile).               
/hotpatch              Creates a hotpatchable image.                                                                       
/LN                    Creates an MSIL module.                                                                             
/openmp                Enables #pragma omp in source code.                                                                 
/QIPF_B                Does not generate sequences of instructions that give unexpected results, according to the errata   
                       for the B CPU stepping. (IPF only).                                                                 
/QIPF_C                Does not generate sequences of instructions that give unexpected results, according to the errata   
                       for the C CPU stepping. (IPF only).                                                                 
/QIPF_fr32             Do not use upper 96 floating-point registers. (IPF only).                                           
/QIPF_noPIC            Generates an image with position dependent code (IPF only).                                         
/QIPF_restrict_plabels Enhances performance for programs that do not create functions at runtime. (IPF only).              
/Zx                    Generates debuggable optimized code. Only available in the IPF cross compiler or IPF native         

2005 to 2008

Option                 Purpose                                                    
------                 -------                                                    
/MP                    Compiles multiple source files by using multiple processes.
/Qfast_transcendentals Generates fast transcendentals.                            
/Qimprecise_fwaits     Removes fwait commands inside try blocks.                  

2008 to 2010

Option Purpose                                
------ -------                                
/Fi    Sets the preprocessed output file name.

2010 to 2012

Option                                         Purpose                                                                     
------                                         -------                                                                     
/kernel                                        The compiler and linker will create a binary that can be executed in the    
                                               Windows kernel.                                                             
/Qpar (Auto-Parallelizer)                      Enables automatic parallelization of loops that are marked with the #pragma 
                                               loop() directive.                                                           
/Qvec-report (Auto-Vectorizer Reporting Level) Enables reporting levels for automatic vectorization.                       
/sdl                                           Enables additional compiler security checks.                                
/volatile                                      Selects how the volatile keyword is interpreted.                            
/ZW                                            Produces an output file to run on the Windows Runtime.                      

2012 to 2013

Option          Purpose                                                                                                    
------          -------                                                                                                    
/cgthreads      Specifies number of cl.exe threads to use for optimization and code generation.                            
/FS             Forces writes to the program database (PDB) file to be serialized through MSPDBSRV.EXE.                    
/Gv             Uses the __vectorcall calling convention. (x86 and x64 only)                                               
/Gw             Enables whole-program global data optimization.                                                            
/Qsafe_fp_loads Uses integer move instructions for floating-point values and disables certain floating point load          
/Zo             Generate enhanced debugging information for optimized code in non-debug builds.                            

2013 to 2015

Option                  Purpose                                                                     
------                  -------                                                                     
/guard:cf               Adds control flow guard security checks.                                    
/W0, /W1, /W2, /W3, /W4 Sets which warning level to output.                                         
/w1, /w2, /w3, /w4      Sets the warning level for the specified warning.                           
/wd                     Disables the specified warning.                                             
/we                     Treats the specified warning as an error.                                   
/wo                     Displays the specified warning only once.                                   
/Wv                     Displays no warnings introduced after the specified version of the compiler.
/WX                     Treats all warnings as errors.                                              

PowerShell to Generate these:

$2003 = $($wr = Invoke-WebRequest (Get-Clipboard); Get-WebRequestTable.ps1 -WebRequest $wr -TableNumber 0)
$2005 = $($wr = Invoke-WebRequest (Get-Clipboard); Get-WebRequestTable.ps1 -WebRequest $wr -TableNumber 0)
$2008 = $($wr = Invoke-WebRequest (Get-Clipboard); Get-WebRequestTable.ps1 -WebRequest $wr -TableNumber 0)
$2010 = $($wr = Invoke-WebRequest (Get-Clipboard); Get-WebRequestTable.ps1 -WebRequest $wr -TableNumber 0)
$2012 = $($wr = Invoke-WebRequest (Get-Clipboard); Get-WebRequestTable.ps1 -WebRequest $wr -TableNumber 0)
$2013 = $($wr = Invoke-WebRequest (Get-Clipboard); Get-WebRequestTable.ps1 -WebRequest $wr -TableNumber 0)
$2015 = $($wr = Invoke-WebRequest (Get-Clipboard); Get-WebRequestTable.ps1 -WebRequest $wr -TableNumber 0)
Compare-Object $2013 $2015 -Property { $_.Option -replace '\s','' } -PassThru | ? SideIndicator -eq '=>' | Format-Table Option,Purpose -Wrap | clip
Compare-Object $2003 $2005 -Property { $_.Option -replace '\s','' } -PassThru | ? SideIndicator -eq '=>' | Format-Table Option,Purpose -Wrap | clip
Compare-Object $2005 $2008 -Property { $_.Option -replace '\s','' } -PassThru | ? SideIndicator -eq '=>' | Format-Table Option,Purpose -Wrap | clip
Compare-Object $2008 $2010 -Property { $_.Option -replace '\s','' } -PassThru | ? SideIndicator -eq '=>' | Format-Table Option,Purpose -Wrap | clip
Compare-Object $2010 $2012 -Property { $_.Option -replace '\s','' } -PassThru | ? SideIndicator -eq '=>' | Format-Table Option,Purpose -Wrap | clip
Compare-Object $2012 $2013 -Property { $_.Option -replace '\s','' } -PassThru | ? SideIndicator -eq '=>' | Format-Table Option,Purpose -Wrap | clip
Compare-Object $2013 $2015 -Property { $_.Option -replace '\s','' } -PassThru | ? SideIndicator -eq '=>' | Format-Table Option,Purpose -Wrap | clip

TimeJournal: Time Profiling for Humans

Time Journal helps you analyze where you spend your time by infrequently asking the simple question: “What are you doing?

[Download here: TimeJournal.zip]



How it Works

Time Journal follows the same principles as a traditional software sampling profiler, but instead samples humans. By randomly recording your current task, Time Journal lets you analyze your answers as a faithful proxy for how you actually spent your time. If 20% of your randomly sampled answers were “Status Meeting,” then you spent close to 20% of your time in status meetings.

An alternative to the sampling approach is an instrumentation approach: faithfully recording your transition between tasks. Time Journal avoids this design, since asking humans to faithfully record transitions between tasks is enormously error-prone. For example, you might not log a task transition for a task that you consider inconsequential (for example, “Checking email”,) when in fact that task may account for a significant portion of your day. Some software attempts to address the human element by tracking window titles, but the level of data captured by window titles often does not map well to the task they support.

Installing Time Journal

  • Extract TimeJournal.exe to a place on your computer (i.e.: a Tools folder)
  • Start | Run | shell:startup
  • Create a shortcut to TimeJournal.exe in that Startup folder

Using Time Journal

Time Journal runs as a background application. Every once in awhile (randomly between 5 and 25 minutes,) it asks you the question, “What are you doing?” It stores your previous answers in a list until you exit the program, which lets you easily re-use your answers to previous questions.

When you press OK, it adds your answer (along with the current window title) to a date-appropriate CSV in your “My Documents\TimeJournal” folder. If you don’t answer within 4 minutes, it dismisses the dialog and records nothing. This lets you keep TimeJournal running when you go home for the day without polluting your journal file.

In addition, when Time Journal auto-dismisses the dialog, it checks Outlook to see if you are in a meeting. If you are, it records the meeting title instead as your activity.

Slicing and Dicing

Time Journal records all output into a CSV in your “My Documents\TimeJournal” directory. For example, use the included Get-TimeJournal PowerShell script to easily see the breakdown of your time:

PS> Get-TimeJournal.ps1

Count Name
----- ----
    2 Bug 127272: System bluescreens when I move the mouse
    1 MEETING: Chat about alignment
    1 TimeJournal fixes
    1 Hubble Space Telescope programming
    1 SCRUM meeting
    1 Security reviews
    1 Chat

Setting Visual Studio Code to Auto-Update in the Background

Visual Studio Code has a built-in feature to check for and install updates, but I've always been frustrated by having to acknowledge the update, allow the browser to restart, watch an installer, and then get back to what I was about to do anyways (which is edit some text).

As a solution, here's a quick little PowerShell script to run. It will create a background task to run every night at 3:14 AM and update VS Code for you automatically if one is available. If you tend to leave your editor open without saving files, you might want to enable the VS Code settings of files.AutoSave, and files.hotExit.

When VS Code comes back up after an update, you'll have to reopen any files that were previously open. Once VS Code gets more fully-featured crash recovery functionality, that annoyance will go away.

And the PowerShell:


Register-ScheduledJob -Name VSCode_Updater -Trigger (New-JobTrigger -Daily -At 3:14) -ScheduledJobOption @{ RunElevated = $true } -ScriptBlock {
    $latestPage = Invoke-WebRequest http://code.visualstudio.com/updates/
    if($latestPage.Content -match '/updates/v(.*?)"') { $latest = [Version] ($matches[1] -replace "_",".") }
    $current = [Version] ((Get-Content 'C:\Program Files (x86)\Microsoft VS Code\resources\app\package.json' | ConvertFrom-Json).Version)
    if($latest -gt $current)
        $codeWasRunning = @(Get-Process -name Code -ErrorAction Ignore).Count -gt 0
            Stop-Process -name Code
        Invoke-WebRequest https://vscode-update.azurewebsites.net/latest/win32/stable -OutFile $env:TEMP/vscode_latest.exe
        & $env:TEMP/vscode_latest.exe /verysilent
        if(-not $codeWasRunning)
            Stop-Process -Name Code
$task = Get-ScheduledTask -TaskName VSCode_Updater
$task.Principal.LogonType = "Interactive"
$task | Set-ScheduledTask

Interactive Rosetta Stone Explorer

In 1799, Napoleon's explorers discovered a 4-foot tall, 700 lb stone slab in Rosetta (Rashid), Egypt. It was carved with three sections of writing. Two were well known to archaeologists: Demotic script, and ancient Greek (Coptic). The most interesting part - the hieroglyphic symbols - were also very well known to the scientific community. But they were also a great mystery. All attempts to decipher hieroglyphic text had so far been unsuccessful.


The discovery of this stone, which we now call the Rosetta Stone, began to unravel the great mystery of Egyptian Hieroglyphs. Nearly immediately, scholars realized that these three sections of writing represented three translations of the exact same passage: a relatively uneventful proclamation of policy and tax changes under King Ptolemy V. Using the more well-known Coptic and Demotic sections as guides, they initially translated phonetic portions of the hieroglyphs. Then, over the next 20 years, they continued to use the Greek and Demotic sections to gradually expand their understanding and translation of the hieroglyphic inscription.

When you look at the Rosetta Stone, it's easy to wonder what a specific section means. To this end, I've created the Interactive Rosetta Stone Explorer. The translation of the hieroglyphic text comes from Sharpe, Samuel. (1871). The Rosetta Stone in Hieroglyphics and Greek. London, with the hieroglyphic transcription of the characters coming primarily from Jim Loy.



Downloading Plain-Text Wikipedia

If you've ever been interested in having all of Wikipedia in a plain-text format, you might have been disappointed to learn that Wikipedia doesn't actually make this format available.

Fortunately, they do offer an XML version of the entire database, so I've written a PowerShell script to convert that XML dump into individual plain-text articles. The script tries to remove as much of Wikipedia's additional markup as possible, and skips inconsequential articles.

This script demonstrates a unique way of processing XML in PowerShell that you rarely see - because it is rarely needed. In XML form, the Wikipedia database is nearly 60GB. This is FAR too large for PowerShell's [xml] cast, due to the memory overhead required for the XmlDocument format on which the [xml] cast is built. It's also far too large for most systems to even hold in memory at once. Instead, this script takes a streaming approach built on System.Xml.XmlReader. The XmlReader class lets you handle tags and elements as the reader sees them, rather than forcing you to wait for that final ill-fated </mediawiki> closing tag while everything buffers in memory.

  1. Install the 'Split-Wikipedia' helper script from a PowerShell prompt:
    1. Install-Script Split-Wikipedia -Scope CurrentUser
    2. The Install-Script command requires Windows 10 or install PowerShell 5.0.
    3. If this is the first time you've used Install-Script, exit PowerShell and launch it again.
  2. Use PowerShell to navigate to a directory that you want to contain your Wikipedia articles
    1. mkdir ~/Documents/Wikipedia
    2. Set-Location ~/Documents/Wikipedia
  3. Download the latest English Wikipedia database (~ 13GB)
    1. Invoke-WebRequest https://dumps.wikimedia.org/enwiki/latest/enwiki-latest-pages-articles.xml.bz2 -Outfile enwiki-latest-pages-articles.xml.bz2
  4. Decompress the XML, using bzip2 (or another tool like 7zip if you wish):
    1. bzip2 -d enwiki-latest-pages-articles.xml.bz2
  5. Process the XML (~ 58GB). This will take about 7 hours:
    1. Split-Wikipedia -Path ./enwiki-latest-pages-articles.xml
  6. (Optional) Delete the source XML
    1. Remove-Item ./enwiki-latest-pages-articles.xml

All 4 million articles are now in your 'Wikipedia\Articles' directory. Within this directory, they are again split into subdirectories of 5,000 articles each - as most software (i.e.: File | Open dialogs, browsing in Explorer) doesn't handle single directories with 4 million items very well.


More Detecting Obfuscated PowerShell

In a recent post, we talked a little bit about detecting obfuscated PowerShell through the use of PowerShell's tokenizer - tackling, as an example, the highly irregular variable names generated by MetaSploit's PowerShell encoder.

Obfuscation has been around as long as computer programs have, so the rise of obfuscated PowerShell scripts shouldn't be much of a surprise. Obfuscated VBScript, Perl, Ruby, Python, and of course assembly language are very common.

A great example comes from this blog: http://perl-users.jp/articles/advent-calendar/2010/sym/11, which relies heavily on dynamic evaluation through Invoke-Expression:

Symbolic PowerShell!

Enabling ScriptBlock logging in PowerShell v5 is an incredibly effective way to gain insight into this style of technique:


Obfuscation through Invoke-Expression or basic things like variable names are one thing, but what happens when people really start obfuscating the mechanics of the scripts themselves? At DerbyCon this year, Daniel Bohannon (noted Red Teamer) recently gave a really great presentation: "Invoke-Obfuscation: PowerShell obFUsk8tion Techniques & How To (Try To) D""e`Tec`T 'Th'+'em' ".

In that presentation, he dropped gems like this that rely heavily on the Format operator:

Token Obfuscation

That obfuscation doesn't rely on the Invoke-Expression command, so would show up basically the same in script block logging. Along with the crazy Invoke-Expression stuff, these examples demonstrate, without a doubt, that relying on string matching alone to detect evil is a fool's errand.

But here's the thing.

Obfuscated scripts aren't normal. Anybody looking at an obfuscated script knows that they're not normal. They stick out like a sore thumb. This alone can be used as an incredibly rich signal that somebody is trying to avoid getting caught, in the same way that all self-respecting SOCs look for the events that come from an attacker turning off Antivirus.

In the previous post, we looked at the letter frequency of variable names, but what if we expanded on that approach to look at all characters in a script? The Invoke-Expression based script above relied entirely on 16 characters. The script that relied on PowerShell's Format operator relied heavily on quoting and brace characters.

But how do you know what's abnormal across "all PowerShell scripts"?

On the PowerShell team, one thing we often use for questions like this is a corpus that we created by downloading everything we could get our grubby little hands on. So, let's take a look at character frequencies using Measure-CharacterFrequency.

PS C:\PowerShellCorpus\PoshCode> $globalFrequency = Measure-CharacterFrequency *.ps1
PS C:\PowerShellCorpus\PoshCode> $globalFrequency | Select -First 20

Name Percent
---- -------
E      9.912
T      7.414
A      5.512
R       5.43
S      5.303
I      5.041
N      5.025
O      4.944
L      3.509
M        3.3
C      3.191
$      3.076
P      2.914
D      2.753
U       2.29
-      1.955
.      1.917
"      1.822
F      1.626
G      1.526

Now, compare that to some of these other ones:

## The Token-based obfuscation that relies on the Format operator
PS > Measure-CharacterFrequency C:\temp\tokenall.ps1 | Select -First 10

Name Percent
---- -------
'     20.175
{      7.456
}      7.456
,      5.702
E      3.947
T      3.509
N      3.509
"      3.509
(       3.07
)       3.07

## The one that relies on Invoke-Expression
PS > Measure-CharacterFrequency C:\temp\symbolic.ps1 | Select -First 10

Name Percent
---- -------
$     21.808
{     21.659
}     21.659
+     13.313
"      7.452
=      2.832
[      2.086
(      1.689
;       1.54
)      1.341


The difference is huge, and unmistakable. But how do we compare these sets in a robust and reliable way? We steal from the field of Information Retrieval, that's how!

The field of Information Retrieval has long used a technique called vector similarity / cosine similarity to compare two sets of things. For example, the similarity of two documents, or how closely a search matches a document.

Cosine Similarity

That's a lot of complicated math-looking symbols - but it turns out it's very easy to calculate. Here's an example, using Measure-VectorSimilarity.

PS > Measure-VectorSimilarity @(1..10) @(4..15)


So, let's automate a vector similarity comparison. Take random selection of scripts from PoshCode, dump in some obfuscated ones, and see if anything sticks out based on the vector similarity score:

PS > md c:\temp\randomscripts
PS > dir | Get-Random -Count 20 | Copy-Item -Destination C:\temp\randomscripts
PS > copy C:\temp\symbolic.ps1 C:\temp\randomscripts
PS > copy C:\temp\tokenall.ps1 C:\temp\randomscripts
PS > dir C:\temp\randomscripts\ | % {
>>>     $scriptFrequency = $_ | Measure-CharacterFrequency.ps1
>>>     $sim = Measure-VectorSimilarity $globalFrequency $scriptFrequency 
>>>             -KeyProperty Name -ValueProperty Percent
>>>     [PSCustomObject] @{ Name = $_.Name; Similarity = $sim }
>>> }

Name                                     Similarity
----                                     ----------
43a28a15-5023-4feb-a71f-abe95aa0f2a6.ps1      0.957
Export-PSCredential_4.ps1                     0.979
Get-BogonList_1.ps1                           0.925
Get-Netstat _1.9.ps1                           0.89
Get-Parameter_8.ps1                           0.959
group-byobject_4.ps1                          0.939
IADsDNWithBinary Cmdlet_1.ps1                 0.924
Import-ExcelToSQL_2.ps1                       0.961
Invoke-Sql_2.ps1                              0.979
List AddRemovePrograms.ps1                    0.961
Lock-WorkStation.ps1                          0.905
Monitor-FileSize_1.ps1                        0.974
symbolic.ps1                                  0.157
Reverse filename sequenc.ps1                  0.874
scriptable telnet client_2.ps1                0.967
Set Active Sync DeviceID.ps1                  0.955
SharePoint Large Lists_1.ps1                  0.944
Show-Sample_1.ps1                             0.919
Start-Verify.ps1                              0.923
tokenall.ps1                                  0.379


In fact, if you graph the similarity scores of the nearly 3500 scripts from PoshCode, only 2% of them have a similarity score less than 80%. And almost all of them are legitimately obfuscated for fun.


The difference is unmistakable. If you're currently trying to detect malicious script-based content, be sure to also look for indicators of script obfuscation. Reliable techniques exist, and you're likely running blind without them.

Fortune 500 PowerPoint Fodder

In preparation for some upcoming presentations, I wanted to make some images of the current Fortune 500 logos. No such resource existed, so now it does.


The attached ZIP has:

  • Logos for the Fortune 500, numbered by their position
  • A PowerPoint deck with a slide where they are composed together
  • An image of the logos in a grid
  • An image of the logos in a grid, with a “Fortune 500” logo on top.

Link: http://www.leeholmes.com/projects/fortune500/fortune500.zip

Fixing carriage jam and 0x61011beb error on HP Photosmart Premium

I recently had my HP Photosmart Premium stop working. Initially, it failed with an error message requesting that I clear the carriage jam. This was caused by the carriage being stuck at the far right of the printer where it normally goes to clean the print head.

The carriage was stuck enough that moving it with my hands didn’t work. When I took the sides of the printer off, there was a movable plate stuck below the carriage that I was able to slide away using a bamboo skewer.

open side

After resetting the printer, the carriage jam message went away – only to be replaced with a generic complaint about “error 0x61011beb.”

Error 0x61011beb appears to be a “catch all” error message indicating physical problems. Online resources helpfully suggest doing a factory reset of the printer, which didn’t resolve my issue.

One thing I did notice before the printer would give the error message was the central rod to the left of the big white gear spinning for a long time.

gear assembly

During the printer reset, the white gear would sometimes spin, thereby turning the black gear meshed to it. But when the rod spun, very little happened. If I rotated the white gear manually, the movable plate that caused the carriage jam would move.

Another thing I noticed was that the whole assembly attached to the central rod wasn’t on very tight. I thought perhaps it somehow got knocked loose, so tried a bunch of printer resets after re-seating it, or holding it against the rod manually. None of those worked.

The rod in the gear assembly goes through a little black plastic gear. After taking the assembly off the rod, I was able to look at the gear. It had a small crack, which I assume was preventing it from gripping the rod properly (which was preventing it from having the power to turn the white gear, which was preventing the plate from moving, which was breaking my printer).

I initially tried to put some glue on the inside of the gear, put it back into the rod assembly, put it all back onto the rod, and let it sit overnight. Unfortunately, that didn’t stick well enough and the printer acted like nothing had changed.

Fortunately, I had access to a 3d printer, and was able re-print the gear. The original is on the left.


I used Parametric Involute Bevel and Spur Gears by GregFrost, with the following dimensions:

gear (
    gear_thickness = 3.8,
    rim_thickness = 3,
    hub_thickness = 3,
    bore_diameter = 4.62,

I’ve shared this gear here: http://www.thingiverse.com/thing:1133610. The inner bore diameter is intentionally thin. I used a needle file to open the gap until it was JUST able to squeeze onto the raised ridges on the rod.

If you’re running into the same issue (slipping of the gear) but don’t have access to a 3d printer, some other things might work:

  • Finding another gear with the same number of teeth, inner bore diameter, and outer bore diameter. It has 15 teeth, an outer diameter of 14.38mm, and an inner bore of about 4.62mm.
  • Filling the central bore of the gear with moldable plastic (like Instamorph), and then drilling out a new central bore.
  • Printing the gear out at Shapeways or a similar service.
  • Asking a local hacker space if they can help you print out the gear.

In the end, I was able to rescue a $300 printer with 2.4 cents of plastic.


Detecting Obfuscated PowerShell

I was recently looking at a sample that was encoded using MSF’s basic template obfuscation (stolen without attribution from Matt Graeber of course):



For example:


function mtKZ {

       Param ($l7PpJu1SE4VO, $qhnBBk5lHo)            

       $pcE6VKGt = ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll') }).GetType('Microsoft.Win32.UnsafeNativeMethods')


       return $pcE6VKGt.GetMethod('GetProcAddress').Invoke($null, @([System.Runtime.InteropServices.HandleRef](New-Object System.Runtime.InteropServices.HandleRef((New-Object IntPtr), ($pcE6VKGt.GetMethod('GetModuleHandle')).Invoke($null, @($l7PpJu1SE4VO)))), $qhnBBk5lHo))



For detection purposes, attempted obfuscation like this (i.e.: the variable names) are themselves an indicator to malicious activity.


PowerShell’s AST APIs make detection of stuff like this a breeze. For example, here’s a way to get all of the variables in $Path:


$tokens = @()

$null = [System.Management.Automation.Language.Parser]::ParseFile($Path, [ref] $tokens, [ref] $null)

$tokens | ? VariablePath | % { $_.VariablePath.UserPath }


With that, we can start to do some variable analysis. Basic entropy is a pretty good start. When you combine that with letter frequency distribution, this creates a pretty good obfuscation metric:

14 [C:\temp]
>> dir *.ps1 | % { Measure-VariableObfuscation.ps1 $_.FullName } | sort ObfuscationMetric

Path                                                  Entropy    TopFourLetters ObfuscationMetric
----                                                  -------    -------------- -----------------
C:\temp\hello.ps1                                           0                 0                 0
C:\temp\foo2.ps1                                            0                 1                 0
C:\temp\2.ps1                                               0                 0                 0
C:\temp\1.ps1                                               0                 0                 0
C:\temp\3.ps1                                               0                 0                 0
C:\temp\verbose.ps1                          3.17281073351987 0.666666666666667  1.05760357783996
C:\temp\msf_template.ps1                     3.07084709362252 0.631578947368421  1.13136471870303
C:\temp\foo.ps1                              3.65719253292414 0.588235294117647  1.50590280767465
C:\temp\pester.temp.tests.ps1                3.47972685963298            0.5625  1.52238050108943
C:\temp\configtest.ps1                       3.75004181130572 0.495934959349594  1.89026497805654
C:\temp\sendmailmessagetest.ps1              3.79012121177685  0.48780487804878  1.94128159627595
C:\temp\Repro.ps1                            4.16910660776366  0.46583850931677  2.22697620042034
C:\temp\sttest.ps1                           4.27537906345103 0.469194312796209  2.26939552183183
C:\temp\TranscriptTest.ps1                   4.17394102071541 0.425629290617849  2.39738946498757
C:\temp\Invoke-ActiveScriptEventConsumer.ps1 4.21710521416516 0.415384615384615  2.46538458674271
C:\temp\mywatch-command.ps1                   4.2973293816282  0.42159383033419  2.48560182741991
C:\temp\Burn-Console.ascii.ps1               4.45029315016471 0.352501867064974  2.88155650574519
C:\temp\Burn-Console.ps1                     4.45029315016471 0.352501867064974  2.88155650574519
C:\temp\Invoke-TokenManipulation.ps1         4.91011096435002 0.384693390598902  3.02122372925736
C:\temp\Invoke-TokenManipulationNonAdmin.ps1 4.91011096435002 0.384693390598902  3.02122372925736
C:\temp\stager.ps1                           5.32866566677047 0.244131455399061  4.02777076220679

MSF could of course adapt to this, but its algorithm would continue to have predictable and detectable output. All you’ve got to do is look J

And of course, Measure-VariableObfuscation:





























#requires -Module PowerShellArsenal






$tokens = @(); $null = [System.Management.Automation.Language.Parser]::ParseFile($Path, [ref] $tokens, [ref] $null)

$bytes = [byte[]][char[]]-join ($tokens | ? VariablePath | % { $_.VariablePath.UserPath })

$entropy = 0

$top4 = 0



    $entropy = Get-Entropy $bytes

    $letterFrequency = Measure-LetterFrequency (-join ($tokens | ? VariablePath | % { $_.VariablePath.UserPath })) -Raw

    $top4 = $letterFrequency[1..4] | Measure-Object -Sum Percent | % Sum


[PSCustomObject] @{

    Path = $Path

    Entropy = $entropy

    TopFourLetters = $top4

    ObfuscationMetric = $entropy * (1 - $top4)


Launching Modern Applications from the Command Line

We had an interesting discussion at work the other day about how to launch modern Windows applications from the command line.

There are a few solutions out there (Tome’s is close), although few of them are happy with their results 🙂

Many of them rely on protocol handlers (i.e.: “start bingnews://”), but that means memorizing a bunch of protocol handler prefixes.

Tome’s blog mentions the Get-AppxPackage cmdlet – the real workhorse of a proper solution.

Modern applications are placed in Appx packages. These packages may contain one or more applications. If an AppxPackage contains several applications, these will be listed in its package manifest – which you can retrieve through Get-AppxPackageManifest. The manifest is XML, which PowerShell’s XML support makes crazy easy to navigate.

Here’s an example of getting all of the modern applications that you can launch – place it in Get-AppxPackageEx.ps1:

param($Name = "*")

foreach($package in Get-AppxPackage)
    foreach($appId in ($package | Get-AppxPackageManifest).Package.Applications.Application.Id)
        if(($package.Name -like $Name) -or ($appId -like $Name))


There’s an example of getting all the apps you might want to launch. When it comes to launching one, the secret is to use the Shell handler to do it. Here’s a script that accomplishes it:


foreach($package in Get-AppxPackage)
    foreach($appId in ($package | Get-AppxPackageManifest).Package.Applications.Application.Id)
        if(($package.Name -like $Name) -or ($appId -like $Name))
            $commandLine = "shell:AppsFolder\$($package.PackageFamilyName)!$appId"
                start $commandLine



204 [C:\temp]
>> Start-AppxPackage *twitter* -whatif
What if: Performing the operation "Start-AppxPackage.ps1" on target "shell:AppsFolder\9E2F88E3.Twitter_wgeqdkkx372wm!App".