用火车头测试采集美拍的数据时无意中发现美拍的视频地址是一段加了混淆字符串的base64代码。如下图

于是好奇之下研究了下解密算法。具体过程省略800字。发现美拍的视频解密是通过js完成,于是找到了具体的解密代码,如下:

 ;(function(a) {
var b = "substring",
c = "split",
d = "replace",
e = "substr",
f = {
getHex: function(a) {
return {
str: a[b](4),
hex: a[b](0, 4)[c]("").reverse().join("")
}
},
getDec: function(a) {
var d = parseInt(a, 16).toString();
return {
pre: d[b](0, 2)[c](""),
tail: d[b](2)[c]("")
}
},
substr: function(a, c) {
var f = a[b](0, c[0]),
g = a[e](c[0], c[1]);
return f + a[b](c[0])[d](g, "")
},
getPos: function(a, b) {
return b[0] = a.length - b[0] - b[1],
b
},
decode: function(a) {
var b = this.getHex(a),
c = this.getDec(b.hex),
d = this[e](b.str, c.pre);
return window.atob(this[e](d, this.getPos(d, c.tail)))
}
};
a.decodeMp4 = f,
window.MP = a
} (window.MP || {}))

通过解密代码可以发现视频地址字符串是base64加密的,只不过在其中插入了一些混淆字符。只要清除混淆字符即可通过base64解密得到视频地址。

js怎样对字符进行base64加密、解密?如下:

 window.atob(str);//解密
window.btoa(str);//加密
//但后来发现这样是不兼容中文的,于是有了下面的兼容中文的方法
decodeURIComponent(escape(window.atob(d)));//解密
window.btoa(unescape(encodeURIComponent(str)));//加密

由于ie兼容问题,所以另外找了个封装好的:

 base64={
atob:function(src){//解密
//用一个数组来存放解码后的字符。
var str=new Array();
var ch1, ch2, ch3, ch4;
var pos=0;
//过滤非法字符,并去掉'='。
src=src.replace(/[^A-Za-z0-9\+\/]/g, '');
//decode the source string in partition of per four characters.
while(pos+4<=src.length){
ch1=this.deKey[src.charCodeAt(pos++)];
ch2=this.deKey[src.charCodeAt(pos++)];
ch3=this.deKey[src.charCodeAt(pos++)];
ch4=this.deKey[src.charCodeAt(pos++)];
str.push(String.fromCharCode(
(ch1<<2&0xff)+(ch2>>4), (ch2<<4&0xff)+(ch3>>2), (ch3<<6&0xff)+ch4));
}
//给剩下的字符进行解码。
if(pos+1<src.length){
ch1=this.deKey[src.charCodeAt(pos++)];
ch2=this.deKey[src.charCodeAt(pos++)];
if(pos<src.length){
ch3=this.deKey[src.charCodeAt(pos)];
str.push(String.fromCharCode((ch1<<2&0xff)+(ch2>>4), (ch2<<4&0xff)+(ch3>>2)));
}else{
str.push(String.fromCharCode((ch1<<2&0xff)+(ch2>>4)));
}
}
//组合各解码后的字符,连成一个字符串。
return str.join('');
},
btoa:function(src){//加密
//用一个数组来存放编码后的字符,效率比用字符串相加高很多。
var str=new Array();
var ch1, ch2, ch3;
var pos=0;
//每三个字符进行编码。
while(pos+3<=src.length){
ch1=src.charCodeAt(pos++);
ch2=src.charCodeAt(pos++);
ch3=src.charCodeAt(pos++);
str.push(this.enKey.charAt(ch1>>2), this.enKey.charAt(((ch1<<4)+(ch2>>4))&0x3f));
str.push(this.enKey.charAt(((ch2<<2)+(ch3>>6))&0x3f), this.enKey.charAt(ch3&0x3f));
}
//给剩下的字符进行编码。
if(pos<src.length){
ch1=src.charCodeAt(pos++);
str.push(this.enKey.charAt(ch1>>2));
if(pos<src.length){
ch2=src.charCodeAt(pos);
str.push(this.enKey.charAt(((ch1<<4)+(ch2>>4))&0x3f));
str.push(this.enKey.charAt(ch2<<2&0x3f), '=');
}else{
str.push(this.enKey.charAt(ch1<<4&0x3f), '==');
}
}
//组合各编码后的字符,连成一个字符串。
return str.join('');
},
enKey:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
deKey: new Array(
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
-1, 0, 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, -1, -1, -1, -1, -1,
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1
),
}

