How to reference memory address returned from Win32::API call

C

cyl

Below is my code to enumerate services. The returned buffer $lpServices
is an array of the structure ENUM_SERVICE_STATUS. After unpacking it, I
got the memory address of the variable lpServiceName which points to
somewhere in the buffer $lpServices. My problem is how do I reference
the address? Unlike a C program, I cannot reference that value
directly. What should I do?

typedef struct _SERVICE_STATUS {
DWORD dwServiceType;
DWORD dwCurrentState;
DWORD dwControlsAccepted;
DWORD dwWin32ExitCode;
DWORD dwServiceSpecificExitCode;
DWORD dwCheckPoint;
DWORD dwWaitHint;
} SERVICE_STATUS;

typedef struct _ENUM_SERVICE_STATUS {
LPTSTR lpServiceName;
LPTSTR lpDisplayName;
SERVICE_STATUS ServiceStatus;
} ENUM_SERVICE_STATUS;


---CODE---
use Win32::API;

my $fnOpenSCManager = Win32::API->new('Advapi32.dll', 'OpenSCManager',
'PPI', 'I');
my $fnEnumServicesStatus = Win32::API->new('Advapi32.dll',
'EnumServicesStatus', 'IIIPIPPP', 'I');

my $hSCManager = $fnOpenSCManager->Call(0,0,0x000f003f);

die unless $hSCManager;

my $cbBufSize = 4;
my $pcbBytesNeeded = pack("N",0);
my $lpServicesReturned = pack("N",0);
my $lpResumeHandle = pack("N",0);
my @Services;
@Services[0 .. 36*$cbBufSize] = 0;
my $lpServices = pack("N*",@Services);


my $ret = $fnEnumServicesStatus->Call( $hSCManager,
0x3B, #SERVICE_DRIVER |
SERVICE_WIN32
0x3, #SERVICE_STATE_ALL
$lpServices,
36*$cbBufSize,
$pcbBytesNeeded,
$lpServicesReturned,
$lpResumeHandle
);
 
S

Sisyphus

cyl said:
Below is my code to enumerate services. The returned buffer $lpServices
is an array of the structure ENUM_SERVICE_STATUS. After unpacking it, I
got the memory address of the variable lpServiceName which points to
somewhere in the buffer $lpServices. My problem is how do I reference
the address? Unlike a C program, I cannot reference that value
directly. What should I do?

typedef struct _SERVICE_STATUS {
DWORD dwServiceType;
DWORD dwCurrentState;
DWORD dwControlsAccepted;
DWORD dwWin32ExitCode;
DWORD dwServiceSpecificExitCode;
DWORD dwCheckPoint;
DWORD dwWaitHint;
} SERVICE_STATUS;

typedef struct _ENUM_SERVICE_STATUS {
LPTSTR lpServiceName;
LPTSTR lpDisplayName;
SERVICE_STATUS ServiceStatus;
} ENUM_SERVICE_STATUS;


---CODE---
use Win32::API;

my $fnOpenSCManager = Win32::API->new('Advapi32.dll', 'OpenSCManager',
'PPI', 'I');
my $fnEnumServicesStatus = Win32::API->new('Advapi32.dll',
'EnumServicesStatus', 'IIIPIPPP', 'I');

my $hSCManager = $fnOpenSCManager->Call(0,0,0x000f003f);

die unless $hSCManager;

my $cbBufSize = 4;
my $pcbBytesNeeded = pack("N",0);
my $lpServicesReturned = pack("N",0);
my $lpResumeHandle = pack("N",0);
my @Services;
@Services[0 .. 36*$cbBufSize] = 0;
my $lpServices = pack("N*",@Services);

Having a bit of trouble following this. You can create your SERVICE_STATUS
struct as:

my $service_status_struct = pack('LLLLLLL', 0, 0, 0, 0, 0, 0, 0);

If any of those values need to be initialised to something other than zero,
then replace the zero with the appropriate value - but if the values are
going to be set by the Call() then zeroes will be fine.

You need to create an lpServiceName buffer (to a size that is at least as
long as the string it will be set to):

my $lpServiceName_buffer = " " x $size_of_lpServiceName; # or longer

Similarly, for $lpDisplayName:

my $lpDisplayName = " " x $size_of_lpDisplayName; # or longer

You can now create the $lpServices variable as follows:

my $lpServices = pack('ppP', $lpServiceName, $lpDisplayName,
$service_status_struct);

(I *think* that 'P' is the correct way to pack $service_status_struct.
That's what 'perldoc -f pack' suggests to me, but I've no experience in
actually using 'P'.)

Then after the Call() you should be able to get at the values you want with:

($lpServiceName, $lpDisplayName, $service_status_struct) = unpack ('ppP',
$lpServices);

If you want to then get at the values in $service_status_struct, you'll need
to also:

