what-is-my-ip – kontunuacja

Windows_PowerShell_iconpo  komentarzach na FB, zamieszczam prosty skrypt, który pozwala odpytać dwóch różnych stron – whatsmyipaddress, API, które zwraca czysty adres, ale ponieważ:

You may programmatically query that server but limit queries to no more than once per five minutes and include an appropriate user agent that will allow us to contact you if needed

dodałem starą metodę przegrzebującą się przez HTML.

get-ExternalIP.ps1

param( [switch]$alternateServer )

if($alternateServer) {
    $page=Invoke-WebRequest http://whatismyipaddress.com/pl/moj-ip
    [regex]$rxIP='((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)'
    $page.AllElements|? id -eq 'section_left'|%{ $IP=$rxIP.Match($_.outerText)}
} else {

    $result=Invoke-WebRequest ipv4bot.whatismyipaddress.com
    $IP=$result.content
}

Write-Host -BackgroundColor DarkYellow -ForegroundColor Black "EXTERNAL IP ADDRESS OF THE CONNECTION: $IP"

eN.

mój zewnętrzny IP

Windows_PowerShell_icondość częste pytanie, jak się ma dynamiczny adres IP. najłatwiej oczywiście wejść na jakąś stronę, która ten adres pokazuje. ale lepiej napisać sobie skrypt (:

nie jest to skomplikowane, wystarczy użyć invoke-webRequest, który jest świetną przeglądarką textową. do testów wybrałem stronę 'http://whatismyipaddress.com’ ale można wybrać dowolny inny. po wejściu od razu widzę, że jestem przekierowany na 'http://whatismyipaddress.com/pl/moj-ip’. żeby oszczędzić sobie trochę grzebaniny robię inspekcję elementu, gdzie jest adres i widzę. że jest to DIV o ID=’section_left’. można to olać, ale przyda się żeby trochę zawęzić wyszukiwanie.

teraz z linii poleceń.

C:\...ive\_ScriptZ :))o- $page=invoke-WebRequest http://whatismyipaddress.com/pl/moj-ip

na początek trzeba się zaznajomić z obiektem więc warto sprawdzić:

C:\...ive\_ScriptZ :))o- $page

StatusCode        : 200
StatusDescription : OK
Content           : <!doctype html>
                    <html lang="pl">
                    <head>
                        <meta charset="utf-8">
                        <meta name="robots" content="noarchive">
                        <title>What Is My IP Address? NarzÄ?dzia do adresA3w IP i nie tylko</title>
                        <meta name="desc...
<CUT>

C:\...ive\_ScriptZ :))o- $page|gm
   TypeName: Microsoft.PowerShell.Commands.HtmlWebResponseObject

Name              MemberType Definition
----              ---------- ----------
Dispose           Method     void Dispose(), void IDisposable.Dispose()
Equals            Method     bool Equals(System.Object obj)
GetHashCode       Method     int GetHashCode()
GetType           Method     type GetType()
ToString          Method     string ToString()
AllElements       Property   Microsoft.PowerShell.Commands.WebCmdletElementCollection AllElements {get;}
BaseResponse      Property   System.Net.WebResponse BaseResponse {get;set;}
Content           Property   string Content {get;}
Forms             Property   Microsoft.PowerShell.Commands.FormObjectCollection Forms {get;}
Headers           Property   System.Collections.Generic.Dictionary[string,string] Headers {get;}
Images            Property   Microsoft.PowerShell.Commands.WebCmdletElementCollection Images {get;}
InputFields       Property   Microsoft.PowerShell.Commands.WebCmdletElementCollection InputFields {get;}
Links             Property   Microsoft.PowerShell.Commands.WebCmdletElementCollection Links {get;}
ParsedHtml        Property   mshtml.IHTMLDocument2 ParsedHtml {get;}
RawContent        Property   string RawContent {get;set;}
RawContentLength  Property   long RawContentLength {get;}
RawContentStream  Property   System.IO.MemoryStream RawContentStream {get;}
Scripts           Property   Microsoft.PowerShell.Commands.WebCmdletElementCollection Scripts {get;}
StatusCode        Property   int StatusCode {get;}
StatusDescription Property   string StatusDescription {get;}

polecam sprawdzić jeszcze $page.links oraz $page.images – może się kiedyś przydać. po wykonaniu

$page.AllElements|? id -eq 'section_left'

będzie już widać kawałek interesującego kodu. teraz trzeba wyłowić IP. do tego celu warto zrobić wyrażenie regularne, bo do tego są niezastąpione. np. takie super-proste, które po prostu szuka czterech liczb oddzielonych kropką, gdzie każda liczba ma od 1-3 cyfr:

[regex]$rxIP="\d{1,3}[.]\d{1,3}[.]\d{1,3}[.]\d{1,3}"

do celów skryptu można by się poważyć o napisanie porządnej walidacji, czyli dodatkowo, żeby te liczby miały wartości od 1-255. ale to nie ten wpis. po przepuszczeniu wyniku będzie wyświetlone IP:

C:\...ive\_ScriptZ :))o- $page.AllElements|? id -eq 'section_left'|%{ write-host -ForegroundColor Yellow $rxIP.Match($_.outerText)}

można teraz się dalej bawić, potestować na innych stronach, analizując elementy zwracanej strony. nie takie trudne (:

eN.

CSV, SSV… ? ULSLSSV!

Windows_PowerShell_iconwpis z cyklu 'człowiek uczy się całe życie’ (; od zawsze pluję się, że durny Excel pliki CSV – Comma Separated Value – zapisuje/odczytuje jako Semicolon Separated Value z niewyjaśnionych przyczyn.

okazuje się, że sprawa jest bardziej skomplikowana i to nie jest ani CSV, ani SSV tylko ULSLSSV – czyli User Language Specyfic List Separator Separated Value (;

listSeparator

o ile dla US „List separator” to przecinek, więc dla Amerykanów CSV to CSV o tyle dla Polaków „List separator” to średnik… jest to dodatkowe utrudnienie na które trzeba uważać w skryptach bo mogą uruchamiać je osoby o różnych ustawieniach regionanych. o ile głupotą jest że CSV to SSV kod jest prosty:

import-csv/export-csv -delimiter ';'

a prawidłowo-bezpiecznie, w skryptach trzeba uważać i zamienić to na:

import-csv/export-csv -delimiter -useCulture

eN.

SecureString

Windows_PowerShell_iconscenariusz użycia

używanie SecureStringów w PS jest poniekąd wymuszone – w taki sposób przechowywane są hasła, np. w credentialsach:

PS C:\_ScriptZ> $cred=Get-Credential

cmdlet Get-Credential at command pipeline position 1
Supply values for the following parameters:
Credential
PS C:\_ScriptZ> $cred|gm


   TypeName: System.Management.Automation.PSCredential

Name                 MemberType Definition
----                 ---------- ----------
Equals               Method     bool Equals(System.Object obj)
GetHashCode          Method     int GetHashCode()
GetNetworkCredential Method     System.Net.NetworkCredential GetNetworkCredential()
GetObjectData        Method     void GetObjectData(System.Runtime.Serialization.SerializationInfo info,
GetType              Method     type GetType()
ToString             Method     string ToString()
Password             Property   securestring Password {get;}
UserName             Property   string UserName {get;}

SecureString przydaje się do przechowywania haseł, używanych w skryptach – np. skrypt do automatycznego podpięcia licencji office365, który wymaga podania credsów użytkownika z uprawnieniami co najmniej 'User Manager’. można hasło zapisać w pliq jako SS a potem wewnątrz skryptu je odczytać i użyć do złożenia credsów:

PS C:\_ScritpZ>ConvertTo-SecureString -String '<HASLO>' -AsPlainText -Force|ConvertFrom-SecureString|out-file c:\_scriptz\pa.ss

PS C:\_ScriptZ> cat .\pa.ss
01000000d08c9ddf0115d1118c7a00c04fc297eb010000006e58e62b318004439a915f2500cbb1c20000000002000000000003660000c0000000100
00000de193527adebd1535b4b243a2dda67de0000000004800000a000000010000000c6781a7fe77fce164d1fcaa0fc68c30f10000000210bc43ae2
3f49acfb0eae607c0b48341400000043264bf254fc25798b847e94d9ea7ea678051bb2

PS C:\_ScritpZ>$ssPassword=Get-Content c:\_scriptz\pa.ss|ConvertTo-SecureString

PS C:\_ScritpZ>$oCreds=New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList "usermanager@w-files.onmicrosoft.com",$ssPassword

bezpieczeństwo SS

rodzą się natychmiast pytania o bezpieczeństwo SecureString – czy takie hasło da się odszyfrować i jak łatwo? hasło jest szyfrowane kluczem, który generowany jest na podstawie hasła użytkownika, a masterkey jest przechowywany na komputerze lokalnym. odpowiedzialne jest za to DPAPI – tutaj znajdziecie więcej informacji. klucz jest przechowywany w $env:USERPROFILE\Application Data\Microsoft\Protect\<GUID>. w efekcie

  • plik jest nie do odczytania na innej maszynie
  • plik jest nie do odczytania przez innego użytkownika

czyli nic da 'wykradzenie’ pliq lub użycie go w kontexcie innego użytkownika.

są potencjalnie metody potrafiące złamać klucz [np. hawkeye inject], ale nie znalazłem praktycznego opisu i na pewno nie są to metody na poziomie script kiddie. można zatem uznać, że jest to świetne narzędzie dla skryptów, które wymagają podania hasła.

addendum

na koniec – jak  odczytać hasło zapisane jako SS? podam dwie metody – dla devów i dla opsów (;

dla devów:

PS C:\_ScriptZ> $ss=cat .\pa.ss|ConvertTo-SecureString
PS C:\_ScriptZ> [System.Runtime.InteropServices.marshal]::PtrToStringAuto([System.Runtime.InteropServices.marshal]::SecureStringToBSTR($ss))
<HASLO>

dla opsów:

po zbudowaniu obiektu credential z wcześniejszego przykładu, mamy zmienną $creds

PS C:\_ScriptZ> $creds.GetNetworkCredential().password
<HASLO>

eN.

dopełnienie zerami

Windows_PowerShell_iconprzykładowy scenariusz: mamy notację nazewniczą serwerów ze stałym prefixem 'server’ oraz jego kolejny numer – liczba z dopełnieniem zerami do trzech znaków. np. server001, server002[…],server011[…],server12o[…]

problem: jak skryptem łatwo wygenerować takie nazwy?

rozwiązanie: i na to oczywiście jest skrót/automat w PS (: potrafi to funkcja 'toString’ – wystarczy jako parametr podać ilość zer odpowiadająca dopełnieniu.

PS> 1..200|%{ 'server'+$_.toString("000") }
server001
server002
[...]
server011
server012
[...]
server099
server100
[...]

eN.

 

 

RemoteSigned i ADS

Windows_PowerShell_iconpo ściągnięciu skryptu z Internetu może być problem z jego uruchomieniem – zależnie od polityki uruchomienia (Execution Policy). imho optymalnym ustawieniem jest ’RemoteSigned’, który pozwala na swobodne uruchamianie skryptów, które się samemu pisze oraz tych zassanych z netu pod warunkiem, że są podpisane certyfikatem. skrypty podpisane certem są jak białe kruki – podobno istnieją. jednak jest to zabezpieczenie przed tym, aby bezmyślnie nie uruchomić pierwszego-lepszego chłamu i chwilę się zastanowić.

a skąd system wie, że skrypt jest ściągnięty z netu? o tym już kilqkrotnie pisałem – informacja zapisana jest w Alternate Data Stream o nazwie 'zone.identifier’. łącząc te dwa klocki w całość, po weryfikacji tego, co skrypt robi, można usunąć informację, iż jest ściągnięty:

remove-item <nazwa> -stream 'zone.identifier'

natomiast nie polecam ustawiania polityki na ’unrestricted’ – mimo wszystko takie proste mechanizmy, zmuszające do krótkiego przemyślenia tego, co się robi, bywają przydatne…

eN.

 

wrażliwość na wielkość

BnSOffTopic

ogólnie staram się szanować nasz piękny język i tępić 'kontenty’ czy 'autentykację’ [choć pewnie lada moment zostaną zapisane jako oficjalnie poprawne]. niemniej tłumaczenia niektórych fraz brzmią … zabiera trochę czasu, żeby się domyślić /: to na tyle jeśli w kwestii filozofii porannej, czas na gimnastykę z innego języka.

PS case sensivity

PS jest miły i przyjemny. stara się użytkownika we wszystkim wyręczyć, pomóc mu, wybaczyć błędy. czasem aż do przesady. ale jak każda istota, ma swoje odchyły, aberracje. tak też jest w przypadq traktowania wielkości znaków – ogólnie wszystko, dla ułatwienia, jest z automatu niewrażliwe na wielkości znaków. czyli mówiąc potocznie, jest baj difolt kejs-insensitive. są jednak wyjątki [bez których reguła nie mogłaby być pełna]:

  1. najbardziej zdradliwe, bo też i często używane, to ’-unique’. z jednej strony logiczne, że unikalne ma być unikalne… z drugiej, skoro PS jest nieczuły na stan [prawda,  że też ładne tłumaczenie?], to skąd nagle taka zmiana…
C:\...ive\_ScriptZ :))o- $PS='nExoR','nexor','Nexor'
C:\...ive\_ScriptZ :))o- $PS|select -Unique

nExoR
nexor
Nexor

jak widać, każdy =))o- jest unikalny. rozsądnie. tak samo zresztą zachowuje się polecenie 'get-unique’, a mogłoby się wyróżniać choćby dodatkowym parametrem ’-CI’. ale się nie wyróżnia.