解密算法已经找到,但我想要的是加密算法,所以只能通过解密算法反推加密算法,于是展开了一系列烧脑操作。终于发现了加密的原理(大概可能是)。

首先说说加密的原理:

1、先用base64对视频地址进行加密。

2、在视频地址前面加上一个4位字符串,字符串要满足以下条件:

①必须是四位16进制的字符串。

②字符串的10进制必须也是一个四位整数。(这个四位整数很重要,用来确定随机字符串的插入位置和个数的)

③插入加密地址前的是四位16进制的字符串的倒序。

3、通过开头加上的4位字符串确定随机字符串以及插入的位置。(前后相应位置都加上一段随机字符串)

看着原理是不是一头大?不用急,现在慢慢来解析一下:

现在用第一张图上的字符串来说明一下解密的过程,然后就能反推加密原理:

base64混淆加密后的地址:b901aHR0TDcDovL212dmlkZW8xLm1laXR1ZGF0YS5jb20vNTlhZmU3MDRmMTBiOTQ3ODIubXA0P2s9NDNiNzhkYjZmYmE1ZjNlZWM4NjY3NTM2MTkxNjI3ZGUmdD01OWIzNjdejNA==

①首先前面4位16进制的字符串为b901,因为是倒序添加的,所以实际上为109b。

②109b对应的10进制为4251。

③通过4251推算,前面添加的随机字符串位置为第4个字符开始,添加2个随机字符串;后台添加的随机字符串位置为倒数第5个添加1个随机字符串。

得出上面的混淆加密的随机字符为(用*号替代):

aHR0**cDovL212dmlkZW8xLm1laXR1ZGF0YS5jb20vNTlhZmU3MDRmMTBiOTQ3ODIubXA0P2s9NDNiNzhkYjZmYmE1ZjNlZWM4NjY3NTM2MTkxNjI3ZGUmdD01OWIzNjd*jNA==

去掉*号的内容即为真实的base64加密地址:aHR0cDovL212dmlkZW8xLm1laXR1ZGF0YS5jb20vNTlhZmU3MDRmMTBiOTQ3ODIubXA0P2s9NDNiNzhkYjZmYmE1ZjNlZWM4NjY3NTM2MTkxNjI3ZGUmdD01OWIzNjdjNA==

然后就可以通过普通的base64解密方法来解密视频地址了。

 decodeURIComponent(escape(window.atob('aHR0cDovL212dmlkZW8xLm1laXR1ZGF0YS5jb20vNTlhZmU3MDRmMTBiOTQ3ODIubXA0P2s9NDNiNzhkYjZmYmE1ZjNlZWM4NjY3NTM2MTkxNjI3ZGUmdD01OWIzNjdjNA==')));
//得出内容为:http://mvvideo1.meitudata.com/59afe704f10b94782.mp4?k=43b78db6fba5f3eec8667536191627de&t=59b367c4

知道了加密原理,那么推算出加密算法就只是时间问题了。

。。。又经过一系列的测试整理,终于出版了js版的base64随机字符混淆加密、解密方法,如下:

 ;(function(base64){
var substring='substring',
split='split',
reverse='reverse',
join='join',
toString='toString',
substr='substr',
replace='replace',
fn={
getHex: function(str) {//获取前4位标记数字
return {
str: str[substring](4),//排除前4位字符串
hex: str[substring](0, 4)[split]("")[reverse]()[join]("")//前4位倒序
}
},
getDec: function(str) {//获取混淆字符位置坐标
str = parseInt(str, 16)[toString]();//前4位倒序的16进制
//str[substring](0, 2)[split]("");
return {
pre: str[substring](0, 2)[split](""),//前面坐标
tail: str[substring](2)[split]("")//后面坐标
}
},
delStr: function(str, pos) {//混淆的字符抽取
var s = str[substring](0, pos[0]),
del = str[substr](pos[0], pos[1]);//需替换的字符
return s + str[substring](pos[0])[replace](del, "");//返回替换完成后的base64字符串
},
getPos: function(str, pos) {
return [str.length - pos[0] - pos[1],pos[1]];
},
decode: function(str) {//解密
var sh = this.getHex(str),//获取前4位标记数字
pos = this.getDec(sh.hex),//获取混淆位置坐标
d = this.delStr(sh.str, pos.pre);//前面混淆的字符抽取
d=this.delStr(d, this.getPos(d, pos.tail));
return decodeURIComponent(escape(this.atob(d)));//base64转成utf-8(兼容中文) atob
},
encode:function(str){//加密
var base64=this.btoa(unescape(encodeURIComponent(str))),//转换成base64格式
random=this.getRanNum(base64),//获取16进制是4位数的随机字符
pos = this.getDec(random);//获取混淆位置坐标
base64 = this.addStr(base64, pos);//插入混淆字符
//console.log(random,pos)
return random[toString]()[split]("")[reverse]()[join]("")+base64;
},
addStr: function(str, pos) {//混淆的字符插入
var r1=this.getRanStr(pos.pre[1]),//获取随机字符串(前)
r2=this.getRanStr(pos.tail[1]),//获取随机字符串(后)
pre=this.insertStr(str,r1,pos.pre[0]),//插入随机字符串(前)
tail=pre.length - pos.tail[0];
str=this.insertStr(pre,r2,tail);//插入随机字符串(后)
return str;
},
atob:function(src){//解密
//用一个数组来存放解码后的字符。
var str=new Array();
var ch1, ch2, ch3, ch4;
var pos=0;
//过滤非法字符,并去掉'='。
src=src.replace(/[^A-Za-z0-9\+\/]/g, '');
//decode the source string in partition of per four characters.
while(pos+4<=src.length){
ch1=this.deKey[src.charCodeAt(pos++)];
ch2=this.deKey[src.charCodeAt(pos++)];
ch3=this.deKey[src.charCodeAt(pos++)];
ch4=this.deKey[src.charCodeAt(pos++)];
str.push(String.fromCharCode(
(ch1<<2&0xff)+(ch2>>4), (ch2<<4&0xff)+(ch3>>2), (ch3<<6&0xff)+ch4));
}
//给剩下的字符进行解码。
if(pos+1<src.length){
ch1=this.deKey[src.charCodeAt(pos++)];
ch2=this.deKey[src.charCodeAt(pos++)];
if(pos<src.length){
ch3=this.deKey[src.charCodeAt(pos)];
str.push(String.fromCharCode((ch1<<2&0xff)+(ch2>>4), (ch2<<4&0xff)+(ch3>>2)));
}else{
str.push(String.fromCharCode((ch1<<2&0xff)+(ch2>>4)));
}
}
//组合各解码后的字符,连成一个字符串。
return str.join('');
},
btoa:function(src){//加密
//用一个数组来存放编码后的字符,效率比用字符串相加高很多。
var str=new Array();
var ch1, ch2, ch3;
var pos=0;
//每三个字符进行编码。
while(pos+3<=src.length){
ch1=src.charCodeAt(pos++);
ch2=src.charCodeAt(pos++);
ch3=src.charCodeAt(pos++);
str.push(this.enKey.charAt(ch1>>2), this.enKey.charAt(((ch1<<4)+(ch2>>4))&0x3f));
str.push(this.enKey.charAt(((ch2<<2)+(ch3>>6))&0x3f), this.enKey.charAt(ch3&0x3f));
}
//给剩下的字符进行编码。
if(pos<src.length){
ch1=src.charCodeAt(pos++);
str.push(this.enKey.charAt(ch1>>2));
if(pos<src.length){
ch2=src.charCodeAt(pos);
str.push(this.enKey.charAt(((ch1<<4)+(ch2>>4))&0x3f));
str.push(this.enKey.charAt(ch2<<2&0x3f), '=');
}else{
str.push(this.enKey.charAt(ch1<<4&0x3f), '==');
}
}
//组合各编码后的字符,连成一个字符串。
return str.join('');
},
insertStr:function(str,addstr,pos){//往指定位置插入字符串
return str[substring](0,pos)+addstr+str[substring](pos);
},
getRanNum:function(str){//获取16进制是4位数的4位随机字符
var ranArr=[];
;(function(){
var n='',
length=str.length;
/** 4101开始16进制是4位数 **/
for(var i=4101;i<=9999;i++){//找出所有符合要求的16进制4位数
n=i[toString](16);//10转成16
if(length>=8&&!(Math.floor(i/100)%10===0||i%10===0)&&n.length===4){
//正常的base64编码长度大于8才前后加混淆字符
//console.log(i,n);
if(Math.floor(i/1000)<=length/2&&Math.floor(i%100/10)<=length/2){//混淆位置不能大于长度一半
ranArr.push(n);
}
}else if(i%100===0&&n.length===4){//只在前面插入混淆字符
if(Math.floor(i/1000)<=length){//混淆位置不能大于长度
ranArr.push(n);
}
}
}
}());
var length=ranArr.length,
ran = Math.round(Math.random()*(length-1));
return ranArr[ran];
},
enKey:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
deKey: new Array(
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
-1, 0, 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, -1, -1, -1, -1, -1,
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1
),
getRanStr:function(num){//获取指定个数随机字符串
var key=this.enKey.split("");
length=key.length,
res = "";
for(; num-- ;) {
var id = Math.round(Math.random()*(length-1));
res += key[id];
}
return res;
}
}
base64.tranCode=fn;
window.base64=base64;
}(window.base64||{}));

以上代码已经带有一些注释,就不详细说明了。不明白或有指教的请留言吧!

另附上php版的加密算法:

 class base64{
public function encode($str){//加密
if($str!=''){
$base64=base64_encode($str);
$random=$this->getRanNum($base64);//获取16进制是4位数的随机字符
$pos = $this->getDec($random);//获取混淆位置坐标
$base64 = $this->addStr($base64, $pos);//插入混淆字符
return join('',array_reverse(str_split($random))).$base64;
}
return null;
}
private function getRanNum($str){
$length=strlen($str);
$ranArr=array();
/** 4101开始16进制是4位数 **/
for($i=4101;$i<=9999;$i++){//找出所有符合要求的16进制4位数
$n=dechex($i);//10转成16
if($length>=8&&!(floor($i/100)%10===0||$i%10===0)&&strlen($n)===4){
//正常的base64编码长度大于8才前后加混淆字符
if(floor($i/1000)<=$length/2&&floor($i%100/10)<=$length/2){//混淆位置不能大于长度一半
array_push($ranArr,$n);
}
}else if($i%100===0&&strlen($n)===4){//只在前面插入混淆字符
if(floor($i/1000)<=$length){//混淆位置不能大于长度
array_push($ranArr,$n);
}
}
}
$ran = rand(0,count($ranArr)-1);
return $ranArr[$ran];
}
private function getDec($str){
$str = hexdec($str);//前4位倒序的16进制
return [
"pre"=> str_split(substr($str,0,2)),//前面坐标
"tail"=> str_split(substr($str,2))//后面坐标
];
}
private function addStr($str, $pos){
$r1=$this->getRanStr($pos["pre"][1]);//获取随机字符串(前)
$r2=$this->getRanStr($pos["tail"][1]);//获取随机字符串(后)
$pre=$this->insertStr($str,$r1,$pos["pre"][0]);//插入随机字符串(前)
$tail=strlen($pre) - $pos["tail"][0];
$str=$this->insertStr($pre,$r2,$tail);//插入随机字符串(后)
return $str;
}
private function getRanStr($num){//获取指定个数随机字符串
$enKey="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
$str=str_split($enKey);
$length=count($str);
$res = "";
for(; $num-- ;) {
$id = rand(0,$length-1);
$res .= $str[$id];
}
return $res;
}
private function insertStr($str,$addstr,$pos){
return substr($str,0,$pos).$addstr.substr($str,$pos);
}
}

算法兼容对美拍视频地址的解密!同时兼容其他内容的加密,同时兼容中文的加密、解密。

反推加密算法的过程是烧脑的,但也是有意思的,在成功的那一瞬间还是有点小兴奋的。

写了一个demo,有兴趣的可以下载看看,如图。下载地址为:https://github.com/zhouxitian/base64

欢迎指教!!!

以上代码只为研究学习使用。