@values = unpack('LLLLLL', $service_status_struct);
my $ret = $fnEnumServicesStatus->Call( $hSCManager,
0x3B, #SERVICE_DRIVER |
SERVICE_WIN32
0x3, #SERVICE_STATE_ALL
$lpServices,
36*$cbBufSize,
$pcbBytesNeeded,
$lpServicesReturned,
$lpResumeHandle
);

Cheers,
Rob
 
S

Sisyphus

Sisyphus said:

I misread that as stating that $lpServices is an ENUM_SERVICE_STATUS
structure.
Consequently what I wrote is possibly not as helpful as I had imagined.
You can create your SERVICE_STATUS
struct as:

my $service_status_struct = pack('LLLLLLL', 0, 0, 0, 0, 0, 0, 0);

If any of those values need to be initialised to something other than zero,
then replace the zero with the appropriate value - but if the values are
going to be set by the Call() then zeroes will be fine.

You need to create an lpServiceName buffer (to a size that is at least as
long as the string it will be set to):

my $lpServiceName_buffer = " " x $size_of_lpServiceName; # or longer

Similarly, for $lpDisplayName:

my $lpDisplayName = " " x $size_of_lpDisplayName; # or longer

You can now create the $lpServices variable as follows:

my $lpServices = pack('ppP', $lpServiceName, $lpDisplayName,
$service_status_struct);

Having had a chance to play with the 'P' template a little, it now seems to
me that you need to specify the size of the structure - hence 'ppP' should
be changed to 'ppP28'.

..
..
Then after the Call() you should be able to get at the values you want with:

($lpServiceName, $lpDisplayName, $service_status_struct) = unpack ('ppP',
$lpServices);

Again, the template would need to be 'ppP28'.
If you want to then get at the values in $service_status_struct, you'll need
to also:

@values = unpack('LLLLLL', $service_status_struct);

The template is 7 'L's - not 6, as appears there.

Cheers,
Rob
 
C

cyl

Sisyphus ¼g¹D¡G
me that you need to specify the size of the structure - hence 'ppP' should
be changed to 'ppP28'.

doesn't work for me. perl crashed if I use
$lpServices = pack('ppP28', $lpServiceName,
$lpDisplayName,$service_status);
as the parameter.

However, I did succeed with the following

my $lpServiceName = " "x128;
my $lpDisplayName = " "x128;
my $lpServices = pack('C128C128I7', $lpServiceName,
$lpDisplayName,0,0,0,0,0,0,0);

while(1){
my $ret = $fnEnumServicesStatus->Call( $hSCManager,
0x3B,
0x3,
$lpServices,
283,
$pcbBytesNeeded,
$lpServicesReturned,
$lpResumeHandle
);
my $BytesNeeded = unpack('I',$pcbBytesNeeded);
my $NumOfServices = unpack('I',$lpServicesReturned);
my $ResumeHandle = unpack('I',$lpResumeHandle);
my @x = unpack('ppI7' x $NumOfServices,$lpServices);
for my $i (0 .. $NumOfServices){
print "$x[$i*9]\n";
}
last if (0 == $ResumeHandle);
}
__END__

I don't know why. I tried dozens of ways to pack the $lpServices
parameter and finally got it to work. The pack thing is really
confusing to me.

I considered to pack this way before but never tried it (I should have)
because I thought to pack this way means your returned buffer looked
like this
servive_name1{128bytes},display_name1{128bytes},{28bytes},
servive_name2{128bytes},display_name2{128bytes},{28bytes},
....

but if you dump the $lpServices you will find that it look like this
pointer_to_service_name1{4bytes},pointer_to_display_name1{4bytes},{28bytes},
pointer_to_service_name2{4bytes},pointer_to_display_name2{4bytes},{28bytes},
.... {variable length of bytes} ...
service_nameN{variable length of bytes}, display_nameN{variable length
of bytes}
service_nameN-1{variable length of bytes}, display_nameN-1{variable
length of bytes}
....
service_name1{variable length of bytes}, display_name1{variable length
of bytes}

Not sure if it's clear enough. If you dump the buffer, you know what I
mean. Very appreciate for the help to motivate the solution anyway.
 
S

Sisyphus

Very appreciate for the help to motivate the solution anyway.

Well ............ I hope there was something there that helped. (Sorry I
wasn't able to provide better assistance :)

Thanks for replying with the solution you finally came up with.

I think that pack/unpack are very useful and powerful tools. Like you, I
find they often stretch me to the limit .... we're not alone in that :)

Cheers,
Rob
 
C

cyl

cyl ¼g¹D¡G

my $lpServiceName = " "x128;
my $lpDisplayName = " "x128;
my $lpServices = pack('C128C128I7', $lpServiceName,
$lpDisplayName,0,0,0,0,0,0,0);
I found it's unnecessary to make it that complicated. Simply use

my $lpServices = pack('C283',' 'x283);

and it works.
my @x = unpack('ppI7' x $NumOfServices,$lpServices);
the unpack part is the key
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top