首页
社区
课程
招聘
[原创]某东算法分析
2022-3-30 16:50 41252

[原创]某东算法分析

2022-3-30 16:50
41252

# 1.首先charles抓包发现每个请求Url后都接了一个sign的参数且每次都不一样。也没有其他的一些别的特别参数,那么关键问题就是分析sign参数的生成了

# 2.jadx反编译,寻找sign的生成的位置

> 直接搜索sign参数匹配的出来的结果太多了,一时间不好区分哪个是真的。于是使用getSign为前缀搜索

> 运气不错搜索到一个native方法 前缀也是getSign的方法名,打开这个类。找下引用位置进一步确认下是否为计算sign的类

> 最终在BaseApplication里面找到这个类引用,可以确定这个就是计算sign的类

# 3.开始分析sign方法的入参

frida hook这个方法得到结果如下:

function main(){
     Java.perform(function(){
       var BitmapkitUtils =  Java.use("com.jingdong.common.utils.BitmapkitUtils")
       BitmapkitUtils.getSignFromJni.overload('android.content.Context', 'java.lang.String', 'java.lang.String', 'java.lang.String', 'java.lang.String', 'java.lang.String').implementation = function( context,  str,  str2,  str3,  str4,  str5){
             console.log("签名入参为: ","str 		   =>",str,"str2=>",str2,"str3=>",str3,"str4=>",str4,"str5=>",str5)
            var result = this.getSignFromJni(context,str,str2,str3,str4,str5)
             console.log('签名sign为 ',result)
            return result
      }
     })
 }

> 根据hook结果稍加分析大概知道各个参数代表的意义:

>context:上下文对象

>str:url路径

>str2:请求body信息

>str3:55a9c688729bb118  为KEY 16位长度,且每次请求都固定

>str4:android 为平台

>str5:版本号

> 查看源码大概得出str3 的为installationId生成规则是UUID 随机16位长度,在app第一次安装的时候创建保存在缓存中.

# 4.使用unidbg进行黑盒调用

> 1.第一步当然是补环境了:

> 需要补充返回一个application对象,偷个懒想从AbstractJni 这里copy一份

> 重新运行一下又报错了,这个错是找不到对应的methodId、于是想着把Application换成Activity也可以,毕竟Activity也是可以获取Application对象的

  @Override
     public DvmObject<?> getStaticObjectField(BaseVM vm, DvmClass dvmClass, String signature) {
         if ("com/jingdong/common/utils/BitmapkitUtils->a:Landroid/app/Application;".equals(signature)) {
             //返回appliation
             return vm.resolveClass("android/app/Activity",
                    vm.resolveClass("android/content/ContextWrapper",
                             vm.resolveClass("android/content/Context"))).newObject(null);
        }
         return super.getStaticObjectField(vm, dvmClass, signature);
     }

> 重新运行就没问题了,接下来继续报错补环境,返回apk的路径。很简单

 @Override
 public DvmObject<?> getObjectField(BaseVM vm, DvmObject<?> dvmObject, String signature) {
     //sourceDir 代表当前apk目录
     if("android/content/pm/ApplicationInfo->sourceDir:Ljava/lang/String;".equals(signature)){
         StringObject stringObject = new StringObject(vm, APK_PATH);
         return stringObject;
     }
     return super.getObjectField(vm, dvmObject, signature);
 }

> 图上这个返回刚开始不知道传具体什么参数,于是打算看看源码。结果没有反编译出来具体的函数实现。于是用frida hook了这个方法拿到图下的返回 

> 第一个参数为:app安装目录  第二个参数为META-INF 目录 第三个是RSA

> 既然这样就直接返回RSA公钥了