na każdego jest oczywiście metoda, tzw. walec. takim walcem w PS jest sortowanie, które również posiada parametr ’-unique’, ale ten unique jest bardziej unique niż inne, bo jest właśnie nieczuły na wielkość:

C:\...ive\_ScriptZ :))o- $PS|sort -Unique

Nexor

[to nie jest prawidłowy =))o- ]

  1. drugim przykładem, zapewne bardziej [nomen omen] unikalnym, są wyrażenia regularne. oczywiście i tutaj są dwie wariacje – CI i CS, więc trzeba uważać. jeśli używa się zwykłych reguł 'match’ lub 'replace’, zachowuje się standardowo [czyli nie zwraca uwagi na wielkość znaków]:
C:\...ive\_ScriptZ :))o- $PS='nExoR jest Niewrażliwy Na Walizki'
C:\...ive\_ScriptZ :))o- $PS -match 'nexor'

True

jeśli jednak zdefiniuje się wyrrega [tak pewnie nazywał by się w polskiej wersji Logo. tłumaczone języki to  sqcha] … regexa, to zachowuje się inaczej [czyli CS]:

C:\...ive\_ScriptZ :))o- $PS='nExoR jest Niewrażliwy Na Walizki'
C:\...ive\_ScriptZ :))o- [regex]$rxeN="nexor"
C:\...ive\_ScriptZ :))o- $PS -match $rxeN

