如何在Java程序中禁用堆栈跟踪生成?

8
我希望禁止在抛出异常时生成堆栈跟踪。我已经使用了以下方法:

Runtime.getRuntime().traceInstructions(false);
Runtime.getRuntime().traceMethodCalls(false);

但我仍然可以看到正在生成的跟踪。你如何做到这一点?同时,我需要检测是否有人在调试我的类。

我想禁用所有异常跟踪。我不能使用混淆,因为我的产品是一个SDK,将被用于开发。我还提供了一个Runtime,用于部署使用我的SDK构建的应用程序。我的要求是,任何使用我的Runtime JAR文件的人都不应该能够调试编写的代码...或者至少通过避免从我的Runtime JAR文件中生成堆栈跟踪来使调试变得困难。

我发现的一种方法是,只需捕获从我的Runtime JAR文件起源的所有异常,并在异常对象上设置一个空的StackTraceElement数组,然后重新抛出它...

为什么会有这样的要求? 假设您使用我的SDK开发应用程序。(SDK JAR文件不能捆绑在您的应用程序中..我已经限制了它,这是最终的 :) !!)现在,为了在客户端机器上运行应用程序,您(或客户端)需要安装Runtime并运行应用程序。现在,如果您的客户开始使用我的Runtime JAR文件开发自己的应用程序!!那就对我的业务构成威胁....这就是可怕的要求的原因。

为什么要禁用堆栈跟踪?
通过禁用堆栈跟踪或方法调用跟踪的生成,我希望使使用我的Runtime JAR文件开发代码变得困难,这就是我以这种方式开始我的问题的原因...请建议其他解决方案来实现这样的要求...


2
请只提供翻译文本:请添加更多细节,例如您为什么要禁用堆栈跟踪?您所说的“禁用堆栈跟踪”是指什么(因为它只是异常链) - Valentin Rocher
2
我认为你在这里为你的用户提供了糟糕的服务。SDK没有提供有用的异常信息? - Thorbjørn Ravn Andersen
我的要求是,任何使用我的运行时JAR文件的人都不应该能够调试编写的代码。这是一个可怕的要求,也是一种可怕的方法来满足这个“要求”。 - Nate
@user279436:这是用于移动应用程序开发吗?无论如何...如果您认为因为您有公共API就不能使用Proguard,那么您就错了。您可以使用Proguard来混淆SDK中所有不公开访问的部分。这是Proguard非常方便和强大的功能。 - SyntaxT3rr0r
通过禁用堆栈跟踪生成或方法调用跟踪生成,我想让使用我的运行时JAR包开发代码变得困难,这就是为什么我以那种方式开始我的问题的原因...请建议其他解决方案来实现这样的要求... - AD.
显示剩余2条评论
6个回答

12

我也很好奇你为什么想这么做,但如果你真的有自己的原因,至少有两个选项:

如果你想禁用自己Exception实现的堆栈跟踪生成,你可以简单地重写fillInStackTrace方法:

public static class MyException extends Exception {
    @Override
    public Throwable fillInStackTrace() {
        return this;
    }       
}

如果你想要禁用所有异常的堆栈跟踪,你可以使用一个字节码插装代理来替换Throwable类中的fillInStackTrace方法。然而这只适用于Java 6,因为在Java 5中,你不允许使用插装来用Java方法替换一个本地方法(fillInStackTrace)。


2
替换所有异常的 fillInStackTrace 可能会破坏某些东西,例如 Java 安全性。 - Stephen C
1
@Stephen:你介意解释一下为什么吗,还是这显而易见? - jarnbjo
3
同时,您需要返回“return this”,而不是“return null”,否则将违反fillInStackTrace()的javadoc。 - Matt McHenry
是的,它可以工作(即删除堆栈跟踪),并且对于许多(甚至大多数)异常来说是无害的。但是对于所有异常都这样做是有风险的。在我看来,除非您已经研究了Java SE代码库和所有依赖项的所有类的情况以内省堆栈跟踪,否则不建议这样做。 - Stephen C
@StephenC 我不确定为什么你现在挑选了一个将近13年的答案,但是有一段时间以来,您已经不能保证异常会被填充堆栈跟踪信息。如果异常从同一位置反复抛出,JVM 将自动优化掉堆栈跟踪信息的生成。我不记得这种行为是何时引入的,但是如果您想禁用它并强制为所有异常生成堆栈跟踪信息,您必须使用 -XX:-OmitStackTraceInFastThrow 选项运行 VM(至少是 Oracle VM)以禁用此优化。 - jarnbjo
显示剩余3条评论

7
  1. 我认为代码无法直接知道自己是否正在被调试,除非通过间接(且不可靠)的方式,如测量代码序列执行所需时间。

  2. 不可能禁用所有堆栈跟踪。您可以通过覆盖Throwable.fillInStackTrace()以什么都不做的方式来禁用自己定义的异常类的堆栈跟踪。 但是对于您无法更改的类,这种方法不起作用。

