破解某国外收费的RTMP Client并成功在Android和Java上调用

Adboe的Red5流媒体服务器免费并且是开源的,与Flash搭配的时候可谓是天生一对,但使用Java和Android作为客户端调用却可谓一波三折。

         Adobe的Red5源代码里有一个RTMPClient的类,这个类在使用上其实不复杂,但却没办法成功调用。观察日志,发现是连接成功后在开始创建流的时候,服务端把连接断开了。我能想到的解释就是可能公司现在所使用的Red5服务器的版本与这个RTMPClient不兼容。

         国内有人把Red5的RTMPClient精简了出来作为一个开源的类库放在google的svn上,网址如下:http://code.google.com/p/android-rtmp-client/。这个类库同样也是没办法成功连接服务器。

         国外还有一个收费的RTMPClient,价值是395刀。具体网址和产品的名称我就不指出了,有心人肯定会找得到。这个客户端类库很强大,使用也很方便,我注册了一个试用的key,发现能和Red5服务器成功连接并且通讯良好。

由于实在是找不到其它的方法了,而且自己去摸索实现Red5的rtmp协议这基本上不太现实,于是我反编译了一下这个类库,发现除了几个入口类之外,其它类全是混淆过的。

其中最重要的几个类是NetConnection,NetStream, License,其中NetConnection,NetStream这两个类是负责创建连接和回调服务端的数据。而License则顾名思义是负责验证有没有授权。由于按照官方给出的使用说明,在使用前必须调用License.setKey()方法传入注册所得到的key。

         按照破解的习惯,一向是先尝试暴力破解,也就是绕过验证。于是先把License.setKey()这个方法调用注释掉,运行后抛出异常:

Exception inthread "main" java.lang.IllegalArgumentException: Your license key isinvalid!

         atcom.smaxe.uv.client.NetConnection.a(Unknown Source)

         atcom.smaxe.uv.client.NetConnection.b(Unknown Source)

         atcom.smaxe.uv.client.NetConnection.connect(Unknown Source)

         打开JD并定位到NetConnect这个类的cononect方法,发现反编译所得的代码如下:

public void connect(String paramString,Object[] paramArrayOfObject)

  {

   b(k);

   UrlInfo localUrlInfo = UrlInfo.parseUrl(paramString);

   com.smaxe.uv.client.a.e locale = new com.smaxe.uv.client.a.e();

   locale.a(this.d);

   locale.a((ILogger)configuration().get("logger"));

   this.b = locale;

   this.c = new a();

   this.b.a(this, this.a.a(localUrlInfo.protocol, localUrlInfo.host,localUrlInfo.port, configuration()), paramString, localUrlInfo.getApp(),this.c, paramArrayOfObject);

   super.connect(paramString, paramArrayOfObject);

  }

大家会发现b(k)这个方法调用有点古怪。再打开License类,其中setKey的代码如下:

public static void setKey(String paramString)

  {

   NetConnection.a(a(paramString));

  }

回过头来再看NetConnection的a(byte[])方法,如下:

static void a(byte[]paramArrayOfByte)

  {

    if ((paramArrayOfByte == null) ||(paramArrayOfByte.length != 25))

      return;

    k = paramArrayOfByte;

  }

果然NetConnection的b()就是用过验证是否具有授权的。把“Your license key is invalid!”作为特征码在所有文件中搜索了一次,却是无法搜索到结果。分析了一下,发现作者很聪明,预先把这句话编码成ASCII码,在使用的时候再将ASCII码转为字符串输出,这样就不能轻易地通过搜索特征码定位到验证的地方。

         不过可惜java的编译特点,在爆破的过程中定位到验证的代码实在是太容易了。下一步就是把整个NetConnection的反编译代码复制到一个新文件里,整理好引用后发现有一堆的错误,分析了一下大部分都是jd的反编译有点瑕疵,都是很容易可以修改好。但其中一个地方却是死活想不明白,代码如下:this.a.a(localUrlInfo.protocol, localUrlInfo.host,localUrlInfo.port, configuration()),eclipse的报错提示是Thetype com.smaxe.uv.a.c cannot be resolved. It is indirectly referenced fromrequired .class files。观察分析后发现com.smaxe.uv.a.c是一个包名,但同时也存在着com.smaxe.uv.a.c这个类,这在Java的编译机制里是不合法的,但Java的VM却是允许这样的存在形式的。混淆器应该就是利用了这一点的特性,将编译后的字节码文件修改成这样古怪的形式来“混淆视听”。

         绕过这种机制的方法很简单,就是利用反射,具体代码等会帖出来,但思考的过程差点把脑袋想破了。调用的方法请查看官方给出的例子,只需要把其中的NetConnection和NetStream替换成以下的两个即可。有需要的朋友请通过邮箱与我联系:[email protected]

修改过的NetConnection:

[java]  view plain copy
  1. import java.io.File;  
  2. import java.lang.reflect.Method;  
  3. import java.util.Calendar;  
  4. import java.util.Map;  
  5. import java.util.concurrent.ExecutorService;  
  6. import java.util.concurrent.Executors;  
  7. import java.util.concurrent.ScheduledExecutorService;  
  8.   
  9. import com.smaxe.logger.ILogger;  
  10. import com.smaxe.uv.ProtocolLayerInfo;  
  11. import com.smaxe.uv.Responder;  
  12. import com.smaxe.uv.UrlInfo;  
  13. import com.smaxe.uv.client.INetConnection;  
  14. import com.smaxe.uv.client.a.d;  
  15. import com.smaxe.uv.client.a.h;  
  16. import com.smaxe.uv.client.a.i;  
  17. import com.smaxe.uv.client.a.k;  
  18.   
  19.   
  20. public final class UltraNetConnection extends i  
  21.   implements INetConnection  
  22. {  
  23.   private final h a;  
  24.   private d b = null;  
  25.   private a c = null;  
  26.   private ExecutorService d = null;  
  27.   private ScheduledExecutorService e = null;  
  28.   private boolean f = false;  
  29.   private boolean g = false;  
  30.   private static final int h = 19;  
  31.   private static final int i = 9;  
  32.   private static final int[] j = { 52710364 };  
  33.   private static byte[] k = { 12345678910111213141516171819202122232425 };  
  34.   
  35.   public static void setSwfFileSizeAndHash(Map<String, Object> paramMap, File paramFile)  
  36.     throws Exception  
  37.   {  
  38.     a(paramMap, paramFile);  
  39.   }  
  40.   
  41.   public UltraNetConnection()  
  42.   {  
  43.     this(null);  
  44.   }  
  45.   
  46.   public UltraNetConnection(Map<String, Object> paramMap)  
  47.   {  
  48.     this(paramMap, nullnull);  
  49.   }  
  50.   
  51.   public UltraNetConnection(Map<String, Object> paramMap, ExecutorService paramExecutorService, ScheduledExecutorService paramScheduledExecutorService)  
  52.   {  
  53.     super(paramMap);  
  54.     this.d = (paramExecutorService == null ? Executors.newCachedThreadPool() : paramExecutorService);  
  55.     this.e = (paramScheduledExecutorService == null ? Executors.newSingleThreadScheduledExecutor() : paramScheduledExecutorService);  
  56.     this.f = (paramExecutorService == null);  
  57.     this.g = (paramScheduledExecutorService == null);  
  58.     this.a = new k(this.e);  
  59.   }  
  60.   
  61.   public void addHeader(String paramString, boolean paramBoolean, Object paramObject)  
  62.   {  
  63.     this.b.a(paramString, paramBoolean, paramObject);  
  64.   }  
  65.   
  66.   public void call(String paramString, Responder paramResponder, Object[] paramArrayOfObject)  
  67.   {  
  68.     if (!connected())  
  69.       return;  
  70.     this.b.a(paramString, paramResponder, paramArrayOfObject);  
  71.   }  
  72.   
  73.   public void close()  
  74.   {  
  75.     b(com.smaxe.uv.a.e.b("NetConnection.Connect.Closed""Connection is closed."));  
  76.   }  
  77.   
  78.   public void connect(String paramString, Object... paramArrayOfObject)  
  79.   {  
  80. //    b(k);  
  81.     UrlInfo localUrlInfo = UrlInfo.parseUrl(paramString);  
  82.     com.smaxe.uv.client.a.e locale = new com.smaxe.uv.client.a.e();  
  83.     locale.a(this.d);  
  84.     locale.a((ILogger)configuration().get("logger"));  
  85.     this.b = locale;  
  86.     this.c = new a();  
  87.     try {  
  88.         Method bitchMethod = com.smaxe.uv.client.a.h.class.getMethod("a", String.class, String.classint.class, Map.class);  
  89.         Object btichResult = bitchMethod.invoke(this.a, localUrlInfo.protocol, localUrlInfo.host, localUrlInfo.port, configuration());  
  90.         Method[] aryMethod = com.smaxe.uv.client.a.d.class.getMethods();  
  91.         for(Method method : aryMethod) {  
  92.             if(method.getName().equals("a") && method.getParameterTypes().length == 6) {  
  93.                 method.invoke(this.b, this, btichResult, paramString, localUrlInfo.getApp(), this.c, paramArrayOfObject);  
  94.                 break;  
  95.             }  
  96.         }  
  97.     } catch(Exception ex) {  
  98.         ex.printStackTrace();  
  99.     }  
  100.       
  101.     super.connect(paramString, paramArrayOfObject);  
  102.   }  
  103.   
  104.   public boolean connected()  
  105.   {  
  106.     if (this.b == null)  
  107.       return false;  
  108.     return this.b.a() == 3;  
  109.   }  
  110.   
  111.   public String connectedProxyType()  
  112.   {  
  113.     return connected() ? this.b.b() : null;  
  114.   }  
  115.   
  116.   public boolean usingTLS()  
  117.   {  
  118.     return connected() ? this.b.c() : false;  
  119.   }  
  120.   
  121.   public ProtocolLayerInfo getInfo()  
  122.   {  
  123.     return this.b.d();  
  124.   }  
  125.   
  126.   public int getUploadBufferSize()  
  127.   {  
  128.     return this.b.e();  
  129.   }  
  130.   
  131.   public void setMaxUploadBandwidth(int paramInt)  
  132.   {  
  133.     if (paramInt < 0)  
  134.       throw new IllegalArgumentException("Parameter 'bandwidth' is negative: " + paramInt);  
  135.     this.b.a(paramInt);  
  136.   }  
  137.   
  138.   public void onBWDone()  
  139.   {  
  140.   }  
  141.   
  142.   public void onBWDone(Object[] paramArrayOfObject)  
  143.   {  
  144.   }  
  145.   
  146.   private void b(Map<String, Object> paramMap)  
  147.   {  
  148.     if (this.b == null)  
  149.       return;  
  150.     this.b.a(paramMap);  
  151.     if ((this.f) && (this.d != null))  
  152.       this.d.shutdown();  
  153.     if ((this.g) && (this.e != null))  
  154.       this.e.shutdown();  
  155.     this.d = null;  
  156.     this.e = null;  
  157.   }  
  158.   
  159.   static void a(byte[] paramArrayOfByte)  
  160.   {  
  161.     if ((paramArrayOfByte == null) || (paramArrayOfByte.length != 25))  
  162.       return;  
  163.     k = paramArrayOfByte;  
  164.   }  
  165.   
  166.   d a()  
  167.   {  
  168.     return this.b;  
  169.   }  
  170.   
  171.   private static void b(byte abyte0[])  
  172.   throws IllegalArgumentException  
  173. {  
  174.   int l = 0;  
  175.   for(int i1 = 1; i1 < abyte0.length - 1; i1++)  
  176.       l += abyte0[i1] & 0xff;  
  177.   
  178.   l &= 0xff;  
  179.   int j1 = abyte0[1] & 0xf;  
  180.   if((abyte0[0] & 0xff) != (byte)(l >> 0 & 0xf) || (abyte0[abyte0.length - 1] & 0xff) != (byte)(l >> 4 & 0xf) || abyte0[1] + abyte0[abyte0.length - 2] != 15)  
  181.       a(16);  
  182.   boolean aflag[] = new boolean[21];  
  183.   byte abyte1[] = new byte[8];  
  184.   int k1 = 1;  
  185.   int l1 = j1;  
  186.   for(int i2 = 0; i2 < abyte1.length; i2++)  
  187.   {  
  188.       for(; aflag[l1 % aflag.length]; l1++);  
  189.       aflag[l1 % aflag.length] = true;  
  190.       abyte1[i2] = abyte0[2 + l1 % aflag.length];  
  191.       k1 += 2;  
  192.       l1 += k1;  
  193.   }  
  194.   
  195.   if((abyte1[1] & 0xf) != 3)  
  196.       a(32);  
  197.   boolean flag = (abyte1[3] & 0xf) >= 8;  
  198.   int j2 = (flag ? abyte1[3] - 8 : abyte1[3]) & 0xf;  
  199.   if(j2 < 1)  
  200.       a(1);  
  201.   if(flag)  
  202.   {  
  203.       Calendar calendar = Calendar.getInstance();  
  204.       calendar.set(12000 + (abyte1[4] & 0xf));  
  205.       calendar.set(2, (abyte1[5] & 0xf) - 1);  
  206.       calendar.set(5, ((abyte1[6] & 0xf) << 4) + (abyte1[7] & 0xf));  
  207.       if(System.currentTimeMillis() - calendar.getTimeInMillis() > 0L)  
  208.           a(18);  
  209.   }  
  210. }  
  211.   
  212.   private static void a(int paramInt)  
  213.   {  
  214.     switch (paramInt & 0xF)  
  215.     {  
  216.     case 0:  
  217.       throw new IllegalArgumentException(a(new long[] { 8460391658548064800L, 8315163859177334048L, 8319872964449869929L, 7205878151055483136L }));  
  218.     case 1:  
  219.       throw new IllegalArgumentException(a(new long[] { 8460391658548064800L, 8315163859177334048L, 8319309735340351598L, 7811060823377406308L, 7162256601089340786L, 8532478991051810162L, 120946281218048L }));  
  220.     case 2:  
  221.       throw new IllegalArgumentException(a(new long[] { 8462924959242482208L, 2314957309810076517L, 2335505025909089656L, 2378011653932580864L }));  
  222.     }  
  223.   }  
  224.   
  225.   private static String a(long[] paramArrayOfLong)  
  226.   {  
  227.     byte[] arrayOfByte = new byte[paramArrayOfLong.length * 8];  
  228.     int m = 0;  
  229.     for (int n = 0; n < paramArrayOfLong.length; n++)  
  230.       for (int i1 = 0; i1 < 8; i1++)  
  231.       {  
  232.         byte i2 = (byte)(int)(paramArrayOfLong[n] >> j[i1] * 8 & 0xFF);  
  233.         if (i2 == 0)  
  234.           break;  
  235.         arrayOfByte[(n * 8 + i1)] = i2;  
  236.         m++;  
  237.       }  
  238.     return new String(arrayOfByte, 0, m);  
  239.   }  
  240.   
  241.   static void a(UltraNetConnection netconnection, String s, Exception exception)  
  242.   {  
  243.       netconnection.a(s, exception);  
  244.   }  
  245.   
  246.   static void a(UltraNetConnection netconnection, String s)  
  247.   {  
  248.       netconnection.a(s);  
  249.   }  
  250.   
  251.   static void a(UltraNetConnection netconnection, Map map)  
  252.   {  
  253.       netconnection.b(map);  
  254.   }  
  255.   
  256.   static void b(UltraNetConnection netconnection, Map map)  
  257.   {  
  258.       netconnection.a(map);  
  259.   }  
  260.   
  261.   static void c(UltraNetConnection netconnection, Map map)  
  262.   {  
  263.       netconnection.a(map);  
  264.   }  
  265.     
  266.   private class a extends d.a  
  267.   {  
  268.     public a()  
  269.     {  
  270.     }  
  271.   
  272.     public void a(String paramString, Exception paramException)  
  273.     {  
  274.       UltraNetConnection.a(UltraNetConnection.this, paramString, paramException);  
  275.     }  
  276.   
  277.     public void a(String paramString)  
  278.     {  
  279.       UltraNetConnection.a(UltraNetConnection.this, paramString);  
  280.     }  
  281.   
  282.     public void a(Map<String, Object> paramMap)  
  283.     {  
  284.       String str = (String)paramMap.get("code");  
  285.       if ((!"NetConnection.Connect.Success".equals(str)) && (!"NetConnection.Connect.Bandwidth".equals(str)) && (!"NetConnection.Call.Failed".equals(str)))  
  286.         UltraNetConnection.a(UltraNetConnection.this, paramMap);  
  287.       UltraNetConnection.b(UltraNetConnection.this, paramMap);  
  288.     }  
  289.   
  290.     public void a(long paramLong1, long paramLong2)  
  291.     {  
  292.       if (!((Boolean)UltraNetConnection.this.configuration().get("enableAcknowledgementEventNotification")).booleanValue())  
  293.         return;  
  294.       Map localMap = com.smaxe.uv.a.e.b("NetConnection.Connect.Bandwidth""'Acknowledgement' event notification.");  
  295.       localMap.put("acknowledgement", Long.valueOf(paramLong1));  
  296.       localMap.put("info"new ProtocolLayerInfo(UltraNetConnection.this.getInfo()));  
  297.       localMap.put("uploadBufferSize", Long.valueOf(paramLong2));  
  298.       UltraNetConnection.c(UltraNetConnection.this, localMap);  
  299.     }  
  300.   }  
  301. }  

你可能感兴趣的:(java,android,null,byte,破解,流媒体服务器)