Frhvjhhv 发表于 2021-2-28 17:18

某5影视ts视频wasm加密分析(wasm逆向)

首先,f12,可以看到网站很卡。是因为这个网站有脏代码,监听了f12,然后pushstate,即疯狂的向地址栏中写入数据,把内存占满。解决办法是hook pushstate。
然后就可以跟踪了。

跟踪的一个关键地方:
d.prototype.processSegment = function(e, t, i, n) {
                        var r = d.nup.Module;
                        this.mem || (this.mem = r._malloc(131072));//1
                        for (var a = 0; a < n; ) {
                            var o = Math.min(n - a, 131072);//2
                            r.HEAPU8.set(t.slice(a, a + o), this.mem),//3
                            this._processSegment(e, this.mem, i + a, o),//4
                            t.set(r.HEAPU8.slice(this.mem, this.mem + o), a),
                            a += o
                        }
                  }


上述代码1处是分配131072大小的内存,返回的数据就是分配的内存的起始地址(这个网站默认地址为6472960),3处是把视频数据分割成131072大小,然后塞入之前已经实例化的wasm内存中,视频的起始指针就是之前分配的内存的起始地址。
4处的参数就两个关键的地方:即视频在wasm的起始地址和视频在wasm的内存中的长度。
跟进4处,他往wasm的内存中又写了一些数据,这里先按下不表,然后就进入了关键的函数:
(func $_nup2p_process_segment (;236;) (export "_nup2p_process_segment") (param $var0 i32) (param $var1 i32) (param $var2 i32) (param $var3 i32)
    (local $var4 i32) (local $var5 i32)
    global.get $global9
    local.set $var4
    global.get $global9
    i32.const 16
    i32.add
    global.set $global9
    global.get $global9
    global.get $global10
    i32.ge_s
    if
      i32.const 16
      call $abortStackOverflow
    end
    local.get $var4
    i32.const 4
    i32.add
    local.tee $var5
    local.get $var0
    i32.store
    local.get $var4
    local.get $var1
    i32.store
    local.get $var0
    if
      local.get $var5
      call $__ZNSt3__26__treeINS_12__value_typeIi14nup2p_secret_tEENS_19__map_value_compareIiS3_NS_4lessIiEELb1EEENS_9allocatorIS3_EEE4findIiEENS_15__tree_iteratorIS3_PNS_11__tree_nodeIS3_PvEElEERKT_
      local.tee $var0
      i32.const 104472
      i32.ne
      if
      local.get $var0
      i32.load8_s offset=44
      if
          local.get $var1
          local.get $var3
          local.get $var4
          local.get $var0
          i32.const 20
          i32.add
          local.get $var2
          call $_decode_bcrypto_buf
          drop
      end
      end
    end
    local.get $var4
    global.set $global9
)


在上面的wasm代码中,__ZNSt3__26__treeINS_12__value_typeIi14nup2p_secret_tEENS_19__map_value_compareIiS3_NS_4lessIiEELb1EEENS_9allocatorIS3_EEE4findIiEENS_15__tree_iteratorIS3_PNS_11__tree_nodeIS3_PvEElEERKT_和_decode_bcrypto_buf很重要
wasm代码肯定是要跟着走一遍的,然后配合jeb和ida分析。首选jeb。因为jeb对wasm的反汇编代码质量很高,阅读性好。但是这个例子中因为导入了很多函数,而且频繁的操作内存,所以jeb会报错。只能用ida了。关键还是看懂wam的汇编代码,走一遍。
wasm很简单,不懂的指令查官网就行了https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md
在这里我要说的指令就两个:i32.load。和i32,store。wasm要想操作内存,就只能用这两个指令。(i32.load8,i64.load8,i32.load16,offset=7等等原理都是一样的)。i32.load就是加载一个四个字节的数,注意wasm的字节都是以小端存储的


在这里,执行i32.load之前,堆栈里的数是104472.那么我们就要去找内存地址104472,然后加载四个字节



可以看到这四个数分为152,196,98,0
我们把他们转化为二进制:
152:10011000
196:11000100
98:   1100010
0    :    0
由于是小端存储,所以低地址在前,高地址在后
所以加载的数的二进制表示形式是:0|1100010|11000100|10011000(|为分隔符)
转换为十进制为6472856
检验一下:


可以看到是正确的。

然后继续跟踪下去。这里放上我跟踪一遍之后的伪代码:
int memory={}
nup2p_process_segment(int var0, int var1,int var2,int var3)
int var4,var5;
var4=global9=107696
global9+=16
global10=5350544
if(global9>globa110)
{
$abortStackOverflow(16);
}
var 5=var4+4   //var 0=11,,var 5=10770x
var0=memory
var1=memory//var1=6472960
if(var0!=undefined)
{
   var0=$__ZNSt3__26__treeINS_12__value_typeIi14nup2(var5)//6468856
       if (var0!=104472)
       {
             if(memory!=undefined)
                     {       //var2=786432;var0+20=6468820;var3=131072;var1=6472960;var4=107696
                             decode_bcrypto_buf(var1,var3,var4,var0+20,var2)
                                                      
                       }
       }
}



         //                         6468876
         //64729601310721076966468820786432
decode_bcrypto_buf(var0,var1,var2,var3,var4)
int var6,var7,var8,var9=0;
if(var3)
{
    if(memory)
           {
             var5=memory//8
                   var8=65535&&var5   //8
             if(var5)
                   {
                     var5=memory;//8
                           if(var5)
                           {
                              var2=var5      //8
                          
                           }//64729071
                           else
                           {
                             cc=var2
                             var2=call $__Znam(var1)
                             memory=cc;
                          
                           }
                           var10=var1>>3;//131072>>3=16384
                           var5=var4;
                              for()
                                      {//6468884: 80   98
                                             if(var9!=var10)
                                               {
                                                  memory= //6472960
                                                       memory^memory[(var3+8)+(var5%var8)] ;35^memory(80) //(1)
                                                        memory[ var2+(var6=var7|1)]=//6472960+(0|1)
                                                       (memory)^memory[(var3+8)+(var5+1)%8]   //6468885
                                                       
                                                        memory[ var2+(var6=var7|2)]
                                                        (memory) ^memory[(var3+8)+(var5+2)%8]   memory(89)
                                               }
                                          }
                   }
           }
}


$__ZNSt3__26__treeINS_12__value_typeIi14nup2(var0)//107700
int var1,var2;
block1;
{
var1=104472;
//memory;//6468800
var2=$__ZNSt3__26__treeINS_12__value_typeIi14nup2p_secret_tEEN(var0,memory,var1)//var2=648856
if(var2==104472)
{goto block1; }
if(memory<memory)
{goto block1;}
var1=var2;
}
return var1;





$__ZNSt3__26__treeINS_12__value_typeIi14nup2p_secret_tEEN(var0,var1,var2)//1077006468800104472
int var3=memory;//12 (2)
for (var1>0)   
{
          var2=memory<var3? var2:var1;//11<12   12==12   6468856
                var0=memory<var3;//1       0
                var1=memory//6468856
}
return var2//6468800
                //6468856





跟踪一遍可以发现在decode_bcrypto_buf函数的1处,他对于原来的视频数据每隔8个字节为一组。分别对这一组的每个字节进行了一次异或,然后覆盖了原来的字节,其中关键的参数是var3+8这个内存地址的数据。var5能被8整除。所以异或的第二个参数就是0,1,2一直到8。所以原来的视频数据每隔8个字节为一组,这一组的每个字节分别与var3+8,var3+8+1,var3+8+2等等一直到vae3+8+7的地址处的数据进行异或。
,var3var3最终是在
$__ZNSt3__26__treeINS_12__value_typeIi14nup2p_secret_tEEN(var0,var1,var2)这个函数的2处进行调用的。

多次跟踪可以发现,内存地址不固定,而且内存地址的值不固定。
所以,内存地址的值就是在最前面按下不表那里写入的。
跟踪可以发现
         string: function(e) {
                var t = 0;
                if (null != e && 0 !== e) {
                  var i = 1 + (e.length << 2);
                  stringToUTF8(e, t = stackAlloc(i), i)
                }
                return t
            },


其中,e = "http://0.0.0.0“,stringToUTF8首先是望wasm的前14个字节写入了一些数据stackAlloc函数调用的是wasm代码:
(func $stackAlloc (;165;) (export "stackAlloc") (param $var0 i32) (result i32)
    (local $var1 i32)
    global.get $global9
    local.set $var1
    local.get $var0
    global.get $global9
    i32.add
    global.set $global9
    global.get $global9
    i32.const 15
    i32.add
    i32.const -16
    i32.and
    global.set $global9
    global.get $global9
    global.get $global10
    i32.ge_s
    if
      local.get $var0
      call $abortStackOverflow
    end
    local.get $var1
)


