Может ли GDB сделать указатель на функцию, указывающую на другое место?

В Java все находится в форме класса.

Если вы хотите использовать любой объект, тогда у вас есть две фазы:

  1. Объявить
  2. Инициализация

Пример:

  • Объявление: Object a;
  • Инициализация: a=new Object();

То же самое для концепции массива

  • Объявление: Item i[]=new Item[5];
  • Инициализация: i[0]=new Item();

Если вы не дают секцию инициализации, тогда возникает NullpointerException.

26
задан AstroCB 30 August 2014 в 20:44
поделиться

10 ответов

Я следовал в этом посте и в этой презентации и придумал следующий набор команд gdb для OSX с исполняемым файлом x86-64, который можно загрузить с опцией -x, когда присоединение к процессу:

set $s = dyld_stub_rand
set $p = ($s+6+*(int*)($s+2))
call (void*)dlsym((void*)dlopen("myrand.dylib"), "my_rand")
set *(void**)$p = my_rand
c

Магия в команде set $p = .... dyld_stub_rand - это 6-байтовая инструкция перехода. Смещение перехода составляет dyld_stub_rand+2 (4 байта). Это $rip -относительный переход, поэтому добавьте смещение к тому, что было бы $rip в этой точке (сразу после инструкции, dyld_stub_rand+6).

Это указывает на запись таблицы символов, которая должна быть либо реальной rand, либо подпрограммой динамического компоновщика для ее загрузки (если она никогда не вызывалась). Затем он заменяется на my_rand.

Иногда GDB извлекает dyld_stub_rand из libSystem или другой разделяемой библиотеки, если это происходит, сначала выгрузите их с помощью remove-symbol-file перед выполнением других команд.

12
ответ дан Alex B 30 August 2014 в 20:44
поделиться
  • 1
    @supercat, я don' t видят то, что Вы - достижение, но правила просты. RHS вычисляется в 32 битах int. Результат умножения, кажется, 0xFFFE0001 так переполнение умножения, и поведение не определено. Это - хороший пример, почему никогда не нужно использовать узкие типы для арифметики. При использовании size_t эта проблема wouldn' t происходят. – Jens Gustedt 22 April 2015 в 07:10
  • 2
    @supercat, я don' t видят то, что Вы - достижение, но правила просты. RHS вычисляется в 32 битах int. Результат умножения, кажется, 0xFFFE0001 так переполнение умножения, и поведение не определено. Это - хороший пример, почему никогда не нужно использовать узкие типы для арифметики. При использовании size_t эта проблема wouldn' t происходят. – Jens Gustedt 22 April 2015 в 07:10
  • 3
    @supercat, я don' t видят то, что Вы - достижение, но правила просты. RHS вычисляется в 32 битах int. Результат умножения, кажется, 0xFFFE0001 так переполнение умножения, и поведение не определено. Это - хороший пример, почему никогда не нужно использовать узкие типы для арифметики. При использовании size_t эта проблема wouldn' t происходят. – Jens Gustedt 22 April 2015 в 07:10
  • 4
    @supercat, я don' t видят то, что Вы - достижение, но правила просты. RHS вычисляется в 32 битах int. Результат умножения, кажется, 0xFFFE0001 так переполнение умножения, и поведение не определено. Это - хороший пример, почему никогда не нужно использовать узкие типы для арифметики. При использовании size_t эта проблема wouldn' t происходят. – Jens Gustedt 22 April 2015 в 07:10
  • 5
    @supercat, я don' t видят то, что Вы - достижение, но правила просты. RHS вычисляется в 32 битах int. Результат умножения, кажется, 0xFFFE0001 так переполнение умножения, и поведение не определено. Это - хороший пример, почему никогда не нужно использовать узкие типы для арифметики. При использовании size_t эта проблема wouldn' t происходят. – Jens Gustedt 22 April 2015 в 07:10

Этот вопрос заинтриговал меня, поэтому я провел небольшое исследование. То, что вы ищете, это « dll инъекция ». Вы пишете функцию для замены некоторой библиотечной функции, помещаете ее в .so и говорите ld предварительно загрузить вашу dll. Я только что попробовал, и это работало отлично! Я понимаю, что на самом деле это не отвечает на ваш вопрос относительно gdb, но я думаю, что он предлагает эффективный обходной путь.

