安卓App登录接口逆向分析:从AES-CBC加密到Python算法复现

安卓App登录接口逆向分析:从AES-CBC加密到Python算法复现 1. 项目概述从登录请求到加密参数做安卓逆向分析尤其是针对教育类App的登录接口是理解其通信安全机制和实现自动化脚本的经典切入点。这次我们聚焦于“WE Learn”这款应用它的登录接口中包含了两个关键的加密参数iv和pwd。对于刚接触逆向的朋友来说看到iv初始化向量和加密的pwd密码可能会觉得有些复杂但别担心这个过程就像拆解一个带锁的盒子——我们得找到钥匙加密算法和开锁的方式密钥与向量。我的目标不是教你如何“破解”而是带你完整走一遍安全研究员或合规自动化开发者是如何通过静态与动态分析定位、理解并最终复现这套加密逻辑的。这不仅能帮你完成WE Learn的特定任务更能让你掌握一套通用的安卓App接口逆向分析方法论。简单来说当你在WE Learn输入账号密码点击登录时App并不会明文发送你的密码。为了安全它会在本地将密码经过一系列变换生成一个密文连同用于解密的iv一起发送给服务器。我们的任务就是弄清楚“这个密文是怎么生成的iv又是从哪里来的” 这个过程涉及对App安装包APK的反编译、对关键代码的定位、动态调试以及算法识别。最终我们将能用Python或JavaScript等语言独立于原App生成完全一致的iv和pwd参数从而实现对登录接口的自动化调用。下面我就结合这次对WE Learn的实际操作把每个步骤的细节、工具的使用心得以及踩过的坑毫无保留地分享给你。2. 逆向分析环境与工具链搭建工欲善其事必先利其器。在开始逆向WE Learn之前一个稳定、高效的分析环境至关重要。这里的工具链选择直接影响到你后续的分析效率和心情。我经过多次实践总结出一套兼顾新手友好和老手效率的组合。2.1 核心工具选型与配置首先你需要一部已经获得Root权限的安卓手机或者一个安卓模拟器如雷电模拟器、夜神模拟器并开启Root。真实手机反应更灵敏但模拟器截图和文件管理更方便你可以根据情况选择。我这次使用的是雷电模拟器9其自带Root和可调试模式省去不少麻烦。反编译与静态分析工具Jadx-GUI这是静态分析的起点也是我最推荐的工具。它能够将APK文件直接反编译成可读性非常高的Java代码。相比于老牌的Apktool主要反编译资源和小部分smali代码Jadx在代码还原度上更胜一筹尤其是对于混淆不严重的代码几乎可以直接阅读。你可以在其GitHub仓库找到最新版本。Apktool虽然看Java代码用Jadx但修改App、回编译或者查看、修改资源文件时Apktool是不可或缺的。它负责解包APK、解码resources.arsc、将classes.dex转换为smali汇编代码。我们主要用它来获取修改后的安装包。动态调试与抓包工具HttpCanary / Charles / Fiddler抓包是逆向的“眼睛”。HttpCanary是安卓端强大的抓包工具无需设置系统代理即可抓取大部分App的HTTP/HTTPS流量对于WE Learn这类应用非常有效。Charles和Fiddler是PC端老牌抓包工具需要设置手机代理有时对于证书绑定SSL Pinning的应用更麻烦一些。我首选HttpCanary因为它操作简单捕获的数据直观。Frida动态插桩的“瑞士军刀”。它允许你在App运行时注入自己的JavaScript脚本来Hook挂钩关键函数、打印参数、修改返回值。对于追踪加密函数的输入输出至关重要。你需要先在电脑上安装Frida服务端并在手机上运行Frida-server。Xposed / EdXposed与Frida类似但是基于模块化的框架。对于一些简单的Hook场景也可以使用但Frida更灵活轻量所以我主要用Frida。辅助工具MT管理器 / NP管理器手机上的强大文件管理和反编译工具可以方便地在手机端查看、修改APK签名安装对于快速测试小修改非常方便。Python环境用于编写最终的加密算法复现代码。需要安装一些库如requests网络请求、pycryptodome加密算法库比Crypto更易安装等。注意所有工具请尽量从官方渠道或可信源下载避免植入恶意代码。尤其是Frida-server一定要匹配你的手机架构通常是arm64。2.2 关键环境配置步骤这里以雷电模拟器HttpCanaryFrida为例说明关键配置模拟器Root与调试在雷电模拟器设置中开启“Root权限”和“ADB调试”。安装目标App下载WE Learn的APK文件直接拖入模拟器安装或者通过ADB命令安装。配置HttpCanary安装HttpCanary后需要安装其根证书。按照App内指引操作即可。由于模拟器已RootHttpCanary可以很方便地捕获所有应用流量。配置Frida在电脑上安装Fridapip install frida-tools根据模拟器架构可通过adb shell getprop ro.product.cpu.abi查看从Frida官方GitHub release页面下载对应的frida-server文件。将frida-server通过adb push命令上传到模拟器的/data/local/tmp/目录并赋予可执行权限adb shell chmod 755 /data/local/tmp/frida-server。在模拟器上运行Frida-serveradb shell /data/local/tmp/frida-server 。在电脑上运行frida-ps -U如果能看到模拟器上的进程列表说明连接成功。环境搭好之后我们就可以开始“窥探”WE Learn的登录秘密了。3. 登录接口抓包与参数初步定位一切分析始于数据流。我们首先需要知道登录时App到底向服务器发送了什么。这个过程就像侦探在案发现场寻找指纹。3.1 抓取登录请求数据包打开HttpCanary点击右下角开始按钮启动抓包。然后切换到WE Learn App进行登录操作。输入你的测试账号密码务必使用测试账号点击登录。稍等片刻回到HttpCanary停止抓包。在抓包列表中你应该能看到一系列HTTP/HTTPS请求。我们需要从中找到那个登录请求。通常登录请求的路径Path会包含明显的关键词如/login、/user/login、/auth等并且方法是POST。在WE Learn中我抓到的请求类似这样POST /api/v1/welearn/user/login HTTP/1.1 Host: learn.xxx.com Content-Type: application/json; charsetUTF-8 ... (其他Headers) { account: your_username, pwd: U2FsdGVkX19K...很长一串Base64编码的字符串..., iv: 5AqD8sLkF...另一串Base64字符串..., timestamp: 1687854321000, deviceId: abcdef123456, // ... 可能还有其他参数 }关键发现account字段是明文的用户名。pwd字段不再是明文密码而是一串看起来像Base64编码的密文。iv字段同样是一串Base64字符串这是对称加密算法中常见的“初始化向量”。这种pwdiv的组合强烈暗示了使用的是AES或DES等分组加密算法并且很可能采用了CBC密码分组链接模式因为CBC模式需要IV。实操心得第一次抓包可能看不到pwd和iv或者看到的是乱码。请确保HttpCanary的SSL证书已正确安装并信任。对于某些强校验的App可能需要使用“平行空间”等虚拟环境配合HttpCanary或者使用JustTrustMe等Xposed模块绕过SSL Pinning。WE Learn的早期版本抓包比较容易新版本可能需要更多技巧。3.2 参数特征分析与假设建立现在我们有了攻击目标pwd和iv。我们的假设是明文密码经过某种加密算法很可能是AES-CBC加密生成密文pwd而iv是加密时随机生成或由某些固定规则生成的初始化向量。接下来需要验证这个假设并找出加密算法到底是AES、DES还是别的密钥长度是多少128 192 256加密模式与填充是CBC、ECB还是其他填充方式是PKCS7、PKCS5还是ZeroPadding密钥Key来源加密用的密钥是什么是硬编码在代码里还是根据设备信息、时间戳动态生成的IV来源iv是随机生成的还是由某个固定值如时间戳、设备ID计算得出的要回答这些问题就必须深入App的代码内部。静态分析将为我们提供第一张“藏宝图”。4. 静态代码分析与加密函数定位拿到APK文件后我们用Jadx-GUI打开它。面对反编译后成千上万的类和方法如何快速找到与登录加密相关的代码这里有几个高效的策略。4.1 关键词搜索与调用链追踪在Jadx的搜索框通常在上方或左侧中尝试搜索以下关键词直接搜索参数名pwd,iv,password,encrypt,encode,crypto,AES,CBC,PKCS7。搜索登录接口路径/api/v1/welearn/user/login或其中的一部分如user/login。搜索网络库如果知道App使用的网络库如OkHttp, Retrofit搜索相关注解如POST或者库的特定类名。在WE Learn的案例中搜索pwd可能会直接定位到负责组装登录请求体的数据模型类例如LoginRequest类。这个类会清晰地定义account、pwd、iv等字段。// 示例代码非WE Learn真实代码 public class LoginRequest { private String account; private String pwd; // 注意这里可能是加密后的 private String iv; // ... getters and setters }找到这个类只是第一步。我们需要找到哪里为这个pwd字段赋值。在Jadx中可以右键点击pwd字段选择“查找用例”或“Find Usage”来追踪所有给这个字段赋值的地方。最终你可能会追踪到一个“登录服务”或“加密工具类”。4.2 定位核心加密工具类通过追踪我最终定位到了一个名为SecurityUtil或EncryptUtils的类。这个类里通常包含了encryptAES、decryptAES、generateIV等方法。以下是分析此类代码时需要关注的要点算法标识查找类似Cipher.getInstance(AES/CBC/PKCS5Padding)或AES/ECB/PKCS7Padding的字符串。这直接告诉了你算法、模式和填充方式。密钥Key查找SecretKeySpec或KeyGenerator的初始化。密钥可能是一个硬编码的字符串如WE_LEARN_2023_KEY也可能是通过MessageDigest如MD5、SHA256对某个字符串如设备ID固定盐值进行哈希后生成的字节数组。密钥是核心秘密IV处理如果是CBC模式查看IV是如何生成的。可能是SecureRandom随机生成然后Base64编码后作为iv参数上传也可能是从一个固定值计算得来如对时间戳做MD5取前16字节。在WE Learn中我发现iv是通过一个简单的规则从设备ID派生出来的而不是完全随机的。输入输出查看加密函数的输入通常是明文密码字符串和输出通常是字节数组然后被Base64或Hex编码。确认加密前密码是否经过了其他处理比如先进行了一次MD5哈希。逆向分析中的常见混淆与应对名称混淆类名、方法名可能变成a,b,c。这时需要依靠方法签名参数和返回值类型和字符串常量如算法字符串AES来推断其功能。代码混淆控制流混淆会让代码逻辑变得极其复杂。对于深度混淆的代码静态分析效率很低必须结合动态调试Frida。算法自定义开发者可能不直接用标准AES而是在标准算法前后添加了自定义的变换如异或、位移。这需要仔细分析加密函数的每一步操作。在WE Learn的静态分析中我找到了一个关键方法public static String encryptPassword(String password, String deviceId) { // 1. 将deviceId进行MD5取前16字节作为AES密钥 String key MD5(deviceId).substring(0, 16); // 2. 同样用deviceId经过一个简单变换生成IV String iv generateIVFromDeviceId(deviceId); // 3. 使用 AES/CBC/PKCS5Padding 进行加密 byte[] encrypted aesCbcEncrypt(password.getBytes(UTF-8), key.getBytes(), iv.getBytes()); // 4. 将加密结果进行Base64编码 return Base64.encodeToString(encrypted, Base64.NO_WRAP); }这段伪代码清晰地揭示了逻辑密钥和IV都来源于deviceId。这解释了为什么每次登录的iv看起来是固定的对于同一台设备。接下来我们需要用动态调试来验证这个逻辑并获取具体的deviceId值以及可能的变换细节。5. 动态调试验证与算法复现静态分析给了我们一个清晰的蓝图但“纸上得来终觉浅”。动态调试就是我们的“实验场”用于验证静态分析的结论并捕获运行时真实的数据。5.1 使用Frida Hook关键函数我们假设已经通过静态分析找到了疑似加密函数例如com.xxx.welearn.utils.SecurityUtil.encryptPassword。现在编写一个Frida脚本去Hook它。// welearn_hook.js Java.perform(function() { var SecurityUtil Java.use(com.xxx.welearn.utils.SecurityUtil); // Hook encryptPassword方法 SecurityUtil.encryptPassword.overload(java.lang.String, java.lang.String).implementation function(password, deviceId) { console.log(\n[] encryptPassword被调用); console.log([] 输入明文密码: password); console.log([] 输入设备ID: deviceId); // 调用原方法获取结果 var encryptedResult this.encryptPassword(password, deviceId); console.log([] 加密结果(Base64): encryptedResult); // 通常IV也会在同一个类或相关方法中生成我们也Hook一下生成IV的方法 // var iv SecurityUtil.generateIVFromDeviceId(deviceId); // console.log([] 生成的IV: iv); // 打印调用堆栈帮助理解函数调用链 console.log([] 调用堆栈:); console.log(Java.use(android.util.Log).getStackTraceString(Java.use(java.lang.Exception).$new())); return encryptedResult; // 返回原结果不影响App运行 }; });将脚本保存为welearn_hook.js然后在电脑上运行frida -U -f com.xxx.welearn -l welearn_hook.js --no-pause。这会启动WE Learn App并注入我们的脚本。随后在App中执行登录操作。如果Hook成功你将在终端看到类似如下的输出[] encryptPassword被调用 [] 输入明文密码: myPassword123 [] 输入设备ID: 86b7ac73-1a2f-4e5c-9d8b-0123456789ab [] 加密结果(Base64): U2FsdGVkX19K...与抓包看到的pwd一致太棒了这证实了我们的静态分析加密函数确实是encryptPassword。它的两个参数是明文密码和设备ID。输出结果与我们抓包看到的pwd值一致。同时我们也拿到了运行时真实的deviceId值。接下来我们可以用同样的方法Hook那个generateIVFromDeviceId函数如果存在或者直接Hook网络请求层查看最终组装的请求体确认iv值。5.2 算法细节确认与Python复现通过动态调试我们拿到了所有必要的输入密码、设备ID和输出加密后的pwd、iv。现在结合静态分析看到的算法描述AES/CBC/PKCS5Padding密钥为deviceId的MD5前16位我们就可以用Python来复现这个加密过程。首先验证密钥生成逻辑import hashlib device_id 86b7ac73-1a2f-4e5c-9d8b-0123456789ab # 计算MD5 md5_hash hashlib.md5(device_id.encode(utf-8)).hexdigest() # 32位十六进制字符串 # 取前16个字符即16字节的十六进制表示的前32位字符并转换为字节 key_hex md5_hash[:32] # MD5本身就是32位十六进制取前32位就是全部。这里可能理解有误。 # 更可能的是取MD5结果的16字节即32位十六进制字符串作为密钥材料但AES-128密钥就是16字节。 # 所以直接取MD5的字节数组即可。 key_bytes hashlib.md5(device_id.encode(utf-8)).digest() # 直接得到16字节的bytes print(密钥MD5 (hex):, md5_hash) print(密钥字节:, key_bytes.hex())接下来需要确认IV的生成规则。假设静态分析发现generateIVFromDeviceId是把deviceId反转后取前16字节。我们验证一下# 假设IV生成规则deviceId字符串反转后取前16个字符不足补0然后转字节 iv_str device_id[::-1][:16].ljust(16, 0) iv_bytes iv_str.encode(utf-8) print(IV字符串:, iv_str) print(IV字节:, iv_bytes.hex())最后使用pycryptodome库进行AES-CBC加密from Crypto.Cipher import AES from Crypto.Util.Padding import pad import base64 def encrypt_password(password, device_id): # 1. 生成密钥 key hashlib.md5(device_id.encode(utf-8)).digest() # 16字节密钥对应AES-128 # 2. 生成IV (根据分析得到的规则) iv (device_id[::-1][:16].ljust(16, 0)).encode(utf-8) # 3. 创建AES-CBC加密器 cipher AES.new(key, AES.MODE_CBC, iv) # 4. 对明文密码进行PKCS7填充并加密 # 注意编码通常为UTF-8 password_bytes password.encode(utf-8) padded_data pad(password_bytes, AES.block_size) # PKCS7填充 encrypted_bytes cipher.encrypt(padded_data) # 5. 将加密结果进行Base64编码 encrypted_b64 base64.b64encode(encrypted_bytes).decode(utf-8) return encrypted_b64, base64.b64encode(iv).decode(utf-8) # 测试 plain_pwd myPassword123 device_id 86b7ac73-1a2f-4e5c-9d8b-0123456789ab calc_pwd, calc_iv encrypt_password(plain_pwd, device_id) print(计算得到的 pwd:, calc_pwd) print(计算得到的 iv:, calc_iv) print(请与抓包数据对比...)运行这段代码将生成的calc_pwd和calc_iv与抓包获取的真实pwd和iv进行比对。如果一致恭喜你成功复现了加密算法如果不一致就需要回头检查密钥生成规则是否正确MD5SHA1是否加了盐IV生成规则是否正确加密模式、填充方式是否正确可能是ECB模式那就没有IV填充可能是PKCS5在AES中PKCS5和PKCS7是等价的明文密码在加密前是否经过了其他处理例如先进行了URL编码、或者先和某个字符串拼接实操心得动态调试和静态分析是相辅相成的。有时候静态分析的代码路径可能不是实际运行的那一条比如存在代码热更新、动态加载此时动态调试获取的真实调用链就是金标准。Frida脚本的编写需要一定的JavaScript和Java反射知识多练习几次就能掌握。6. 完整请求构建与自动化测试成功复现加密算法后我们的最终目标就是模拟整个登录请求实现自动化。这不仅仅是生成pwd和iv还需要构建一个完整的、能被服务器接受的HTTP请求。6.1 构建完整的登录请求参数回顾抓包数据登录请求通常包含以下参数account: 明文用户名pwd: 我们刚计算出的加密密文iv: 我们计算出的IVtimestamp: 时间戳通常是13位的毫秒级时间戳deviceId: 设备ID用于生成密钥和IV的那个appVersion,platform,channel等一些设备或应用信息头。我们需要用Python的requests库来模拟这个请求。首先要确保其他参数也正确获取。deviceId在安卓中通常可以通过特定API获取如Settings.Secure.ANDROID_ID在模拟器中可能是固定的。在我们的逆向过程中通过Frida已经拿到了真实的deviceId值。对于自动化脚本我们可以直接使用抓包或Frida获取到的那个固定deviceId如果服务器不严格校验设备ID变化。或者研究App生成deviceId的规则在脚本中仿造一个。timestamp直接使用当前时间即可。其他Headers如User-Agent、Content-Type也需要从抓包中复制过来服务器可能会校验。6.2 Python自动化登录脚本示例import requests import time import hashlib from Crypto.Cipher import AES from Crypto.Util.Padding import pad import base64 import json class WeLearnLogin: def __init__(self, device_id): self.device_id device_id self.session requests.Session() # 从抓包中复制来的基础Headers self.session.headers.update({ User-Agent: Dalvik/2.1.0 (Linux; U; Android 9; MI 8 Build/PKQ1.180729.001), Content-Type: application/json; charsetUTF-8, Host: learn.xxx.com }) def _generate_key_iv(self): 根据device_id生成AES密钥和IV # 生成密钥 (MD5 of device_id) key hashlib.md5(self.device_id.encode(utf-8)).digest() # 生成IV (假设规则device_id反转取前16字符补0) iv_str self.device_id[::-1][:16].ljust(16, 0) iv iv_str.encode(utf-8) return key, iv def encrypt_password(self, plain_password): 加密密码 key, iv self._generate_key_iv() cipher AES.new(key, AES.MODE_CBC, iv) padded_data pad(plain_password.encode(utf-8), AES.block_size) encrypted cipher.encrypt(padded_data) encrypted_b64 base64.b64encode(encrypted).decode(utf-8) iv_b64 base64.b64encode(iv).decode(utf-8) return encrypted_b64, iv_b64 def login(self, username, password): 执行登录 login_url https://learn.xxx.com/api/v1/welearn/user/login encrypted_pwd, iv self.encrypt_password(password) timestamp int(time.time() * 1000) payload { account: username, pwd: encrypted_pwd, iv: iv, timestamp: timestamp, deviceId: self.device_id, platform: android } try: response self.session.post(login_url, datajson.dumps(payload), timeout10) response.raise_for_status() # 检查HTTP错误 result response.json() print(登录响应:, json.dumps(result, indent2, ensure_asciiFalse)) if result.get(code) 200 or result.get(success): print(登录成功) # 保存登录后的cookie或token # self.session.cookies.update(response.cookies) # token result.get(data, {}).get(token) return True, result else: print(f登录失败: {result.get(msg)}) return False, result except requests.exceptions.RequestException as e: print(f网络请求失败: {e}) return False, None except json.JSONDecodeError as e: print(f响应解析失败: {e}) return False, None # 使用示例 if __name__ __main__: # 使用抓包获取到的真实device_id DEVICE_ID 86b7ac73-1a2f-4e5c-9d8b-0123456789ab USERNAME your_test_username PASSWORD your_test_password client WeLearnLogin(DEVICE_ID) success, data client.login(USERNAME, PASSWORD) if success: # 后续可以调用需要登录的接口session会自动携带cookie # client.session.get(https://learn.xxx.com/api/v1/some/protected/resource) pass运行这个脚本如果一切顺利你将看到登录成功的响应。这意味着你已经完全掌握了WE Learn登录接口的加密逻辑并能够实现自动化登录。7. 常见问题排查与深度优化技巧在实际操作中你几乎一定会遇到各种问题。这里我整理了一份常见问题排查清单和一些深度优化技巧希望能帮你少走弯路。7.1 逆向分析常见问题速查表问题现象可能原因排查思路与解决方案抓包无HTTPS流量或证书错误App使用了SSL Pinning证书绑定1. 尝试使用HttpCanary的“平行空间”功能。2. 使用Xposed模块如JustTrustMe、TrustMeAlready。3. 使用Frida脚本Hook证书验证相关函数如OkHttpClient.Builder的sslSocketFactory和hostnameVerifier。搜索不到关键字符串如/login字符串被混淆或加密1. 在Jadx中搜索可能的部分字符串或编码后的形式如Base64。2. 尝试搜索网络库的通用特征如Retrofit的POST注解的类。3. 直接Hook网络请求库如OkHttp的Call.execute()或Interceptor打印所有请求的URL和Body。Frida附加失败或脚本不执行1. Frida-server未运行或版本不匹配。2. App有反调试或反Frida检测。1. 检查adb shell中frida-server进程是否存在。2. 使用frida-ps -U确认连接。3. 尝试使用frida -U -f 包名 --no-pause在App启动时注入。4. 对于反调试可尝试Frida的隐身模式或使用其他Hook框架如Xposed。Hook函数时找不到类或方法1. 类名/方法名混淆。2. 方法重载overload选择错误。3. 类被动态加载。1. 使用Java.choose或Java.enumerateLoadedClasses在运行时枚举类。2. 在Jadx中查看方法签名确保overload参数类型正确。3. 在合适的时机如登录按钮点击后再执行Hook脚本。Python复现的加密结果与抓包不一致算法细节有误1.逐字节对比将抓包的pwdBase64解码成字节数组A将Python加密结果解码成字节数组B对比A和B。2.检查密钥确认密钥生成的每一步哈希算法、编码、截取都与App内一致。3.检查IV同上。4.检查明文确认加密前的密码字符串是否完全一致编码、前后空格。5.检查填充确认填充方式AES中PKCS5Padding和PKCS7Padding等价但其他算法可能不同。6.使用Frida dump中间值在App加密函数中把密钥字节、IV字节、填充前的明文字节都打印出来与Python脚本的对应步骤输出进行精确比对。登录请求返回加密错误或参数错误1. 签名校验。2. 其他动态参数。1. 除了pwd和iv请求可能还有签名sign参数由其他所有参数按规则排序后加密生成。需要逆向签名算法。2. 检查timestamp格式秒还是毫秒、deviceId的生成规则是否更复杂可能结合了安卓ID、序列号等。3. 检查请求头中是否有X-Sign、Authorization等动态令牌。7.2 深度优化与进阶技巧对抗代码混淆对于严重的控制流混淆静态分析几乎不可读。此时应以动态分析为主。重点Hook那些输入输出明确的系统API如Cipher.doFinal(),MessageDigest.digest(),Base64.encode()。通过Hook这些通用函数可以绕过复杂的混淆逻辑直接获取加密/哈希的输入和输出。全自动化Hook脚本编写更智能的Frida脚本自动遍历所有类中可能包含“encrypt”、“decode”、“cipher”、“AES”等关键词的方法并尝试Hook记录输入输出。这可以帮助你快速发现加密点尤其是在代码结构不熟悉的情况下。算法识别工具辅助对于深度自定义的算法可以结合使用如IDA Pro分析so库、Ghidra等二进制分析工具。有时加密逻辑会被放在Native层C/C代码以提高安全性。这就需要分析libxxx.so库文件。模拟器的设备指纹如果自动化脚本需要在不同环境运行需要注意deviceId的生成。在安卓中ANDROID_ID(Settings.Secure.ANDROID_ID) 是常用的设备标识。在模拟器中这个值可能是固定的或者可以通过ADB命令设置。在脚本中你可以研究原App生成deviceId的规则并仿造或者直接使用一个固定的、有效的ID。请求签名Sign的逆向这是比密码加密更常见的防护手段。签名通常是将所有请求参数包括pwd、iv、timestamp等按特定顺序如字典序拼接成一个字符串然后加上一个“盐值”secret再进行MD5或SHA256哈希。逆向的关键是找到这个拼接规则和盐值。搜索代码中的“sign”、“md5”、“sha256”等关键词或者Hook网络库的拦截器Interceptor查看最终发出的请求体与代码中组装的请求体进行对比差异部分很可能就是计算出的签名。通过以上步骤你不仅能够解决WE Learn登录接口的逆向问题更能建立起一套应对大多数安卓App接口加密的分析框架。记住逆向工程是一场与开发者的智力博弈需要耐心、细心和不断的实践。每一个成功解开的加密逻辑都是对你技术能力的又一次坚实提升。