但如果您想要这样做来防止反向工程,即使您能够这样做,也会浪费时间。黑客可以轻松识别应用程序的防反向工程代码,并编辑相关的字节码文件以禁用它。

编辑 - 我已经修改了自己的观点。考虑到您正在分发一个SDK,您希望客户将其嵌入到自己的应用程序中,禁用整个Java应用程序的堆栈跟踪属于对客户不友好的行为,我认为。保护您“珍贵”的IP的副作用是使客户/开发人员难以调试自己的代码。甚至是没有您珍贵的方法在调用堆栈上的代码!

如果我是客户,我可能更喜欢您提供混淆的代码而不是这样做。但最有可能的是,我会非常努力地寻找一个没有将付费客户视为小偷的替代软件供应商。


如果我可以分享我的经验:我曾经为一个简单但昂贵的操作编写了一个非常好的Java解决方案。我的客户许可证是,只要他们使用该产品,他们每月支付我一次。有一天,他们停止了付款。通过非正式渠道,我发现他们已经分析了我的代码并雇用了更便宜的人来利用我学到的所有业务逻辑(你可以在代码中清晰地看到,但我花费了很长时间来聚合)。从那时起,我开发了自己的本地编程语言和编译器,这些都是保密的。祝你好运分析它! - DraxDomax
假设是“如果你的代码易于阅读,那么它就容易编程,因此不值得任何价值”。这就像老笑话:“500美元?你只用了15分钟修理漏洞!” - “但我花了25年才知道如何找到它!” - DraxDomax
1
你的错误在于试图将你的应用程序变成一种收入流。你应该只是以一次性付款的形式将其出售给他们。如果你想要一种收入流,你应该将应用程序制作成在你控制的硬件上运行的服务。我想第二个错误是没有起诉他们。但是这里有一件事情:即使按照OP提出的步骤也无法阻止决心逆向工程的人。 - Stephen C
在这里?你在英国对吧?他们有一个健全的法律体系,对吧?如果你在起草合同/许可协议时注意到了细节,那么逆向工程应该是明显违反合同的行为。找一家愿意按成功费用计费的律师事务所... - Stephen C
我不想解释我的人生故事 :) 我住在英国,但我也曾经住过其他几个国家,在那里知识产权的概念被认为是可笑的。即使是英国公司有时也不会战略性地依赖知识产权的概念。我曾经在一家巨大的公司工作过,他们有意不申请专利。涉及到的技术非常出色,但很容易被复制。专利会意味着向可能在英国或其他国家的公众透露有关该技术的详细信息。假设“你可以起诉他们”,这并不是一种万无一失的策略。 - DraxDomax

3

如果禁用了堆栈跟踪生成(至少在Sun的JVM实现中),一些JVM的复杂部分就无法正常工作(我在反射的一些支持方法的实现中看到了这一点)。因此,我认为堆栈跟踪生成完全不能被禁用。 Runtime.trace*() 方法与堆栈跟踪无关,而是一个比堆栈跟踪更彻底的调试工具。

总的来说,任何Java代码都可以透明地分析,即使只通过字节码插装(加载时修改的带有额外指令的字节码)进行分析。目前唯一已知的防御措施(假设您正在努力保护代码内部信息的机密性)是混淆。例如,请参见ProGuard。混淆将使得任何过于好奇的用户无法使用堆栈跟踪(不幸的是,出于同样的原因,它也会使调试变得非常困难)。


我想禁用所有异常跟踪。由于我的产品是一个SDK,将被用于开发,因此我无法使用混淆。我还提供了一个Runtime,当人们想要部署使用我的SDK构建的应用程序时会使用它。我的要求是,任何使用我的Runtime Jars的人都不应该能够调试所编写的代码...或者至少我会通过避免从我的运行时Jars生成堆栈跟踪来使其更难以调试。 - AD.
2
我发现的一种方法是,所有来自我的运行时JAR的异常,我只需捕获它们并在异常对象上设置一个空的StackTraceElement数组,然后重新抛出它... - AD.
1
为什么不分发单独的运行时和开发JAR包,其中运行时JAR包是混淆的? - Chinmay Kanchi

2
你想要禁用所有异常吗?
不知道你想达到什么目的,但我认为这是错误的做法。如果你期望一个可以忽略的异常被抛出,你应该显式地捕获并处理它(处理它可能只意味着忽略它,或者记录一个简短的信息而不包含完整的堆栈跟踪)。

1

Throwable.setStackTrace(StackTraceElement[] stackTrace) 在调用后会阻止向 stackTrace 添加任何内容:

Throwable t = new Throwable();
StackTraceElement[] myStackTrace = new StackTraceElement[] {
 new StackTraceElement("MySDKClass","MySDKMethod","MySDKFile",0)
};
t.setStackTrace(trace);

1

您可以将所有异常重定向到不同的位置,例如:

Thread.setDefaultUncaughtExceptionHandler(
                (t, e) -> System.err.println("There's nothing to see here"));

或者简单地说:
Thread.setDefaultUncaughtExceptionHandler(null);

注意:除非你想给同事们带来麻烦,否则永远不要在生产环境中使用上述代码。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接