转载

Java谜题5:球(ball)-解决方案

如果你不能投球,那就没什么乐趣了。但扩展Throwable也使它实现了可序列化,这就是真正有趣的开始。使用序列化,我们可以创建一个球,该球应该被捕获的次数与序列化数据声明的次数相同。

这场比赛似乎破坏了乐趣。你不能抛出它;但更重要的是你不能序列化它。如果您试图直接将球序列化,它也将尝试序列化它所附加到的游戏,从而导致NotSerializableException。

请注意,从技术上讲,从球到其比赛的参考只是一个类似于 this$0的字段。我说的“附加”是指指定给那个字段。因此,这个问题相当于序列化一个具有不可序列化的正常字段的对象。

问题在于我们根本不需要序列化游戏和球。我们只需要将球反序列化,并将其附加到游戏中。该Game实例不需要直接来自序列化流。我们可以在它的位置放置替代品,并覆盖readResolve,在它附着到球之前用一个game替换它。

在实践中作弊

有多种方法可以创建包含附加到替代游戏的球的原始数据(字节数组)。我们创建了一个类似于ball(ba)的类。我们给它和ball一样的serialversionuid和我们期望的caught值。它附加到我们的替代实现readResolve(Player)上。我们将其序列化,并将ba类的名称替换为ball类。将byte[]转换为一个String并返回,允许我们使用string.replace。

选择ba这个名称是为了让play.player$ba的长度与game.game$ball的长度相同。否则,直接用一个替换另一个会损坏流。

<b>package</b> play;
 
<b>import</b> game.Game;
<b>import</b> game.Game.Ball;
 
<b>import</b> java.io.*;
 
<b>public</b> <b>class</b> Player implements Serializable {
 
    <b>public</b> <b>static</b> <b>void</b> main(String[] args) throws Exception {
        ByteArrayOutputStream bos = <b>new</b> ByteArrayOutputStream();
        <b>new</b> ObjectOutputStream(bos).writeObject(<b>new</b> Player().<b>new</b> Ba());
        byte[] bytes = <b>new</b> String(bos.toByteArray(), <font>"ISO-8859-1"</font><font>)
                .replace(</font><font>"play.Player$Ba"</font><font>, </font><font>"game.Game$Ball"</font><font>)
                .getBytes(</font><font>"ISO-8859-1"</font><font>);
        Ball ball = (Ball) <b>new</b> ObjectInputStream(<b>new</b> ByteArrayInputStream(bytes))
                .readObject();
        ball.caught();
    }
 
    <b>class</b> Ba implements Serializable {
        <b>static</b> <b>final</b> <b>long</b> serialVersionUID = -7172046060844866133L;
 
        <b>private</b> <b>long</b> caught = -1;
    }
 
    Object readResolve() {
        <b>return</b> <b>new</b> Game();
    }
}
</font>

这就是发生的情况:

  • 在调用时readObject(),首先Ball获取反序列化,然后caught设置为-1。
  • Ball.this$0反序列化的值是一个实例Player。
  • 在Player分配给该字段之前(因为它的类型错误,它将失败),readResolve调用其方法,创建一个新Game的score0
  • 这Game被分配给Ball.this$0,readObject()返回Ball。
  • ball.catched()是用catched=-1和这个$0.score==0来调用的,你作弊就被抓住了!

结论

创建具有对不可序列化对象的引用的可序列化对象是一个坏主意,因为您无法对它们进行序列化。但是,你仍然可以反序列化它们。

Java序列化充满了令人讨厌的意外可能性。关于这一点你可以做一系列的谜题。但是,如果你真的陷入此种境地,那么你需要做的就是查看过去几年的JDK安全漏洞。

原文  https://www.jdon.com/53140
正文到此结束
Loading...