Florian Poujade Ответов: 3

Read или lseek странно работают в программе notesearch.c, программа не печатает заметки


Эй все, я читаю книгу Джона Эриксона "искусство эксплуатации", и там есть программа, которая не работает, и даже после отладки и тестирования нескольких вещей я, кажется, не могу найти решение...

Эта программа должна искать заметки, сделанные с помощью другой программы, notetaker, в файле по адресу /var/notes. Дело в том, что он не печатает заметку, а только [DEBUG] линия говорит, что он нашел записку... Вот код souce, он документирован и довольно прост для понимания:
https://github.com/florian-pjde/Art_of_exploitation/blob/master/notesearch_debug.c

Вот ожидаемый результат от книги:

reader@hacking:~/booksrc $ ./notesearch
[DEBUG] found a 34 byte note for user id 999
this is a test of multiuser notes
-------[ end of note data ]-------

и что я получаю:

florian@parrot:~/Documents/Art_of_Exploitation/book_code $ ./notesearch
[DEBUG] found a 34 byte note for user id 1000
-------[ end of note data ]-------

Проблема в том, что note_buffer пуст, так что, очевидно, печатать нечего, похоже, исходит из любой из этих двух строк:

read(fd, note_buffer, note_length);
(строка 45)

fd-это идентификатор файла для /var/notes, note_buffer-массив символов длиной 100, а note_length-реальная длина заметки, что корректно выполняется другой функцией. Таким образом, вполне возможно, что read ничего не записывает из файла в note_buffer. Или,
lseek(fd, length * -1, SEEK_CUR); // rewind file reading by length bytes
(строка 77)

fd снова является идентификатором файла, length-длиной заметки (мы находимся в другой функции), а SEEK_CUR перематывается с текущей позиции из того, что я понял. Возможно, он не перематывается правильно, поэтому чтение не работает.

Что я уже пробовал:

Я поместил несколько строк [DEBUG] в программу и снова скомпилировал, кажется, длина правильная, но note_buffer равен нулю, поэтому либо он не читает файл в нее, либо lseek не перематывает правильно, чтобы он читал нужное место, я не вижу другого способа... Вот вывод программы с дополнительными строками [DEBUG] :

[DEBUG] length is 34
[DEBUG] found a 34 byte note for user id 1000
[DEBUG] note_length is 34
[DEBUG] read(fd, note_buffer, note_length) results 0
[DEBUG] note_buffer is
[DEBUG] note_buffer[0] is
[DEBUG] note_buffer[note_length] is
-------[ end of note data ]-------


Я искал на Stackoverflow и здесь об этом но кажется что ни один другой читатель не счел нужным понять почему это не работает :/

Может быть, у кого-то есть другой способ попытаться выяснить, что происходит, или начало решения? Я счастлив делать все сам, но иногда я ограничен опытом, поэтому я и пришел к вам :)

3 Ответов

Рейтинг:
2

OriginalGriff

Я не собираюсь заходить на случайный сайт и скачивать случайный исходный код! Так что все будет зависеть от тебя.
К счастью, у вас есть инструмент, который поможет вам выяснить, что происходит: отладчик. Как вы его используете, зависит от вашей системы компилятора, но быстрый поиск в Google имени вашей IDE и "отладчика" должен дать вам необходимую информацию.

Поставьте точку останова на обеих линиях и посмотрите, в какую она попадет первой.
Если он попадает в чтение, хорошо - один шаг его и посмотрите на note_buffer и длину и посмотрите, что именно он читает. Если он выглядит хорошо, то приложение будет продолжать работать до тех пор, пока не попадет в другой. Что же происходит? Буфер заметок все еще действителен?

Затем посмотрите на свой код и на свои данные и определите, что должно произойти вручную. Затем по одному шагу в каждой строке проверяйте, что то, что вы ожидали, произойдет именно так, как и произошло. Когда это не так, тогда у вас есть проблема, и вы можете вернуться назад (или запустить ее снова и посмотреть более внимательно), чтобы выяснить, почему.

Извините, но мы не можем сделать это за вас - пришло время вам освоить новый (и очень, очень полезный) навык: отладку!


Florian Poujade

Привет, спасибо за ответ :) Я действительно пытался отладить его с помощью gdb, но столкнулся с проблемами разрешения. Перед запуском программы я должен сделать chown root:root и chmod u+s, чтобы она могла получить доступ к /var/notes, а затем запустить ее от своего имени. Но когда я запускаю его с gdb как сам, он не может получить доступ к /var/notes... И запуск его с помощью sudo не распознает заметки, оставленные как root ...

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

Рейтинг:
10

Florian Poujade

Наконец я нашел проблему с lseek, он, по-видимому , принимает переменную off_t в качестве параметра, а не int для смещения, поэтому мне пришлось создать переменную off_t и сохранить там длину*-1, и это сделало трюк! К сожалению, были и другие проблемы, которые возникли впоследствии, так как он напечатал только первую заметку, но это была базовая отладка, и я заставил ее работать :)

Вот как выглядел мой код для lseek:

off_t cur_p;
cur_p = length*-1;
lseek(fd,cur_p,SEEK_CUR);


Рейтинг:
0

Jochen Arndt

При этом выводится возвращаемое значение параметра read() функция:

[DEBUG] read(fd, note_buffer, note_length) results 0
Это означает, что конец файла был достигнут:
При успешном выполнении возвращается количество прочитанных байтов (ноль указывает на конец файла),
Так что проблема, вероятно, связана с lseek() вызов. Поскольку я не видел ошибки в вашем коде, все, что я могу предложить, - это проверить возвращаемое значение этого вызова (результирующее смещение или -1 при ошибках) или сохранить текущую позицию файла в другой переменной (когда начинаете читать длину) и использовать ее позже с помощью SEEK_SET.

Но в вашем коде есть логическая ошибка:
find_user_note() всегда возвращает успех, даже если соответствующий идентификатор не найден. В этом случае возвращается последняя запись.

int find_user_note(int fd, int user_uid) {
    int note_uid=-1;
    unsigned char byte;
    int length;
    
    while(note_uid != user_uid) {  // loop until a note for user_uid is found
        if(read(fd, &note_uid, 4) != 4) // read the uid data
            return -1; // if 4 bytes aren't read, return end of file code
        if(read(fd, &byte, 1) != 1) // read the newline separator
            return -1;
        
        byte = length = 0;
        while(byte != '\n') {  // figure out how many bytes to the end of line
            if(read(fd, &byte, 1) != 1) // read a single byte
                return -1;     // if byte isn't read, return end of file code
            length++;
        }
    }
    // Must check for matching ID here!
    // Otherwise the last record is returned as matching
    if (note_uid != user_uid)
        return -1;
    printf("[DEBUG] length is %d\n", length);

    if (lseek(fd, length*(-1), SEEK_CUR) < 0) // rewind file reading by length bytes
    {
        printf("[DEBUG] seek error %d\n", errno);
        return -1;
    }
    
    printf("[DEBUG] found a %d byte note for user id %d\n", length, note_uid);
    // Commented this:
    //char note_buffer[100];
    return length;
}


Florian Poujade

Спасибо за кровянистые выделения, что! Я внес изменения в код с дополнительной проверкой на uid и [DEBUG] и снова скомпилировал его. Однако, несмотря на то, что он решил то, что вы заметили, seek [DEBUG] не печатается, поэтому проблема не возникает из-за этого... Я немного растерялся там, ха-ха! find_user_note() работает так, как и должно быть, поскольку правильная длина найдена и передана, и она, по-видимому, перематывается без ошибок, но каким-то образом читает конец файла после этого...

Jochen Arndt

Как уже отмечалось, для меня это выглядит прекрасно. Таким образом, все, что можно сделать, - это отладка (с помощью отладчика или путем печати значений, как это уже сделано).

Сначала я бы вставил несколько вызовов lseek(fd, 0, SEEK_CUR) и распечатал результаты, чтобы убедиться, что положение файла соответствует ожидаемому.

Также еще раз: read() возвращает ноль, когда в конце файла (или переданная длина равна нулю).

Florian Poujade

Спасибо, сделаю!

Florian Poujade

Найдя проблему, я написал решение ниже на случай, если оно вас заинтересует :)

Jochen Arndt

Прекрасно, что Вы наконец-то решили эту проблему, и спасибо вам за ваши отзывы.

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

Однако, согласно вашему решению, это также должно работать:
lseek(fd, (off_t)(-length), SEEK_CUR);

Florian Poujade

Так и должно быть, да. Моя честная ошибка заключается в том, что я недостаточно внимательно прочитал документацию lseek, чтобы понять, что off_t на самом деле является переменным типом!