Для решения только для GDB, см. Мое другое решение.


// -*- compile-command: "gcc -Wall -ggdb -o test test.c"; -*-
// test.c

#include "stdio.h"
#include "stdlib.h"

int main(int argc, char** argv)
{
    //should print a fairly random number...
    printf("Super random number: %d\n", rand());

    return 0;
}

/ -*- compile-command: "gcc -Wall -fPIC -shared my_rand.c -o my_rand.so"; -*-
//my_rand.c

int rand(void)
{
    return 42;
}

скомпилируйте оба файла, затем выполните: LD_PRELOAD="./my_rand.so" ./test

Super random number: 42

9
ответ дан Community 30 August 2014 в 20:44
поделиться
  • 1
    @JensGustedt: До недавнего времени большинство встроенных систем использовало 16-разрядное int. Если Вы писали код такой системы для обновления 16-разрядного two' s-дополнение или one' контрольная сумма s-дополнения после записи повторного значения к потоку, умножаясь два uint16_t была бы естественным способом сделать это. Далее, до самого последнего времени, 99,9% компиляторов C для 32-разрядных систем привел бы точно к тому же вычислению без любой трудности. В то время как некоторые утверждали бы, что выражение будет лучше записано как 1u*x*x, я расцениваю потребность в последней форме как дефицит в спецификации языка. – supercat 22 April 2015 в 15:04
  • 2
    @JensGustedt: До недавнего времени большинство встроенных систем использовало 16-разрядное int. Если Вы писали код такой системы для обновления 16-разрядного two' s-дополнение или one' контрольная сумма s-дополнения после записи повторного значения к потоку, умножаясь два uint16_t была бы естественным способом сделать это. Далее, до самого последнего времени, 99,9% компиляторов C для 32-разрядных систем привел бы точно к тому же вычислению без любой трудности. В то время как некоторые утверждали бы, что выражение будет лучше записано как 1u*x*x, я расцениваю потребность в последней форме как дефицит в спецификации языка. – supercat 22 April 2015 в 15:04
  • 3
    @JensGustedt: До недавнего времени большинство встроенных систем использовало 16-разрядное int. Если Вы писали код такой системы для обновления 16-разрядного two' s-дополнение или one' контрольная сумма s-дополнения после записи повторного значения к потоку, умножаясь два uint16_t была бы естественным способом сделать это. Далее, до самого последнего времени, 99,9% компиляторов C для 32-разрядных систем привел бы точно к тому же вычислению без любой трудности. В то время как некоторые утверждали бы, что выражение будет лучше записано как 1u*x*x, я расцениваю потребность в последней форме как дефицит в спецификации языка. – supercat 22 April 2015 в 15:04
  • 4
    @JensGustedt: До недавнего времени большинство встроенных систем использовало 16-разрядное int. Если Вы писали код такой системы для обновления 16-разрядного two' s-дополнение или one' контрольная сумма s-дополнения после записи повторного значения к потоку, умножаясь два uint16_t была бы естественным способом сделать это. Далее, до самого последнего времени, 99,9% компиляторов C для 32-разрядных систем привел бы точно к тому же вычислению без любой трудности. В то время как некоторые утверждали бы, что выражение будет лучше записано как 1u*x*x, я расцениваю потребность в последней форме как дефицит в спецификации языка. – supercat 22 April 2015 в 15:04
  • 5
    @JensGustedt: До недавнего времени большинство встроенных систем использовало 16-разрядное int. Если Вы писали код такой системы для обновления 16-разрядного two' s-дополнение или one' контрольная сумма s-дополнения после записи повторного значения к потоку, умножаясь два uint16_t была бы естественным способом сделать это. Далее, до самого последнего времени, 99,9% компиляторов C для 32-разрядных систем привел бы точно к тому же вычислению без любой трудности. В то время как некоторые утверждали бы, что выражение будет лучше записано как 1u*x*x, я расцениваю потребность в последней форме как дефицит в спецификации языка. – supercat 22 April 2015 в 15:04

У меня есть новое решение, основанное на исходных ограничениях new . (Я не удаляю свой первый ответ, поскольку другие могут посчитать его полезным.)

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

  1. В вашем .so переименуйте функцию замены rand, например my_rand
  2. Скомпилируйте все и загрузите gdb
  3. Используйте info functions, чтобы найти адрес rand в таблице символов
  4. Используйте dlopen, а затем dlsym, чтобы загрузить функцию в память и получить ее адрес

    call (int) dlopen("my_rand.so", 1) -> -val-

    call (unsigned int) dlsym(-val-, "my_rand") -> my_rand_addr

  5. -сложная часть- Найти шестнадцатеричный код инструкции jumpq 0x*my_rand_addr*
  6. Используйте set {int}*rand_addr* = *my_rand_addr* для изменения инструкции таблицы символов
  7. Continue выполнение: теперь всякий раз, когда вызывается rand, он переходит к my_rand вместо

Это немного сложно и очень приблизительно, но я уверен, что это сработает , Единственное, чего я еще не достиг - это создание кода инструкции jumpq. Все до этого момента работает нормально.

5
ответ дан zdav 30 August 2014 в 20:44
поделиться
  • 1
    @JensGustedt: Далее, я don' t думают size_t и ptrdiff_t действительно справка очень; учитывая char foo[100], *p1 = foo, *p2 = foo+100;, каково значение (p1-p2) > sizeof foo? В некоторых системах, где самый большой размер объекта между 32 768 и 65535, или между 2147483648 и 4294967295, выражение уступило бы 0, но во многих других системах это уступит 1. – supercat 22 April 2015 в 15:18
  • 2
    @JensGustedt: Далее, я don' t думают size_t и ptrdiff_t действительно справка очень; учитывая char foo[100], *p1 = foo, *p2 = foo+100;, каково значение (p1-p2) > sizeof foo? В некоторых системах, где самый большой размер объекта между 32 768 и 65535, или между 2147483648 и 4294967295, выражение уступило бы 0, но во многих других системах это уступит 1. – supercat 22 April 2015 в 15:18
  • 3
    @JensGustedt: Далее, я don' t думают size_t и ptrdiff_t действительно справка очень; учитывая char foo[100], *p1 = foo, *p2 = foo+100;, каково значение (p1-p2) > sizeof foo? В некоторых системах, где самый большой размер объекта между 32 768 и 65535, или между 2147483648 и 4294967295, выражение уступило бы 0, но во многих других системах это уступит 1. – supercat 22 April 2015 в 15:18
  • 4
    @JensGustedt: Далее, я don' t думают size_t и ptrdiff_t действительно справка очень; учитывая char foo[100], *p1 = foo, *p2 = foo+100;, каково значение (p1-p2) > sizeof foo? В некоторых системах, где самый большой размер объекта между 32 768 и 65535, или между 2147483648 и 4294967295, выражение уступило бы 0, но во многих других системах это уступит 1. – supercat 22 April 2015 в 15:18
  • 5
    @JensGustedt: Далее, я don' t думают size_t и ptrdiff_t действительно справка очень; учитывая char foo[100], *p1 = foo, *p2 = foo+100;, каково значение (p1-p2) > sizeof foo? В некоторых системах, где самый большой размер объекта между 32 768 и 65535, или между 2147483648 и 4294967295, выражение уступило бы 0, но во многих других системах это уступит 1. – supercat 22 April 2015 в 15:18

Вы можете использовать нас LD_PRELOAD, если вы заставите предварительно загруженную функцию понимать ситуации, в которых она используется. Вот пример, который будет использовать rand() как обычно, за исключением внутри разветвленного процесса, когда он всегда будет возвращать 42. Я использую подпрограммы dl для загрузки функции стандартной библиотеки rand() в указатель функции для использования угнанным rand().

// -*- compile-command: "gcc -Wall -fPIC -shared my_rand.c -o my_rand.so -ldl"; -*-
//my_rand.c
#include <sys/types.h>
#include <unistd.h>

#include <dlfcn.h>


int pid = 0;
int (*real_rand)(void) = NULL;

void f(void) __attribute__ ((constructor));

void f(void) {
    pid = getpid();
    void* dl = dlopen("libc.so.6", RTLD_LAZY);
    if(dl) {
        real_rand = dlsym(dl, "rand");
    }
}

