在Golang中加密Windows的远程桌面(RDP)密码

huangapple go评论92阅读模式
英文:

windows encrypted rdp passwords in golang

问题

像这样的代码可以在http://play.golang.org/p/fD7mx2k4Yc找到。

窗口rdp密码加密可以在http://www.remkoweijnen.nl/blog/2007/10/18/how-rdp-passwords-are-encrypted/找到。

package main

import (
    "fmt"
    "log"
    "syscall"
    "unsafe"
)

const (
    CRYPTPROTECT_UI_FORBIDDEN = 0x1
)

var (
    dllcrypt32  = syscall.NewLazyDLL("Crypt32.dll")
    dllkernel32 = syscall.NewLazyDLL("Kernel32.dll")

    procEncryptData = dllcrypt32.NewProc("CryptProtectData")
    procDecryptData = dllcrypt32.NewProc("CryptUnprotectData")
    procLocalFree   = dllkernel32.NewProc("LocalFree")
)

type DATA_BLOB struct {
    cbData uint32
    pbData *byte
}

func NewBlob(d []byte) *DATA_BLOB {
    if len(d) == 0 {
        return &DATA_BLOB{}
    }
    return &DATA_BLOB{
        pbData: &d[0],
        cbData: uint32(len(d)),
    }
}

func (b *DATA_BLOB) ToByteArray() []byte {
    d := make([]byte, b.cbData)
    copy(d, (*[1 << 30]byte)(unsafe.Pointer(b.pbData))[:])
    return d
}

func Encrypt(data []byte) ([]byte, error) {
    var outblob DATA_BLOB
    r, _, err := procEncryptData.Call(uintptr(unsafe.Pointer(NewBlob(data))), 0, 0, 0, 0, 0, uintptr(unsafe.Pointer(&outblob)))
    if r == 0 {
        return nil, err
    }
    defer procLocalFree.Call(uintptr(unsafe.Pointer(outblob.pbData)))
    return outblob.ToByteArray(), nil
}

func Decrypt(data []byte) ([]byte, error) {
    var outblob DATA_BLOB
    r, _, err := procDecryptData.Call(uintptr(unsafe.Pointer(NewBlob(data))), 0, 0, 0, 0, 0, uintptr(unsafe.Pointer(&outblob)))
    if r == 0 {
        return nil, err
    }
    defer procLocalFree.Call(uintptr(unsafe.Pointer(outblob.pbData)))
    return outblob.ToByteArray(), nil
}

func main() {
    const secret = "MYpasswd"
    enc, err := Encrypt([]byte(secret))
    if err != nil {
        log.Fatalf("Encrypt failed: %v", err)
    }
    dec, err := Decrypt(enc)
    if err != nil {
        log.Fatalf("Decrypt failed: %v", err)
    }
    if string(dec) != secret {
        log.Fatalf("decrypted secret \"%s\" does not match to \"%s\".", dec, secret)
    }
    fmt.Println(fmt.Sprintf("%x", enc))
    fmt.Println(string(dec))
}

输出结果:01000000d08c9ddf0115d1118c7a00c04fc297eb01000000de7c90fbe3c9854381f0a0ffe1d496f3000000000200000000001066000000010000200000000790b641e1a9d4bfe54d81966c4d7aaeabf19b63c36dff42668e3b256edbeed8000000000e8000000002000020000000d6385d3352d5a4b011e171ab25b30271e73a4ddc0b9f9bfb8ecd13f230362a0110000000da71663217c163d7ab77231282e7d8d64000000025fbcbb72efcdc711f3a74c38bddbf0b71538f0ffe27d133c0c5cd2434f88d55d924f598ac2f94758d66a448682ed841fb56ce8c9de38601dcce6bd42aa41fbb

MYpasswd

创建tmp.rdp文件:

screen mode id:i:1
....
winposstr:s:0,1,153,64,953,664
username:s:{{username}}
domain:s:
password 51:b:01000000d08c9ddf0115d1118c7a00c04fc297eb0100000............
disable wallpaper:i:1
disable full window drag:i:1

最终命令:

mstsc.exe tmp.rdp

但是登录失败。

在Python中,"win32crypt.CryptProtectData"是有效的。

pwdHash = win32crypt.CryptProtectData(u"MYpasswd", u'pws', None, None, None, 0)
enc_password = binascii.hexlify(pwdHash)
英文:

like http://play.golang.org/p/fD7mx2k4Yc

window rdp password encrypted http://www.remkoweijnen.nl/blog/2007/10/18/how-rdp-passwords-are-encrypted/

   package main
