Adboe的Red5流媒体服务器免费并且是开源的,与Flash搭配的时候可谓是天生一对,但使用Java和Android作为客户端调用却可谓一波三折。
Adobe的Red5源代码里有一个RTMPClient的类,这个类在使用上其实不复杂,但却没办法成功调用。观察日志,发现是连接成功后在开始创建流的时候,服务端把连接断开了。我能想到的解释就是可能公司现在所使用的Red5服务器的版本与这个RTMPClient不兼容。
国内有人把Red5的RTMPClient精简了出来作为一个开源的类库放在google的svn上,网址如下:。这个类库同样也是没办法成功连接服务器。
国外还有一个收费的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替换成以下的两个即可。
完整代码请以这里下载:
没资源分的朋友请留下邮箱。
修改过的NetConnection:import java.io.File; import java.lang.reflect.Method; import java.util.Calendar; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import com.smaxe.logger.ILogger; import com.smaxe.uv.ProtocolLayerInfo; import com.smaxe.uv.Responder; import com.smaxe.uv.UrlInfo; import com.smaxe.uv.client.INetConnection; import com.smaxe.uv.client.a.d; import com.smaxe.uv.client.a.h; import com.smaxe.uv.client.a.i; import com.smaxe.uv.client.a.k; public final class UltraNetConnection extends i implements INetConnection { private final h a; private d b = null; private a c = null; private ExecutorService d = null; private ScheduledExecutorService e = null; private boolean f = false; private boolean g = false; private static final int h = 19; private static final int i = 9; private static final int[] j = { 5, 2, 7, 1, 0, 3, 6, 4 }; 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 }; public static void setSwfFileSizeAndHash(Map<String, Object> paramMap, File paramFile) throws Exception { a(paramMap, paramFile); } public UltraNetConnection() { this(null); } public UltraNetConnection(Map<String, Object> paramMap) { this(paramMap, null, null); } public UltraNetConnection(Map<String, Object> paramMap, ExecutorService paramExecutorService, ScheduledExecutorService paramScheduledExecutorService) { super(paramMap); this.d = (paramExecutorService == null ? Executors.newCachedThreadPool() : paramExecutorService); this.e = (paramScheduledExecutorService == null ? Executors.newSingleThreadScheduledExecutor() : paramScheduledExecutorService); this.f = (paramExecutorService == null); this.g = (paramScheduledExecutorService == null); this.a = new k(this.e); } public void addHeader(String paramString, boolean paramBoolean, Object paramObject) { this.b.a(paramString, paramBoolean, paramObject); } public void call(String paramString, Responder paramResponder, Object[] paramArrayOfObject) { if (!connected()) return; this.b.a(paramString, paramResponder, paramArrayOfObject); } public void close() { b(com.smaxe.uv.a.e.b("NetConnection.Connect.Closed", "Connection is closed.")); } 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(); try { Method bitchMethod = com.smaxe.uv.client.a.h.class.getMethod("a", String.class, String.class, int.class, Map.class); Object btichResult = bitchMethod.invoke(this.a, localUrlInfo.protocol, localUrlInfo.host, localUrlInfo.port, configuration()); Method[] aryMethod = com.smaxe.uv.client.a.d.class.getMethods(); for(Method method : aryMethod) { if(method.getName().equals("a") && method.getParameterTypes().length == 6) { method.invoke(this.b, this, btichResult, paramString, localUrlInfo.getApp(), this.c, paramArrayOfObject); break; } } } catch(Exception ex) { ex.printStackTrace(); } super.connect(paramString, paramArrayOfObject); } public boolean connected() { if (this.b == null) return false; return this.b.a() == 3; } public String connectedProxyType() { return connected() ? this.b.b() : null; } public boolean usingTLS() { return connected() ? this.b.c() : false; } public ProtocolLayerInfo getInfo() { return this.b.d(); } public int getUploadBufferSize() { return this.b.e(); } public void setMaxUploadBandwidth(int paramInt) { if (paramInt < 0) throw new IllegalArgumentException("Parameter 'bandwidth' is negative: " + paramInt); this.b.a(paramInt); } public void onBWDone() { } public void onBWDone(Object[] paramArrayOfObject) { } private void b(Map<String, Object> paramMap) { if (this.b == null) return; this.b.a(paramMap); if ((this.f) && (this.d != null)) this.d.shutdown(); if ((this.g) && (this.e != null)) this.e.shutdown(); this.d = null; this.e = null; } static void a(byte[] paramArrayOfByte) { if ((paramArrayOfByte == null) || (paramArrayOfByte.length != 25)) return; k = paramArrayOfByte; } d a() { return this.b; } private static void b(byte abyte0[]) throws IllegalArgumentException { int l = 0; for(int i1 = 1; i1 < abyte0.length - 1; i1++) l += abyte0[i1] & 0xff; l &= 0xff; int j1 = abyte0[1] & 0xf; if((abyte0[0] & 0xff) != (byte)(l >> 0 & 0xf) || (abyte0[abyte0.length - 1] & 0xff) != (byte)(l >> 4 & 0xf) || abyte0[1] + abyte0[abyte0.length - 2] != 15) a(16); boolean aflag[] = new boolean[21]; byte abyte1[] = new byte[8]; int k1 = 1; int l1 = j1; for(int i2 = 0; i2 < abyte1.length; i2++) { for(; aflag[l1 % aflag.length]; l1++); aflag[l1 % aflag.length] = true; abyte1[i2] = abyte0[2 + l1 % aflag.length]; k1 += 2; l1 += k1; } if((abyte1[1] & 0xf) != 3) a(32); boolean flag = (abyte1[3] & 0xf) >= 8; int j2 = (flag ? abyte1[3] - 8 : abyte1[3]) & 0xf; if(j2 < 1) a(1); if(flag) { Calendar calendar = Calendar.getInstance(); calendar.set(1, 2000 + (abyte1[4] & 0xf)); calendar.set(2, (abyte1[5] & 0xf) - 1); calendar.set(5, ((abyte1[6] & 0xf) << 4) + (abyte1[7] & 0xf)); if(System.currentTimeMillis() - calendar.getTimeInMillis() > 0L) a(18); } } private static void a(int paramInt) { switch (paramInt & 0xF) { case 0: throw new IllegalArgumentException(a(new long[] { 8460391658548064800L, 8315163859177334048L, 8319872964449869929L, 7205878151055483136L })); case 1: throw new IllegalArgumentException(a(new long[] { 8460391658548064800L, 8315163859177334048L, 8319309735340351598L, 7811060823377406308L, 7162256601089340786L, 8532478991051810162L, 120946281218048L })); case 2: throw new IllegalArgumentException(a(new long[] { 8462924959242482208L, 2314957309810076517L, 2335505025909089656L, 2378011653932580864L })); } } private static String a(long[] paramArrayOfLong) { byte[] arrayOfByte = new byte[paramArrayOfLong.length * 8]; int m = 0; for (int n = 0; n < paramArrayOfLong.length; n++) for (int i1 = 0; i1 < 8; i1++) { byte i2 = (byte)(int)(paramArrayOfLong[n] >> j[i1] * 8 & 0xFF); if (i2 == 0) break; arrayOfByte[(n * 8 + i1)] = i2; m++; } return new String(arrayOfByte, 0, m); } static void a(UltraNetConnection netconnection, String s, Exception exception) { netconnection.a(s, exception); } static void a(UltraNetConnection netconnection, String s) { netconnection.a(s); } static void a(UltraNetConnection netconnection, Map map) { netconnection.b(map); } static void b(UltraNetConnection netconnection, Map map) { netconnection.a(map); } static void c(UltraNetConnection netconnection, Map map) { netconnection.a(map); } private class a extends d.a { public a() { } public void a(String paramString, Exception paramException) { UltraNetConnection.a(UltraNetConnection.this, paramString, paramException); } public void a(String paramString) { UltraNetConnection.a(UltraNetConnection.this, paramString); } public void a(Map<String, Object> paramMap) { String str = (String)paramMap.get("code"); if ((!"NetConnection.Connect.Success".equals(str)) && (!"NetConnection.Connect.Bandwidth".equals(str)) && (!"NetConnection.Call.Failed".equals(str))) UltraNetConnection.a(UltraNetConnection.this, paramMap); UltraNetConnection.b(UltraNetConnection.this, paramMap); } public void a(long paramLong1, long paramLong2) { if (!((Boolean)UltraNetConnection.this.configuration().get("enableAcknowledgementEventNotification")).booleanValue()) return; Map localMap = com.smaxe.uv.a.e.b("NetConnection.Connect.Bandwidth", "'Acknowledgement' event notification."); localMap.put("acknowledgement", Long.valueOf(paramLong1)); localMap.put("info", new ProtocolLayerInfo(UltraNetConnection.this.getInfo())); localMap.put("uploadBufferSize", Long.valueOf(paramLong2)); UltraNetConnection.c(UltraNetConnection.this, localMap); } } }
顶 10 踩 0
我的同类文章
猜你在找
查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场