face api协议分析

face api协议分析应用编号 24091 Account Kit 应用密匙 91f1de0b6fb6 Account Kit 客户端口令 3fd5c567b499 先有两个大问题 一个是这个线程交互实现的是什么 另一个是从主线恢复类功能 并尽可能少的使用函数 登录资料和头像自动填写

大家好,我是讯享网,很高兴认识大家。

应用编号
24091
Account Kit 应用密匙
91f1de0b6fb697a92e6a89761d40dbb3
Account Kit 客户端口令
3fd5c567b4996d4f45ed43cebc535a2a

先有两个大问题,一个是这个线程交互实现的是什么。
另一个是从主线恢复类功能,并尽可能少的使用函数。

登录资料和头像自动填写

批量注册gmail

EMAIL_LOGIN_COMPLETE
event {
{
name EMAIL_LOGIN_COMPLETE
hashcode
offset 0
count 20
}
ordinal 3
}


  if(contentController instanceof EmailLoginContentController) {
                            manager2 = intent.getStringExtra(EXTRA_EMAIL);//这里也是获取之前putextra()打包内容
                            EmailLoginFlowManager manager5 = (EmailLoginFlowManager)AccountKitActivity.this.loginFlowManager;
                            ((ActivityEmailHandler)manager5.getActivityHandler()).onEmailLoginComplete(AccountKitActivity.this, manager5, manager2);
                        }

public void onEmailLoginComplete(AccountKitActivity activity, EmailLoginFlowManager emailManager, String email) {
        activity.pushState(LoginFlowState.SENDING_CODE, (OnPushListener)null);
        emailManager.setEmail(email);//这里只是设置参数的
        emailManager.logInWithEmail(this.configuration.getResponseType(), this.configuration.getInitialAuthState());
    }
参数一是个句柄没啥用,参数二控制流似乎只是控制跳转方式控制的 参数三 请求的email地址这个重要

 public ResponseType getResponseType() {
        return this.responseType;//responseType; “TOKEN”
    }
responseType
value=“token”
name=“TOKEN”
ordinal=1

 public String getInitialAuthState() {
        return this.initialAuthState;//null
    }

 public void logInWithEmail(ResponseType responseType, @Nullable String initialAuthState) {//TOKEN null
        if(this.isValid() && this.email != null) {
            AccountKitController.logInWithEmail(this.email, responseType.getValue(), initialAuthState);//这三个值分别是取得的email token null
        }
    }

 public static EmailLoginModel logInWithEmail(String email, String responseType, @Nullable String initialAuthState) {
        if(getCurrentAccessToken() != null) {
            logOut();
        }

        return initializer.getLoginManager().logInWithEmail(email, responseType, initialAuthState);
    }

 LoginManager getLoginManager() {
        Validate.sdkInitialized();
        return this.data.loginManager;
    }

 static void sdkInitialized() {
        if(!AccountKit.isInitialized()) {
            throw new AccountKitException(Type.INITIALIZATION_ERROR, InternalAccountKitError.SDK_NOT_INITIALIZED);
        }
    }
public static boolean isInitialized() {
        return AccountKitController.isInitialized();
    }
public static boolean isInitialized() {
        return initializer.isInitialized();
    }
public boolean isInitialized() {
        return this.state == Initializer.State.INITIALIZED;//这里检测一下状态 state:“INITIALIZED”
    }


这里比较重要了com.facebook.accountkit.internal;final class LoginManager
 EmailLoginModelImpl logInWithEmail(@NonNull String email, @NonNull String responseType, @Nullable String initialAuthState) {
        Utility.assertUIThread();
        this.cancelExisting();
        EmailLoginModelImpl loginModel = new EmailLoginModelImpl(email, responseType);//一个类
        EmailLoginController loginHandler = new EmailLoginController(this.accessTokenManager, this, loginModel);
        loginHandler.logIn(initialAuthState);
        this.onLoginStart(loginModel);
        this.currentLoginController = loginHandler;
        return loginModel;
    }