import (
&quot;fmt&quot;
&quot;log&quot;
&quot;syscall&quot;
&quot;unsafe&quot;
)
const (
CRYPTPROTECT_UI_FORBIDDEN = 0x1
)
var (
dllcrypt32  = syscall.NewLazyDLL(&quot;Crypt32.dll&quot;)
dllkernel32 = syscall.NewLazyDLL(&quot;Kernel32.dll&quot;)
procEncryptData = dllcrypt32.NewProc(&quot;CryptProtectData&quot;)
procDecryptData = dllcrypt32.NewProc(&quot;CryptUnprotectData&quot;)
procLocalFree   = dllkernel32.NewProc(&quot;LocalFree&quot;)
)
type DATA_BLOB struct {
cbData uint32
pbData *byte
}
func NewBlob(d []byte) *DATA_BLOB {
if len(d) == 0 {
return &amp;DATA_BLOB{}
}
return &amp;DATA_BLOB{
pbData: &amp;d[0],
cbData: uint32(len(d)),
}
}
func (b *DATA_BLOB) ToByteArray() []byte {
d := make([]byte, b.cbData)
copy(d, (*[1 &lt;&lt; 30]byte)(unsafe.Pointer(b.pbData))[:])
return d
}
func Encrypt(data []byte) ([]byte, error) {
var outblob DATA_BLOB
r, _, err := procEncryptData.Call(uintptr(unsafe.Pointer(NewBlob(data))), 0, 0, 0, 0, 0, uintptr(unsafe.Pointer(&amp;outblob)))
if r == 0 {
return nil, err
}
defer procLocalFree.Call(uintptr(unsafe.Pointer(outblob.pbData)))
return outblob.ToByteArray(), nil
}
func Decrypt(data []byte) ([]byte, error) {
var outblob DATA_BLOB
r, _, err := procDecryptData.Call(uintptr(unsafe.Pointer(NewBlob(data))), 0, 0, 0, 0, 0, uintptr(unsafe.Pointer(&amp;outblob)))
if r == 0 {
return nil, err
}
defer procLocalFree.Call(uintptr(unsafe.Pointer(outblob.pbData)))
return outblob.ToByteArray(), nil
}
func main() {
const secret = &quot;MYpasswd&quot;
enc, err := Encrypt([]byte(secret))
if err != nil {
log.Fatalf(&quot;Encrypt failed: %v&quot;, err)
}
dec, err := Decrypt(enc)
if err != nil {
log.Fatalf(&quot;Decrypt failed: %v&quot;, err)
}
if string(dec) != secret {
log.Fatalf(&quot;decrypted secret \&quot;%s\&quot; does not match to \&quot;%s\&quot;.&quot;, dec, secret)
}
fmt.Println(fmt.Sprintf(&quot;%x&quot;, enc))
fmt.Println(string(dec))
}

out: 01000000d08c9ddf0115d1118c7a00c04fc297eb01000000de7c90fbe3c9854381f0a0ffe1d496f3000000000200000000001066000000010000200000000790b641e1a9d4bfe54d81966c4d7aaeabf19b63c36dff42668e3b256edbeed8000000000e8000000002000020000000d6385d3352d5a4b011e171ab25b30271e73a4ddc0b9f9bfb8ecd13f230362a0110000000da71663217c163d7ab77231282e7d8d64000000025fbcbb72efcdc711f3a74c38bddbf0b71538f0ffe27d133c0c5cd2434f88d55d924f598ac2f94758d66a448682ed841fb56ce8c9de38601dcce6bd42aa41fbb

MYpasswd

create tmp.rdp

screen mode id:i:1
....
winposstr:s:0,1,153,64,953,664
username:s:{{username}}
domain:s:
password 51:b:01000000d08c9ddf0115d1118c7a00c04fc297eb0100000............
disable wallpaper:i:1
disable full window drag:i:1

final:
mstsc.exe tmp.rdp

But Login failed

python "win32crypt.CryptProtectData" is work.

    pwdHash = win32crypt.CryptProtectData(u&quot;MYpasswd&quot;, u&#39;pws&#39;, None, None, None, 0)
enc_password = binascii.hexlify(pwdHash)

答案1

得分: 2

对上面不详细的答案进行补充。根据@Fuqiang的回答,只需在加密之前将普通字符串的编码转换为UTF-16LE,然后它就可以正常工作。代码如下所示:

func convertToUTF16LittleEndianBytes(s string) []byte {
    u := utf16.Encode([]rune(s))
    b := make([]byte, 2*len(u))
    for index, value := range u {
        binary.LittleEndian.PutUint16(b[index*2:], value)
    }
    return b
}

func main() {
    const secret = "MYpasswd"
    s := convertToUTF16LittleEndianBytes(secret)
    enc, err := Encrypt(s)
    if err != nil {
        log.Fatalf("Encrypt failed: %v", err)
    }
    ...
}

解密后,在比较或使用解密后的字符串之前,你必须将解密后的字符串从utf-16le解码,不要直接使用string(dec)utf-16le字符转换为字符串。

英文:

make an addition for the not-detailed answer above. According @Fuqiang's answer, just transfer the encoding of the plain string to UTF-16LE before encrypting, and it finally works. So it looks like this:

....
func convertToUTF16LittleEndianBytes(s string) []byte {
u := utf16.Encode([]rune(s))
b := make([]byte, 2*len(u))
for index, value := range u {
binary.LittleEndian.PutUint16(b[index*2:], value)
}
return b
}
func main() {
const secret = &quot;MYpasswd&quot;
s := convertToUTF16LittleEndianBytes(secret)
enc, err := Encrypt(s)
if err != nil {
log.Fatalf(&quot;Encrypt failed: %v&quot;, err)
}
...
}

And after decrypting, you have to decode the decrypted string from utf-16le before you comparing or using it, don't directly transfer the utf-16le char to string by string(dec).

答案2

得分: 1

问题已经解决。

secret = "MYpasswd"

字符串必须使用 UTF-16LE 编码。

英文:

The issue has been solved.

secret = &quot;MYpasswd&quot;

string must use UTF-16LE encode.

huangapple
  • 本文由 发表于 2015年11月4日 15:29:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/33516053.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定