油联合伙人app逆向

1. 分析登录请求

  • 登录请求

    POST https://chinayltx.com/app/api/v1/partnerLogin/login
    
    X-App: native
    X-Noncestr: 123456
    X-OS: partnerApp_android
    X-Req-Time: 1649812409948
    X-Sign: d955531c2fb9770c0ea071126818dd5d
    X-Token: 
    X-UserID: 251531
    
    phone=1888888888&password=42dcf678c6da01e7fa3ebe03cbe1f0e3
    
  • 需要逆向的两个参数的加密算法

    X-Sign
    password
    
  • 使用Jadx反编译apk

    • 定位Java代码
      • 分析 password 算法

        搜索请求url v1/partnerLogin/login

        public interface NetworkApi {
            // ...
            @FormUrlEncoded
            @POST("api/v1/partnerLogin/login")
            Observable<HttpResult<LoginInfo>> submitLogin(@Field("phone") String str, @Field("password") String str2);
            // ...
        }
        
        需要寻找什么地方调用了方法: NetworkApi -> submitLogin
        

        搜索 NetworkApi

        @Singleton
        public class RestDataSource implements DataSource {
            @Override // com.yltx.oil.partner.data.repository.Repository
            public Observable<HttpResult<LoginInfo>> loginWithToken(String str, String str2) {
                return this.networkApi.submitLogin(str, str2).flatMap(new Func1<HttpResult<LoginInfo>, Observable<HttpResult<LoginInfo>>>() {
                    /* class com.yltx.oil.partner.data.datasource.RestDataSource.AnonymousClass3 */
        
                    public Observable<HttpResult<LoginInfo>> call(HttpResult<LoginInfo> httpResult) {
                        return Observable.just(httpResult);
                    }
                });
            }
        }
        

        搜索 loginWithToken

        public class LoginUseCase extends UseCase<HttpResult<LoginInfo>> {
            private Repository mRepository;
            private String name;
            private String pwd;
        
            public String getName() {
                return this.name;
            }
        
            public void setName(String str) {
                this.name = str;
            }
        
            public String getPwd() {
                return this.pwd;
            }
        
            public void setPwd(String str) {
                this.pwd = str;
            }
        
            @Inject
            public LoginUseCase(Repository repository) {
                this.mRepository = repository;
            }
        
            /* access modifiers changed from: protected */
            @Override // com.yltx.oil.partner.mvp.domain.UseCase
            public Observable<HttpResult<LoginInfo>> buildObservable() {
                return this.mRepository.loginWithToken(this.name, this.pwd);
            }
        }
        

        搜索 LoginUseCase -> setPwd

        @ActivityScope
        public class LoginPresenter implements Presenter {
            public void submitLogin(String str, String str2) {
                this.mLoginUseCase.setName(str);
                this.mLoginUseCase.setPwd(Md5.md5(str2));
                this.mLoginUseCase.execute(new LoginSubscriber(this.view));
            }
        }
        

        找到密码加密方法 Md5.md5 跳进加密算法

        public class Md5 {
            public static String md5(String str) {
                try {
                    byte[] digest = MessageDigest.getInstance("MD5").digest(str.getBytes("UTF-8"));
                    StringBuilder sb = new StringBuilder(digest.length * 2);
                    for (byte b : digest) {
                        int i = b & 255;
                        if (i < 16) {
                            sb.append("0");
                        }
                        sb.append(Integer.toHexString(i));
                    }
                    return sb.toString();
                } catch (NoSuchAlgorithmException e) {
                    throw new RuntimeException("MD5 should not be supported!", e);
                } catch (UnsupportedEncodingException e2) {
                    throw new RuntimeException("UTF-8 should not be supported!", e2);
                }
            }
        }
        
      • 使用Python实现password md5加密

        import hashlib
        
        def set_md5_pwd(pwd):
            m = hashlib.md5()
            m.update(pwd.encode("utf-8"))
            return m.hexdigest()
        
      • 分析请求头 X-Sign 算法

        搜索 X-Sign

        final class RequestParamsWrapper {
            // ...
            RequestParamsWrapper(Context context, RequestBody requestBody) {
                this(context);
                StringBuilder sb = new StringBuilder();
                if (requestBody instanceof FormBody) {
                    FormBody formBody = (FormBody) requestBody;
                    int size = formBody.size();
                    for (int i = 0; i < size; i++) {
                        sb.append(formBody.name(i));
                        sb.append("=");
                        sb.append(formBody.value(i));
                        if (i < size - 1) {
                            sb.append(a.b);
                        }
                    }
                    this.sign = sign(sb.toString());
                } else if (requestBody instanceof MultipartBody) {
                    this.sign = "";
                } else {
                    this.sign = "";
                }
            }
            // ...
            private static final String PARAM_SIGN = "X-Sign";
            // ...
            private String sign(String str) {
                return Md5.md5(this.token + this.reqTime + this.noncestr.substring(2) + str).toLowerCase();
            }
        }
        
        1. 找到 sign 的加密来源 this.sign = sign(sb.toString())
        2. 以及 sign 的加密算法
          需要 理解sb.toString() 的生成方式 以及 找到this.token的生成方式
        • sb.toString() 是对请求体的拼接

          k1=v1&k2=v2&k3=v3

        • this.token的生成方式

          set_md5_pwd(token + reqtime + concestr[:2] + sb.toString()).lower()

  • 编写python代码

    # @file           : YouLian.py
    # @description    : 油联合伙人
    # @date           : 2022/04/13 08:51:36
    # @author         : miaokela
    import requests
    import time
    import hashlib
    from urllib.parse import urlencode
    
    
    def set_md5_pwd(pwd):
        m = hashlib.md5()
        m.update(pwd.encode("utf-8"))
        return m.hexdigest()
    
    url = "https://chinayltx.com/app/api/v1/partnerLogin/login"
    
    data = {
        "phone": "188682.....",
        "password": set_md5_pwd("password")
    }
    
    token = ""
    req_time = str(int(time.time()*10**3))
    noncestr = "123456"
    headers = {
        "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.75 Safari/537.36",
        "X-App": "native",
        "X-Noncestr": noncestr,
        "X-OS": "partnerApp_android",
        "X-Req-Time": req_time,
        "X-Sign": set_md5_pwd(f"{token}{req_time}{noncestr[2:]}{urlencode(data)}".lower()),
        "X-Token": token,
        "X-UserID": "251531"
    }
    
    resp = requests.post(url, data=data, headers=headers)
    
    if resp.status_code == 200:
        print(resp.content.decode("utf-8"))
    else:
        print(resp.status_code)
    

    响应结果

    {
        "data": {
            "userInfo": {
                "rowId": 251531,
                "nickname": "会员62134",
                "phone": "188682...",
                "attribute": "app",
                "isPush": "1",
                "token": "3c7b968e4b91428ea5a88bede99d62ff1649831858536"
            },
            "partnerInfo": {
                "isPartner": "0",
                "sale": 0,
                "level": 0,
                "partnerLevel": "",
                "levelName": "",
                "row_id": 0,
                "saleNeed": 0,
                "goodProportion": 0
            }
        }
    }
    
posted on 2022-04-13 14:42  _tiny_coder  阅读(177)  评论(0编辑  收藏  举报