It’s a geeky way of obfuscating your email address. If you copy paste that bit of code into a PowerShell window and press ENTER, you get an email address.

I’d like to make something similar. Especially since it just coincides with what I was playing around with today morning. But before that I’d also like to understand what’s going on above. So let’s break this one-liner and peek behind the curtain.

Understanding

First things first: if I remove the [string] type cast and run the code I get a bunch of characters – one on each line. So what the type cast does is convert these characters to a one-liner string.

Next, I piped the email address that was output to the Measure-Object cmdlet with the -Character switch. That gave me the number of characters in the address: 34. This coincides with the next bit of the code – 0..33 – which is shorthand for generating the numbers 0 to 33 (34 numbers). So looks like that bit is responsible for driving something to generate each character of the address.

Great. What’s happening next?

The code generates 34 numbers. For each of these numbers it does something.

First a sub-string is extracted from that long series of numbers. This long series of numbers is stored as a string (hence the double-quotes). From this a sub-string of 2 characters is extracted. The starting point of extraction jumps forward by 2 each time (position 0, then 2, then 4, 6, 10, and so on up to 66) – breaking the long string into a series of 2 digit numbers.

To each of these the code adds the number 46. So the numbers actually output are 114,111,98, … 116. Each of these is then converted to a character – which is what the [char][int] bit does. And the rest we already know – these characters are converted to one long string showing the email address. (There’s one gotcha though, in that the string has the characters separated by a space. Which is why we have a -replace " " tacked on the end – this simply replaces are spaces with nothing, combining all the characters into one word).

Genius! (At least for a PowerShell child like me).

Creating

Okay. So how do I create something similar?

First things first: if I were to just convert an email address to ASCII/ UTF-8 codes what do I get?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

PSC:\>[int[]][char[]]"abc@example.com"

97

98

99

64

101

120

97

109

112

108

101

46

99

111

109

I could join these numbers, with dots perhaps, to form a long string.

1

2

PSC:\>[int[]][char[]]"abc@example.com"-join"."

97.98.99.64.101.120.97.109.112.108.101.46.99.111.109

If I wanted to get my address from such a string I’d do something like this:

Great. So that’s something I could paste in a signature somewhere and anyone running that code will get an email address.

I got side tracked though. What would I do to get something similar to the code the user created? Backtracking:

1

2

PSC:\>[int[]][char[]]"abc@example.com"-join" "

97989964101120971091121081014699111109

I have a series of numbers. They are of varying length so I can’t just club them together and hope to extract. I need to get them all to a uniform length, so an easy idea would be to just subtract them all by some number to make them 2 digits each. The number has to be larger than 46 – as that’s the smallest number – so why not just use 46 as the number to subtract from all?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

PSC:\>[int[]][char[]]"abc@example.com"|%{$_-46}

51

52

53

18

55

74

51

63

66

62

55

0

53

65

63

PSC:\>([int[]][char[]]"abc@example.com"|%{$_-46})-join""

51525318557451636662550536563

So now all I need to do to reverse the process is (a) split the long number into chunks of two, (b) add 46 to each, (c) convert to a char, and (d) convert the array of characters into a string.

It worked! Sort of. Something messed up though. And the email address is mostly generated but for the last 4 characters. It looks like the string is falling short for the Substring method.

A few minutes of head scratching later, I realize the problem. When I converted all the numbers to two digits, one of them was one digit – the number 46 that got subtracted by itself. This was resulting in a string that fell short. Not to worry, all I had to do was pad a 0 to that number and then it will work.

Automating

Can I automate this process? One where any string I give is converted to code that will generate the string when run.

A proper generic code would take a bit more effort and time, but since I am mainly concerned with email addresses I can take some short cuts. If I were going to build a generic code I’d (1) need a way to find the smallest ASCII/ UTF-8 code in the set entered, (2) while subtracting that number from every number I’d have to ensure any single digit number is accordingly padded, and (3) I’d also have to be concerned with any text that may have an ASCII/ UTF-8 code which won’t reduce to two digits when I do the subtraction above.

First thing to notice is that the number that we are subtracting – in my example above, as well as the code I started off with – is 46. Which turns out to be the dot in the email address. Next thing to notice is that letters A-Z and a-z have codes 65-90 and 97-122. Similarly the numbers 0-9 have codes 48-57. So by limiting myself to this set of characters I can be sure that the lowest number will always be 46, and that I can subtract it from any of the expected characters and expect a single or double digit result.

But why not got a teeny weeny step further. Checking WikiPedia for the syntax of valid email address I see that the lowest ASCII/ UTF-8 code for an allowed character is 32 (the character being “) and the highest allowed ASCII/ UTF-8 code for an allowed character is 126 (the character being ~). So the code I generate will be similar to the code I started this post with, except that I’ll be adding 32 instead of 46. And so the code I create to generate this code will output 32.