False

zmusić regexa do niewrażliwości można na dwa sposoby – albo definiując dodatkową opcję podczas deklaracji, lub modyfiqjąc samą definicję wyrażenia. pierwsza wersja może przestraszyć swoją monstrualnością, ale jak się w kodzie definiuje wiele regexów to warto raz zdefiniować i potem używać:

C:\...ive\_ScriptZ :))o- $PS='wyczUleniE na poKrowce to nie nExoR'
C:\...ive\_ScriptZ :))o- $rxOptCI = ([System.Text.RegularExpressions.RegexOptions]::IgnoreCase -bor [System.Text.Regular
Expressions.RegexOptions]::Compiled)
C:\...ive\_ScriptZ :))o- $rxeN=New-Object System.Text.RegularExpressions.Regex("nexor",$rxOptCI)
C:\...ive\_ScriptZ :))o- $PS -match $rxeN

True

opcje są wartością binarną, której każdy bit ma inne znaczenie. stąd binarny OR modyfiqjący jej wartość. jeśli wyrażeń regularnych w kodzie jest niewiele, bardziej opłaca się CI ustawić w jego definicji:

C:\...ive\_ScriptZ :))o- $PS='deLikAtny ProCeS nExoRa'
C:\...ive\_ScriptZ :))o- [regex]$rxeN="(?i)nexor"
C:\...ive\_ScriptZ :))o- $PS -match $rxeN

True

PostScriptum

znajomość sztuczek i tricków języka jest bardzo przydatna. można zaoszczędzić wiele linii kodu, a czasem bardzo wiele czasu, zmarnowanego na wyważanie otwartych okien. polecam również zajrzeć na powershellpl.net, ponieważ Bartek się w końcu uaktywnił.

eN.