onLoginStart
 public void logLoginModel(String eventName, LoginModelImpl loginModel) {


AccountKitController这个类是最开始进入的internal包
然后是LoginManager的loginwithEmail;这里看下初始化的时候参数是啥。。。。。。。。



看了下协议中很多界面交互的和数据传输的设计,跟核心的服务器交互关系不大,关系最紧的是那个graphrequest和graphresponse,但这个应该不是直接调用的关键。我现在想跟踪email的具体执行流,把这个调用request和response的核心类给找出来应该对分析有很大帮助。

accountpreference
appevent
EmailLoginController   //目测这个类里面的login函数是登录函数需要动态跟进一下
ExperimentionConfiguration
LoginController
LoginManager

emailLoginController.class
login函数里
        ((EmailLoginModelImpl)this.loginModel).setInitialAuthState(initialAuthState);
        AccountKitGraphRequest graphRequest = this.buildGraphRequest(“start_login”, parameters);//这里结合上面的赋值初始化了graphRequest
        AccountKitGraphRequestAsyncTask.cancelCurrentAsyncTask();
        AccountKitGraphRequestAsyncTask task = AccountKitGraphRequest.executeAsync(graphRequest, requestCallback);
        AccountKitGraphRequestAsyncTask.setCurrentAsyncTask(task);


下面这个函数通过异步和回调调用,貌似是连接服务器的接受数据的函数,位置AccountKitGraphResponse类
static AccountKitGraphResponse fromHttpConnection(HttpURLConnection connection, AccountKitGraphRequest request) {
        InputStream stream = null;

        AccountKitGraphResponse var4;
        try {
            if(connection.getResponseCode() >= 400) {
                stream = connection.getErrorStream();
            } else {
                stream = connection.getInputStream();
            }

            AccountKitGraphResponse exception = createResponseFromStream(stream, connection, request);
            return exception;
        } catch (AccountKitException var9) {
            ConsoleLogger.log(LoggingBehavior.REQUESTS, “AccountKitGraphResponse”, “Response <ERROR>: %s”, new Object[]{var9});
            var4 = new AccountKitGraphResponse(request, connection, new AccountKitRequestError(var9));
        } catch (IOException | SecurityException | JSONException var10) {
            ConsoleLogger.log(LoggingBehavior.REQUESTS, “AccountKitGraphResponse”, “Response <ERROR>: %s”, new Object[]{var10});
            var4 = new AccountKitGraphResponse(request, connection, new AccountKitRequestError(new AccountKitException(Type.SERVER_ERROR, var10)));
            return var4;
        } finally {
            Utility.closeQuietly(stream);
        }

        return var4;
    }

AccountKitGraphRequestAsyncTsk类中通过一系列设置,启动异步线程执行数据交互
protected AccountKitGraphResponse doInBackground(Void… params) {
        try {
            return this.connection == null?this.request.executeAndWait():AccountKitGraphRequest.executeConnectionAndWait(this.connection, this.request);
        } catch (Exception var3) {
            this.exception = var3;
            return null;
        }
    }
我们这里看到executeConnectionAndWait执行连接是要有一个connect,和request的,但这里是连接后的返回结果,我们需要找到请求的过程。
应该是个回调函数。。。。。。。。。
  public interface Callback {
        void onCompleted(AccountKitGraphResponse var1);
    }
这个是request类里的回调接口。



emailLoginController.class
login函数里
        ((EmailLoginModelImpl)this.loginModel).setInitialAuthState(initialAuthState);
        AccountKitGraphRequest graphRequest = this.buildGraphRequest(“start_login”, parameters);//这里结合上面的赋值初始化了graphRequest
        AccountKitGraphRequestAsyncTask.cancelCurrentAsyncTask();
        AccountKitGraphRequestAsyncTask task = AccountKitGraphRequest.executeAsync(graphRequest, requestCallback);
        AccountKitGraphRequestAsyncTask.setCurrentAsyncTask(task);
我们有回到了这个登录管理流
buildgraph目测只是实现了初始化各种参数
第二句应该是取消之前执行的异步执行
第三句函数通过这两个参数初始化了类,也没有回调函数的定义
那么我们就要看下这个requestCallback到底是哪里来的了。

一看之下发现,回调函数正是紧挨上面的代码,也在login函数中
 Callback requestCallback = new Callback() {
            public void onCompleted(AccountKitGraphResponse response) {
                LoginManager loginManager = EmailLoginController.this.getLoginManager();
                if(loginManager != null) {
                    try {
                        if(response.getError() != null) {
                            Pair result1 = Utility.createErrorFromServerError(response.getError());
                            EmailLoginController.this.onError((AccountKitError)result1.first);
                        } else {
                            JSONObject result = response.getResponseObject();
                            if(result == null) {
                                EmailLoginController.this.onError(Type.LOGIN_INVALIDATED, InternalAccountKitError.NO_RESULT_FOUND);
                            } else {
                                String privacyPolicy = result.optString(“privacy_policy”);
                                if(!Utility.isNullOrEmpty(privacyPolicy)) {
                                    ((EmailLoginModelImpl)EmailLoginController.this.loginModel).putField(“privacy_policy”, privacyPolicy);
                                }

                                String termsOfService = result.optString(“terms_of_service”);
                                if(!Utility.isNullOrEmpty(termsOfService)) {
                                    ((EmailLoginModelImpl)EmailLoginController.this.loginModel).putField(“terms_of_service”, termsOfService);
                                }

                                String expiresInString;
                                long expiresIn;
                                try {
                                    boolean e = result.getBoolean(“can_attempt_seamless_login”);
                                    expiresInString = result.getString(“expires_at”);
                                    expiresIn = Long.parseLong(expiresInString) * 1000L;
                                    if(e && expiresIn > System.currentTimeMillis()) {
                                        ((EmailLoginModelImpl)EmailLoginController.this.loginModel).setStatus(LoginStatus.ACCOUNT_VERIFIED);
                                        return;
                                    }
                                } catch (JSONException var17) {
                                    ;
                                }

                                try {
                                    String e1 = result.getString(“login_request_code”);
                                    ((EmailLoginModelImpl)EmailLoginController.this.loginModel).setLoginCode(e1);
                                    expiresInString = result.getString(“expires_in_sec”);
                                    expiresIn = Long.parseLong(expiresInString);
                                    ((EmailLoginModelImpl)EmailLoginController.this.loginModel).setExpiresInSeconds(expiresIn);
                                    String intervalSecondsString = result.getString(“interval_sec”);
                                    int intervalSeconds = Integer.parseInt(intervalSecondsString);
                                    ((EmailLoginModelImpl)EmailLoginController.this.loginModel).setInterval(intervalSeconds);
                                    ((EmailLoginModelImpl)EmailLoginController.this.loginModel).setStatus(LoginStatus.PENDING);
                                    loginManager.handle(EmailLoginController.this.loginModel);
                                } catch (NumberFormatException | JSONException var16) {
                                    EmailLoginController.this.onError(Type.LOGIN_INVALIDATED, InternalAccountKitError.INVALID_GRAPH_RESULTS_FORMAT);
                                }

                            }
                        }
                    } finally {
                        EmailLoginController.this.broadcastLoginStateChange();
                    }
                }
            }
        };
回调函数中有个关键参数AccountKitGraphResponse response。这个应该包含了对服务器的请求过程,应为整个回调函数中只是对相应的各种判断。
回调函数的返回值Callback requestCallback恰巧是AccountKitGraphRequestAsyncTask task = AccountKitGraphRequest.executeAsync(graphRequest, requestCallback);的第二个参数,上面我们知道第一个参数仅仅包含一些参数的初始化。

这里又到了异步请求的类,这里的参数result会被引用到callback回调函数作为参数,需要找到
 protected void onPostExecute(AccountKitGraphResponse result)
 
根据异步任务调用原则,我们的onPostExecute在执行的参数将会是DoInBackgroud的返回值。如下:
protected AccountKitGraphResponse doInBackground(Void… params) {
        try {
            return this.connection == null?this.request.executeAndWait():AccountKitGraphRequest.executeConnectionAndWait(this.connection, this.request);//这里这个三目运算符比较奇怪,按照定义,条件一为空则代表条件一为0.我们总是会执行条件三的表达式,哪只写一个表达式不就行了
        } catch (Exception var3) {
            this.exception = var3;
            return null;
        }
    }
正常情况下不会返回空。

AccountKitGraphResponse executeAndWait() {
        HttpURLConnection connection;
            connection = toHttpConnection(this);//参数初始化
        AccountKitGraphResponse response = executeConnectionAndWait(connection, this);//启动请求
            return response;
    }

 static HttpURLConnection toHttpConnection(AccountKitGraphRequest request) {
        URL url;
            String connection = request.getUrlForSingleRequest();
            url = new URL(connection);
        

            HttpURLConnection connection1 = createConnection(url);
            serializeToUrlConnection(request, connection1);//请求参数初始化
            return connection1;
    }

static AccountKitGraphResponse executeConnectionAndWait(HttpURLConnection connection, AccountKitGraphRequest request) {
        AccountKitGraphResponse response = AccountKitGraphResponse.fromHttpConnection(connection, request);//启动请求
        Utility.disconnectQuietly(connection);
        return response;
    }

 static AccountKitGraphResponse fromHttpConnection(HttpURLConnection connection, AccountKitGraphRequest request) {
        InputStream stream = null;

        AccountKitGraphResponse var4;
        try {
            if(connection.getResponseCode() >= 400) {
                stream = connection.getErrorStream();
            } else {
                stream = connection.getInputStream();//这里是请求的关键,连接到了服务器得到流
            }

            AccountKitGraphResponse exception = createResponseFromStream(stream, connection, request);//创建了响应流
            return exception;
        } finally {
            Utility.closeQuietly(stream);
        }

        return var4;
    }

这里比较可疑
stream = connection.getInputStream();
这个HttpURLConnection connection是个java.net包里的函数。所以貌似可疑伪造

http://blog.csdn.net/it_oracle/article/details/




 private static void serializeToUrlConnection(AccountKitGraphRequest request, HttpURLConnection connection) throws IOException, JSONException {
        ConsoleLogger consoleLogger = new ConsoleLogger(LoggingBehavior.REQUESTS, “Request”);
        HttpMethod connectionHttpMethod = request.httpMethod;
        connection.setRequestMethod(connectionHttpMethod.name());
        boolean isMultipart = isMultiPart(request.parameters);
        setConnectionContentType(connection, isMultipart);
        URL url = connection.getURL();
我们看到所有的参数都在控制范围内,但有一个参数例外
request.parameter
这个参数我分析了很久
他经过了很多复杂的查询和赋值,本来我想尽量少的改动源代码,可惜。这个赋值涉及到很多android源码的加密解密,和类操作,根本抠不出来。
当然不计代价,肯定能实现,但这里我不会这么做。
我们动态跟踪下
parameter里面有很多成员,我们把关于android系统相关的元素忽略。然后可以看到有个,mMap如下:
“fb_app_events_enabled” -> “false”
“response_type” -> “token”
“credentials_type” -> “email”
“redirect_uri” -> “ak7033://authorize”
“email” -> “”
“locale” -> “zh_CN”
“sdk” -> “android”
“access_token” -> “AA|7033|799de49b357b77afede08488ced2f721”
“logging_ref” -> “2fc145f1-1137-43ee-a1ae-1cc945ccc5a4”
“fields” -> “terms_of_service,privacy_policy”
这里有很多有实际意义的参数
但我没只关心email的值,其他的没有必要动态生成,直接设置成固定参数
所以思路来了

URL=https://graph.accountkit.com/v1.2/start_login

isMultipart=false

private String getUrlForSingleRequest() throws MalformedURLException {
        URL builder = new URL(“https://graph.accountkit.com");//这里对URL进行编码
        Matcher matcher = versionPattern.matcher(this.graphPath);
       /*if(!matcher.matches()) {
            builder.appendPath(this.version);
        }

        builder.appendPath(this.graphPath);
        //this.addCommonParameters();
        if(this.httpMethod != HttpMethod.POST) {
            this.appendQueryParametersToUri(builder);
        }
*/
        return builder.toString();
    }

我们发现toHttpConnection调用getUrlForSingleRequest
  static HttpURLConnection toHttpConnection(AccountKitGraphRequest request) {
        URL url;
        try {
            String connection = request.getUrlForSingleRequest();
            url = new URL(connection);
        } catch (MalformedURLException var6) {
            throw new AccountKitException(Type.INTERNAL_ERROR, InternalAccountKitError.CANNOT_CONSTRUCT_URL, var6);
        }

        try {
            HttpURLConnection connection1 = createConnection(url);
            serializeToUrlConnection(request, connection1);
            return connection1;
        } catch (UnknownHostException var4) {
            throw new AccountKitException(Type.NETWORK_CONNECTION_ERROR, InternalAccountKitError.NO_NETWORK_CONNECTION);
        } catch (JSONException | IOException var5) {
            throw new AccountKitException(Type.INTERNAL_ERROR, InternalAccountKitError.CANNOT_CONSTRUCT_MESSAGE_BODY, var5);
        }
    }
这一段有很多的请求参数
而getUrlsingleRequest明显是用来获取服务器相应目录资源的。这里说实话动态构造这个比较麻烦。
我们根据多方的分析这里应该是请求参数构造的关键
不过没有必要非按这个还原,因为确实比较麻烦。我们的思路是构建url连接就可以了,把关键参数抠出来就行了。
唯一要确定的是有没有什么序列化的对象呗传输到接口中了。

所以我把重点还是要放在serializeToUrlConnection这个函数上。

最终我们得到的核心交互位于这里
AccountKitGraphResponse executeAndWait() {
        HttpURLConnection connection;
            connection = toHttpConnection(this);//参数初始化,这里可能有序列化的东西
        AccountKitGraphResponse response = executeConnectionAndWait(connection, this);//启动请求并返回数据,这里可以参看我们的请求参数到底有些什么
            return response;
    }
toHttpConnection–》serializeToUrlConnection–》connection.getOutputStream();
executeConnectionAndWait—》fromHttpConnection–》connection.getInputStream();
这里其实就圆满了。我们把这个动态跟踪并伪造相应的结构发出请求。大功告成。


如果有序列化的东西,我们需要寻找OutputStream outputStream()对象的write函数,应该在getoutstream()附近。



try {
                OutputStream outputStream1 = connection.getOutputStream();
                outputStream = new BufferedOutputStream(outputStream1);
                if(!isMultipart) {
                    outputStream = new GZIPOutputStream((OutputStream)outputStream);
                }

                processRequest(request, (OutputStream)outputStream, isMultipart);

然而我们只找到个processRequest,

 private static void processRequest(AccountKitGraphRequest request, OutputStream outputStream, boolean isMultipart) throws IOException {
        AccountKitGraphRequest.Serializer serializer = new AccountKitGraphRequest.Serializer(outputStream, !isMultipart);
        serializeParameters(request.parameters, serializer);
        if(request.requestObject != null) {
            processRequestObject(request.requestObject, serializer);
        }

    }

private static class Serializer implements AccountKitGraphRequest.KeyValueSerializer {
        private boolean firstWrite = true;
        private final OutputStream outputStream;
        private boolean useUrlEncode = false;

        Serializer(OutputStream outputStream, boolean useUrlEncode) {
            this.outputStream = outputStream;
            this.useUrlEncode = useUrlEncode;
        }

这里有个接口Serializers实现,服了。
writeObject(String key, Object value)
writeString(String key, String value)
writeBitmap(String key, Bitmap bitmap)
writeBytes(String key, byte[] bytes)
writeContentUri(String key, Uri contentUri, String mimeType)
writeFile(String key, ParcelFileDescriptor descriptor, String mimeType)
这几个接口貌似是实现序列化写入的。
并且不约而同的调用了函数
writeContentDisposition(String name, String filename, String contentType)

而此函数有调用了outputstreaml类的write函数。虽然这其中还有一些小波折,但总体是这个意思。

这里还有两个封装了write的函数
 void write(String format, Object… args) throws IOException {
            if(!this.useUrlEncode) {
                if(this.firstWrite) {
                    this.outputStream.write(”–“.getBytes());
                    this.outputStream.write(”3i2ndDfv2rTHiSisAbouNdArYfORhtTPEefj3q2f“.getBytes());
                    this.outputStream.write(”\r\n“.getBytes());
                    this.firstWrite = false;
                }

                this.outputStream.write(String.format(format, args).getBytes());
            } else {
                this.outputStream.write(URLEncoder.encode(String.format(Locale.US, format, args), ”UTF-8“).getBytes());
            }

        }

        void writeLine(String format, Object… args) throws IOException {
            this.write(format, args);
            if(!this.useUrlEncode) {
                this.write(”\r\n“, new Object[0]);
            }

        }
    }
接着分析发现
 private static void processRequest(AccountKitGraphRequest request, OutputStream outputStream, boolean isMultipart) throws IOException {
        AccountKitGraphRequest.Serializer serializer = new AccountKitGraphRequest.Serializer(outputStream, !isMultipart);
        serializeParameters(request.parameters, serializer);
        if(request.requestObject != null) {
            processRequestObject(request.requestObject, serializer);
        }

    }
我们的outputstream流会通过serializeParameters把bundle 类 request.parameters参数赋进去,也就是说我们的序列化对象通过serializeParameters传输

还有个参数request.requestObject ,这是一个JSONObject类,我们知道这个协议通信就是基于这东西的。虽然目前我们无法迅速找到这个参数赋值的情况,但我们只需动态调试一下,把我们看到的参数分析一下就行了。
 private static void processRequestObject(JSONObject requestObject, AccountKitGraphRequest.KeyValueSerializer serializer) throws IOException {
        Iterator keyIterator = requestObject.keys();

        while(keyIterator.hasNext()) {
            String key = (String)keyIterator.next();
            Object value = requestObject.opt(key);
            processRequestObjectProperty(key, value, serializer);
        }

    }

private static void processRequestObjectProperty(String key, Object value, AccountKitGraphRequest.KeyValueSerializer serializer) throws IOException {
        Class valueClass = value.getClass();
        if(!String.class.isAssignableFrom(valueClass) && !Number.class.isAssignableFrom(valueClass) && !Boolean.class.isAssignableFrom(valueClass)) {
            if(Date.class.isAssignableFrom(valueClass)) {
                Date date = (Date)value;
                SimpleDateFormat iso8601DateFormat = new SimpleDateFormat(”yyyy-MM-dd'T'HH:mm:ssZ“, Locale.US);
                serializer.writeString(key, iso8601DateFormat.format(date));
            }
        } else {
            serializer.writeString(key, value.toString());
        }

    }


讯享网

小讯
上一篇 2025-03-23 16:44
下一篇 2025-01-12 08:10

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/38891.html