Of course, replace “abc@example.com” with the email address you’d like to obfuscate.

For the curious, here’s an explanation of my code (the bit after the first pipeline).

1

2

3

"[string](0..$($_.Length-1)|%{[char][int](32+(`""+`

-join([int[]][char[]]$_|%{if($_-32-lt9){"0$($_-32)"}else{$_-32}})+`

"`").substring((`$_*2),2))})"+'-replace " "'

Line 1 outputs the part of the final code that starts with a [string] and ends at the beginning of the series of digits (just before the left bracket).

Line 3 outputs the part of the final code that starts where the digits end (just after the right bracket) and goes all the way up to -replace " ".

These two lines are pretty straight-forward. Only point to note is I escape the $ symbols with a backtick (`) so they don’t get interpreted as variables. And I get the length of the input text via $_.Length (this is why I put the whole block within a ForEach-Object loop as that’s the only way to make $_ variable available). Also, I put the expression as $($_.Length-1) because if I don’t do that PowerShell expands $_ into a string and then tacks “.Length-1” to it as another string.

Line 2 is a variant of what I created earlier. It converts the input string into an array of characters, cast into an array of integers, and pipes these into another ForEach-Object loop that subtracts the number 32 from each and if the result is less than 9 (i.e. single digit) pads a “0” before outputting the number.

Now how about the reverse? Can I get the ASCII/ UTF-8 code of a character.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

PS>[int]'a'

Cannot convert value"a"to type"System.Int32".Error:"Input string was not in a correct format."

At line:1char:1

+[int]'a'

+~~~~~~~~

+CategoryInfo:InvalidArgument:(:)[],RuntimeException

+FullyQualifiedErrorId:InvalidCastFromStringToInteger

# Bummer! How about if I set it in a variable and then try?

PS>$a="a"

PS>[int]$a

Cannot convert value"a"to type"System.Int32".Error:"Input string was not in a correct format."

At line:1char:1

+[int]$a

+~~~~~~~

+CategoryInfo:InvalidArgument:(:)[],RuntimeException

+FullyQualifiedErrorId:InvalidCastFromStringToInteger

# Maybe the double quotes were causing the letter to be interpreted as a string rather than a character?

PS>$a='a'

PS>[int]$a

Cannot convert value"a"to type"System.Int32".Error:"Input string was not in a correct format."

At line:1char:1

+[int]$a

+~~~~~~~

+CategoryInfo:InvalidArgument:(:)[],RuntimeException

+FullyQualifiedErrorId:InvalidCastFromStringToInteger

No luck. But I think am the right track.

The error InvalidCastFromStringToInteger is what tells me PowerShell is trying to cast from a [string] to [int] – which is not what I want and will obviously fail. I want to cast from a [char] to [int] so let’s be explicit about that.

1

2

PS>[int][char]'a'

97

Good, that works!

Now how about getting the ASCII/ UTF-8 of a string. Can I do that?

As expected, you can’t just pass two characters and hope it works!

1

2

3

4

5

6

7

PS>[int][char]'as'

Cannot convert value"as"to type"System.Char".Error:"String must be exactly one character long."

At line:1char:1

+[int][char]'as'

+~~~~~~~~~~~~~~~

+CategoryInfo:InvalidArgument:(:)[],RuntimeException

+FullyQualifiedErrorId:InvalidCastParseTargetInvocation

What I need is an array of [char] elements. Which I can then type cast to an array of [int] elements.

First let’s look whether there’s any method available to convert a string to an array of characters?

I don’t need the ToCharArray() method either as if I just type cast a string to an array of characters the method is invoked implicitly. Sweet!

Armed with this info I try type casting the string to an array of integers to get their ASCII/ UTF-8 values:

1

2

3

4

PS>[int[]][char[]]"abc"

97

98

99

Nice!

Can I make this better? As in, say I had a longish string; currently the above snippet just gives a bunch of codes and that’s not very helpful if I want to see the code of a particular letter. Can I get the output such that it shows each character followed by it’s ASCII/ UTF-8 code? Something like this:

1

2

3

4

PS>[char[]]"abc"|%{"$_ -> [int]$_"}

a->[int]a

b->[int]b

c->[int]c

D’oh! Doesn’t help. But I am on the right track, and I know what to do. You see, within double quotes the [int] is not evaluated (thanks to this Hey, Scripting Guy! post) and so I have to force evaluation through any one of the methods mentioned in that post. I prefer the VBScript approach, so here goes: