2024年java 基础类 log 拦截

java 基础类 log 拦截作者 RednaxelaFX 系列文章 要让 CLR 挂掉的话 要让 CLR 挂掉的话 第二弹 前几天跟浩飞老兄闲聊的时候 聊到说一个不知道什么地方在面试人的时候 如果面试者说自己精通 Java 他们就出题考面试者如何让 JVM 挂掉 这种面试方式或许是比较激进 不过倒也可以考考别人对特定 JVM 的实现的认识 于是在爆栈上有这么一帖 How do you crash a JVM

大家好,我是讯享网,很高兴认识大家。




作者:RednaxelaFX

系列文章:
要让CLR挂掉的话……
要让CLR挂掉的话(第二弹)……
前几天跟浩飞老兄闲聊的时候,聊到说一个不知道什么地方在面试人的时候,如果面试者说自己精通Java,他们就出题考面试者如何让JVM挂掉。这种面试方式或许是比较激进,不过倒也可以考考别人对特定JVM的实现的认识。
于是在爆栈上有这么一帖:How do you crash a JVM?。跟帖中一些同学的观点一样,我也java 基础类 log 拦截不认为爆栈或者爆堆能算得上是“crash”,因为JVM还能正确捕捉到错误,并且执行合适的异常处理。真正的“crash”应该是连正常的异常处理都没起作用,直接就出crash log了;要是能连出crash log的步骤都破坏掉那就更彻底了。
爆栈帖里有人建议说:

ralfs 写道

1. Use JNI and crash in the native code.
2. If no security manager is installed you can use reflection to crash the VM. This is VM specific, but normally a VM stores a bunch of pointers to native resources in private fields (e.g. a pointer to the native thread object is stored in a long field injava.lang.Thread). Just change them via reflection and the VM will crash sooner or later.
3. All VMs have bugs, so you just have to trigger one.

第一点基本上就映射到P/Invoke的使用了。如果被P/Invoke的native code里有非常糟糕的错误而且不使用SEH,那CLR什么办法也没有,只能让程序crash了。

CLR里也有许多看起来很无辜的东西实际上是指针来的(注意我是说CLR不是CLI)。一个典型的例子是Type.TypeHandle属性,在CLR里它实际上就是指向类型的MethodTable的指针。通过它我们可以找到很多关于类型的“裸”信息。“裸”是指CLR内部的实现细节,本来不应该暴露出来的部分)。还有一个典型的例子是.NET的类型安全函数指针,委托。下面会看看委托的例子。

第三点是说VM自身的实现有bug。嗯这种状况常有,像先前我就看到HotSpot的JIT有bug挂掉了。CLR小组也没少遇到内部发生内存管理错误的问题,组里有专人盯着这种问题在修。如果发现这样的bug并有意利用的话,也能有效让VM挂掉,甚至进一步做别的事情……呵呵

_target:委托调用的目标对象。

_methodPtr:委托调用的目标方法的指针。

_methodPtrAux:委托调用的目标方法的第二个指针。

要让CLR挂掉的话……_ide

C#代码

讯享网

引用

Unhandled Exception: System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

引用

Process is terminated due to StackOverflowException.

cescshen 写道

发错,这儿不能删自己的留言。。

C#代码

 

X86 asm代码

讯享网

X86 asm代码

 

爆栈帖里有一个回复可以mark一下:

eckes 写道

On Linux/Unix you can easyly make a JVM crash by sending it a Signal to the running process. Note: you should not use "SIGSEGV" for this, since Hotspot catches this signal and rethrows it as a NullPointerException in most places. So it is better to send a SIGBUS for example.

(Disclaimer:如果需要转载请先与我联系。
作者:RednaxelaFX -> rednaxelafx.iteye.com)

系列文章:
要让CLR挂掉的话…… 要让CLR挂掉的话(第二弹)……

同前一篇的后半段一样,以下内容都是以PC上的32位x86的CLR、版本2.0.50727.3082,以及Windows XP SP3为前提的讨论。

前一篇提到了通过改变委托中的指针来改变实际的调用目标。修改委托实例中的_target、_methodPtr、_methodPtrAux这三个成员,都能够改变跳转目标;特别是后两个,它们的类型是IntPtr,可以构造出任意数值的指针设置进去,那样就可以跳转到任意目标了。
但只能指定目标地址,却不能随意控制目标里的代码,显然还不够好玩。如果要跳转的目标是托管方法,那构造一个正常的委托就够了。如果能在不使用P/Invoke也不使用unsafe code的条件下在C#程序里执行一小块自定义的native code就好玩多了。先前的两篇日志(这里和这里)我提到在内存里生成native code并执行并不是件难事。那么在CLR上的C#也能做到么?

===========================================================================

要玩怎样的代码?

如果能执行任意native code的话,不得不说可玩的东西就多了。例如说把整个调用栈给乱搅一通、把SEH链全都破坏掉;或者……嗯还是别想那么可怕的玩法了,我还不想把自己的系统弄垮。等什么时候我再装个用了就扔的虚拟机镜像再试可怕的玩法……

还是跟前面一样,写段类似HelloWorld的代码,往标准输出流写句话,表明“可以做”就算了。想用System.Console.WriteLine()会有点不爽,因为它要接收CLR对象(的引用)为参数,而我不想费事去在自己的native code里去找出System.String的type token、调用CORINFO_HELP_NEWSFAST创建新实例、调用构造器之类的一大堆麻烦事。我就想把一个C风格的字符串输出而已。那么,就不用.NET标准库里的方法了,干脆直接用Win32 API,简单省事,WriteConsole()函数正好够用。调用Win32函数时也懒得通过P/Invoke,而是在native code里直接call过去。

要用Win32 API,首先得确保需要的DLL已经被加载到当前进程中。CLR为了自身的正常运行,本来就需要加载很多模块。可以看看一个HelloWorld式的托管程序都加载些什么模块进来。

C#代码

 

引用

ADVAPI32
COMCTL32
COMCTL_1
GDI32
IMM32
KERNEL32
LPK
MSCOREE
MSCORJIT
MSCORLIB
MSCORWKS
MSVCR80
MSVCRT
OLE32
RPCRT4
SECUR32
SHELL32
SHLWAPI32
USER32
USP10

所以干脆ad-hoc点,既然是玩嘛就先别那么麻烦,先弄出能演示的版本再说。用别的办法找到需要用的函数的地址,然后硬编码到我们生成的native code里就算了。因为DLL有默认的加载地址,只要一个进程里加载的DLL没有地址冲突,它们所在的位置就是可预测的,其中函数的位置也是可预测的。当然,我是在XP上玩,在Vista之后有ASLR,地址就不好预测了……但如果按照下文的方法保存生成的native code的话,程序在遇到ASLR前先就被DEP干掉了。

C代码

 

要让CLR挂掉的话……_ide

 

我准备把要输出的字符串紧接在代码后面。注意这里用了之前介绍过的一个技巧,通过call指令来获取当前IP(指令指针)的值,并由此计算出要输出的字符串的地址。其实不用这个技巧也可以的,毕竟我已经知道代码的起始地址了。不过玩嘛,呵呵~

如何存放生成的native code?

之前用C来演示运行时操纵代码的时候,是把native code“生成”到malloc出来的一块堆空间上的。在C#里我要怎么找到可写可执行的一块放代码的空间呢?

在前一篇的后半段中我介绍了改变委托的_target成员也可以改变最终被调用的目标。留意到_target的类型是System.Object,也就是说任何对象都可以放在里面。如果构造一个委托实例时,它是指向静态方法的,那么它的_methodPtr成员就会指向那个特定的stub:(stub会根据委托类型的参数个数不同而不同,下面是接收一个参数的委托的情况)

要让CLR挂掉的话……_ide_03

 
 
 

在Windows XP上,DEP还没有对所有程序默认开启,所以基本上在堆上申请到的空间都是可写可执行的。CLR里的托管数组都在堆上分配空间,可以把native code“生成”到数组里保存着。不过Vista和Windows 7上DEP默认对所有程序都启用,而不通过VirtualAlloc()或者VirtualProtect()就没办法申请到可写可执行的空间,所以下面的办法在这些新的系统上运行会看到AccessViolationException。

 

如何获得对象的地址?

Java的java.lang.Object和.NET的System.Object都支持获取对象的hashcode。如果一个对象在“活着”的时候不会被移动,则其起始地址不会发生改变;对象间不应该有交叠,所以用对象地址直接作为hashcode是一种很直观的实现。事实上Android里的Dalvik虚拟机就是这样实现Object.hashCode()的,详细可查看Dalvik源码的vm/native/java_lang_Object.c中的Dalvik_java_lang_Object_hashCode()和InternalNative.c中的dvmGetObjectHashCode()。

C代码

 

也可以留意一下Apache Harmony里的其中一种hashcode计算方式,第16页:Design a Product-Ready JVM for Apache Harmony