int rand(void) 
{
    if(pid == getpid() && real_rand)
        return real_rand();
    else
        return 42;
}
<час>
//test.c
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char** argv)
{

    printf("Super random number: %d\n", rand());
    if(fork()) {
        printf("original process rand: %d\n", rand());

    } else {
        printf("forked process rand: %d\n", rand());
    }

    return 0;
}
<час>
jdizzle@pudding:~$ ./test
Super random number: 1804289383
original process rand: 846930886
forked process rand: 846930886

jdizzle@pudding:~$ LD_PRELOAD="/lib/ld-linux.so.2 ./my_rand.so" ./test
Super random number: 1804289383
original process rand: 846930886
forked process rand: 42
1
ответ дан jdizzle 30 August 2014 в 20:44
поделиться
  • 1
    @BlueRaja: кавычка явно указывает, что необходимо использовать неподписанные типы данных, когда код вызова, который ожидает неподписанный интервал. – Brian 16 July 2010 в 17:35
  • 2
    @BlueRaja: кавычка явно указывает, что необходимо использовать неподписанные типы данных, когда код вызова, который ожидает неподписанный интервал. – Brian 16 July 2010 в 17:35
  • 3
    @BlueRaja: кавычка явно указывает, что необходимо использовать неподписанные типы данных, когда код вызова, который ожидает неподписанный интервал. – Brian 16 July 2010 в 17:35
  • 4
    @BlueRaja: кавычка явно указывает, что необходимо использовать неподписанные типы данных, когда код вызова, который ожидает неподписанный интервал. – Brian 16 July 2010 в 17:35
  • 5
    @BlueRaja: кавычка явно указывает, что необходимо использовать неподписанные типы данных, когда код вызова, который ожидает неподписанный интервал. – Brian 16 July 2010 в 17:35

Для исполняемых файлов вы можете легко найти адрес, где хранится указатель на функцию, используя objdump. Например:

objdump -R /bin/bash | grep write
00000000006db558 R_X86_64_JUMP_SLOT  fwrite
00000000006db5a0 R_X86_64_JUMP_SLOT  write

Следовательно, 0x6db5a0 является адресом указателя для write. Если вы измените его, вызовы для записи будут перенаправлены на выбранную вами функцию. Загрузка новых библиотек в gdb и получение указателей на функции были рассмотрены в предыдущих статьях. Исполняемый файл и каждая библиотека имеют свои собственные указатели. Замена влияет только на модуль, указатель которого был изменен.

Для библиотек вам нужно найти базовый адрес библиотеки и добавить его к адресу, указанному в objdump. В Linux /proc/<pid>/maps выдает это. Я не знаю, будут ли работать независимые от позиции исполняемые файлы с рандомизацией адресов. maps -информация может быть недоступна в таких случаях.

1
ответ дан Juho Östman 30 August 2014 в 20:44
поделиться
  • 1
    Как я упоминаю в своем сообщении, Вы должны использовать неподписанные типы данных для API, которые требуют параметра размера (использование size_t). Дело обстоит не так в.Net, где переполнение буфера является надуманным вопросом. – BlueRaja - Danny Pflughoeft 16 July 2010 в 16:48
  • 2
    Как я упоминаю в своем сообщении, Вы должны использовать неподписанные типы данных для API, которые требуют параметра размера (использование size_t). Дело обстоит не так в.Net, где переполнение буфера является надуманным вопросом. – BlueRaja - Danny Pflughoeft 16 July 2010 в 16:48
  • 3
    Как я упоминаю в своем сообщении, Вы должны использовать неподписанные типы данных для API, которые требуют параметра размера (использование size_t). Дело обстоит не так в.Net, где переполнение буфера является надуманным вопросом. – BlueRaja - Danny Pflughoeft 16 July 2010 в 16:48
  • 4
    Как я упоминаю в своем сообщении, Вы должны использовать неподписанные типы данных для API, которые требуют параметра размера (использование size_t). Дело обстоит не так в.Net, где переполнение буфера является надуманным вопросом. – BlueRaja - Danny Pflughoeft 16 July 2010 в 16:48
  • 5
    Как я упоминаю в своем сообщении, Вы должны использовать неподписанные типы данных для API, которые требуют параметра размера (использование size_t). Дело обстоит не так в.Net, где переполнение буфера является надуманным вопросом. – BlueRaja - Danny Pflughoeft 16 July 2010 в 16:48