@Override
     public DvmObject<?> callStaticObjectMethod(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {
         if ("com/jingdong/common/utils/BitmapkitZip->unZip(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)[B".equals(signature)) {
             byte[] unzip = vm.unzip("META-INF/xxxx.RSA");
             System.out.println("unzip " + new String(unzip));
             return new ByteArray(vm, unzip);
         }
         return super.callStaticObjectMethod(vm, dvmClass, signature, varArg);
     }

> 继续补充环境

 @Override
 public DvmObject<?> newObject(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {
     if ("sun/security/pkcs/PKCS7-><init>([B)V".equals(signature)) {
         DvmObject<?> objectArg = varArg.getObjectArg(0);
         try {
             PKCS7 pkcs7 = new PKCS7((byte[]) objectArg.getValue());
             return vm.resolveClass("sun/security/pkcs/PKCS7").newObject(pkcs7);
         } catch (ParsingException e) {
             e.printStackTrace();
         }
 
     }
     return super.newObject(vm, dvmClass, signature, varArg);
 }

> 继续补充环境,需补充如下

 @Override
     public DvmObject<?> callObjectMethod(BaseVM vm, DvmObject<?> dvmObject, String signature, VarArg varArg) {
         if ("sun/security/pkcs/PKCS7->getCertificates()[Ljava/security/cert/X509Certificate;".equals(signature)) {
             PKCS7 pkcs7 = (PKCS7) dvmObject.getValue();
             X509Certificate[] certificates = pkcs7.getCertificates();
             DvmObject<?> object = ProxyDvmObject.createObject(vm, certificates);
             return object;
 
 
         }
         return super.callObjectMethod(vm, dvmObject, signature, varArg);
     }

> 继续补充环境,从反编译的源码中copy 一份objectToBytes方法即可然后构造ByteArray返回即可

 if("com/jingdong/common/utils/BitmapkitZip->objectToBytes(Ljava/lang/Object;)[B".equals(signature)){
     DvmObject<?> objectArg = varArg.getObjectArg(0);
     byte[] bytes = objectToBytes(objectArg.getValue());
     return new ByteArray(vm,bytes);
 }

# 5.开始黑盒调用计算sign的方法

 List<Object> params = new ArrayList<>();
        params.add(dalvikVM.getJNIEnv());
        params.add(0);
        DvmClass context = dalvikVM.resolveClass("android/content/Context");
        params.add(dalvikVM.addLocalObject(context.newObject(null)));
        params.add(dalvikVM.addLocalObject(new StringObject(dalvikVM, "personinfoBusiness")));
        params.add(dalvikVM.addLocalObject(new StringObject(dalvikVM, "{\"callCJH\":\"1\",\"callNPS\":\"1\",\"closeJX\":\"0\",\"headTaskRefresh\":\"1\",\"locationArea\":\"0_0_0_0\",\"menuStaticSource\":\"0\",\"menuTimeStamp\":\"1631586010000\"}")));
        params.add(dalvikVM.addLocalObject(new StringObject(dalvikVM, INSTALL_ID)));
        params.add(dalvikVM.addLocalObject(new StringObject(dalvikVM, PLAT_FROM)));
        params.add(dalvikVM.addLocalObject(new StringObject(dalvikVM, VERSION)));
        Number numbers = moduleModule.callFunction(androidEmulator, 0x028B4+1, params.toArray())[0];

> 运行报错,还缺少环境

 @Override
 public DvmObject<?> newObjectV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {
     if("java/lang/StringBuffer-><init>()V".equals(signature)){
         StringBuffer stringBuffer = new StringBuffer();
         return vm.resolveClass("java/lang/StringBuffer").newObject(stringBuffer);
     }
     return super.newObjectV(vm, dvmClass, signature, vaList);
 }

> 重新继续补充环境 这部分环境补充比较简单 直接上最后运行结果

> 黑盒调用没问题了,接下来开始分析sign具体是怎么生成的。全部代码如下:

 package jd;
 
 import com.github.unidbg.AndroidEmulator;
 import com.github.unidbg.Module;
 import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
 import com.github.unidbg.linux.android.AndroidResolver;
 import com.github.unidbg.linux.android.dvm.*;
 import com.github.unidbg.linux.android.dvm.apk.Apk;
 import com.github.unidbg.linux.android.dvm.array.ArrayObject;
 import com.github.unidbg.linux.android.dvm.array.ByteArray;
 import com.github.unidbg.linux.android.dvm.jni.ProxyClassFactory;
 import com.github.unidbg.linux.android.dvm.jni.ProxyDvmObject;
 import com.github.unidbg.memory.Memory;
 import org.apache.log4j.Level;
 import org.apache.log4j.Logger;
 import sun.security.pkcs.PKCS7;
 import sun.security.pkcs.ParsingException; 
 import java.io.*;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.List;
 
 public class JD extends AbstractJni {
 
 
     private static final String SO_PATH = "";
     private static final String APK_PATH = "";
 
     private static final String INSTALL_ID = "55a9c688729bb118";
     private static final String PLAT_FROM = "android";
     private static final String VERSION = "10.4.6";
     private AndroidEmulator androidEmulator;
 
     public static void main(String[] args) {
         Logger.getLogger("com.github.unidbg.AbstractEmulator").setLevel(Level.DEBUG);
         Logger.getLogger("com.github.unidbg.linux.android.dvm.DalvikVM").setLevel(Level.DEBUG);
         Logger.getLogger("com.github.unidbg.linux.android.dvm.BaseVM").setLevel(Level.DEBUG);
         Logger.getLogger("com.github.unidbg.linux.android.dvm").setLevel(Level.DEBUG);
         JD jd = new JD();
         jd.start();
     }
 
 
     public void start() {
         androidEmulator = AndroidEmulatorBuilder.for32Bit().setProcessName("com.jingdong.android")
                 .build();
          Memory androidEmulatorMemory = androidEmulator.getMemory();
          androidEmulatorMemory.setLibraryResolver(new AndroidResolver(23));
          VM dalvikVM = androidEmulator.createDalvikVM(new File(APK_PATH));
         DalvikModule module = dalvikVM.loadLibrary(new File(SO_PATH), false);
         dalvikVM.setJni(this);
         Module moduleModule = module.getModule();
         dalvikVM.callJNI_OnLoad(androidEmulator, moduleModule);
         List<Object> params = new ArrayList<>();
         params.add(dalvikVM.getJNIEnv());
         params.add(0);
         DvmClass context = dalvikVM.resolveClass("android/content/Context");
         params.add(dalvikVM.addLocalObject(context.newObject(null)));
         params.add(dalvikVM.addLocalObject(new StringObject(dalvikVM, "personinfoBusiness")));
         params.add(dalvikVM.addLocalObject(new StringObject(dalvikVM, "{\"callCJH\":\"1\",\"callNPS\":\"1\",\"closeJX\":\"0\",\"headTaskRefresh\":\"1\",\"locationArea\":\"0_0_0_0\",\"menuStaticSource\":\"0\",\"menuTimeStamp\":\"1631586010000\"}")));
         params.add(dalvikVM.addLocalObject(new StringObject(dalvikVM, INSTALL_ID)));
         params.add(dalvikVM.addLocalObject(new StringObject(dalvikVM, PLAT_FROM)));
         params.add(dalvikVM.addLocalObject(new StringObject(dalvikVM, VERSION)));
         Number numbers = moduleModule.callFunction(androidEmulator, 0x028B4 + 1, params.toArray())[0];
         DvmObject<?> object = dalvikVM.getObject(numbers.intValue());
         System.out.println("加密结果为:" + object.getValue());
     }
 
     @Override
     public DvmObject<?> getStaticObjectField(BaseVM vm, DvmClass dvmClass, String signature) {
         if ("com/jingdong/common/utils/BitmapkitUtils->a:Landroid/app/Application;".equals(signature)) {
             //返回appliation
             return vm.resolveClass("android/app/Activity",
                     vm.resolveClass("android/content/ContextWrapper",
                             vm.resolveClass("android/content/Context"))).newObject(null);
         }
         return super.getStaticObjectField(vm, dvmClass, signature);
     }
 
     @Override
     public DvmObject<?> callStaticObjectMethod(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {
         if ("com/jingdong/common/utils/BitmapkitZip->unZip(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)[B".equals(signature)) {
             byte[] unzip = vm.unzip("META-INF/xx.RSA");
             System.out.println("unzip " + new String(unzip));
             return new ByteArray(vm, unzip);
         } else if ("com/jingdong/common/utils/BitmapkitZip->objectToBytes(Ljava/lang/Object;)[B".equals(signature)) {
             DvmObject<?> objectArg = varArg.getObjectArg(0);
             byte[] bytes = objectToBytes(objectArg.getValue());
             return new ByteArray(vm, bytes);
         }
         return super.callStaticObjectMethod(vm, dvmClass, signature, varArg);
     }
 
     public static byte[] objectToBytes(Object obj) {
         try {
             ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
             ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
             objectOutputStream.writeObject(obj);
             objectOutputStream.flush();
             byte[] byteArray = byteArrayOutputStream.toByteArray();
             objectOutputStream.close();
             byteArrayOutputStream.close();
             return byteArray;
         } catch (IOException e) {
             return null;
         }
     }
 
     @Override
     public DvmObject<?> newObject(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {
         if ("sun/security/pkcs/PKCS7-><init>([B)V".equals(signature)) {
             ByteArray byteArray = varArg.getObjectArg(0);
             try {
                 PKCS7 pkcs7 = new PKCS7(byteArray.getValue());
                 return vm.resolveClass("sun/security/pkcs/PKCS7").newObject(pkcs7);
             } catch (ParsingException e) {
                 e.printStackTrace();
             }
 
         }
         return super.newObject(vm, dvmClass, signature, varArg);
     }
 
     @Override
     public DvmObject<?> callObjectMethod(BaseVM vm, DvmObject<?> dvmObject, String signature, VarArg varArg) {
         if ("sun/security/pkcs/PKCS7->getCertificates()[Ljava/security/cert/X509Certificate;".equals(signature)) {
             PKCS7 pkcs7 = (PKCS7) dvmObject.getValue();
             X509Certificate[] certificates = pkcs7.getCertificates();
             DvmObject<?> object = ProxyDvmObject.createObject(vm, certificates);
             return object;
 
 
         }
         return super.callObjectMethod(vm, dvmObject, signature, varArg);
     }
 
     @Override
     public DvmObject<?> getObjectField(BaseVM vm, DvmObject<?> dvmObject, String signature) {
         //sourceDir 代表当前apk目录
         if ("android/content/pm/ApplicationInfo->sourceDir:Ljava/lang/String;".equals(signature)) {
             StringObject stringObject = new StringObject(vm, APK_PATH);
             return stringObject;
         }
         return super.getObjectField(vm, dvmObject, signature);
     }
 
 
     @Override
     public DvmObject<?> callObjectMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) {
         if ("java/lang/StringBuffer->append(Ljava/lang/String;)Ljava/lang/StringBuffer;".equals(signature)) {
             StringBuffer stringBuffer = (StringBuffer) dvmObject.getValue();
             DvmObject<?> objectArg = vaList.getObjectArg(0);
             stringBuffer.append(objectArg.getValue().toString());
             return vm.resolveClass("java/lang/StringBuffer").newObject(stringBuffer);
         } else if ("java/lang/Integer->toString()Ljava/lang/String;".equals(signature)) {
             Integer integer = (Integer) dvmObject.getValue();
             return new StringObject(vm, integer.toString());
         }else if("java/lang/StringBuffer->toString()Ljava/lang/String;".equals(signature)){
             StringBuffer stringBuffer = (StringBuffer) dvmObject.getValue();
             return new StringObject(vm, stringBuffer.toString());
         }
         return super.callObjectMethodV(vm, dvmObject, signature, vaList);
     }
 
     @Override
     public DvmObject<?> newObjectV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {
         if ("java/lang/StringBuffer-><init>()V".equals(signature)) {
             StringBuffer stringBuffer = new StringBuffer();
             return vm.resolveClass("java/lang/StringBuffer").newObject(stringBuffer);
         } else if ("java/lang/Integer-><init>(I)V".equals(signature)) {
             int intArg = vaList.getIntArg(0);
             Integer integer = Integer.valueOf(intArg);
             return vm.resolveClass("java/lang/Integer").newObject(integer);
         }
         return super.newObjectV(vm, dvmClass, signature, vaList);
     }
 }

# 6.ida+unidbg分析sign的具体如何生成的

ida导入so 包  export导出中找到sign生成方法  截图如下:

F5生成伪代码

根据方法的具体逻辑修改了下方法名称 方便直观查看。初步观察是讲传入参数通过key=value 的方式拼接起来

通过unidbg的执行日志中也可以发现这点

接下来开始从函数返回的地方开始往上分析,单独调用执行返回如下:


st=1648374072802&sign=8102aefd41782d405174725cd433c5a0&sv=111


从上可知要分析的为st 、sign、sv这个三个值

1.st生成分析:生成一个时间戳 长度为13位的 用于计算sign

2.sign的生成:

分析伪代码找到sign的生成方法为sub_126AC,该方法上面的伪代码都是一些拼接字符串的操作,就是把入参拼接起来。

> 接下来开始unidbg hook该方法

> 然后开启无尽的S 单步执行往下走当程序走到switch判断时 发现bl指令跳转到sub_10de4函数  多次在这个位置debugger 到 发现 到 sv =111 走的是 case 2   还遇到过sv = 110 走的是case 1 、这部分我们只分析为2的情况也就是走sub_10DE4,至于为啥呢?就是因为这后面走的加密算法看起来简单些 像md5 

> 查看下函数sub_10de4入参,hook该函数

> 打印mr1,为一串固定的字符串

> mr2 为0x1 固定值

> mr3 打印如下,为前面拼接的参数串 长度为0x106

> 接着分析sub_10de4 这个函数,点击进入sub_12ECC函数 ,这里为啥不分析sub_12FF0 呢 因为具体看了下没啥特别的。

hook sub_12ECC函数分析下入参

> 参数1:打印看看 ,看不太出来是什么 但是是64位  看后面的伪代码这个值主要是 跟 md5魔数有关系

> 参数2: 固定字符串 80306f4370b39fd5630ad0529f77adb6

> 参数3: 0x1

> 参数4: 为明文的参数拼接串

> functionId=personinfoBusiness&body={"callCJH":"1","callNPS":"1","closeJX":"0","headTaskRefresh":"1","locationArea":"0_0_0_0","menuStaticSource":"0","menuTimeStamp":"1631586010000"}&uuid=55a9c688729bb118&client=android&clientVersion=10.4.6&st=1648455172760&sv=111

> 参数5:为长度 0x106

#开始分析代码

>  从上面分析 v27 应该就是 md5 魔数 数组咯 大致为

>  v27[1]= 0x68449237;

>  v27[2]= 0x7FCC3DA5;

>  v27[3] = 0x88D90FBB;

>  v27[4] = 0x5AE99AEE;

>  继续往下分析

这个区域是具体的实现的位置,flag 是之前传入的0x1 所以走的是if 判断  。这个do-while 大致是 从v27中取具体魔数  s 往下走 v18 = 0x37 猜测应该是不断从v27中取值  还原算法时v27 就是这个下面这个数组

 {0x37, 0x92, 0x44, 0x68, 0xA5, 0x3D, 0xCC, 0x7F, 0xBB, 0x0F, 0xD9, 0x88, 0xEE, 0x9A,
0xE9, 0x5A};


> 继续S 往下走 0x38 就是传入固定字符串的第一个字符   80306f4370b39fd5630ad0529f77adb6

>  r0 = 0x66 是从拼接的字符串中取出的第一个 

>  r0=0x66  r1=0x0 r2=0x37 r3=0x1 r4=0x38    

>  R0 = R0 ^ R2  = 0x51 

>  R0 = R0 ^ R4  = 0x69

> 按照汇编 分析猜测 后面就是 将前面异或的结果 加上R2 0x37  然后在 R2 = R2 ^ R0     

*(v5 - 1) = md5_init_key_index ^ *(_BYTE *)(key + v17);// EOR.W R2, R2, R1    STRB.W R2, [R10,#-1]

> 根据ida 上伪代码 结和汇编 大致可知 这部分是每执行一次do while 就修改一次v5的索引对应的值  一直循环到循环到v5数据长度,所以结合伪代码 用C还原 sign 的算法 如下:

 void encryption(char *data) {
     uint32_t v1, v2, v3 = 0;
     unsigned char init_key[60] = {0x37, 0x92, 0x44, 0x68, 0xA5, 0x3D, 0xCC, 0x7F, 0xBB,    0x0F, 0xD9, 0x88, 0xEE, 0x9A,
                                   0xE9, 0x5A};
     char *sign = "80306f4370b39fd5630ad0529f77adb6";
     int data_length = strlen(data);
     unsigned char md5_init_key = 0;
     unsigned char sign_key = 0;
     do {
         v1 = v3 & 0xF;
         v2 = v3 & 7;
         v3 = v3 + 1;
         sign_key = sign[v2];
         md5_init_key = init_key[v1];
         md5_init_key = md5_init_key ^ ((md5_init_key ^ *data ^sign_key) + md5_init_key);
         *data = md5_init_key ^ sign_key;
         data++;
     } while (v3 != data_length);
 }

> 用python 来调用C代码  跟unidbg 黑盒调用结果比对如下:

> 总结: 某东的sign有三个算法 这边只还原了其中比较简单的 sv =111 的这种 。还原这部分算法也花的时间挺多的,结合unidbg 来分析算法 真的方便。


[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

最后于 2022-3-30 17:16 被那年没下雪编辑 ,原因:
收藏
点赞27
打赏
分享
打赏 + 1.00雪花
打赏次数 1 雪花 + 1.00
 
赞赏  抚琴   +1.00 2022/03/31
最新回复 (35)
雪    币: 8191
活跃值: (5879)
能力值: ( LV12,RANK:430 )
在线值:
发帖
回帖
粉丝
王cb 8 2022-3-30 16:51
2
0
写的不错
雪    币: 1376
活跃值: (9742)
能力值: ( LV9,RANK:240 )
在线值:
发帖
回帖
粉丝
misskings 4 2022-3-30 17:04
3
0
666。收藏+1
雪    币: 2191
活跃值: (1007)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
Tajang 2022-3-30 17:13
4
0
真他妈牛逼,666,我滴宝贝,甘拜下风。代码逻辑分析那块到位,unidbg和ida用的着实不错。顺藤摸瓜找到sign生成原理,步步为营,细节分析到位。
雪    币: 232
活跃值: (237)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
james_mvb 2022-3-30 17:27
5
0
牛逼 收藏+1
雪    币: 481
活跃值: (2163)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
Zard_ 2022-3-30 17:40
6
0
666 都开始搞
雪    币: 359
活跃值: (388)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
月影小子 2022-3-30 19:34
7
0
666,膜拜
雪    币: 27
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
wx_叶_540 2022-3-31 11:50
8
0
膜 6
雪    币: 57
活跃值: (50)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
eyascn 2022-3-31 13:12
9
0
def encryption(input):
       arr = [0x37, 0x92, 0x44, 0x68, 0xA5, 0x3D, 0xCC, 0x7F, 0xBB, 0xF, 0xD9, 0x88, 0xEE, 0x9A, 0xE9, 0x5A]
       key2 = b"80306f4370b39fd5630ad0529f77adb6"
       arr1 = [0 for _ in range(len(input))]
       for i in range(len(input)):
               r0 = int(input[i])
               r2 = arr[i & 0xf]
               r4 = int(key2[i & 7])
               r0 = r2 ^ r0
               r0 = r0 ^ r4
               r0 = r0 + r2
               r2 = r2 ^ r0
               r1 = int(key2[i & 7])
               r2 = r2 ^ r1
               arr1[i] = r2 & 0xff
       return bytes(arr1)

直接使用python验证,看是否正确

最后于 2022-3-31 13:19 被eyascn编辑 ,原因:
雪    币: 1034
活跃值: (5984)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
那年没下雪 1 2022-3-31 13:56
10
0
eyascn def&nbsp;encryption(input): &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nb ...
是正确的
雪    币:
活跃值: (149)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
抚琴 2022-3-31 14:22
11
0
大佬666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666
雪    币:
活跃值: (149)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
抚琴 2022-3-31 14:22
12
0
6666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666
雪    币:
活跃值: (149)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
抚琴 2022-3-31 14:22
13
0
真他妈牛逼,666,我滴宝贝,甘拜下风。代码逻辑分析那块到位,unidbg和ida用的着实不错。顺藤摸瓜找到sign生成原理,步步为营,细节分析到位。
雪    币:
活跃值: (149)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
抚琴 2022-3-31 14:23
14
0
雪    币: 17
活跃值: (138)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
jsbba 2022-3-31 22:48
15
0
666,我滴宝贝,甘拜下风
雪    币: 1753
活跃值: (1057)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
南枫 2022-4-5 21:40
16
0
写的不错,好多工具都不太会用
雪    币: 566
活跃值: (990)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
浮夸进进 2022-4-6 18:47
17
0
感谢分享
雪    币: 2569
活跃值: (1183)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
zhenzhentx 2022-4-6 23:46
18
0
厉害了大神
雪    币: 231
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
nanfang- 2022-4-14 01:48
19
0
eyascn def&nbsp;encryption(input): &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nb ...
老哥,请问input内容是什么呢,谢谢
雪    币: 1036
活跃值: (4999)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
suuuuu 2022-4-20 11:04
20
0
nb666
雪    币: 1753
活跃值: (1057)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
南枫 2022-4-24 10:41
21
0
nb666
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
动动舒舒 2022-4-24 11:48
22
0
写的不错
雪    币: 1768
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
Z-Taul 2022-5-27 16:17
23
0
太厉害了
雪    币: 714
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
mb_gutbiyhl 2022-5-28 13:58
24
0
nanfang- 老哥,请问input内容是什么呢,谢谢
看文章里面参数4就是input
雪    币: 212
活跃值: (22)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
zhanhq 2022-5-28 16:35
25
0
太厉害了
游客
登录 | 注册 方可回帖
返回