base64随机字符混淆加密、解密-美拍视频地址解密,反推加密算法的更多相关文章

  1. 生成二维码 加密解密类 TABLE转换成实体、TABLE转换成实体集合(可转换成对象和值类型) COOKIE帮助类 数据类型转换 截取字符串 根据IP获取地点 生成随机字符 UNIX时间转换为DATETIME\DATETIME转换为UNIXTIME 是否包含中文 生成秘钥方式之一 计算某一年 某一周 的起始时间和结束时间

    生成二维码 /// <summary>/// 生成二维码/// </summary>public static class QRcodeUtils{private static ...

  2. 解析php混淆加密解密的手段,如 phpjm,phpdp神盾,php威盾

    原文 解析php混淆加密解密的手段,如 phpjm,phpdp神盾,php威盾 php做为一门当下非常流行的web语言,常常看到有人求解密php文件,想当年的asp也是一样.一些人不理解为什么要混淆( ...

  3. php+js的 authcode 混淆加密和解密,php和js可以通用加密和解密

    <script> //md5.js var hexcase = 0; function hex_md5(a) { return rstr2hex(rstr_md5(str2rstr_utf ...

  4. 使用Base64进行string的加密和解密 公钥加密—私钥签名

    使用Base64进行string的加密和解密   //字符串转bytesvar ebytes = System.Text.Encoding.Default.GetBytes(keyWord);//by ...

  5. 读取本地json文件,转出为指定格式json 使用Base64进行string的加密和解密

    读取本地json文件,转出为指定格式json   引用添加Json.Net 引用命名空间 using Newtonsoft.Json //读取自定目录下的json文件StreamReader sr = ...

  6. php混淆加密解密实战

    在查看别人的php源码的时候,我们经常会看到加密后的php代码.那么php加密原理是什么呢?怎么解密呢? 混淆加密 我们从百度随便搜索一个加密网站,例如:http://dezend.qiling.or ...

  7. base64和Xxtea的加密和解密

    base64和Xxtea的加密和解密 数据加密是web数据安全的一种方式,前几天拿到一个base64+xxtea加密的数据,现在在这里整理一下使用的过程.首先当然是全网站找解密方法,但是最后的结果不是 ...

  8. 爬虫之Js混淆&加密案例

    需求: 中国空气质量在线监测分析平台是一个收录全国各大城市天气数据的网站,包括温度.湿度.PM 2.5.AQI 等数据,链接为:https://www.aqistudy.cn/html/city_de ...

  9. JS客户端RSA加密,Java服务端解密

    常用语网页客户端对密码加密,在后端java解密还原 java代码依赖    <dependency>      <groupId>commons-codec</group ...

随机推荐

  1. 单双引号的区别,defined容易疏忽的小地方

    单双引号的区别(面试题)                    1.双引号可以解析变量,单引号不行                    2.双引号解析转义字符,单引号不解析转义字符.但是单引号能解析 ...

  2. CentOS 6 下无法wget https链接的解决方法

    CentOS6下最高版本的wget是1.11,但非常遗憾的是这个版本有bug,是没办法用来下载https链接的东西的,所以有些人为了避免这种情况会帮脚本加上不检查ssl的参数--no-check-ce ...

  3. 34. leetcode 447. Number of Boomerangs

    Given n points in the plane that are all pairwise distinct, a "boomerang" is a tuple of po ...

  4. Spring中使用Map、Set、List、数组、属性集合的注入方法配置文件

    (1)下边的一个Java类包含了所有Map.Set.List.数组.属性集合等这些容器,主要用于演示spring的注入配置: package com.lc.collection; import jav ...

  5. swift UITapGestureRecognizer获取点击事件点击的位置point

    func picTap(sender: UITapGestureRecognizer) { let point = sender.location(in: sender.view) } 其中获取的po ...

  6. 【EntityFramework 6.1.3】个人理解与问题记录(2)

    前言 才看完一季动漫,完结撒花,末将于禁,原为曹家世代赴汤蹈火!想必看过的都会知道这个,等一下要不吐槽一下翻拍的真人版,○( ^皿^)っHiahia-,好了快醒醒改办正事儿了,好的,我们接着上一篇文章 ...

  7. suffix tree

    文章出处:http://www.cnblogs.com/snowberg/archive/2011/10/21/2468588.html   3   What is a Suffix Tree Suf ...

  8. ARP协议详解RARP

    简单来说,ARP协议是IP地址转换成MAC地址的协议.链路层协议.过程如下: 1:首先,每个主机都会在自己的ARP缓冲区中建立一个ARP列表,以表示IP地址和MAC地址之间的对应关系. 2:当源主机要 ...

  9. 写java代码遇到的一些问题

    记录一些做论文实验写代码时遇到的问题. 数据库SQL语句方面: 1.java通过JDBC连接MySql数据库方法 参见:http://hzy3774.iteye.com/blog/1689525 2. ...

  10. 关于Genymotion无法启动虚拟设备的问题解决

    只针对于该问题的解决,只需要将该虚拟机的网络设为混杂模式即可. 具体原因,未知.,设置如下