Я часто использую внедрение кода в качестве метода имитации для автоматического тестирования кода на языке Си. Если вы находитесь в такой ситуации - если вы используете 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.
jar tf
выведет для вас список содержимого. javap
позволит вам увидеть более подробную информацию о классах (см. Руководство по инструментам).
Например, если у вас есть класс с именем mypkg.HelloWorld
в банке myjar.jar
, затем запустите его как
javap -classpath myjar.jar mypkg.HelloWorld
Однако вы уверены, что хотите использовать эти неопубликованные API? Обычно это действительно плохая идея.
Как сказал Скотт, вы можете использовать Eclipse, чтобы получить то, что ищете.
Я бы рекомендовал получить плагин JadClipse , который будет декомпилировать .class файлы "на лету" и показывают реальный код Java при просмотре классов в среде IDE.
Если вы используете eclipse, вы можете просто добавить его в путь к классам проекта и исследовать его с помощью древовидного представления и / или помощника по содержанию.
Я предполагаю, что другие IDE могут делать аналогичные .
С точки зрения командной строки, вы можете отключить его (jar xf foo.jar) и использовать javap для всех файлов.
Eclipse отлично работал бы
A Декомпилятор Java преобразовал бы классы обратно в некое подобие исходного кода, который вы могли бы изучить, чтобы узнать о классах, методах, их подписи и, возможно, даже некоторое представление о допустимых значениях для некоторых аргументов (например, не передавайте значение null для этого аргумента, иначе вы немедленно вызовете исключение NullPointerException). Но засучите рукава, чтобы открыть банку и запустить декомпилятор для всех файлов классов. По сути, это то, что Eclipse делает для справочного текста с недокументированными классами.
Наконец, конечно, «настоящий программист» прочитал бы байт-код напрямую без необходимости в декомпилятор.