Thought I'd ask. Saw some code that uses
System.Security.SecureString to encrypt information, then two methods from
system.runtime.interopservices.marshal to interpret:
Marshal.SecureStringToCoTaskMemUnicode
and
Marshal.PtrToStringUni
This will probably end up as another one of those classes the CLR cannot handle, but I gave it a shot. Attached are two files, (1) the text "Hello, I am safe." as a secure string, (2) a WB script that attempts to interpret it.
The script will fail by (1) inability to create the secretdata.txt as a secure string, and inability to recognize SecureStringToGlobalAllocUnicode as a method.
I realize there are existing API's to secure passwords or data, but this looks interesting.
It does work through powershell. The attached script and .ps1 file will interpret the previously posted secretdata.txt file.
and... if anyone is interested, here is the front end, a script to build a secure string. Had to run WB through PS and beginning to wonder if this secure string stuff needs .NET 4.5
Here is a WIL code Example to create a securestring:
;***************************************************************************
;** Create a SecureString
;**
;** Purpose: Create a dotNet SecureString
;** Inputs: String
;** Outputs: Displays the length of the secureString
;** Reference:
;** REQUIRES WinBatch 2013A or newer
;**
;** Developer: Deana Falk 2014.04.07
;***************************************************************************
If Version( )< '2013A'
Pause('Notice', 'Need 2013A or Newer Version of WinBatch')
Exit
EndIf
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; Load assemblies into the WinBatch process.
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; mscorlib assembly is automatically loaded by WinBatch when the CLR is loaded.
; ObjectClrOption ('use','mscorlib, version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089')
ObjectClrOption("use","System.Security, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; Prompt for input
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
initString = 'TestString'
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; Create a class implemented by a managed assembly.
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;Create a SecureString
testString = ObjectClrNew( 'System.Security.SecureString')
length = StrCharCount( initString )
for i = 0 to length-1
ochar = ObjectClrNew( 'System.Char', Char2Num(StrSub( initString, i, 1 )) )
testString.AppendChar(ochar)
next
length = testString.Length
testString.MakeReadOnly
Pause(testString.ToString(), Length)
Exit
Here is another code sample that tries to convert the input string back using System.Runtime.InteropServices.Marshal. It appears most of the methods exposed return a pointer of type VT_INT, which seems to convert to a WIL data type VT_I4. I then pass the pointer to a UDF that attempts to read the memory from that pointer using IntControl 32. However it seems to truncate the data att he last character and I am not quite sure how to resolve this.....yet.
;***************************************************************************
;** Create a SecureString and attempt to convert it back to a string.
;**
;** Purpose: Create a dotNet SecureString
;** Inputs: String
;** Outputs: Displays the converted string.
;** Reference:
;** REQUIRES WinBatch 2013A or newer
;**>> ISSUE: It appears most of the methods exposed return a pointer of type VT_INT, which seems to convert
;** to a WIL data type VT_I4. I then pass the pointer to a UDF that attempts to read the memory from that pointer
;** using IntControl 32. However it seems to truncate the data att he last character and I am not quite sure how to
;** resolve this.....yet.
;** Developer: Deana Falk 2014.04.07
;***************************************************************************
If Version( )< '2013A'
Pause('Notice', 'Need 2013A or Newer Version of WinBatch')
Exit
EndIf
#DefineFunction udfGetStringFromPointer(pointer)
DebugTrace(22)
strFromPointer=""
;errormode(@off)
cValue=IntControl(32, pointer, "LONG", 0, 0)
;errormode(@on)
While cValue
strFromPointer=strFromPointer:Num2Char(cValue)
pointer=pointer + 1
cValue=IntControl(32, pointer, "LONG", 0,0)
EndWhile
Return(strFromPointer)
#EndFunction
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; Load assemblies into the WinBatch process.
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; mscorlib assembly is automatically loaded by WinBatch when the CLR is loaded.
; ObjectClrOption ('use','mscorlib, version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089')
ObjectClrOption("use","System.Security, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; Prompt for input
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;initString = '0123456789'
initString = 'TestString'
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; Create a class implemented by a managed assembly.
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;Create a SecureString
testString = ObjectClrNew( 'System.Security.SecureString')
length = StrCharCount( initString )
for i = 0 to length-1
ochar = ObjectClrNew( 'System.Char', Char2Num(StrSub( initString, i, 1 )) )
testString.AppendChar(ochar)
next
length = testString.Length
testString.MakeReadOnly
;Pause(testString.ToString(), Length)
;Note that SecureString has no members that inspect, compare, or convert the value of a SecureString. The absence of such members helps protect the value of the
;instance from accidental or malicious exposure. Use appropriate members of the System.Runtime.InteropServices.Marshal class, such as the SecureStringToBSTR method,
;to manipulate the value of a SecureString object.
;The SecureString class and its members are not visible to COM. For more information, see ComVisibleAttribute.
;When you wish to use the value stored in the SecureString object, you will have to use the Marshal class (which can be found in the System.Runtime.InteropServices namespace).
;Among other things, this class provides methods for allocating unmanaged memory and copying unmanaged memory blocks. This includes methods that will convert the contents of
;your SecureSting object into an object of type BSTR (basic string or binary string) or a block of ANSI or Unicode memory.
Marshal = ObjectClrNew("System.Runtime.InteropServices.Marshal")
; ptr Returned from the following methods is type VT INT which is not currently supported bt WinBatch
ptr = Marshal.SecureStringToGlobalAllocUnicode(testString)
;ptr = Marshal.SecureStringToGlobalAllocAnsi(testString)
;ptr = Marshal.SecureStringToBSTR(testString)
WILptr = ObjectType( 'I4', ptr )
result = udfGetStringFromPointer(WILptr)
Pause(initString, result)
Exit
Interesting.....
This part
ochar = ObjectClrNew( 'System.Char', Char2Num(StrSub( initString, i, 1 )) )
especially. I assume this defaults to the -plaintext option in PS.
Quote from: stanl on April 08, 2014, 10:24:42 AM
Interesting.....
This part
ochar = ObjectClrNew( 'System.Char', Char2Num(StrSub( initString, i, 1 )) )
especially. I assume this defaults to the -plaintext option in PS.
Actually no. That line merely creates variable (of type 'System.Char') that contains a single character. The AppendChar method is what actually creates the secure string.
Quote from: Deana on April 08, 2014, 10:22:43 AM
Here is another code sample that tries to convert the input string back using System.Runtime.InteropServices.Marshal. It appears most of the methods exposed return a pointer of type VT_INT, which seems to convert to a WIL data type VT_I4. I then pass the pointer to a UDF that attempts to read the memory from that pointer using IntControl 32. However it seems to truncate the data att he last character and I am not quite sure how to resolve this.....yet.
Well, at least in my original posts, the .wbt code was on the right track. I tried the PtrToStringUni method in the Marshal class. Could you not use that?
A few suggested mods to experiment with
; Take advantage of a MSFT's largess
#DefineFunction udfGetStringFromPointer(pointer, nLen)
hBin = BinaryAlloc((nLen+1) *2)
DllCall("kernel32.dll",long:"lstrcpyW", lpbinary:hBin, long:pointer)
BinaryEodSet(hBin, (nLen+1)*2)
strResult = BinaryPeekStrW(hBin, 0, nLen*2 )
BinaryFree(hBin)
Return(strResult)
#EndFunction
And here
length = StrCharCount( initString )
for i = 1 to length ; 1 instead of 0
;ochar = ObjectClrNew( 'System.Char', Char2Num(StrSub( initString, i, 1 )) )
ochar = Char2Num(StrSub( initString, i, 1 ))
testString.AppendChar(ui2:ochar)
next
testString.AppendChar(ui2:0) ; Null terminate to be safe.
Thanks Tony! Here is the code sample that I came up with the help of your suggestions:
;***************************************************************************
;** Create a SecureString and attempt to convert it back to a WIL string.
;**
;** Purpose: Create then Read a dotNet SecureString
;** Inputs: String
;** Outputs: Displays the converted string.
;** Reference:
;** REQUIRES WinBatch 2013A or newer
;**
;**
;** Developer: Deana Falk, Tony Deets 2014.04.08
;***************************************************************************
If Version( )< '2013A'
Pause('Notice', 'Need 2013A or Newer Version of WinBatch')
Exit
EndIf
;Take advantage of a MSFT's largess
; It appears most of the methods exposed return a pointer of type VT_INT, which seems to convert
; to a WIL data type VT_I4. Pass the pointer to a UDF that attempts to read the memory from that pointer
#DefineFunction udfGetStringFromPointer(pointer, nLen)
hBin = BinaryAlloc((nLen+1) *2)
DllCall("kernel32.dll",long:"lstrcpyW", lpbinary:hBin, long:pointer)
BinaryEodSet(hBin, (nLen+1)*2)
strResult = BinaryPeekStrW(hBin, 0, nLen*2 )
BinaryFree(hBin)
Return(strResult)
#EndFunction
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; Load assemblies into the WinBatch process.
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; mscorlib assembly is automatically loaded by WinBatch when the CLR is loaded.
; ObjectClrOption ('use','mscorlib, version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089')
ObjectClrOption("use","System.Security, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; Prompt for input
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
initString = 'TestString'
initString = '01234567890'
initString = '!@#$^&*()_+'
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; Create a class implemented by a managed assembly.
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;Create a SecureString
testString = ObjectClrNew( 'System.Security.SecureString')
length = StrCharCount( initString )
for i = 1 to length ; 1 instead of 0
;ochar = ObjectClrNew( 'System.Char', Char2Num(StrSub( initString, i, 1 )) )
ochar = Char2Num(StrSub( initString, i, 1 ))
testString.AppendChar(ui2:ochar)
next
testString.AppendChar(ui2:0) ; Null terminate to be safe.
;length = testString.Length
testString.MakeReadOnly
;Note that SecureString has no members that inspect, compare, or convert the value of a SecureString. The absence of such members helps protect the value of the
;instance from accidental or malicious exposure. Use appropriate members of the System.Runtime.InteropServices.Marshal class, such as the SecureStringToBSTR method,
;to manipulate the value of a SecureString object.
;The SecureString class and its members are not visible to COM. For more information, see ComVisibleAttribute.
;When you wish to use the value stored in the SecureString object, you will have to use the Marshal class (which can be found in the System.Runtime.InteropServices namespace).
;Among other things, this class provides methods for allocating unmanaged memory and copying unmanaged memory blocks. This includes methods that will convert the contents of
;your SecureSting object into an object of type BSTR (basic string or binary string) or a block of ANSI or Unicode memory.
Marshal = ObjectClrNew("System.Runtime.InteropServices.Marshal")
; ptr Returned from the following methods is type VT INT which is not currently supported bt WinBatch
ptr = Marshal.SecureStringToBSTR(testString)
WILptr = ObjectType( 'I4', ptr )
result = udfGetStringFromPointer(WILptr,length)
Pause(initString, result)
Exit
If I might summarize: again, a good learning experience. In other example scripts using the CLR a property that accepted a string value could just be assigned it. In this case the securestring property needed a few hoops. Also, the method in the Marshal class to obtain the string from the memory pointer [ PtrToStringUni ] requires a WB udf.
I had a need (helping a friend) for a compiled WB script that used secure strings. I ended up with a script that used the powershell class (combining the two I posted). Today, I tested it against Deana's code with no real differences... the good part being both are WB code. Obviously PS cmdlets hide a lot of the 'innards', and so I appreciate both Tony and Deana totally winbatching this thread....
Quote from: stanl on April 10, 2014, 01:44:28 PM
If I might summarize: again, a good learning experience. In other example scripts using the CLR a property that accepted a string value could just be assigned it. In this case the securestring property needed a few hoops.
Actually SecureString is a "Class". The SecureString Class expects a subarray of System.Char objects, which is NOT the same as a BSTR/WIL string. The code I posted calls the AppendChar method which appends a character to the end of the current secure string.
Reference: http://msdn.microsoft.com/en-us/library/system.security.securestring%28v=vs.110%29.aspx
Quote from: stanl on April 10, 2014, 01:44:28 PM
Also, the method in the Marshal class to obtain the string from the memory pointer [ PtrToStringUni ] requires a WB udf.
Not necessarily. That is just the solution I came up with. The Marshal Class also seems to provide a ReadByte method that you might be able to use.
Reference: http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal(v=vs.100).ASPX
Quote from: Deana on April 10, 2014, 02:05:01 PM
Actually SecureString is a "Class".
Yes, I confused property with class.