Mono在使用不移动对象的GC时,采用的hashcode算法来自Thomas Wang,Address Based Hash Function

C代码

 

那要是GC会移动对象呢,例如说采用了拷贝式收集器或者压缩式收集器的话?一个办法是可以拿对象第一次被分配的地址为hashcode的源,另一个办法是干脆无视对象的地址,用别的办法来得到能够区分对象身份的值;还有些别的办法是上面两种的混合。Xiao-Feng在Object hashcode implementation一帖中描述了Apache Harmony实现hashcode的三种方案,可以参考阅读一下。

C代码

 

C#代码

 

引用

其实.NET标准库里有System.Runtime.InteropServices.GCHandle这么个值类型。它的作用主要体现在托管代码与native code的互操作中,以避免需要用到的托管对象在native code执行过程中意外被GC回收。GCHandle也可以把对象“定”(pin)住,以避免native code访问对象的过程中对象地址发生改变。

GCHandle有个AddrOfPinnedObject()方法,正好可以提供对象的地址;使用GCHandle还附带了保证对象不被回收的功能,对这帖想要玩的代码是正合适。

3、元素为值类型且Blittable的数组

C#代码

 

引用

Unhandled Exception: System.ArgumentException: Object contains non-primitive or
non-blittable data.
   at System.Runtime.InteropServices.GCHandle.InternalAlloc(Object value, GCHandleType type)
   at TestCLR2Crash.Program.Main(String[] args) in F:documentVisual Studio 2008ProjectsTestDev9TestCLR2CrashProgram.cs:line 8

C#代码

 

GCHandle.Alloc()文档中对此有提及。我差点看漏了以为文档没说……

有趣的是,GCHandle对类型的挑剔还不止于此。GCHandle.AddrOfPinnedObject()方法乍一看像是返回对象自身的起始地址,实际不然,返回的是对象的“数据区”的起始地址。对System.String来说,“数据区”就是实际存放字符的char数组(CLR的String实现把char数组融合到String里了,没有单独的“char数组成员”。忽略ObjHeader,跳过MethodTable指针和其它成员,例如m_arrayLength、m_stringLength,跳到m_firstChar也就是融合后char数组的开始);而对数组来说,“数据区”就是实际存放值的区域(忽略ObjHeader,跳过MethodTable指针和Length);对其它Blittable类型来说,“数据区”就是Object里MethodTable指针之后的部分。

C#代码

 

引用

X86 asm代码

 

在家实验的同学们千万注意了:要自行尝试引发错误的话,一定要小心,不要在有重要资料的系统上试。任意篡改代码或者栈上/堆上的数据,实际会引发什么后果很难预料。发生什么糟糕后果责任要自己承担的哦~

X86 asm代码

 

C#代码

 

引用

Process is terminated due to StackOverflowException.

X86 asm代码

 

引用

Unhandled Exception: System.Runtime.InteropServices.SEHException: External component has thrown an exception.

引用

> 0012f4f9()
mscorwks.dll!_CallDescrWorker@20() + 0x33 bytes
mscorwks.dll!_CallDescrWorkerWithHandler@24() + 0x9f bytes
mscorwks.dll!MethodDesc::CallDescr() + 0x15a bytes
mscorwks.dll!MethodDesc::CallTargetWorker() + 0x1f bytes
mscorwks.dll!MethodDescCallSite::CallWithValueTypes() + 0x1a bytes
mscorwks.dll!ClassLoader::RunMain() - 0x39028 bytes
mscorwks.dll!Assembly::ExecuteMainMethod() + 0xa4 bytes
mscorwks.dll!SystemDomain::ExecuteMainMethod() + 0x416 bytes
mscorwks.dll!ExecuteEXE() + 0x49 bytes
mscorwks.dll!__CorExeMain@0() + 0x98 bytes
mscoree.dll!__CorExeMain@0() + 0x34 bytes
kernel32.dll!_BaseProcessStart@4() + 0x23 bytes

X86 asm代码

 

P.P.S. 查了文档,Silverlight 3里的GCHandle.Alloc和GCHandle.AddrOfPinnedObject果然是SecurityCritical的,从用户代码里无法调用它。然而Type.GetMethod和Type.GetField之类还是Transparent的,还是有搞头——至少在Moonlight上 XD

P.P.P.S. 呜,但是FieldInfo.SetValue只能用来设置可访问的域的值……又没搞头了

MSDN 写道

In Silverlight, only accessible fields can be set using reflection.  

小讯
上一篇 2024-12-30 08:19
下一篇 2024-12-24 13:42

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/298.html