在我前面的文章中提到,在首次拿到App的情况下,我选择首先查看日志,因为很多app在编译的时候,还是会残留一些日志信息。
对于有混淆的app来讲,尤其是TCP协议,哪怕app完全没有混淆,定位关键函数都比较麻烦。因此需要借助日志中的关键信息来帮忙定位。
当然,在日志中的关键信息无法准确定位的情况下,选择Androidmonitor或者查看堆栈信息也都是一些切入点。
在输出日志的情况下,开启fiddler以及wireshark,运行该app,查看日志发现了一些有意思的信息:
Line:D/duoyi_inc():startRespondcmd:0xLine:I/duoyi_inc():0xonRespondLine:I/duoyi_inc():LoginOp(genLogin)CipherrsaDECRYPT_MODEbeginLine:I/duoyi_inc():LoginOp(genLogin)CipherrsaDECRYPT_MODEendLine:I/duoyi_inc():debugTest:LoginOp(genLogin)parsekeylength:16Line:I/duoyi_inc():LoginOp(genLogin)hexStringToBytebeginLine:I/duoyi_inc():LoginOp(genLogin)hexStringToByteendLine:D/duoyi_inc():sendCmd0xLine:I/duoyi_inc():CCProtocolHandler,startHandle0,60Line:D/duoyi_inc():protocolInfo:CCProtocolHandler(handle),pos0,0fill:60enindex0Line:D/duoyi_inc():CCProtocolHandler,handleCmdcmd=,60Line:I/duoyi_inc():protocolInfo:ReadBuffer(clear),net:falseLine:D/duoyi_inc():startRespondcmd:0xLine:I/duoyi_inc():loginInfo:0x,respond,info=time=,uid=0,digitID=,result=0_notice=帐号错误,请重新输入Line:D/duoyi_inc():protocolInfo:CCNodeServer(disconnect):startcleardataLine:I/duoyi_inc():protocol_info:BaseLoopThread(run):onThreadLoopFinishLoginOp,即为登录操作。搜索相关信息,定位在了此处:
try{aa.d("LoginOp(genLogin)CipherrsaDECRYPT_MODEbegin");Cipherv5=Cipher.getInstance("RSA");v5.init(2,((Key)v1_1));v10=v5.doFinal(newDecoder.a().a(v0_4));aa.d("LoginOp(genLogin)CipherrsaDECRYPT_MODEend");v6=0;v5_1=0;v0_12=0;v1_2=((byte[])v4);gotolabel_93;}emmm这儿我插一句,JEB在处理多次循环嵌套的情况下,goto这种标签跳转特别多,很影响分析,GDA在这方面处理的比JEB要好,但是GDA处理分包情况不如JEB。因此我选择JEB结合GDA进行分析。上面只是列出了一小段代码,纵观这段代码所处的函数,可以确信登录就是在这一块儿完成的:
this.g.r_();aa.d("newstartlogin");aa.d("keyPairGeneratorgenerateKeyPaircreatbegin");v0_1=KeyPairGenerator.getInstance("RSA");v0_1.initialize();v1=v0_1.generateKeyPair();aa.d("keyPairGeneratorgenerateKeyPaircreatend");v0_2=v1.getPublic();v1_1=v1.getPrivate();dp.Auto_setValue(0);if(dp.a(this.a.f(),newStringBuilder().append(v0_2.getModulus()).append(",").append(v0_2.getPublicExponent()).toString())){aa.c("YGDLoginOP,genLogin,0xistimeout");this.a.k().c=-10;this.a.k().d="";this.f.d();v2=false;}else{aa.d("LoginOp(genLogin)CipherrsaDECRYPT_MODEbegin");v5=Cipher.getInstance("RSA");v5.init(2,v1_1);v1.Null_init();v10=v5.doFinal(v1.a(this.a.k().t));aa.d("LoginOp(genLogin)CipherrsaDECRYPT_MODEend");v6=0;v5=0;v0=0;v1=v4;while((v6v10.length())){if(v0){v1[v5]=v10[v6];v5++;}if((!v10[v6])(!v0)){v1_2=newbyte[((v10.length()-v6)-1)];aa.d("debugTest",newStringBuilder().append("LoginOp(genLogin)parsekeylength:").append(v1_2.length()).toString());v0=v2;}v6++;}if(!v1){aa.a("debugTest","LoginOp(genLogin):keyisnullerror");v2=false;}else{aa.d("LoginOp(genLogin)hexStringToBytebegin");v0_3=j.a(v1);aa.d("LoginOp(genLogin)hexStringToByteend");this.a.k().t="";m.a().a(v0_3);d.a().a(v0_3);ba.Auto_setValue(0);if(ba.a(this.d)){this.a.k().c=-10;this.a.k().d="";this.f.d();v2=false;......接下来就是耐心的进行分析各个参数的含义以及具体的流程了。与此同时,我在观察目录结构的时候发现一个类: