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

Java 同时被 3 个专栏收录
35 篇文章 0 订阅
18 篇文章 0 订阅
7 篇文章 0 订阅

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替换成以下的两个即可。

完整代码请以这里下载:http://download.csdn.net/detail/visualcatsharp/4294733

没资源分的朋友请留下邮箱。

修改过的NetConnection:

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




实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值

举报

选择你想要举报的内容(必选)
  • 内容涉黄
  • 政治相关
  • 内容抄袭
  • 涉嫌广告
  • 内容侵权
  • 侮辱谩骂
  • 样式问题
  • 其他
新手
引导
客服 举报 返回
顶部