Я не уверен, как это сделать в работающей программе, но, возможно, LD_PRELOAD будет работать для вас. Если вы установите для этой переменной среды список общих объектов, загрузчик времени выполнения загрузит общий объект в начале процесса и позволит его функциям иметь приоритет над другими.

LD_PRELOAD=path_to_library/asdf.so path/to/prog 

Вы должны сделать это, прежде чем начать процесс, но вам не нужно перестраивать программу.

2
ответ дан R Samuel Klatchko 30 August 2014 в 20:44
поделиться

Некоторые ответы здесь и статья о внедрении кода , на которую вы ссылались в своем ответе, охватывают фрагменты того, что я считаю оптимальным gdb -ориентированным решением, но ни один из них не сводит все это вместе и не охватывает все точки. Кодовое выражение решения немного длинное, поэтому вот краткое изложение важных шагов:

  1. Загрузите код для внедрения . Большинство ответов, опубликованных здесь, используют то, что я считаю лучшим подходом - вызов dlopen() в подчиненном процессе для связи в общей библиотеке, содержащей введенный код. В статье, которую вы связали с автором, вместо этого загрузили перемещаемый объектный файл и связали его вручную с подчиненным. Это, откровенно говоря, безумие - перемещаемые объекты не являются «готовыми к работе» и включают перемещения даже для внутренних ссылок. А ручная компоновка утомительна и подвержена ошибкам - гораздо проще позволить реальной работе динамического компоновщика во время выполнения. Это, в первую очередь, означает вовлечение в процесс libdl, но для этого есть много вариантов.
  2. Создать объезд . Большинство ответов, опубликованных здесь до сих пор, включали в себя поиск записи PLT для интересующей функции, использование которой для нахождения соответствующей записи GOT, а затем изменение записи GOT для указания на введенную функцию. Это хорошо до некоторой степени, но некоторые функции компоновщика, например, использование dlsym, могут обойти GOT и обеспечить прямой доступ к интересующей функции. Единственный способ быть уверенным в перехвате всех вызовов определенной функции - это переписать начальные инструкции кода этой функции в памяти, чтобы создать «обходной» путь, перенаправляющий выполнение на вашу внедренную функцию.
  3. Создать батут (необязательно). Часто при выполнении такого рода инъекций вы захотите вызвать исходную функцию, вызов которой вы перехватываете. Способ сделать это с помощью обхода функции заключается в создании небольшого кода «батут», который включает перезаписанные инструкции исходной функции, а затем переход к оставшейся части оригинала. Это может быть сложно, потому что любые относящиеся к IP инструкции в скопированном наборе необходимо изменить, чтобы учесть их новые адреса.
  4. Автоматизировать все это . Эти шаги могут быть утомительными, даже если вы делаете некоторые из простых решений, опубликованных в других ответах. Лучший способ убедиться, что шаги выполняются правильно каждый раз с переменными параметрами (внедрение различных функций и т. Д.), - это автоматизировать их выполнение. Начиная с серии 7.0, в gdb появилась возможность писать новые команды на Python. Эта поддержка может быть использована для реализации решения «под ключ» для внедрения и обхода кода в / в подчиненном процессе.

Вот пример. У меня есть такие же исполняемые файлы a и b, как и раньше, и inject2.so, созданный из следующего кода:

#include <unistd.h>
#include <stdio.h>

int (*rand__)(void) = NULL;

int
rand(void)
{
    int result = rand__();
    printf("rand invoked! result = %d\n", result);
    return result % 47;
}

Затем я могу поместить свою команду Python detour в detour.py и иметь следующий сеанс gdb:

(gdb) source detour.py
(gdb) exec-file a
(gdb) set follow-fork-mode child
(gdb) catch exec
Catchpoint 1 (exec)
(gdb) run
Starting program: /home/llasram/ws/detour/a 
a: 1933263113
a: 831502921
[New process 8500]
b: 918844931
process 8500 is executing new program: /home/llasram/ws/detour/b
[Switching to process 8500]

