line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Win32::CryptData;
|
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
6206
|
use 5.006;
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
36
|
|
4
|
1
|
|
|
1
|
|
6
|
use strict;
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
29
|
|
5
|
1
|
|
|
1
|
|
5
|
use Carp;
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
67
|
|
6
|
|
|
|
|
|
|
|
7
|
1
|
|
|
1
|
|
1577
|
use Win32;
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
use Win32::API '0.20';
|
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
require Exporter;
|
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
our @ISA = qw(Exporter);
|
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
# Items to export into callers namespace by default. Note: do not export
|
15
|
|
|
|
|
|
|
# names by default without a very good reason. Use EXPORT_OK instead.
|
16
|
|
|
|
|
|
|
# Do not simply export all your public functions/methods/constants.
|
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
# This allows declaration use Win32::IPHelper ':all';
|
19
|
|
|
|
|
|
|
# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
|
20
|
|
|
|
|
|
|
# will save memory.
|
21
|
|
|
|
|
|
|
our %EXPORT_TAGS = (
|
22
|
|
|
|
|
|
|
'all' => [ qw( CryptProtectData CryptUnprotectData CRYPTPROTECT_PROMPT_ON_UNPROTECT CRYPTPROTECT_PROMPT_ON_PROTECT CRYPTPROTECT_PROMPT_STRONG CRYPTPROTECT_UI_FORBIDDEN CRYPTPROTECT_LOCAL_MACHINE CRYPTPROTECT_AUDIT CRYPTPROTECT_VERIFY_PROTECTION ) ],
|
23
|
|
|
|
|
|
|
'flags' => [ qw( CRYPTPROTECT_PROMPT_ON_UNPROTECT CRYPTPROTECT_PROMPT_ON_PROTECT CRYPTPROTECT_PROMPT_STRONG CRYPTPROTECT_UI_FORBIDDEN CRYPTPROTECT_LOCAL_MACHINE CRYPTPROTECT_AUDIT CRYPTPROTECT_VERIFY_PROTECTION ) ]
|
24
|
|
|
|
|
|
|
);
|
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
|
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
our @EXPORT = qw();
|
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
our $VERSION = '0.02';
|
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
my $CryptProtectData = new Win32::API ('Crypt32', 'CryptProtectData', ['P', 'P', 'P', 'P', 'P', 'N', 'P'], 'N') or croak 'can\'t find CryptProtectData() function';
|
33
|
|
|
|
|
|
|
my $CryptUnprotectData = new Win32::API ('Crypt32', 'CryptUnprotectData', ['P', 'P', 'P', 'P', 'P', 'N', 'P'], 'N') or croak 'can\'t find CryptUnprotectData() function';
|
34
|
|
|
|
|
|
|
my $LocalFree = new Win32::API ('Kernel32', 'LocalFree', ['N'], 'N') or croak 'can\'t find LocalFree() function';
|
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
#############
|
37
|
|
|
|
|
|
|
# Constants #
|
38
|
|
|
|
|
|
|
#############
|
39
|
|
|
|
|
|
|
# $dwPromptFlags
|
40
|
|
|
|
|
|
|
use constant CRYPTPROTECT_PROMPT_ON_UNPROTECT => 0x1;
|
41
|
|
|
|
|
|
|
use constant CRYPTPROTECT_PROMPT_ON_PROTECT => 0x2;
|
42
|
|
|
|
|
|
|
#use constant CRYPTPROTECT_PROMPT_RESERVED => 0x4; # reserved, do not use.
|
43
|
|
|
|
|
|
|
use constant CRYPTPROTECT_PROMPT_STRONG => 0x8;
|
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
# $dwFlags
|
46
|
|
|
|
|
|
|
use constant CRYPTPROTECT_UI_FORBIDDEN => 0x1;
|
47
|
|
|
|
|
|
|
use constant CRYPTPROTECT_LOCAL_MACHINE => 0x4;
|
48
|
|
|
|
|
|
|
use constant CRYPTPROTECT_AUDIT => 0x10;
|
49
|
|
|
|
|
|
|
use constant CRYPTPROTECT_VERIFY_PROTECTION => 0x40;
|
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
our $DEBUG = 0;
|
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
#################################
|
54
|
|
|
|
|
|
|
# PUBLIC Functions (exportable) #
|
55
|
|
|
|
|
|
|
#################################
|
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
#######################################################################
|
58
|
|
|
|
|
|
|
# Win32::CryptData::CryptProtectData()
|
59
|
|
|
|
|
|
|
#
|
60
|
|
|
|
|
|
|
# The CryptProtectData function performs encryption on the data in a
|
61
|
|
|
|
|
|
|
# DATA_BLOB structure. Typically, only a user with the same logon
|
62
|
|
|
|
|
|
|
# credential as the encrypter can decrypt the data. In addition, the
|
63
|
|
|
|
|
|
|
# encryption and decryption usually must be done on the same computer.
|
64
|
|
|
|
|
|
|
#
|
65
|
|
|
|
|
|
|
#######################################################################
|
66
|
|
|
|
|
|
|
# Usage:
|
67
|
|
|
|
|
|
|
# $ret = CryptProtectData(\$pDataIn, \$szDataDescr, \$pOptionalEntropy, \$pvReserved, \%pPromptStruct, $dwFlags, \$pDataOut);
|
68
|
|
|
|
|
|
|
#
|
69
|
|
|
|
|
|
|
# Output:
|
70
|
|
|
|
|
|
|
# $ret = 1 for success, undef for failure
|
71
|
|
|
|
|
|
|
#
|
72
|
|
|
|
|
|
|
# Input:
|
73
|
|
|
|
|
|
|
# \$pDataIn = Pointer to the plaintext string to be encrypted
|
74
|
|
|
|
|
|
|
# \$szDataDescr = String with a readable description of the data to be encrypted
|
75
|
|
|
|
|
|
|
# \$pOptionalEntropy = Pointer to a password or other additional entropy used to encrypt the data
|
76
|
|
|
|
|
|
|
# \$pvReserved = reserved, must be set to undef
|
77
|
|
|
|
|
|
|
# \%pPromptStruct = Pointer to a hash that provides information about where and when prompts are to be displayed
|
78
|
|
|
|
|
|
|
# PromptFlags => one or more of the following values:
|
79
|
|
|
|
|
|
|
# CRYPTPROTECT_PROMPT_ON_PROTECT = This flag is used to provide the prompt for the protect phase
|
80
|
|
|
|
|
|
|
# CRYPTPROTECT_PROMPT_ON_UNPROTECT = This flag can be combined with CRYPTPROTECT_PROMPT_ON_PROTECT to enforce the UI (user interface) policy of the caller
|
81
|
|
|
|
|
|
|
# CRYPTPROTECT_PROMPT_STRONG = This flag forces user to provide an encryption password
|
82
|
|
|
|
|
|
|
# hwndApp => handle of the parent window if needed modal behaviour
|
83
|
|
|
|
|
|
|
# Prompt => caption for the prompt
|
84
|
|
|
|
|
|
|
# $dwFlags = The following flag values are defined:
|
85
|
|
|
|
|
|
|
# CRYPTPROTECT_LOCAL_MACHINE = When this flag is set, it associates the data encrypted with the current computer instead of with an individual user
|
86
|
|
|
|
|
|
|
# CRYPTPROTECT_UI_FORBIDDEN = This flag is used for remote situations where presenting a user interface (UI) is not an option
|
87
|
|
|
|
|
|
|
# CRYPTPROTECT_AUDIT = This flag generates an audit on protect and unprotect operations
|
88
|
|
|
|
|
|
|
# CRYPTPROTECT_VERIFY_PROTECTION = This flag verifies the protection of a protected string
|
89
|
|
|
|
|
|
|
#
|
90
|
|
|
|
|
|
|
# Output:
|
91
|
|
|
|
|
|
|
# \$pDataOut = Pointer to the string that receives the encrypted data
|
92
|
|
|
|
|
|
|
#
|
93
|
|
|
|
|
|
|
#######################################################################
|
94
|
|
|
|
|
|
|
# function CryptProtectData
|
95
|
|
|
|
|
|
|
#
|
96
|
|
|
|
|
|
|
# The CryptProtectData function performs encryption on the data in a DATA_BLOB structure
|
97
|
|
|
|
|
|
|
#
|
98
|
|
|
|
|
|
|
# BOOL WINAPI CryptProtectData(
|
99
|
|
|
|
|
|
|
# DATA_BLOB* pDataIn,
|
100
|
|
|
|
|
|
|
# LPCWSTR szDataDescr,
|
101
|
|
|
|
|
|
|
# DATA_BLOB* pOptionalEntropy,
|
102
|
|
|
|
|
|
|
# PVOID pvReserved,
|
103
|
|
|
|
|
|
|
# CRYPTPROTECT_PROMPTSTRUCT* pPromptStruct,
|
104
|
|
|
|
|
|
|
# DWORD dwFlags,
|
105
|
|
|
|
|
|
|
# DATA_BLOB* pDataOut
|
106
|
|
|
|
|
|
|
# );
|
107
|
|
|
|
|
|
|
#
|
108
|
|
|
|
|
|
|
#######################################################################
|
109
|
|
|
|
|
|
|
sub CryptProtectData
|
110
|
|
|
|
|
|
|
{
|
111
|
|
|
|
|
|
|
if(scalar(@_) ne 7)
|
112
|
|
|
|
|
|
|
{
|
113
|
|
|
|
|
|
|
croak 'Usage: CryptProtectData(\\\$pDataIn, \\\$szDataDescr, \\\$pOptionalEntropy, \\\$pvReserved, \\\%pPromptStruct, \$dwFlags, \\\$pDataOut)';
|
114
|
|
|
|
|
|
|
}
|
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
my $DataIn = shift;
|
117
|
|
|
|
|
|
|
my $szDataDescr = _ToUnicodeSz(${shift()});
|
118
|
|
|
|
|
|
|
my $OptionalEntropy = shift;
|
119
|
|
|
|
|
|
|
my $pvReserved = pack("L", shift);
|
120
|
|
|
|
|
|
|
my %PromptStruct = %{ shift() };
|
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
my $dwFlags = shift || 0;
|
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
my $DataOut = shift;
|
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
my $pDataOut = pack("LL", 0x0, 0x0);
|
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
my $pDataIn = pack("LL", length($$DataIn), unpack("L!", pack("P", $$DataIn)));
|
129
|
|
|
|
|
|
|
my $pOptionalEntropy = pack("LL", length($$OptionalEntropy)+1, unpack("L!", pack("P", $$OptionalEntropy)));
|
130
|
|
|
|
|
|
|
my $szPrompt = _ToUnicodeSz($PromptStruct{'Prompt'});
|
131
|
|
|
|
|
|
|
my $pPromptStruct = pack("L4",
|
132
|
|
|
|
|
|
|
16,
|
133
|
|
|
|
|
|
|
$PromptStruct{'PromptFlags'} || 0,
|
134
|
|
|
|
|
|
|
$PromptStruct{'hwndApp'} || 0,
|
135
|
|
|
|
|
|
|
unpack("L!", pack("P", $szPrompt))
|
136
|
|
|
|
|
|
|
);
|
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
if($CryptProtectData->Call($pDataIn, $szDataDescr, $pOptionalEntropy, $pvReserved, $pPromptStruct, $dwFlags, $pDataOut))
|
139
|
|
|
|
|
|
|
{
|
140
|
|
|
|
|
|
|
my($len, $ptr) = unpack("LL", $pDataOut);
|
141
|
|
|
|
|
|
|
$$DataOut = unpack('P'.$len, pack('L!', $ptr));
|
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
$LocalFree->Call($ptr) and warn "Cannot LocalFree() pDataOut buffer: $^E";
|
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
return 1;
|
146
|
|
|
|
|
|
|
}
|
147
|
|
|
|
|
|
|
}
|
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
#######################################################################
|
150
|
|
|
|
|
|
|
# Win32::CryptData::CryptUnprotectData()
|
151
|
|
|
|
|
|
|
#
|
152
|
|
|
|
|
|
|
# The CryptUnprotectData function decrypts and does an integrity check
|
153
|
|
|
|
|
|
|
# of the data in a DATA_BLOB structure. Usually, only a user with the
|
154
|
|
|
|
|
|
|
# same logon credentials as the encrypter can decrypt the data.
|
155
|
|
|
|
|
|
|
#
|
156
|
|
|
|
|
|
|
#######################################################################
|
157
|
|
|
|
|
|
|
# Usage:
|
158
|
|
|
|
|
|
|
# $ret = CryptUnprotectData(\$pDataIn, \$szDataDescr, \$pOptionalEntropy, \$pvReserved, \%pPromptStruct, $dwFlags, \$pDataOut);
|
159
|
|
|
|
|
|
|
#
|
160
|
|
|
|
|
|
|
# Output:
|
161
|
|
|
|
|
|
|
# $ret = 1 for success, undef for failure
|
162
|
|
|
|
|
|
|
#
|
163
|
|
|
|
|
|
|
# Input:
|
164
|
|
|
|
|
|
|
# \$pDataIn = Pointer to the plaintext string to be decrypted
|
165
|
|
|
|
|
|
|
# \$pOptionalEntropy = Pointer to a password or other additional entropy used to encrypt the data
|
166
|
|
|
|
|
|
|
# \$pvReserved = reserved, must be set to undef
|
167
|
|
|
|
|
|
|
# \%pPromptStruct = Pointer to a hash that provides information about where and when prompts are to be displayed
|
168
|
|
|
|
|
|
|
# PromptFlags => one or more of the following values:
|
169
|
|
|
|
|
|
|
# CRYPTPROTECT_PROMPT_ON_PROTECT = This flag is used to provide the prompt for the protect phase
|
170
|
|
|
|
|
|
|
# CRYPTPROTECT_PROMPT_ON_UNPROTECT = This flag can be combined with CRYPTPROTECT_PROMPT_ON_PROTECT to enforce the UI (user interface) policy of the caller
|
171
|
|
|
|
|
|
|
# CRYPTPROTECT_PROMPT_STRONG = This flag forces user to provide an encryption password
|
172
|
|
|
|
|
|
|
# hwndApp => handle of the parent window if needed modal behaviour
|
173
|
|
|
|
|
|
|
# Prompt => caption for the prompt
|
174
|
|
|
|
|
|
|
# $dwFlags = The following flag values are defined:
|
175
|
|
|
|
|
|
|
# CRYPTPROTECT_UI_FORBIDDEN = This flag is used for remote situations where presenting a user interface (UI) is not an option
|
176
|
|
|
|
|
|
|
#
|
177
|
|
|
|
|
|
|
# Output:
|
178
|
|
|
|
|
|
|
# \$szDataDescr = String with a readable description of the data to be encrypted
|
179
|
|
|
|
|
|
|
# \$pDataOut = Pointer to the string that receives the encrypted data
|
180
|
|
|
|
|
|
|
#
|
181
|
|
|
|
|
|
|
#######################################################################
|
182
|
|
|
|
|
|
|
# function CryptUnprotectData
|
183
|
|
|
|
|
|
|
#
|
184
|
|
|
|
|
|
|
# The CryptUnprotectData function decrypts and does an integrity check of the data in a DATA_BLOB structure
|
185
|
|
|
|
|
|
|
#
|
186
|
|
|
|
|
|
|
# BOOL WINAPI CryptUnprotectData(
|
187
|
|
|
|
|
|
|
# DATA_BLOB* pDataIn,
|
188
|
|
|
|
|
|
|
# LPWSTR* ppszDataDescr,
|
189
|
|
|
|
|
|
|
# DATA_BLOB* pOptionalEntropy,
|
190
|
|
|
|
|
|
|
# PVOID pvReserved,
|
191
|
|
|
|
|
|
|
# CRYPTPROTECT_PROMPTSTRUCT* pPromptStruct,
|
192
|
|
|
|
|
|
|
# DWORD dwFlags,
|
193
|
|
|
|
|
|
|
# DATA_BLOB* pDataOut
|
194
|
|
|
|
|
|
|
# );
|
195
|
|
|
|
|
|
|
#
|
196
|
|
|
|
|
|
|
#######################################################################
|
197
|
|
|
|
|
|
|
sub CryptUnprotectData
|
198
|
|
|
|
|
|
|
{
|
199
|
|
|
|
|
|
|
if(scalar(@_) ne 7)
|
200
|
|
|
|
|
|
|
{
|
201
|
|
|
|
|
|
|
croak 'Usage: CryptUnprotectData(\\\$pDataIn, \\\$szDataDescr, \\\$pOptionalEntropy, \\\$pvReserved, \\\$pPromptStruct, \$dwFlags, \\\$pDataOut)';
|
202
|
|
|
|
|
|
|
}
|
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
my $DataIn = shift;
|
205
|
|
|
|
|
|
|
my $szDataDescr = shift;
|
206
|
|
|
|
|
|
|
my $pszDataDescr = pack('L', 0);
|
207
|
|
|
|
|
|
|
my $OptionalEntropy = shift;
|
208
|
|
|
|
|
|
|
my $pvReserved = pack('L', shift);
|
209
|
|
|
|
|
|
|
my %PromptStruct = %{ shift() };
|
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
my $dwFlags = shift || 0;
|
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
my $DataOut = shift;
|
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
my $pDataOut = pack('LL', 0, 0);
|
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
my $pDataIn = pack('LL', length($$DataIn)+1, unpack('L!', pack('P', $$DataIn)));
|
218
|
|
|
|
|
|
|
my $pOptionalEntropy = pack('LL', length($$OptionalEntropy)+1, unpack('L!', pack('P', $$OptionalEntropy)));
|
219
|
|
|
|
|
|
|
my $szPrompt = _ToUnicodeSz($PromptStruct{'Prompt'});
|
220
|
|
|
|
|
|
|
my $pPromptStruct = pack('L4',
|
221
|
|
|
|
|
|
|
16,
|
222
|
|
|
|
|
|
|
$PromptStruct{'PromptFlags'} || 0,
|
223
|
|
|
|
|
|
|
$PromptStruct{'hwndApp'} || 0,
|
224
|
|
|
|
|
|
|
unpack('L!', pack('P', $szPrompt))
|
225
|
|
|
|
|
|
|
);
|
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
if($CryptUnprotectData->Call($pDataIn, $pszDataDescr, $pOptionalEntropy, $pvReserved, $pPromptStruct, $dwFlags, $pDataOut))
|
228
|
|
|
|
|
|
|
{
|
229
|
|
|
|
|
|
|
my($len, $ptr) = unpack('LL', $pDataOut);
|
230
|
|
|
|
|
|
|
$$DataOut = unpack('P'.$len, pack('L!', $ptr));
|
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
$LocalFree->Call($ptr) and warn "Cannot LocalFree() pDataOut buffer: $^E";
|
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
my $i = 0;
|
235
|
|
|
|
|
|
|
do {
|
236
|
|
|
|
|
|
|
$i += 2;
|
237
|
|
|
|
|
|
|
$$szDataDescr = unpack('P'.$i, $pszDataDescr);
|
238
|
|
|
|
|
|
|
}
|
239
|
|
|
|
|
|
|
while(substr($$szDataDescr, -2) ne "\0\0");
|
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
$$szDataDescr = _FromUnicode($$szDataDescr);
|
242
|
|
|
|
|
|
|
$LocalFree->Call(unpack('L', $pszDataDescr)) and warn "Cannot LocalFree() pszDataDescr buffer: $^E";
|
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
return 1;
|
245
|
|
|
|
|
|
|
}
|
246
|
|
|
|
|
|
|
}
|
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
#######################################################################
|
249
|
|
|
|
|
|
|
# WCHAR = _ToUnicodeChar(string)
|
250
|
|
|
|
|
|
|
# converts a perl string in a 16-bit (pseudo) unicode string
|
251
|
|
|
|
|
|
|
#######################################################################
|
252
|
|
|
|
|
|
|
sub _ToUnicodeChar
|
253
|
|
|
|
|
|
|
{
|
254
|
|
|
|
|
|
|
my $string = shift or return(undef);
|
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
$string =~ s/(.)/$1\x00/sg;
|
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
return $string;
|
259
|
|
|
|
|
|
|
}
|
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
#######################################################################
|
263
|
|
|
|
|
|
|
# WSTR = _ToUnicodeSz(string)
|
264
|
|
|
|
|
|
|
# converts a perl string in a null-terminated 16-bit (pseudo) unicode string
|
265
|
|
|
|
|
|
|
#######################################################################
|
266
|
|
|
|
|
|
|
sub _ToUnicodeSz
|
267
|
|
|
|
|
|
|
{
|
268
|
|
|
|
|
|
|
my $string = shift or return(undef);
|
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
return _ToUnicodeChar($string."\x00");
|
271
|
|
|
|
|
|
|
}
|
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
#######################################################################
|
275
|
|
|
|
|
|
|
# string = _FromUnicode(WSTR)
|
276
|
|
|
|
|
|
|
# converts a null-terminated 16-bit unicode string into a regular perl string
|
277
|
|
|
|
|
|
|
#######################################################################
|
278
|
|
|
|
|
|
|
sub _FromUnicode
|
279
|
|
|
|
|
|
|
{
|
280
|
|
|
|
|
|
|
my $string = shift or return(undef);
|
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
$string = unpack("Z*", pack( "C*", unpack("S*", $string)));
|
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
return($string);
|
285
|
|
|
|
|
|
|
}
|
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
1;
|
289
|
|
|
|
|
|
|
__END__
|