Подтвердить что ты не робот

Предотвращение использования System.exit() API

Я использую стороннюю библиотеку, которая выполняет System.exit(), если она встречает исключения. Я использую API из банки. В любом случае, я могу предотвратить вызов System.exit(), потому что это заставляет мое приложение отключиться? Я не могу декомпилировать и перекомпилировать банку после удаления System.exit() из-за множества других проблем с лицензированием. Я однажды наткнулся на ответ [на другой вопрос, который не помню] в stackoverflow, что мы можем использовать SecurityManager в Java, чтобы сделать что-то вроде этого.

4b9b3361

Ответ 1

Здесь есть запись в блоге,

http://jroller.com/ethdsy/entry/disabling_system_exit

В основном он устанавливает менеджера безопасности, который отключает System.exit() с кодом здесь,

  private static class ExitTrappedException extends SecurityException { }

  private static void forbidSystemExitCall() {
    final SecurityManager securityManager = new SecurityManager() {
      public void checkPermission( Permission permission ) {
        if( "exitVM".equals( permission.getName() ) ) {
          throw new ExitTrappedException() ;
        }
      }
    } ;
    System.setSecurityManager( securityManager ) ;
  }

  private static void enableSystemExitCall() {
    System.setSecurityManager( null ) ;
  }

Изменить: Max указывает в комментариях ниже, что

как и в случае с Java 6, имя разрешения фактически является статусом "exitVM." +, например. "ExitVM.0".

Однако разрешение exitVM.* ссылается на все статусы выхода, а exitVM сохраняется как сокращенное обозначение exitVM.*, поэтому приведенный выше код все еще работает (см. документация для RuntimePermission).

Ответ 2

Посмотрите мой ответ на Как избежать операции JFrame EXIT_ON_CLOSE для выхода из всего приложения?.

Изменить 1: источник, который был связан. Демонстрирует, как использовать SecurityManager для предотвращения System.exit(n).

import java.awt.*;
import java.awt.event.*;
import java.security.Permission;

/** NoExit demonstrates how to prevent 'child'
applications from ending the VM with a call
to System.exit(0).
@author Andrew Thompson */
public class NoExit extends Frame implements ActionListener {

  Button frameLaunch = new Button("Frame"),
     exitLaunch = new Button("Exit");

  /** Stores a reference to the original security manager. */
  ExitManager sm;

  public NoExit() {
     super("Launcher Application");

     sm = new ExitManager( System.getSecurityManager() );
     System.setSecurityManager(sm);

     setLayout(new GridLayout(0,1));

     frameLaunch.addActionListener(this);
     exitLaunch.addActionListener(this);

     add( frameLaunch );
     add( exitLaunch );

     pack();
     setSize( getPreferredSize() );
  }

  public void actionPerformed(ActionEvent ae) {
     if ( ae.getSource()==frameLaunch ) {
        TargetFrame tf = new TargetFrame();
     } else {
        // change back to the standard SM that allows exit.
        System.setSecurityManager(
           sm.getOriginalSecurityManager() );
        // exit the VM when *we* want
        System.exit(0);
     }
  }

  public static void main(String[] args) {
     NoExit ne = new NoExit();
     ne.setVisible(true);
  }
}

/** This example frame attempts to System.exit(0)
on closing, we must prevent it from doing so. */
class TargetFrame extends Frame {
  static int x=0, y=0;

  TargetFrame() {
     super("Close Me!");
     add(new Label("Hi!"));

     addWindowListener( new WindowAdapter() {
        public void windowClosing(WindowEvent we) {
           System.out.println("Bye!");
           System.exit(0);
        }
     });

     pack();
     setSize( getPreferredSize() );
     setLocation(++x*10,++y*10);
     setVisible(true);
  }
}

/** Our custom ExitManager does not allow the VM
to exit, but does allow itself to be replaced by
the original security manager.
@author Andrew Thompson */
class ExitManager extends SecurityManager {

  SecurityManager original;

  ExitManager(SecurityManager original) {
     this.original = original;
  }

  /** Deny permission to exit the VM. */
   public void checkExit(int status) {
     throw( new SecurityException() );
  }

  /** Allow this security manager to be replaced,
  if fact, allow pretty much everything. */
  public void checkPermission(Permission perm) {
  }

  public SecurityManager getOriginalSecurityManager() {
     return original;
  }
}

Ответ 3

Использование SecurityManager для запрета вызовов System.exit() не является совершенным, по крайней мере по двум причинам:

  • Приложения Java, работающие с включенным и включенным SecurityManager, сильно отличаются. Поэтому в конечном итоге его необходимо отключить, но это невозможно сделать с помощью System.setSecurityManager(null). Этот вызов приведет к другой проверке разрешений безопасности, которая неизбежно завершится неудачей, поскольку код приложения (подкласс SecurityManager) находится в верхней части вызывающего стека.

  • Все приложения Java являются многопоточными, а другие потоки могут выполнять различные действия между forbidSystemExitCall() и enableSystemExitCall(). Некоторые из этих вещей могут быть защищены с помощью проверок разрешения безопасности, которые будут сбой по тем же причинам, что описаны выше. Если checkExit() переопределяется вместо [гораздо более общий] checkPermission(), он будет охватывать большинство случаев.

Единственный способ (я знаю) разрешить это - предоставить все привилегии подклассу SecurityManager. Это, скорее всего, потребует, чтобы он загружался отдельным загрузчиком классов, например. bootstrap (null) загрузчик классов.

Ответ 4

Предыдущий пример кода является частично правильным, но я обнаружил, что он блокировал мой доступ к файлам. Чтобы обойти эту проблему, я написал свой SecurityManager несколько иначе:

public class MySecurityManager extends SecurityManager {

private SecurityManager baseSecurityManager;

public MySecurityManager(SecurityManager baseSecurityManager) {
    this.baseSecurityManager = baseSecurityManager;
}

@Override
public void checkPermission(Permission permission) {
    if (permission.getName().startsWith("exitVM")) {
        throw new SecurityException("System exit not allowed");
    }
    if (baseSecurityManager != null) {
        baseSecurityManager.checkPermission(permission);
    } else {
        return;
    }
}

}

В моем случае мне нужно было запретить сторонней библиотеке завершать работу виртуальной машины. Но были также некоторые тесты Grails, которые вызывали System.exit. Итак, я написал свой код, чтобы он только активировал настраиваемый диспетчер безопасности непосредственно перед вызовом в стороннюю библиотеку (а не обычное событие), а затем сразу же восстановил первоначальный менеджер безопасности, если он есть, после.

Все это немного уродливо. В идеале я бы предпочел просто удалить код System.exit, но у меня нет доступа к исходному коду сторонней библиотеки.