File Coverage

blib/lib/Win32/Security/Raw.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1             #############################################################################
2             #
3             # Win32::Security::Raw - low-level access Win32 Security API calls
4             #
5             # Author: Toby Ovod-Everett
6             #
7             #############################################################################
8             # Copyright 2003, 2004 Toby Ovod-Everett. All rights reserved
9             #
10             # This program is free software; you can redistribute it and/or modify it
11             # under the same terms as Perl itself.
12             #
13             # For comments, questions, bugs or general interest, feel free to
14             # contact Toby Ovod-Everett at toby@ovod-everett.org
15             #############################################################################
16            
17             =head1 NAME
18            
19             C - low-level access Win32 Security API calls
20            
21             =head1 SYNOPSIS
22            
23             use Win32::Security::Raw;
24            
25             =head1 DESCRIPTION
26            
27             This module provides access to a limited number of Win32 Security API calls.
28             As I have need for other functions I will add them to the module. If anyone
29             has suggestions, feel free to ask - I will be quite happy to extend this
30             module.
31            
32            
33             =head2 Installation instructions
34            
35             This installs as part of C. See
36             C for more information.
37            
38             It depends upon the C and C modules, which
39             should be installable via PPM or available on CPAN.
40            
41             =cut
42            
43 3     3   18 use Carp;
  3         6  
  3         212  
44 3     3   4708 use Win32::API;
  0            
  0            