Catchpoint 1 (exec'd /home/llasram/ws/detour/b), 0x00007ffff7ddfaf0 in _start ()
   from /lib64/ld-linux-x86-64.so.2
(gdb) break main
Breakpoint 2 at 0x4005d0: file b.c, line 7.
(gdb) cont
Continuing.

Breakpoint 2, main (argc=1, argv=0x7fffffffdd68) at b.c:7
7       {
(gdb) detour libc.so.6:rand inject2.so:rand inject2.so:rand__
(gdb) cont
Continuing.
rand invoked! result = 392103444
b: 22

Program exited normally.

В дочернем процессе я создаю обходной путь от функции rand() в libc.so.6 к функции rand() в inject2.so и сохраняю указатель на батут для оригинала rand() в переменной rand__ из inject2.so. И, как и ожидалось, введенный код вызывает оригинал, отображает полный результат и возвращает этот результат по модулю 47.

Из-за длины, я просто ссылаюсь на pastie, содержащий код моей команды detour . Это довольно поверхностная реализация (особенно с точки зрения создания батута), но она должна хорошо работать в большом проценте случаев. Я протестировал его с gdb 7.2 (последняя выпущенная версия) в Linux с 32-битными и 64-битными исполняемыми файлами. Я не проверял это на OS X, но любые различия должны быть относительно незначительными.

2
ответ дан llasram 30 August 2014 в 20:44
поделиться
  • 1
    @BlueRaja: определенными примерами является C#-specific, но общие точки, которые делают комментарии, все еще довольно верны. – Brian 16 July 2010 в 01:41
  • 2
    @BlueRaja: определенными примерами является C#-specific, но общие точки, которые делают комментарии, все еще довольно верны. – Brian 16 July 2010 в 01:41
  • 3
    @BlueRaja: определенными примерами является C#-specific, но общие точки, которые делают комментарии, все еще довольно верны. – Brian 16 July 2010 в 01:41
  • 4
    @BlueRaja: определенными примерами является C#-specific, но общие точки, которые делают комментарии, все еще довольно верны. – Brian 16 July 2010 в 01:41
  • 5
    @BlueRaja: определенными примерами является C#-specific, но общие точки, которые делают комментарии, все еще довольно верны. – Brian 16 July 2010 в 01:41

Пока функция, которую вы хотите заменить, находится в общей библиотеке, вы можете перенаправлять вызовы этой функции во время выполнения (во время отладки), нажимая PLT. Вот статья, которая может быть полезна:

Перенаправление вызовов из общей библиотеки с использованием ELF PLT-инфекции

Она написана с точки зрения вредоносной программы, модифицирующей программу, но намного проще Процедура адаптируется для живого использования в отладчике. По сути, вам просто нужно найти запись функции в PLT и перезаписать адрес адресом функции, которой вы хотите заменить ее.

Поиск в формате «PLT» вместе с такими терминами, как «ELF», «разделяемая библиотека», «динамическое связывание», «PIC» и т. Д. Может найти для вас более подробную информацию по этому вопросу.

1
ответ дан R.. 30 August 2014 в 20:44
поделиться
  • 1
    В 32-разрядной системе, учитывая uint16_t x = 0xFFFF; uint16_t y=x*x;, что стандарт говорит о значении y? – supercat 21 April 2015 в 16:46
  • 2
    В 32-разрядной системе, учитывая uint16_t x = 0xFFFF; uint16_t y=x*x;, что стандарт говорит о значении y? – supercat 21 April 2015 в 16:46
  • 3
    В 32-разрядной системе, учитывая uint16_t x = 0xFFFF; uint16_t y=x*x;, что стандарт говорит о значении y? – supercat 21 April 2015 в 16:46
  • 4
    В 32-разрядной системе, учитывая uint16_t x = 0xFFFF; uint16_t y=x*x;, что стандарт говорит о значении y? – supercat 21 April 2015 в 16:46
  • 5
    В 32-разрядной системе, учитывая uint16_t x = 0xFFFF; uint16_t y=x*x;, что стандарт говорит о значении y? – supercat 21 April 2015 в 16:46

Я нашел этот учебник невероятно полезным, и до сих пор это единственный способ, которым мне удалось достичь того, что я искал с помощью GDB: Внедрение кода в работающее приложение Linux : http: // www. codeproject.com/KB/DLL/code_injection.aspx

Здесь также есть хорошая Q & amp; A по внедрению кода для Mac здесь: http: // www. mikeash.com/pyblog/friday-qa-2009-01-30-code-injection.html

0
ответ дан karlphillip 30 August 2014 в 20:44
поделиться
  • 1
    " wrapping" единственная интересная функция неподписанного ints (для регулярного ints, у Вас только есть неопределенное поведение). Если обертывание будет проблемой (или если you' ve, чтобы стараться избежать его) тогда it' s ясный знак это " unsigned" был неправильный выбор. Используя неподписанные и имеющие проблемы с обертыванием (that' s наиболее отличительная особенность неподписанных типов), не имеет смысла..., когда Вы используете неподписанный, Вы ХОТИТЕ обертывание..., необходимо выбрать неподписанный ИЗ-ЗА переносящегося поведения... – 6502 15 July 2010 в 21:24
  • 2
    " wrapping" единственная интересная функция неподписанного ints (для регулярного ints, у Вас только есть неопределенное поведение). Если обертывание будет проблемой (или если you' ve, чтобы стараться избежать его) тогда it' s ясный знак это " unsigned" был неправильный выбор. Используя неподписанные и имеющие проблемы с обертыванием (that' s наиболее отличительная особенность неподписанных типов), не имеет смысла..., когда Вы используете неподписанный, Вы ХОТИТЕ обертывание..., необходимо выбрать неподписанный ИЗ-ЗА переносящегося поведения... – 6502 15 July 2010 в 21:24
  • 3
    " wrapping" единственная интересная функция неподписанного ints (для регулярного ints, у Вас только есть неопределенное поведение). Если обертывание будет проблемой (или если you' ve, чтобы стараться избежать его) тогда it' s ясный знак это " unsigned" был неправильный выбор. Используя неподписанные и имеющие проблемы с обертыванием (that' s наиболее отличительная особенность неподписанных типов), не имеет смысла..., когда Вы используете неподписанный, Вы ХОТИТЕ обертывание..., необходимо выбрать неподписанный ИЗ-ЗА переносящегося поведения... – 6502 15 July 2010 в 21:24
  • 4
    " wrapping" единственная интересная функция неподписанного ints (для регулярного ints, у Вас только есть неопределенное поведение). Если обертывание будет проблемой (или если you' ve, чтобы стараться избежать его) тогда it' s ясный знак это " unsigned" был неправильный выбор. Используя неподписанные и имеющие проблемы с обертыванием (that' s наиболее отличительная особенность неподписанных типов), не имеет смысла..., когда Вы используете неподписанный, Вы ХОТИТЕ обертывание..., необходимо выбрать неподписанный ИЗ-ЗА переносящегося поведения... – 6502 15 July 2010 в 21:24
  • 5
    " wrapping" единственная интересная функция неподписанного ints (для регулярного ints, у Вас только есть неопределенное поведение). Если обертывание будет проблемой (или если you' ve, чтобы стараться избежать его) тогда it' s ясный знак это " unsigned" был неправильный выбор. Используя неподписанные и имеющие проблемы с обертыванием (that' s наиболее отличительная особенность неподписанных типов), не имеет смысла..., когда Вы используете неподписанный, Вы ХОТИТЕ обертывание..., необходимо выбрать неподписанный ИЗ-ЗА переносящегося поведения... – 6502 15 July 2010 в 21:24

Я часто использую внедрение кода в качестве метода имитации для автоматического тестирования кода на языке Си. Если вы находитесь в такой ситуации - если вы используете GDB просто потому, что вас не интересуют родительские процессы, а не потому, что вы хотите интерактивно выбирать интересующие процессы - тогда вы все равно можете используйте LD_PRELOAD для достижения вашего решения. Ваш внедренный код просто должен определить, находится ли он в родительском или дочернем процессах. Есть несколько способов сделать это, но в Linux, поскольку ваш дочерний процесс exec(), проще всего, вероятно, взглянуть на активный исполняемый образ.

Я создал два исполняемых файла, один с именем a, а другой b. Исполняемый файл a печатает результат вызова rand() дважды, затем fork() с и exec() с b дважды. Исполняемый файл b выводит результат вызова rand() один раз. Я использую LD_PRELOAD, чтобы добавить результат компиляции следующего кода в исполняемые файлы:

// -*- compile-command: "gcc -D_GNU_SOURCE=1 -Wall -std=gnu99 -O2 -pipe -fPIC -shared -o inject.so inject.c"; -*-
#include <sys/types.h>
#include <unistd.h>
#include <limits.h>
#include <stdio.h>
#include <dlfcn.h>

#define constructor __attribute__((__constructor__))

typedef int (*rand_t)(void);

typedef enum {
    UNKNOWN,
    PARENT,
    CHILD
} state_t;

state_t state = UNKNOWN;
rand_t rand__ = NULL;

state_t
determine_state(void)
{
    pid_t pid = getpid();
    char linkpath[PATH_MAX] = { 0, };
    char exepath[PATH_MAX] = { 0, };
    ssize_t exesz = 0;

    snprintf(linkpath, PATH_MAX, "/proc/%d/exe", pid);
    exesz = readlink(linkpath, exepath, PATH_MAX);
    if (exesz < 0)
        return UNKNOWN;

    switch (exepath[exesz - 1]) {
    case 'a':
        return PARENT;
    case 'b':
        return CHILD;
    }

    return UNKNOWN;
}

int
rand(void)
{
    if (state == CHILD)
        return 47;
    return rand__();
}

constructor static void
inject_init(void) 
{
    rand__ = dlsym(RTLD_NEXT, "rand");
    state = determine_state();
}

Результат выполнения a с и без внедрения:

$ ./a
a: 644034683
a: 2011954203
b: 375870504
b: 1222326746
$ LD_PRELOAD=$PWD/inject.so ./a
a: 1023059566
a: 986551064
b: 47
b: 47

I ' Позже я опубликую решение, ориентированное на gdb.

0
ответ дан llasram 30 August 2014 в 20:44
поделиться
  • 1
    @6502: Вы делаете действительно правильное замечание, и я честно думаю, что иногда использую неподписанные типы, когда типы со знаком могли бы быть лучшим выбором. Но я думаю, что существуют также исключения; например, при контакте с размерами файла Вы, возможно, должны быть в состоянии иметь дело с полным спектром size_t (или даже некоторый больший неподписанный тип), но Вы, возможно, все еще должны были бы обработать переносящиеся ошибки. – Michael Burr 15 July 2010 в 21:55
  • 2
    @6502: Вы делаете действительно правильное замечание, и я честно думаю, что иногда использую неподписанные типы, когда типы со знаком могли бы быть лучшим выбором. Но я думаю, что существуют также исключения; например, при контакте с размерами файла Вы, возможно, должны быть в состоянии иметь дело с полным спектром size_t (или даже некоторый больший неподписанный тип), но Вы, возможно, все еще должны были бы обработать переносящиеся ошибки. – Michael Burr 15 July 2010 в 21:55
  • 3
    @6502: Вы делаете действительно правильное замечание, и я честно думаю, что иногда использую неподписанные типы, когда типы со знаком могли бы быть лучшим выбором. Но я думаю, что существуют также исключения; например, при контакте с размерами файла Вы, возможно, должны быть в состоянии иметь дело с полным спектром size_t (или даже некоторый больший неподписанный тип), но Вы, возможно, все еще должны были бы обработать переносящиеся ошибки. – Michael Burr 15 July 2010 в 21:55
  • 4
    @6502: Вы делаете действительно правильное замечание, и я честно думаю, что иногда использую неподписанные типы, когда типы со знаком могли бы быть лучшим выбором. Но я думаю, что существуют также исключения; например, при контакте с размерами файла Вы, возможно, должны быть в состоянии иметь дело с полным спектром size_t (или даже некоторый больший неподписанный тип), но Вы, возможно, все еще должны были бы обработать переносящиеся ошибки. – Michael Burr 15 July 2010 в 21:55
  • 5
    @6502: Вы делаете действительно правильное замечание, и я честно думаю, что иногда использую неподписанные типы, когда типы со знаком могли бы быть лучшим выбором. Но я думаю, что существуют также исключения; например, при контакте с размерами файла Вы, возможно, должны быть в состоянии иметь дело с полным спектром size_t (или даже некоторый больший неподписанный тип), но Вы, возможно, все еще должны были бы обработать переносящиеся ошибки. – Michael Burr 15 July 2010 в 21:55
Другие вопросы по тегам:

Похожие вопросы: