逆向 订阅
《逆向》收录于胡夏2010年发行的第一张国语专辑《胡爱夏》,位于曲目3. 展开全文
《逆向》收录于胡夏2010年发行的第一张国语专辑《胡爱夏》,位于曲目3.
信息
中文名称
逆向
谱    曲
苏亦承
演唱者
胡夏
填    词
葛大为
逆向歌曲介绍
   逆向—男人的后悔与心碎 男版「可惜不是你」最主打「逆向」是一首狠适合的K歌,副歌狠容易朗朗上口。失恋最痛苦的莫过於「放不下」,葛大为的词先是细腻描述男人在分手后装作坚强的表面平静,这些逞强的自我安慰却只会加重失落感;到副歌塑造了一种「思念来袭」的过程,听歌的人思绪被「逆向」带回,痛苦与折磨又再次侵袭,才吐露出内心真正的疯狂后悔。胡夏在这首里充分表现出男人的压抑、爱逞强装没事的性格,以及副歌里真心诚\意的后悔与忏悔,也达到配唱制作人唱出「自我态度」的要求,让听歌的人感同身受。 [1] 
收起全文
精华内容
参与话题
问答
  • 逆向

    2011-08-09 10:22:30
    什么是逆向工程? 逆向工程(又名反向工程,Reverse Engineering-RE)是对产品设计过程的一种描述。在工程技术人员的一般概念中,产品设计过程是一个从设计到产品的过程,即设计人员首先在大脑中构思产品的外形、...
    
    

    什么是逆向工程?

    逆向工程(又名反向工程,Reverse Engineering-RE)是对产品设计过程的一种描述。在工程技术人员的一般概念中,产品设计过程是一个从设计到产品的过程,即设计人员首先在大脑中构思产品的外形、性能和大致的技术参数等,然后在详细设计阶段完成各类数据模型,最终将这个模型转入到研发流程中,完成产品的整个设计研发周期。这样的产品设计过程我们称为“正向设计”过程。逆向工程产品设计可以认为是一个从产品到设计的过程。简单地说,逆向工程产品设计就是根据已经存在的产品,反向推出产品设计数据(包括各类设计图或数据模型)的过程。从这个意义上说,逆向工程在工业设计中的应用已经很久了。比如早期的船舶工业中常用的船体放样设计就是逆向工程的很好实例。
      随着计算机技术在各个领域的广泛应用,特别是软件开发技术的迅猛发展,基于某个软件,以反汇编阅读源码的方式去推断其数据结构、体系结构和程序设计信息成为软件逆向工程技术关注的主要对象。软件逆向技术的目的是用来研究和学习先进的技术,特别是当手里没有合适的文档资料,而你又很需要实现某个软件的功能的时候。也正因为这样,很多软件为了垄断技术,在软件安装之前,要求用户同意不去逆向研究。
      逆向工程的实施过程是多领域、多学科的协同过程。


    为什么学习逆向工程?

    在2007年初,我国相关的法律为逆向工程正名,承认了逆向技术用于学习研究的合法性。获悉这个消息,国内软件逆向界人士纷纷奔走相告,雀跃不已。众所周知,我国的软件产业落后于西方列强,甚至落后于邻国印度和日本,而这个举措意味着我国的软件研发人员如果利用逆向技术去研究学习国外一流软件的开发方法,那么我国的软件技术将会有极大的提升,此举何其开明也!
      然而,逆向技术相关的培训实在少之又少,而各个大专院校虽然开设计算机相关专业,但是对此技术也尚未重视。社会上虽然涌现出大量软件开发培训机构,但是以培养软件蓝领为主要目标,对逆向技术更是避而远之。时下的IDE是极其优秀的,拜其所赐,市面上的程序员多出十几倍,但是又有多少能理解程序内部的机制呢?虽然很多人认为,去研究程序的内部原理,就是破坏了“黑盒子”封装性。但是如果只是能够在别人搭建好的平台上做开发,那么就只会被别人牵引技术方向,而自己始终只能追逐技术。如果我们能够充分掌握逆向分析的方法,就可以在一流的软件里直接学习各类先进技术,取长补短,为我所用。若如此,实为我国软件产业之幸事。

    怎么学习逆向工程?

     逆向工程是一个综合性和实践性很强的学科,最需要的是耐心和毅力。初学者往往一开始就拿一个成熟软件去反汇编分析,结果被海量的指令和错综复杂的流程搞得晕头转向,然后大多数人就中途放弃了。
      最好从自己用汇编写的“Hello World”程序开始。然后在学习的过程中逐步将难度升级,一点点去看懂流程特性。等把汇编程序看顺眼了,就开始研究C/C++的流程特性,一边在老师的带领下去学习研究,一边勤做笔记、总结方法。几个月下来,就会形成属于自己的一套分析代码的风格或习惯,这样以来,任何软件在你眼中都没有了神秘感,只要你愿意投入时间去分析就一定会得到你所需要的知识。

    展开全文
  • Reversing:逆向工程揭密

    热门讨论 2010-06-21 17:00:47
    1.2 软件逆向工程:逆向 4 1.3 逆向应用 4 1.3.1 与安全相关的逆向 5 1.3.2 软件开发中的逆向 8 1.4 底层软件 9 1.4.1 汇编语言 10 1.4.2 编译器 11 1.4.3 虚拟机和字节码 12 1.4.4 操作系统 13 1.5 逆向过程 13 ...
  • Malware Analysis Please refer to the additional document
    展开全文
  • 人工智能,零基础入门!... 一、新建一个maven项目,pom文件引入jar包依赖: ...-- MBG==MyBatis逆向工程代码生成依赖包 --> <dependency> <groupId>org.mybatis.generator</groupId> <...

    人工智能,零基础入门!http://www.captainbed.net/inner

    一、新建一个maven项目,pom文件引入jar包依赖:

    <!-- MBG==MyBatis逆向工程代码生成依赖包 -->
    <dependency>
        <groupId>org.mybatis.generator</groupId>
        <artifactId>mybatis-generator-core</artifactId>
        <version>1.3.5</version>
    </dependency>

    二、添加MBG配置文件generatorConfig.xml

    【1】图解

    generatorConfig.xml配置文件


    【2】详细代码

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE generatorConfiguration
      PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
      "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
    <generatorConfiguration>
    	<!-- context元素用于指定生成一组对象的环境。targetRuntime:此属性用于指定生成的代码的运行时环境。MyBatis3:*这是默认值*-->
    	<context id="testTables" targetRuntime="MyBatis3">
    		<commentGenerator>
    			<!-- 是否去除自动生成的注释 true:是 : false:否 -->
    			<property name="suppressAllComments" value="true" />
    		</commentGenerator>
    		<!--数据库连接的信息:驱动类、连接地址、用户名、密码 -->
    		<jdbcConnection 
    		    driverClass="com.mysql.jdbc.Driver"
    			connectionURL="${jdbc.url}" 
    			userId="${jdbc.username}"
    			password="${jdbc.password}">
    		</jdbcConnection>
    		<!-- targetProject:生成PO类的位置 -->
    		<javaModelGenerator targetPackage="com.zzw.pojo"
    			targetProject=".\src\main\java">
    			<!-- enableSubPackages:是否让schema作为包的后缀 -->
    			<property name="enableSubPackages" value="false" />
    			<!-- 从数据库返回的值被清理前后的空格 -->
    			<property name="trimStrings" value="true" />
    		</javaModelGenerator>
            <!-- targetProject:mapper映射文件生成的位置 -->
    		<sqlMapGenerator targetPackage="com.zzw.mapper" 
    			targetProject=".\src\main\java">
    			<!-- enableSubPackages:是否让schema作为包的后缀 -->
    			<property name="enableSubPackages" value="false" />
    		</sqlMapGenerator>
    		<!-- targetPackage:mapper接口生成的位置 -->
    		<javaClientGenerator type="XMLMAPPER"
    			targetPackage="com.zzw.mapper" 
    			targetProject=".\src\main\java">
    			<!-- enableSubPackages:是否让schema作为包的后缀 -->
    			<property name="enableSubPackages" value="false" />
    		</javaClientGenerator>
    		<!-- 指定数据库表 -->
    		<table tableName="test" schema="" >
    		</table>
    	</context>
    </generatorConfiguration>
    

    三、运行MyBatis Generator

    新建一个java类,使用junit测试方式或者main方式启动,这里采用main方法运行方式:

    【1】图

    【2】代码:

    import java.io.File;
    import java.util.ArrayList;
    import java.util.List;
    
    import org.mybatis.generator.api.MyBatisGenerator;
    import org.mybatis.generator.config.Configuration;
    import org.mybatis.generator.config.xml.ConfigurationParser;
    import org.mybatis.generator.internal.DefaultShellCallback;
    
    /**
     * 直接运行,生成mybatis代码
     * @author: zheng
     */
    public class GeneratorSqlmap {
    
    	public void generator() throws Exception{
    		List<String> warnings = new ArrayList<String>();
    		boolean overwrite = true;		
    		//指定 逆向工程配置文件	
    		File configFile = new File("src/main/resources/mybatis/generatorConfig.xml"); 
    		ConfigurationParser cp = new ConfigurationParser(warnings);		
    		Configuration config = cp.parseConfiguration(configFile);
    		DefaultShellCallback callback = new DefaultShellCallback(overwrite);
    		MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);		
    		myBatisGenerator.generate(null);
    	} 	
    	
    	public static void main(String[] args) throws Exception {		
    		try {
    			GeneratorSqlmap generatorSqlmap = new GeneratorSqlmap();			
    			generatorSqlmap.generator();
    		} catch (Exception e) {
    			e.printStackTrace();
    		}		
    	}
    
    }

    四、效果

     

     

     

    展开全文
  • Python爬虫:逆向分析某云音乐加密参数

    万次阅读 多人点赞 2020-09-05 09:54:07
    本篇博文通过对网易云音乐进行逆向分析,用Python代码模拟了AES和RSA加密过程,并在文章的末尾提供了一些参数,可以用这些参数来获取歌曲对应的歌词及用户的评论。

    前言

      免责声明:
        本篇博文的初衷是分享自己学习逆向分析时的个人感悟,所涉及的内容仅供学习、交流,请勿将其用于非法用途!!!任何由此引发的法律纠纷均与作者本人无关,请自行负责!!!
      版权声明:
        未经作者本人授权,禁止转载!!!

    在这里插入图片描述
      各大音乐平台是从何时开始收费的这个问题没有追溯过,印象中酷狗在16年就已经开始收费了,貌似当时的收费标准是付费音乐下载一首2元,会员一月8元,可以下载300首。虽然下载收费,但是还可以正常听歌。陆陆续续,各平台不仅收费,而且还更在乎版权问题,因为缺少版权,酷狗上以前收藏的音乐也不能听了,更过分的是,有些歌非VIP会员只能试听60秒(•́へ•́╬)。

    在这里插入图片描述

      版权问题重视起来当然是好事,但只是闲暇时来听听音乐放松一下自己的我来说,不会因为想听音乐而开通各个音乐平台的VIP的┗( ▔, ▔ )┛,所以渐渐就有了些想法:能不能将这些音乐整合起来,比如我去酷狗音乐听某一首歌,发现没有版权或只能试听,能不能自动去网易云音乐搜索下载到本地(干脆直接下载到酷狗对应的文件夹里),如果还没有就去QQ音乐、虾米音乐、百度音乐等等。

    在这里插入图片描述
      本篇就是在这样的背景下,通过对网易云音乐进行逆向分析,进而用代码的方式来*********(此处自己体会哦( ̄︶ ̄)↗)。
      目标:通过输入歌名或者歌手名,列出相应的音乐信息,然后通过选择某一项,将对应的音乐下载到本地指定目录。
      工具:Google Chrome、PyCharm
      这里以我最喜欢的歌手本兮为例,通过搜索网易云的Web端和PC端发现,Web端不支持下载,PC端需要RMB才能下载(不愧是我兮的歌(✪ω✪)),咳咳咳,OK,Fine,意料之中。

    在这里插入图片描述

    在这里插入图片描述

    1. 请求分析

      如果想要下载一首歌,我们首先要获取到这首歌所对应的 urlurl。随机选择一首歌进行播放,打开Chrome的开发者工具,刷新看一下对应的请求,找到我们想要的歌曲文件的 urlurl,就是下面这个:

    在这里插入图片描述

      然后找到该请求对应的 urlurl,分析一下该请求:

    在这里插入图片描述

      可知,获取数据的 urlurlhttps://music.xxx.com/weapi/song/enhance/player/url/v1?csrf_token=,请求方式为POST。继续往下滑,找到提交的数据:

    在这里插入图片描述

      POST提交了两个参数paramsencSecKey,很明显这两个参数都经过了加密处理,而且经过不断提交刷新发现,这两个参数值会变,可以猜测到加密时应该是有随机操作,但其长度始终不变,即参数params的长度为152,参数encSecKey的长度为256
      需要的 urlurl 及请求所需要的参数已经找到,下面需要确定一下两个参数是如何加密的。

    2. 参数分析

      通过Ctrl + Shift + F全局搜索参数encSecKey定位到了两个文件,然后在core_7a734ef25ee51b62727eb55c7f6eb1e8.js这个文件里通过Ctrl + F定位到了接口函数:

    在这里插入图片描述

      摘取这部分函数分析一下:

    var bVZ8R = window.asrsea(JSON.stringify(i0x), bqN0x(["流泪", "强"]), bqN0x(Wx5C.md), bqN0x(["爱心", "女孩", "惊恐", "大笑"]));
    e0x.data = j0x.cs1x({
        params: bVZ8R.encText,
        encSecKey: bVZ8R.encSecKey
    })
    

      函数window.asrsea()应该就是加密函数,传入四个参数,将加密后的结果赋值给变量bVZ8R,返回的结果有两个属性,即encTextencSecKey,也就是我们想要的参数paramsencSecKey。在这里设置一个断点,看一下这几个参数:

    在这里插入图片描述

      通过最右边的变量查看区Watch可以看到变量bVZ8R的值就是我们需要的参数的值,这证实了函数window.asrsea()就是加密函数,然后我们在控制台Console打印一下这几个变量:

    >JSON.stringify(i0x)
    <"{"csrf_token":""}"
    >bqN0x(["流泪", "强"])
    <"010001"
    >bqN0x(Wx5C.md)
    <"00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"
    >bqN0x(["爱心", "女孩", "惊恐", "大笑"])
    <"0CoJUm6Qyw8W8jud"
    

      即加密函数window.asrsea()所需的四个参数值已经确定,分别是字符串"{"csrf_token":""}""010001""00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7""0CoJUm6Qyw8W8jud",如果没有猜错的话第三个参数是十六进制的形式,其实也就是如此。通过几次刷新,这几个值不变。

    3. 加密分析

      百度搜索发现函数window.asrsea()不是JavaScript的原生函数,应该是开发者自己定义的,然后我通过搜索asrsea定位到了该函数的初始定义位置:

    在这里插入图片描述

      函数window.asrsea()就是函数d,它就是我们要找的加密函数,它接收的d、e、f、g四个参数对应的就是window.asrsea()函数的四个参数,即

    	d = "{\"csrf_token\":\"\"}"
    	e = "010001"
    	f = "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"
    	g = "0CoJUm6Qyw8W8jud"
    

      或许已经发现了吧,这里面的函数名、变量名及参数都是一个字母,而且它们有的还相同,没错,这是一种很常见的反爬虫手段------JS代码混淆。
      摘取这部分加密函数分析一下:

        function a(a) {
            var d, e, b = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", c = "";
            for (d = 0; a > d; d += 1)
                e = Math.random() * b.length,
                e = Math.floor(e),
                c += b.charAt(e);
            return c
        }
    

      函数a的作用是从字符串"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"中随机生成长度为a的字符串。

        function b(a, b) {
            var c = CryptoJS.enc.Utf8.parse(b)
              , d = CryptoJS.enc.Utf8.parse("0102030405060708")
              , e = CryptoJS.enc.Utf8.parse(a)
              , f = CryptoJS.AES.encrypt(e, c, {
                iv: d,
                mode: CryptoJS.mode.CBC
            });
            return f.toString()
        }
    

      函数b的作用是对数据a进行AES加密,模式为CBC,最后通过toString()方法将结果转成字符串。

        function c(a, b, c) {
            var d, e;
            return setMaxDigits(131),
            d = new RSAKeyPair(b,"",c),
            e = encryptedString(d, a)
        }
    

      函数c的作用是对数据a进行RSA加密,返回的结果是十六进制形式的字符串。

        function d(d, e, f, g) {
            var h = {}
              , i = a(16);
            return h.encText = b(d, g),
            h.encText = b(h.encText, i),
            h.encSecKey = c(i, e, f),
            h
        }
    

      函数d的作用是对数据d进行加密,得到两个加密的结果encTextencSecKey,加密流程是通过函数a随机产生一个长度为16的字符串,然后通过函数b进行第一次AES加密,然后再通过函数b对第一次的加密结果进行一次AES加密,得到结果encText,即对应我们的params,最后通过函数c进行一次RSA加密,得到结果encSecKey

    4. 模拟加密

      这里使用一个非常强大的加密算法库-----PyCryptodome,具体使用方法请参考官方文档

      这里定义了一个EncryptText类,专门用来模拟JavaScript的加密过程:

    class EncryptText:
        def __init__(self):
            self.character = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
            self.iv = '0102030405060708'
            self.public_key = '010001'
            self.modulus = '00e0b509f6259df8642dbc35662901477df22677ec152b' \
                           '5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417' \
                           '629ec4ee341f56135fccf695280104e0312ecbda92557c93' \
                           '870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b' \
                           '424d813cfe4875d3e82047b97ddef52741d546b8e289dc69' \
                           '35b3ece0462db0a22b8e7'
            self.nonce = '0CoJUm6Qyw8W8jud'
    

      在函数d中打上断点,来分析看一下abc三个函数返回的结果,方便比对我们模拟的结果:

    在这里插入图片描述

      程序执行到函数a处,在最右边变量作用域区Scope可以看到各个变量的值及函数a返回的的结果i: "mEXyqHtNW5dxT5IK"
      这里先模拟函数a来随机产生长度为16的字符串,首先使用的是官方提供的API:Crypto.Random.get_random_bytes(N),返回长度为N的随机字节串。

        def create16RandomBytes(self):
            """
            # 产生16位随机字符, 对应函数a
            :return:
            """
            generated_string = get_random_bytes(16)
            return generated_string
    

      我们需要将该字节串通过decode()方法转换成字符串,但是随机产生的字节串是这样的:b'\xe0\xda\xf9\x8fd\xb4M\xaa\xa7\x1fW\xaay\x12\x90@',在转换字符串时就会产生UnicodeDecodeError,所以这里就自己写了一个方法:

        def create16RandomBytes(self):
            """
            # 产生16位随机字符, 对应函数a
            :return:
            """
            generate_string = random.sample(self.character, 16)
            generated_string = ''.join(generate_string)
            return generated_string
    

      该方法产生的结果就是16位随机的字符串:

    在这里插入图片描述
      程序执行到函数b处,传入的参数dg的值我们已经知道,看一下加密后的结果:

    在这里插入图片描述

      加密后的结果为encText: "eHhjXckqrtZkqcwCalCMx0QuU6Lj9L7Wxouw1iMCnB4=",下面来用官方的API来模拟一下:

        def AESEncrypt(self, clear_text, key):
            """
            AES加密, 对应函数b
            :param clear_text: 需要加密的数据
            :return:
            """
            # 数据填充
            clear_text = pad(data_to_pad=clear_text.encode(), block_size=AES.block_size)
            key = key.encode()
            iv = self.iv.encode()
            aes = AES.new(key=key, mode=AES.MODE_CBC, iv=iv)
            cipher_text = aes.encrypt(plaintext=clear_text)
            # 字节串转为字符串
            cipher_texts = base64.b64encode(cipher_text).decode()
            return cipher_texts
    

      我们将需要加密的数据"{"csrf_token":""}"传入到该函数中,看一下模拟的结果:

    在这里插入图片描述

      很nice,结果一模一样,然后再进行一次AES加密,因为第二次加密用到了函数a产生的16位随机字符,为了结果一致,这里也使用相同的随机字符进行模拟。先看一下原始的结果:

    在这里插入图片描述

      第二次AES加密产生的结果为encText: "JWuA4mdNsTdrLdDkD9UWs8ShPCZNK0n4BLpdQEDSAaD/kFKKih8XQp8W/mICYPlN",然后对比一下自己模拟的结果:

    在这里插入图片描述

      哈哈哈哈(⁎˃ᴗ˂⁎)也是OK的,结果一样。

      AES具体的加密原理这里不做过多的介绍,感兴趣的话可以参考相关的书籍或自行百度,这里只介绍一些基本概念。
      高级加密标准(Advanced(Advanced EncryptionEncryption Standard,AES)Standard,AES)是一种分组密码算法,又称RijndaelRijndael算法,是对称密钥加密中最流行的算法之一。AES的分组长度固定为128位,密钥长度则可以是128、192或256位。
      密码分组链模式,即CBC,是分组密码工作模式之一,它需要一个初始向量(Initialization(Initialization Vector,IV)Vector,IV)组进行异或运算,而且CBC模式要求数据长度必须是密码分组长度的整数倍。因此数据长度不够的话需要进行填充。

      最后就是RSA加密了,看一下函数c返回的结果:

    在这里插入图片描述

      很长的一串,长度为256:encSecKey: "d58e873a2e908c0599b497456f1842d1734e1d17e834a221ed84d828b06b149d0bac2ddd449e38b7e5e9ce53dcb1aa43a241742a2b273434b67825743fbca6371aa143a4460477704ba3fd33b517619386daf8da4c7fe8d67a604ea0e461aedee5ae2698400a6c7340ab250c97622aa221d871b7352d81ea09262978facf5480"
      下面来模拟一下,我首先使用的是官方的API:Crypto.PublicKey.RSA产生密钥对,然后使用Crypto.Cipher.PKCS1_OAEP进行加密,加密后的数据长度是256位,通过它进行请求 urlurl 时请求状态码是200,但请求的内容为空,由于RSA每次加密得到数据都不一样,所以目前我还没有好的想法来确定问题出在哪里。

        def RSAEncrypt(self, session_key):
            """
            RSA加密的结果每次都不一样
            :param session_key:
            :return:
            """
            # n和e构成公钥
            # (n, e)
            # key = RSA.RsaKey(n=int(self.modulus, 16), e=int(self.public_key, 16))
            key = RSA.construct(rsa_components=(int(self.modulus, 16), int(self.public_key, 16)))
            public_key = key.publickey()
            rsa = PKCS1_OAEP.new(key=public_key)
            cipher_text = rsa.encrypt(message=session_key).hex()
            return cipher_text
    

    在这里插入图片描述

      根据RSA加密原理,我就自己写了一个函数来模拟RSA加密的过程:

        def RSAEncrypt(self, i, e, n):
            """
            RSA加密, 对应函数c
            :param i:
            :return:
            """
            # num = pow(x, y) % z
            # 加密C=M^e mod n
            num = pow(int(i[::-1].encode().hex(), 16), int(e, 16), int(n, 16))
            result = format(num, 'x')
            return result
    

    在这里插入图片描述

      没错,也是一模一样的(^_^)Y Ya!!

      RSA是由美国麻省理工学院的三名密码学者RivestRivestShamirShamirAdlemanAdleman提出的一种基于大合数因式分解困难性的公开密钥密码,简称RSA密码。RSA算法基于一个十分简单的数论事实,即将两个大素数相乘很容易,但想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。由于这次只用到了加密过程,所以RSA的解密过程不做过多的涉及。
      加密运算:C=MeC=M^e modmod nn,其中CC是加密后的数据,MM是被加密的数据,ee是随机的一个整数,1<e<ϕ(n)1<e<\phi (n)ϕ(n)\phi (n)是一个数论函数,称为欧拉函数,表示在比nn小的正整数中与nn互素的数的个数,nn是两个大素数的乘积,eenn是公开的,它们构成了用户的公钥。

      整个加密流程我们模拟完了,结果也是正确的,但是,这里还存在一个问题,我们模拟出来的encText,也就是参数params长度不够。这里可以确定的是加密算法是没有错误的,传入的参数中d、e、f、g后面三个值是固定的,所以问题就基本锁定了:参数d的值不对。
      我继续debug,然后发现了一些端倪:函数d又接收到了新的参数d,它的值是这样的:

    在这里插入图片描述

      将它进行两次AES加密后encText的数据长度达到了128,说明这个还不是正确的,而且Network面板并没有出现我们想要的v1?csrf_token=,然后继续debug,最终得到了参数d真正的值:d: "{"ids":"[35440198]","level":"standard","encodeType":"aac","csrf_token":""}",最后我们看一下最终的结果:

    在这里插入图片描述

    在这里插入图片描述

      使用模拟加密获取到的两个参数再次发起请求,便可以得到我们想要的数据:

    在这里插入图片描述
      歌曲的文件对应的 urlurl 我们已经找到,根据结果可知,它是一个字符串,准确来说是个json格式的,而且里面只有一条数据是我们需要的,所以直接提取:

    在这里插入图片描述
      然后再去用代码请求该 urlurl,将请求到的内容以二进制形式进行保存,文件名后缀为.mp3

    5. 获取ID

      上面实现的只是一首歌的下载,如果要实现我们的要求,还需要再修改一些参数d,有两个参数需要注意,即idslevel,一个是歌曲的id,另一个应该是歌曲的质量(有标准、无损等,我猜的),这里只关注一个,那就是歌曲的id。很容易猜到,一首歌对应一个id,我们选择哪首歌,就会得到哪首歌的id,那在哪选择呢???毫无疑问,肯定是在搜索结果中选择的。
      正常情况下,我们输入歌手名,会搜索出来许多歌手的音乐,就像下面这样:

    在这里插入图片描述

      我们通过代码直接访问https://music.xxx.com/#/search/m/?s=本兮&type=1并不会得到我们想要的信息,该 urlurl 请求得到的是网站的源代码,不包含数据在里面,很明显是通过 JavaScriptJavaScript 动态获得的,所以我们要找到请求数据的 urlurl。打开Chrome的开发者工具,刷新看一下对应的请求,找到我们想要的数据,就是下面这个:

    在这里插入图片描述

      然后找到对应的 urlurl,分析一下该请求:

    在这里插入图片描述

      可知,获取数据的 urlurlhttps://music.xxx.com/weapi/cloudsearch/get/web?csrf_token=,请求方式为依旧是POST。继续往下滑,找到提交的数据:

    在这里插入图片描述

      POST提交了两个参数paramsencSecKey,和我们获取歌曲 urlurl 时一样,但参数params的长度变为了280,参数encSecKey的长度依旧不变,为256。由此可以确定,又是参数d发生了变化。经过几次debug,最终确定了参数d的值:d = "{"hlpretag":"<span class=\"s-fc7\">","hlposttag":"</span>","s":"本兮","type":"1","offset":"0","total":"true","limit":"30","csrf_token":""}"

    在这里插入图片描述

      结果也是一样的:

    在这里插入图片描述

      使用模拟加密获取到的两个参数再次发起请求,发现得到的结果是空的,然后改了一下,将字典转为json格式,AES二次加密后参数params长度变为了300,然而却得到了数据。和我们在开发者模式下看到的结果一样,里面包含歌曲名、歌曲的id以及歌手名等信息。

    在这里插入图片描述
      从Network更容易看到json里面的数据结构:

    在这里插入图片描述

      提取到的结果如下,分别是歌手名、歌曲名、歌曲id、时长、专辑名、专辑图片的url:

    在这里插入图片描述
      这里简单分析一下参数d,关键字s表示你要搜索的内容,关键字type表示搜索的类型(见下面的表格),如果需要下载其他歌手的歌曲,只需要将参数d中的关键字s的值改一下即可,为了方便,可以用input()方法传递这个值。

    typetype 含义
    1 单曲
    100 歌手
    10 专辑
    1014 视频
    1006 歌词
    1000 歌单
    1009 主播电台
    1002 用户

    6. 代码框架

    # -*- coding: utf-8 -*-
    # @Time    : 2020/9/2 11:23
    # @Author  : XiaYouRan
    # @Email   : youran.xia@foxmail.com
    # @File    : wangyiyun_music2.py
    # @Software: PyCharm
    
    import requests
    from Crypto.Cipher import AES, PKCS1_OAEP
    from Crypto.Util.Padding import pad
    from Crypto.PublicKey import RSA
    from Crypto.Random import get_random_bytes
    import random
    import base64
    import json
    import os
    
    
    class EncryptText:
        def __init__(self):
            self.character = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
            self.iv = '0102030405060708'
            self.public_key = '010001'
            self.modulus = '00e0b509f6259df8642dbc35662901477df22677ec152b' \
                           '5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417' \
                           '629ec4ee341f56135fccf695280104e0312ecbda92557c93' \
                           '870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b' \
                           '424d813cfe4875d3e82047b97ddef52741d546b8e289dc69' \
                           '35b3ece0462db0a22b8e7'
            self.nonce = '0CoJUm6Qyw8W8jud'
    
        def create16RandomBytes(self):
    
    
        def AESEncrypt(self, clear_text, key):
    
    
        def RSAEncrypt(self, i, e, n):
    
    
        def resultEncrypt(self, input_text):
            """
            对应函数d
            :param input_text:
            :return:
            """
            i = self.create16RandomBytes()
            encText = self.AESEncrypt(input_text, self.nonce)
            encText = self.AESEncrypt(encText, i)
            encSecKey = self.RSAEncrypt(i, self.public_key, self.modulus)
            from_data = {
                'params': encText,
                'encSecKey': encSecKey
            }
            return from_data
    
    
    class WangYiYunMusic(object):
        def __init__(self):
            self.headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
                                          'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'}
    
        def get_html(self, url, method='GET', from_data=None):
            try:
                if method == 'GET':
                    response = requests.get(url, headers=self.headers)
                else:
                    response = requests.post(url, from_data, headers=self.headers)
                response.raise_for_status()
                response.encoding = 'utf-8'
                return response.text
            except Exception as err:
                print(err)
                return '请求异常'
    
        def parse_text(self, text):
            ids_list = json.loads(text)['result']['songs']
            count = 0
            info_list = []
            print('{:*^80}'.format('搜索结果如下'))
            print('{0:{5}<5}{1:{5}<20}{2:{5}<10}{3:{5}<10}{4:{5}<20}'.format('序号', '歌名', '歌手', '时长(s)', '专辑', chr(12288)))
            print('{:-^84}'.format('-'))
            for id_info in ids_list:
                song_name = id_info['name']
                id = id_info['id']
                time = id_info['dt'] // 1000
                album_name = id_info['al']['name']
                picture_url = id_info['al']['picUrl']
                singer = id_info['ar'][0]['name']
                info_list.append([id, song_name, singer])
                print('{0:{5}<5}{1:{5}<20}{2:{5}<10}{3:{5}<10}{4:{5}<20}'.format(count, song_name, singer, time, album_name, chr(12288)))
                count += 1
                if count == 8:
                    # 为了测试方便, 这里只显示了9条数据
                    break
            print('{:*^80}'.format('*'))
            return info_list
    
        def save_file(self, song_text, download_info):
            filepath = './download'
            if not os.path.exists(filepath):
                os.mkdir(filepath)
            filename = download_info[1] + '-' + download_info[2]
            music_url = json.loads(song_text)['data'][0]['url']
            response = requests.get(music_url, headers=self.headers)
            with open(os.path.join(filepath, filename) + '.mp3', 'wb') as f:
                f.write(response.content)
                print("下载完毕!")
    
    
    if __name__ == '__main__':
        id_url = 'https://music.163.com/weapi/cloudsearch/get/web?csrf_token='
        song_url = 'https://music.163.com/weapi/song/enhance/player/url/v1?csrf_token='
    
        id_d = {
            "hlpretag": "<span class=\"s-fc7\">",
            "hlposttag": "</span>",
            "s": input("请输入歌名或歌手: "),
            "type": "1",
            "offset": "0",
            "total": "true",
            "limit": "30",
            "csrf_token": ""
        }
    
        encrypt = EncryptText()
        id_from_data = encrypt.resultEncrypt(str(id_d))
    
        wyy = WangYiYunMusic()
        id_text = wyy.get_html(id_url, method='POST', from_data=id_from_data)
        info_list = wyy.parse_text(id_text)
    
        while True:
            input_index = eval(input("请输入要下载歌曲的序号(-1退出): "))
            if input_index == -1:
                break
            download_info = info_list[input_index]
            song_d = {
                "ids": str([download_info[0]]),
                "level": "standard",
                "encodeType": "aac",
                "csrf_token": ""
            }
            song_from_data = encrypt.resultEncrypt(str(song_d))
    
            song_text = wyy.get_html(song_url, method='POST', from_data=song_from_data)
            wyy.save_file(song_text, download_info)
    
    

      测试结果如下,等有时间了再做一个GUI٩(๑>◡<๑)۶ :

    在这里插入图片描述
    在这里插入图片描述

    结束语

      最后,加一个彩蛋吧,这个代码不仅可以download,还可以搜集用户的评论、歌曲对应的歌词等信息,只需要改一下参数d和请求的 urlurl 即可。这里给出这些参数:

    功能 参数dd urlurl
    搜索信息 “{“hlpretag”:”<span class=“s-fc7”>",“hlposttag”:"",“s”:"你要搜索的信息",“type”:"1",“offset”:“0”,“total”:“true”,“limit”:“30”,“csrf_token”:""}" https://music.xxx.com/weapi/cloudsearch/get/web?csrf_token=
    下载音乐 “{“ids”:”[歌曲id]",“level”:"standard",“encodeType”:“aac”,“csrf_token”:""}" https://music.xxx.com/weapi/song/enhance/player/url/v1?csrf_token=
    下载歌词 “{“id”:”歌曲id",“lv”:-1,“tv”:-1,“csrf_token”:""}" https://music.xxx.com/weapi/song/lyric?csrf_token=
    搜集用户评论 “{“rid”:“R_SO_4_歌曲id”,“threadId”:“R_SO_4_歌曲id”,“pageNo”:“1”,“pageSize”:“20”,“cursor”:”-1",“offset”:“0”,“orderType”:“1”,“csrf_token”:""}" https://music.xxx.com/weapi/comment/resource/comments/get?csrf_token=

      这些参数并不是一成不变的,如果网站更新了这些参数,那就需要重新做分析了。

    开源代码仓库


      如果喜欢的话记得给我的GitHub仓库点个Star哦!ヾ(≧∇≦*)ヾ

    展开全文
  • 常用Android程序逆向与保护技术

    千人学习 2017-10-27 16:43:44
    常用Android程序逆向与保护技术视频教程,该课程对目前Android程序逆向技术与脱壳技术进行原理和工具介绍,Android程序保护方法和技术。移动应用加固(安全)保护技术介绍:代码混淆保护技术、Dex文件整体加密保护...
  • 任鸟飞逆向C++高级篇

    2020-09-08 09:31:04
    本课程为任鸟飞逆向C++高级篇,注重在逆向中运用的技巧和思维逻辑,掌握后相关工具与手法可以熟练运用,在反汇编的世界里如鱼得水。   本套课程不只是一套深入学习C++的课程,更是一套深入学习汇编逆向课程、...
  • Mybatis框架|Mybatis逆向工程

    千次阅读 2020-02-15 15:20:46
    文章目录一、项目开发顺序二、Mybatis逆向工程jar包下载三、Mybatis逆向工程测试1.generatorConfig.xml逆向工程配置文件2.测试Mybatis逆向工程 一、项目开发顺序 在一般的项目开发中,①开发人员根据需要设计出...
  • 任鸟飞逆向分析基础教程

    千人学习 2019-09-20 14:48:32
    本套课程为任鸟飞课程游戏逆向分析系列的入门课程,面向所有无基础的对游戏安全,对逆向分析感兴趣的学员。课程结合了C++,汇编,反外挂等元素,让你轻松走进游戏安全的大门。   逆向思路技术随笔,请关注我的...
  • Reversing--逆向工程揭密/安全技术大系 本书描述的是在逆向与反逆向之间展开的一场旷日持久的拉锯战。作者Eldad Eilam以一个解说人的身份为我们详尽地评述了双方使用的每一招每一式的优点与不足。  书中包含的...
  • 逆向工程】逆向工程阅读表 Android

    万次阅读 2019-12-14 18:58:27
    Android Offensive & Defensive Android Reverse Engineering
  • 《Android逆向小技巧①:从Activity下手找到切入点,逆向分析支付宝APP》 上一章,我们借助 Android SDK自带工具【Android Device Monitor】( 位于路径【tools\monitor.bat】)快速定位Activity上一个按钮对应的...
  • 反编译逆向助手

    热门讨论 2016-11-06 09:47:12
    第一步:下载逆向助手解压包,解压即可 第二步:给逆向助手的每一个目录都配置Path环境变量 第三步:使用cmd命令行进入要反编译apk的目录下,输入apktool d -f 应用程序名,得到新的文件夹,里面可以直接看到res目录...
  • 编译原理实验二:Python实现中文分词–最大正向匹配和最大逆向匹配 最大匹配法:最大匹配是指以词典为依据,取词典中最长单词为第一个次取字数量的扫描串,在词典中进行扫描(为提升扫描效率,还可以跟据字数多少...
  • Windows Win32 PInvoke.net: PInvoke.net is primarily a wiki, allowing developers to find, edit and add PInvoke* signatures, user-defined types, and any other information related to calling Win32 and o...
  • Data Structures Automatic Reverse Engineering of Data Structures from Binary Execution [PDF] Digging For Data Structures [PDF] Howard: a dynamic excavator for reverse engineering data structures [PDF...
  • iOS逆向开发案例

    万人学习 2019-02-26 15:12:09
    本套课程相信能够帮助到想学习逆向的你,为你节约大量的时间,将时间充分运用到实践的过程中,并非在逆向的“门前”爬坑。在这个系列里,我们从密码学入手,了解常见加密算法,通过数字签名等技术学习iOS应用签名。...
  • 逆向目录

    万次阅读 2020-06-05 15:36:00
    https://blog.csdn.net/Hello_MyDream/article/details/106505072汇编学习一 https://blog.csdn.net/Hello_MyDream/article/details/106515598JCC ... https://blog.csdn.net/Hello_MyDream/article/details...
  • 逆向工程】逆向工程阅读表 Network

    万次阅读 2019-12-14 18:57:38
    Network Reverse Engineering of Protocols from Network Traces [PDF]
  • Exploitation Automated Vulnerability Discovery Techniques [PDF] Dismantling Megamos Crypto: Wirelessly Lockpicking a Vehicle Immobilizer [PDF] Memory Graph Approach for Program Data Introspection and...
  • 190326 逆向-MFC逆向技巧

    千次阅读 2019-03-27 23:43:02
    MFC 简介 微软基础类库(英语:Microsoft Foundation Classes,简称MFC)是微软公司提供的一个类库(class libraries),以C++类的形式封装了Windows API,并且包含一个应用程序框架,以减少应用程序开发人员的工作...
  • 逆向工程

    千次阅读 2015-12-20 13:34:08
    逆向工程(Reverse Engineering)解析apk, 验证特性, 替换资源. 在批量生成应用时, 通过服务器脚本修改apk, 节约人力和时间. 我来讲述一下, 逆向工程的常用工具和方法.1. apkapk表示Application PacKage, 是zip文件, ...
  • 逆向工程】逆向工程阅读表 Books

    万次阅读 2019-12-14 18:58:12
    Books Modern X86 Assembly Language Programming: 32-bit, 64-bit, SSE, and AVX: Fundamentals of x86 assembly language programming. It focuses on the aspects of the x86 instruction set that are most rel...
  • 逆向工程】逆向工程阅读表 Basics

    万次阅读 2019-12-14 18:58:17
    Basics Reverse Engineering 101 Speaker Presentation Reverse Engineering 101 - NYU: Poly 2010: Intro to Reverse Engineering given at NYU:Poly on October 4th, 2010 by Aaron Portnoy and Peter Silberman....
  • Instruction Sets Intel® 64 and IA-32 Architectures Software Developer Manuals: These manuals describe the architecture and programming environment of the Intel® 64 and IA-32 architectures. ...
  • Assembly / Disassembly Analyzing Uncommon Firmware ARM v7 Disassembling UROBOROS - Reassembling Disassembling - a tool that can disassemble executables to the extent that the generated code can be a....
  • Windows Other HyperV and vmbus internals
  • Research Tools Genetic Programming for Reverse Engineering IR Transformation Synthesis for Assembly Instructions
  • Windows Patch Guard Bypassing PatchGuard on Windows x64 Disable PatchGuard Vista & Windows 7 Disable PatchGuard Windows 8 & Windows 10 Universal Patchguard and Driver Signature Enforcement Di...

空空如也

1 2 3 4 5 ... 20
收藏数 42,231
精华内容 16,892
关键字:

逆向