45             use Data::BitMask '0.13';
46            
47             use strict;
48            
49             package Win32::Security::Raw;
50            
51             =head1 Function Reference
52            
53             =head2 C
54            
55             =cut
56            
57             {
58             my $call;
59             sub AdjustTokenPrivileges {
60             my($TokenHandle, $DisableAllPrivileges, $NewState) = @_;
61            
62             ref($NewState) eq 'ARRAY' or Carp::croak("AdjustTokenPrivileges requires array ref for NewState.");
63             scalar(@{$NewState}) >= 1 or Carp::croak("AdjustTokenPrivileges requires at least one element for NewState.");
64             my $pNewState = pack('V', scalar(@{$NewState}));
65             foreach my $laa (@{$NewState}) {
66             ref($laa) eq 'ARRAY' or Carp::croak("AdjustTokenPrivileges requires all elements of NewState to be array refs.");
67             scalar(@{$laa}) == 2 or Carp::croak("AdjustTokenPrivileges requires all elements of NewState to be array refs with two elements.");
68             length($laa->[0]) == 8 or Carp::croak("AdjustTokenPrivileges requires all LUID values to be 8 bytes.");
69             $pNewState .= $laa->[0].pack('V', &Win32::Security::LUID_ATTRIBUTES->build_mask($laa->[1]));
70             }
71            
72             my $BufferLength = length($pNewState);
73             my $pPreviousState = ("\0" x $BufferLength);
74             my $pReturnLength = pack('V', $BufferLength);
75            
76             $call ||= Win32::API->new('advapi32',
77             'AdjustTokenPrivileges', [qw(I I P I P P)], 'I') or
78             Carp::croak("Unable to connect to AdjustTokenPrivileges.");
79            
80             $call->Call($TokenHandle, $DisableAllPrivileges, $pNewState, $BufferLength, $pPreviousState, $pReturnLength);
81             my $error = Win32::GetLastError();
82             $error and Carp::croak(&_format_error('AdjustTokenPrivileges', $error));
83            
84             my $PreviousCount = unpack('V', $pPreviousState);
85             my $PreviousState = [];
86             foreach my $i (0..$PreviousCount-1) {
87             my $Luid = substr($pPreviousState, $i*12+4, 8);
88             my $Attributes = &Win32::Security::LUID_ATTRIBUTES->break_mask(unpack('V', substr($pPreviousState, $i*12+12, 4)));
89             push(@{$PreviousState}, [$Luid, $Attributes]);
90             }
91            
92             return $PreviousState;
93             }
94             }
95            
96            
97             =head2 C
98            
99             Uses C to read an arbitrary memory location. You should pass a
100             pointer in the form of a Perl integer and the number of bytes to read from that
101             location. The function will return the data read in a Perl string.
102            
103             =cut
104            
105             {
106             my $call;
107             sub CopyMemory_Read {
108             my($pSource, $Length) = @_;
109            
110             $call ||= Win32::API->new('kernel32',
111             'RtlMoveMemory', [qw(P I I)], 'V') or
112             Carp::croak("Unable to connect to RtlMoveMemory.");
113            
114             my $Destination = "\0"x$Length;
115             $call->Call($Destination, $pSource, $Length);
116             return $Destination;
117             }
118             }
119            
120            
121             =head2 C
122            
123             Uses C to write to an arbitrary memory location. You should pass
124             a string that will be copied and a pointer in the form of a Perl integer. The
125             caller is responsible for ensuring that the data to be written will not overrun
126             the memory location.
127            
128             =cut
129            
130             {
131             my $call;
132             sub CopyMemory_Write {
133             my($string, $pDest) = @_;
134            
135             $call ||= Win32::API->new('kernel32',
136             'RtlMoveMemory', [qw(I P I)], 'V') or
137             Carp::croak("Unable to connect to RtlMoveMemory.");
138             $call->Call($pDest, $string, length($string));
139             }
140             }
141            
142            
143             =head2 C
144            
145             Returns a handle to the C as an integer.
146            
147             =cut
148            
149             {
150             my $call;
151             sub GetCurrentProcess {
152             $call ||= Win32::API->new('kernel32',
153             'GetCurrentProcess', [], 'I') or
154             Carp::croak("Unable to connect to GetCurrentProcess.");
155            
156             $call->Call();
157             }
158             }
159            
160            
161             =head2 C
162            
163             This expects a pointer to an ACL and an C value (i.e.
164             C<'AclSizeInformation'> or C<'AclRevisionInformation'>). It returns the
165             approriate data for the C value (the C in the
166             case of C, the C, C, and
167             C in the case of C).
168            
169             =cut
170            
171             {
172             my $call;
173             sub GetAclInformation {
174             my($pAcl, $dwAclInformationClass) = @_;
175            
176             $call ||= Win32::API->new('advapi32',
177             'GetAclInformation', [qw(I P I I)], 'I') or
178             Carp::croak("Unable to connect to GetAclInformation.");
179            
180             my $structures = {
181             AclRevisionInformation => 'V',
182             AclSizeInformation => 'VVV'
183             };
184            
185             my($pAclInformation) = (pack($structures->{$dwAclInformationClass}));
186            
187             $call->Call($pAcl, $pAclInformation, length($pAclInformation),
188             &Win32::Security::ACL_INFORMATION_CLASS->build_mask($dwAclInformationClass)) or
189             Carp::croak(&_format_error('GetAclInformation'));
190            
191             my(@retvals) = unpack($structures->{$dwAclInformationClass}, $pAclInformation);
192            
193             return(@retvals);
194             }
195             }
196            
197            
198             =head2 C
199            
200             This accepts a pointer to a SID as an integer and returns the length.
201            
202             =cut
203            
204             {
205             my $call;
206             sub GetLengthSid {
207             my($pSid) = @_;
208            
209             $call ||= Win32::API->new('advapi32',
210             'GetLengthSid', [qw(I)], 'I') or
211             Carp::croak("Unable to connect to GetLengthSid.");
212            
213             return $call->Call($pSid);
214             }
215             }
216            
217            
218             =head2 C
219            
220             This expects an object name (i.e. a path to a file, registry key, etc.), an
221             object type (i.e. C<'SE_FILE_OBJECT'>), and a C mask (i.e.
222             C<'OWNER_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION'>). It returns pointers
223             (as integers) to C, C, C, C, and the
224             C. Some of these may be null pointers.
225            
226             =cut
227            
228             {
229             my $call;
230             sub GetNamedSecurityInfo {
231             my($pObjectName, $ObjectType, $SecurityInfo) = @_;
232            
233             $call ||= Win32::API->new('advapi32',
234             'GetNamedSecurityInfo', [qw(P I I P P P P P)], 'I') or
235             Carp::croak("Unable to connect to GetNamedSecurityInfo.");
236            
237             $ObjectType = &Win32::Security::SE_OBJECT_TYPE->build_mask($ObjectType);
238             $SecurityInfo = &Win32::Security::SECURITY_INFORMATION->build_mask($SecurityInfo);
239            
240             my($ppsidOwner, $ppsidGroup, $ppDacl, $ppSacl, $ppSecurityDescriptor) = ("\0"x4) x 5;
241            
242             my $retval = $call->Call($pObjectName, int($ObjectType),
243             $SecurityInfo, $ppsidOwner, $ppsidGroup, $ppDacl, $ppSacl, $ppSecurityDescriptor);
244            
245             $retval and Carp::croak(&_format_error('GetNamedSecurityInfo', $retval));
246            
247             foreach ($ppsidOwner, $ppsidGroup, $ppDacl, $ppSacl, $ppSecurityDescriptor) {
248             $_ = unpack("V", $_);
249             }
250            
251             return($ppsidOwner, $ppsidGroup, $ppDacl, $ppSacl, $ppSecurityDescriptor);
252             }
253             }
254            
255            
256             =head2 C
257            
258             This expects a pointer to a C. It returns the
259             C form for the
260             C mask.
261            
262             =cut
263            
264             {
265             my $call;
266             sub GetSecurityDescriptorControl {
267             my($pSecurityDescriptor) = @_;
268            
269             $call ||= Win32::API->new('advapi32',
270             'GetSecurityDescriptorControl', [qw(I P P)], 'I') or
271             Carp::croak("Unable to connect to GetSecurityDescriptorControl.");
272            
273             my($pControl, $lpdwRevision) = ("\0"x2, "\0"x4);
274            
275             $call->Call($pSecurityDescriptor, $pControl, $lpdwRevision) or
276             Carp::croak(&_format_error('GetSecurityDescriptorControl'));
277            
278             $pControl = &Win32::Security::SECURITY_DESCRIPTOR_CONTROL->break_mask(unpack("S", $pControl));
279             $lpdwRevision = unpack("V", $lpdwRevision);
280            
281             return($pControl, $lpdwRevision);
282             }
283             }
284            
285            
286             =head2 C
287            
288             Calls C on the passed pointer. C is
289             optional - if omitted, revision 1 is used. Dies if the call fails.
290            
291             =cut
292            
293             {
294             my $call;
295             sub InitializeSecurityDescriptor {
296             my($pSecurityDescriptor, $dwRevision) = @_;
297            
298             $dwRevision = 1 unless defined $dwRevision;
299            
300             $call ||= Win32::API->new('advapi32',
301             'InitializeSecurityDescriptor', [qw(I I)], 'I') or
302             Carp::croak("Unable to connect to InitializeSecurityDescriptor.");
303            
304             $call->Call($pSecurityDescriptor, $dwRevision) or Carp::croak("Unable to InitializeSecurityDescriptor $pSecurityDescriptor.");
305             }
306             }
307            
308            
309             =head2 C
310            
311             Calls C with the passed C and C. It returns the
312             pointer, but dies if a null pointer is returned from the call. The C
313             parameter can be passed as either an integer or as legal C.
314            
315             =cut
316            
317             {
318             my $call;
319             sub LocalAlloc {
320             my($uFlags, $uBytes) = @_;
321            
322             $uFlags = &Win32::Security::LMEM_FLAGS->build_mask($uFlags);
323            
324             $call ||= Win32::API->new('kernel32',
325             'LocalAlloc', [qw(I I)], 'I') or
326             Carp::croak("Unable to connect to LocalAlloc.");
327            
328             my $ptr = $call->Call($uFlags, $uBytes);
329             $ptr or Carp::croak("Unable to LocalAlloc $uBytes.");
330             return $ptr;
331             }
332             }
333            
334            
335             =head2 C
336            
337             Calls C on the passed pointer. The passed pointer should be in the
338             form of a Perl integer.
339            
340             =cut
341            
342             {
343             my $call;
344             sub LocalFree {
345             my($pObject) = @_;
346            
347             $call ||= Win32::API->new('kernel32',
348             'LocalFree', [qw(I)], 'I') or
349             Carp::croak("Unable to connect to LocalFree.");
350            
351             $call->Call($pObject);
352             }
353             }
354            
355            
356             =head2 C
357            
358             Pass C (undef permitted) and a privilege C (i.e.
359             C). Returns the C.
360            
361             =cut
362            
363             {
364             my $call;
365             sub LookupPrivilegeValue {
366             my($lpSystemName, $lpName) = @_;
367            
368             my($lpLuid) = ("\0"x8);
369            
370             $call ||= Win32::API->new('advapi32',
371             'LookupPrivilegeValue', [qw(P P P)], 'I') or
372             Carp::croak("Unable to connect to LookupPrivilegeValue.");
373            
374             $call->Call($lpSystemName, $lpName, $lpLuid) or
375             Carp::croak(&_format_error('LookupPrivilegeValue'));
376            
377             return $lpLuid;
378             }
379             }
380            
381            
382             =head2 C
383            
384             Pass C and C (C). Returns
385             C.
386            
387             =cut
388            
389             {
390             my $call;
391             sub OpenProcessToken {
392             my($ProcessHandle, $DesiredAccess) = @_;
393            
394             $DesiredAccess = &Win32::Security::TokenRights->build_mask($DesiredAccess);
395            
396             my($TokenHandle) = ("\0"x4);
397            
398             $call ||= Win32::API->new('advapi32',
399             'OpenProcessToken', [qw(I I P)], 'I') or
400             Carp::croak("Unable to connect to OpenProcessToken.");
401            
402             $call->Call($ProcessHandle, $DesiredAccess, $TokenHandle) or
403             Carp::croak(&_format_error('OpenProcessToken'));
404            
405             $TokenHandle = unpack("V", $TokenHandle);
406            
407             return $TokenHandle;
408             }
409             }
410            
411            
412             =head2 C
413            
414             Pass C, C, and C. Useful for
415             setting permissions without propagating inheritable ACEs.
416            
417             =cut
418            
419             {
420             my $call;
421             my $os2k;
422             sub SetFileSecurity {
423             my($pFileName, $SecurityInfo, $pSecurityDescriptor) = @_;
424            
425             $call ||= Win32::API->new('advapi32',
426             'SetFileSecurity', [qw(P I I)], 'I') or
427             Carp::croak("Unable to connect to SetFileSecurity.");
428            
429             $SecurityInfo = &Win32::Security::SECURITY_INFORMATION->build_mask($SecurityInfo);
430            
431             unless (defined($os2k)) {
432             my(@osver) = Win32::GetOSVersion();
433             $os2k = ($osver[4] == 2 && $osver[1] >= 5);
434             }
435            
436             if (!$os2k && scalar(grep(/PROTECTED/, keys %{&Win32::Security::SECURITY_INFORMATION->break_mask($SecurityInfo)})) ) {
437             Carp::croak("Use of PROTECTED SECURITY_INFORMATION constant on a non Win2K or greater OS.");
438             }
439            
440             $call->Call($pFileName, $SecurityInfo, $pSecurityDescriptor) or
441             Carp::croak(&_format_error('SetFileSecurity'));
442             }
443             }
444            
445            
446            
447             =head2 C
448            
449             This expects an object name (i.e. a path to a file, registry key, etc.), an
450             object type (i.e. C<'SE_FILE_OBJECT'>), and a C mask (i.e.
451             C<'OWNER_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION'>), and pointers (as
452             integers) to C, C, C, and C. These may be null
453             pointers if they are not referenced in the C mask.
454            
455             =cut
456            
457             {
458             my $call;
459             my $os2k;
460             sub SetNamedSecurityInfo {
461             my($pObjectName, $ObjectType, $SecurityInfo, $psidOwner, $psidGroup, $pDacl, $pSacl) = @_;
462            
463             $call ||= Win32::API->new('advapi32',
464             'SetNamedSecurityInfo', [qw(P I I P P P P)], 'I') or
465             Carp::croak("Unable to connect to SetNamedSecurityInfo.");
466            
467             $ObjectType = &Win32::Security::SE_OBJECT_TYPE->build_mask($ObjectType);
468             $SecurityInfo = &Win32::Security::SECURITY_INFORMATION->build_mask($SecurityInfo);
469            
470             unless (defined($os2k)) {
471             my(@osver) = Win32::GetOSVersion();
472             $os2k = ($osver[4] == 2 && $osver[1] >= 5);
473             }
474            
475             if (!$os2k && scalar(grep(/PROTECTED/, keys %{&Win32::Security::SECURITY_INFORMATION->break_mask($SecurityInfo)})) ) {
476             Carp::croak("Use of PROTECTED SECURITY_INFORMATION constant on a non Win2K or greater OS.");
477             }
478            
479             my $retval;
480             {
481             local $^W = 0; #Turn off warnings about uninitialized values . . .
482             $retval = $call->Call($pObjectName, int($ObjectType),
483             $SecurityInfo, $psidOwner, $psidGroup, $pDacl, $pSacl);
484             }
485            
486             $retval and Carp::croak(&_format_error('SetNamedSecurityInfo', $retval));
487             }
488             }
489            
490            
491             =head2 C
492            
493             Calls C. Expects a pointer to a
494             C, C, C, and C. Dies if
495             the call fails.
496            
497             =cut
498            
499             {
500             my $call;
501             sub SetSecurityDescriptorDacl {
502             my($pSecurityDescriptor, $bDaclPresent, $pDacl, $bDaclDefaulted) = @_;
503            
504             $call ||= Win32::API->new('advapi32',
505             'SetSecurityDescriptorDacl', [qw(I I I I)], 'I') or
506             Carp::croak("Unable to connect to SetSecurityDescriptorDacl.");
507            
508             $call->Call($pSecurityDescriptor, $bDaclPresent, $pDacl, $bDaclDefaulted) or Carp::croak("Unable to SetSecurityDescriptorDacl.");
509             }
510             }
511            
512            
513            
514             sub _format_error {
515             my($func, $retval) = @_;
516            
517             (my $msg = $func.": ".Win32::FormatMessage($retval || Win32::GetLastError()))
518             =~ s/[\r\n]+$//;
519             return $msg;
520             }
521            
522            
523            
524             package Win32::Security;
525            
526             =head1 C Objects
527            
528             The objects are accessed via class methods on C. The
529             C objects are created by the first call and lexically cached.
530            
531             =cut
532            
533             =head2 &Win32::Security::SE_OBJECT_TYPE
534            
535             Win32 constants for C, along with the following aliases:
536            
537             =over 4
538            
539             =item *
540            
541             C (C)
542            
543             =item *
544            
545             C (C)
546            
547             =item *
548            
549             C (C)
550            
551             =item *
552            
553             C (C)
554            
555             =item *
556            
557             C (C)
558            
559             =item *
560            
561             C (C)
562            
563             =back
564            
565             =cut
566            
567             {
568             my $cache;
569             sub SE_OBJECT_TYPE {
570             $cache ||= Data::BitMask->new(
571             SE_UNKNOWN_OBJECT_TYPE => 0,
572             SE_FILE_OBJECT => 1,
573             SE_SERVICE => 2,
574             SE_PRINTER => 3,
575             SE_REGISTRY_KEY => 4,
576             SE_LMSHARE => 5,
577             SE_KERNEL_OBJECT => 6,
578             SE_WINDOW_OBJECT => 7,
579             SE_DS_OBJECT => 8,
580             SE_DS_OBJECT_ALL => 9,
581             SE_PROVIDER_DEFINED_OBJECT => 10,
582             FILE => 1,
583             SERVICE => 2,
584             PRINTER => 3,
585             REG => 4,
586             REGISTRY => 4,
587             SHARE => 5,
588             );
589             }
590             }
591            
592             =head2 &Win32::Security::SECURITY_INFORMATION
593            
594             =cut
595            
596             {
597             my $cache;
598             sub SECURITY_INFORMATION {
599             $cache ||= Data::BitMask->new(
600             OWNER_SECURITY_INFORMATION => 0x1,
601             GROUP_SECURITY_INFORMATION => 0x2,
602             DACL_SECURITY_INFORMATION => 0x4,
603             SACL_SECURITY_INFORMATION => 0x8,
604             UNPROTECTED_SACL_SECURITY_INFORMATION => 0x10000000,
605             UNPROTECTED_DACL_SECURITY_INFORMATION => 0x20000000,
606             PROTECTED_SACL_SECURITY_INFORMATION => 0x40000000,
607             PROTECTED_DACL_SECURITY_INFORMATION => 0x80000000,
608             );
609             }
610             }
611            
612             =head2 &Win32::Security::SECURITY_DESCRIPTOR_CONTROL
613            
614             =cut
615            
616             {
617             my $cache;
618             sub SECURITY_DESCRIPTOR_CONTROL {
619             $cache ||= Data::BitMask->new(
620             SE_OWNER_DEFAULTED => 0x0001,
621             SE_GROUP_DEFAULTED => 0x0002,
622             SE_DACL_PRESENT => 0x0004,
623             SE_DACL_DEFAULTED => 0x0008,
624             SE_SACL_PRESENT => 0x0010,
625             SE_SACL_DEFAULTED => 0x0020,
626             SE_DACL_AUTO_INHERIT_REQ => 0x0100,
627             SE_SACL_AUTO_INHERIT_REQ => 0x0200,
628             SE_DACL_AUTO_INHERITED => 0x0400,
629             SE_SACL_AUTO_INHERITED => 0x0800,
630             SE_DACL_PROTECTED => 0x1000,
631             SE_SACL_PROTECTED => 0x2000,
632             SE_SELF_RELATIVE => 0x8000,
633             );
634             }
635             }
636            
637             =head2 &Win32::Security::ACL_INFORMATION_CLASS
638            
639             =cut
640            
641             {
642             my $cache;
643             sub ACL_INFORMATION_CLASS {
644             $cache ||= Data::BitMask->new(
645             AclRevisionInformation => 1,
646             AclSizeInformation => 2,
647             );
648             }
649             }
650            
651             =head2 &Win32::Security::TokenRights
652            
653             =cut
654            
655             {
656             my $cache;
657             sub TokenRights {
658             unless ($cache) {
659             $cache = Data::BitMask->new(
660             TOKEN_ASSIGN_PRIMARY => 0x00000001,
661             TOKEN_DUPLICATE => 0x00000002,
662             TOKEN_IMPERSONATE => 0x00000004,
663             TOKEN_QUERY => 0x00000008,
664             TOKEN_QUERY_SOURCE => 0x00000010,
665             TOKEN_ADJUST_PRIVILEGES => 0x00000020,
666             TOKEN_ADJUST_GROUPS => 0x00000040,
667             TOKEN_ADJUST_DEFAULT => 0x00000080,
668             TOKEN_ADJUST_SESSIONID => 0x00000100,
669            
670             DELETE => 0x00010000,
671             READ_CONTROL => 0x00020000,
672             WRITE_DAC => 0x00040000,
673             WRITE_OWNER => 0x00080000,
674            
675             ACCESS_SYSTEM_SECURITY => 0x01000000,
676            
677             STANDARD_RIGHTS_READ => 0x00020000,
678             STANDARD_RIGHTS_WRITE => 0x00020000,
679             STANDARD_RIGHTS_EXECUTE => 0x00020000,
680             );
681            
682             $cache->add_constants(
683             TOKEN_EXECUTE => $cache->build_mask('STANDARD_RIGHTS_EXECUTE TOKEN_IMPERSONATE'),
684             TOKEN_READ => $cache->build_mask('STANDARD_RIGHTS_READ TOKEN_QUERY'),
685             TOKEN_WRITE => $cache->build_mask('STANDARD_RIGHTS_WRITE TOKEN_ADJUST_PRIVILEGES TOKEN_ADJUST_GROUPS TOKEN_ADJUST_DEFAULT'),
686             TOKEN_ALL_ACCESS => $cache->build_mask('ACCESS_SYSTEM_SECURITY') | 0x000F01FF,
687             );
688             }
689             return $cache;
690             }
691             }
692            
693            
694             =head2 &Win32::Security::LUID_ATTRIBUTES
695            
696             =cut
697            
698             {
699             my $cache;
700             sub LUID_ATTRIBUTES {
701             $cache ||= Data::BitMask->new(
702             SE_PRIVILEGE_USED_FOR_ACCESS => 0x0000,
703             SE_PRIVILEGE_ENABLED_BY_DEFAULT => 0x0001,
704             SE_PRIVILEGE_ENABLED => 0x0002,
705             );
706             }
707             }
708            
709            
710             =head2 &Win32::Security::LMEM_FLAGS
711            
712             =cut
713            
714             {
715             my $cache;
716             sub LMEM_FLAGS {
717             unless ($cache) {
718            
719             $cache = Data::BitMask->new(
720             LMEM_FIXED => 0x0000,
721             LMEM_MOVEABLE => 0x0002,
722             LMEM_NOCOMPACT => 0x0010,
723             LMEM_NODISCARD => 0x0020,
724             LMEM_ZEROINIT => 0x0040,
725             LMEM_MODIFY => 0x0080,
726             LMEM_DISCARDABLE => 0x0F00,
727             LMEM_VALID_FLAGS => 0x0F72,
728             LMEM_INVALID_HANDLE => 0x8000,
729             );
730            
731             $cache->add_constants(
732             LHND => $cache->build_mask('LMEM_MOVEABLE LMEM_ZEROINIT'),
733             LPTR => $cache->build_mask('LMEM_FIXED LMEM_ZEROINIT'),
734             NONZEROLHND => $cache->build_mask('LMEM_MOVEABLE'),
735             NONZEROLPTR => $cache->build_mask('LMEM_FIXED'),
736             );
737             }
738             return $cache;
739             }
740             }
741            
742            
743             =head1 AUTHOR
744            
745             Toby Ovod-Everett, toby@ovod-everett.org
746            
747             =cut
748            
749             1;