翻译成伪代码为:
stackAlloc(var0) //57
global9=107696
var1=107696
global9=var0+global9
global9=(global9+15)^(-16)
globa110=5350544
if(global9>globa110)
{
   abortStackOverflow(var0);
}   
return var1

最终可以发现是调用的这句话填充数据的:
(func $_nup2p_start (;179;) (export "_nup2p_start") (param $var0 i32) (param $var1 i32)
    (local $var2 i32)
    i32.const 104424
    i32.load
    i32.eqz
    if
      i32.const 128
      call $__Znwm
      local.set $var2
      i32.const 0
      global.set $global5
      i32.const 65
      local.get $var2
      local.get $var0
      local.get $var1
      call $invoke_viii
      global.get $global5
      local.set $var0
      i32.const 0
      global.set $global5
      local.get $var0
      i32.const 1
      i32.and
      if
      call $___cxa_find_matching_catch_2
      local.set $var0
      call $getTempRet0
      drop
      local.get $var2
      call $__ZN8CryptoPP19UnalignedDeallocateEPv
      local.get $var0
      call $___resumeException
      else
      i32.const 104424
      local.get $var2
      i32.store
      i32.const 44
      call $__Znwm
      local.tee $var0
      i64.const 0
      i64.store
      local.get $var0
      i64.const 0
      i64.store offset=8
      local.get $var0
      i64.const 0
      i64.store offset=16
      local.get $var0
      i64.const 0
      i64.store offset=24
      local.get $var0
      i64.const 0
      i64.store offset=32
      local.get $var0
      i32.const 0
      i32.store offset=40
      local.get $var0
      call $__ZN2nu5FilesC2Ev
      i32.const 104428
      local.get $var0
      i32.store
      i32.const 104424
      i32.load
      local.get $var0
      call $__ZN2nu6Engine9addPluginEPNS_6PluginE
      end
    end
)
而这里面又调用了其他的函数,所以慢慢分析是最终是可以解出他是如何填充数据的,但是太慢了。我们跳出来看看他解密前后的数据:

可以看到解密后的数据中从第22个开始就是255.已知视频是每八个一组进行解密。那么我们从第24个开始分别让原来的视频数据与155进行异或,往后推8个,不就得到了那8个异或的参数?
说干就干
,在上图第一个断点处拦截一下,然后console处运行:

var array = Array.from(t)
arr=^255,array^255,array^255,array^255,array^255,array^255,array^255,array^255]
arra=new Uint8Array(array.length)
for (var ii=0; ii<array.length;ii+=8)
{
for(var j=0;j<8;j++)
{arra=(array^arr);}



}

var blob2 = new Blob();var aaaa = document.createElement('a');
var url2 = window.URL.createObjectURL(blob2);
var filename ="vv.mp4";
aaaa.href = url2;
aaaa.download = filename;
aaaa.click();


视频下载下来了,打开一下:



成功!

凌晨四点半 发表于 2021-3-2 15:58

这种没意思

FUCKPINKGUN 发表于 2021-3-21 18:35

不错不错,感觉这思路也可以套用在其他在线影视网站上,比如PH之类……

逍遥一仙 发表于 2021-2-28 23:31

挺好的,方法和我想一块去了。下载可以用我下载器插件来处理。{:301_988:}

nonamer 发表于 2021-3-1 09:02

谢谢楼主分享~

我总是用菊花 发表于 2021-3-1 02:13

请问一下hook pushstate 要怎么操作==

a3322a 发表于 2021-3-1 06:51

这加密者也是费了不少功夫

羋羋羋 发表于 2021-3-1 06:56

好文要顶!

wzz131 发表于 2021-3-1 07:17

学习一下,试试手!

km7687547 发表于 2021-3-1 08:32

学习一下,试试手!

dj215 发表于 2021-3-1 08:43

啥脏代码啊~

临时户口 发表于 2021-3-1 09:11

是破解了以后跳过下载ts直接下载到视频源MP4格式的吗?
页: [1] 2 3 4 5 6 7 8
查看完整版本: 某5影视ts视频wasm加密分析(wasm逆向)