车联网通信协议,车载通讯协议
一、目标之前我们已经用unidbg跑通了libencrypt.so,那么如何判断跑出来的结果是对是错?再如何纠正unidbg跑错误的流程,是我们今天的目标。
6.1.0版
二、步骤找到明显的接口来判断校验码是加密,加密的结果确实不好判断是否正确。不过我们可以试试解密,能解密就是对的,简单粗暴。这里解密函数是解码。
公共void callB() {
string strA= flk 6 xivmcwpse 3 sk 6 b 71m 9 wbwd/gztlajqgxhjmwezzir 51 rvwsedwui 4 un 9 rnocgblnmqi 80 fiog 4 SW==;
String方法名称= de校验码(Ljava/lang/String;)Ljava/lang/String;;
DvmObject ret=dvm类。callstaticjnimethodobject(仿真器,methodName,strA);
String strOut=(String)ret。getvalue();
系统。出去。println( call de check code: strOut );跑一下,这个结果明显不对,死心了
调用decheckcode:分析问题结果不对,肯定是过程不对。
那么解决方案就是分析对比unidbg运行的流程和应用运行的流程有哪里有不同?
对比运行流程有三个粒度,函数、代码块和代码。 (在艾达山里按空格,出现的流程图中的每个块就是代码块)
我们今天主要要对比去校验码函数,所以先从代码块的粒度来做追踪。
跟踪块unidbg提供一个木块钩,每运行到一个代码块就触发这钩子,我们就利用他来做跟踪块
//保存需要用弗里达胡克的街区的地址
公共静态映射Integer,Integer subTraceMap=new HashMap Integer,Integer();
//保存命中的街区地址的次数,命中次数太多的就忽略掉。
公共静态映射Integer,Integer calc Map=new HashMap Integer,Integer();
//入参是因此基地址,需要微量代码的开始地址和结束地址
私有空跟踪块(最终长基址地址,最终长星地址,最终长{
emulator.getBackend()。hook_add_new(new BlockHook() {
@覆盖
公共空钩座(后端后端,长地址,int {
//代码块需要大于20个字节,块太小影响弗里达的钩
如果(20码){
指令[]insns=仿真器。拆卸(地址,4,0);
int iSize=insns[0].getSize();
int iUseAddr=0;
if (iSize==4) {
//手臂模式四字节
iuse addr=(int)(地址基addr);
}否则{
//拇指模式2字节,挂钩的时候1 ,
iuse addr=(int)(地址基addr)1;
}
如果(计算映射。包含密钥(iUseAddr)){
//保存命中次数
int iValue=计算映射。get(iUseAddr);
calcMap.put(iUseAddr,iValue 1);
//4次以上的调用就不显示了,也不用弗里达迹线了
if (iValue 3) {
子轨迹图。remove(iUseAddr);
}否则{
系统。出去。println( sub _ 整数。tohexstring((int)(address-base addr))“);
}
}否则{
calcMap.put(iUseAddr,1);
subTraceMap.put(iUseAddr,1);
系统。出去。println( sub _ 整数。tohexstring((int)(address-base addr))“);
}
}
}
@覆盖
公共void onAttach(UnHook unHook) {
}
@覆盖
公共空的分离(){
}
},starAddr,endAddr,0);
}跟踪块结束之后,把命中的代码块地址都打印出来,用于在弗里达中去钩
public void printucksubinfo(){
系统。出去。println( subTrace len= subtracemap。size());
string strOut=
对于(图Entry Integer,Integer entry:subtracemap。条目集()){
int iShow=entry。getkey();
//为了和unidbg显示一致这里处理下
如果(正在显示% 2!=0){
iShow=iShow-1;
}
strOut=strOut ,[ sub _ 整数。tohexstring(iShow)“”,整数“0x”。tohexstring((int)entry。getkey())“]”;
}
系统。出去。println(strOut);
}有了这两个函数就可以干活了
公共void callB(){
traceBlock(模块.基础,模块.基础,模块.基础模块。大小);
.
String方法名称= de校验码(Ljava/lang/String;)Ljava/lang/String;;
DvmObject ret=dvm类。callstaticjnimethodobject(emulator,methodName,strA);
.
打印挂钩subinfo();
}在执行去校验码之前去做痕迹,执行之后去打印所有命中的街区地址。
提示:这个样本没那么复杂,所以就直接微量所有代码范围,讲究人是需要缩小范围,只微量自己感兴趣的部分。
查找本机函数Java _ com _ bangcle _ comapi protect _ CheckCodeUtil _ decheckcode=RX @0x 4002 B1 BC[lib encrypt。所以]公元前0x 2 B1
sub_2b1bc
sub_2b20c
sub_2b238
sub_2b254
sub_2b270
sub_2b28c
sub_2b2a8
sub_2b2c4
sub_2b2e0
sub_2b2fc
sub_2b318
sub_2b334
.
subTrace len=127
,[sub_21400 ,0x21400],[sub_21c00 ,0x21c00],[sub_22200 ,0x22200],[sub_2b604 ,0x2b604]Trace的结果出来了,命中的地址列表也打印出来了,一共命中了127个地址块。
弗里达胡克对比把命中的地址列表导入到弗里达里面去钩子,然后就可以对比出来unidbg跑的流程和应用跑的流程的差别了。
函数钩子_可疑_函数(目标函数){
常量函数=[
[sub_21400 ,0x21400],[sub_21c00 ,0x21c00],[sub_.
];
对于(函数中的变量I){
设相对ptr=funcs[I][1];
设func ptr=targetso。add(相对ptr);
let describe=funcs[I][0];
let handler=(function() {
返回函数(参数){
//控制台。日志( \ n );
console.log(标签描述);
};
})();
Interceptor.attach(funcPtr,{ onEnter:handler });
}
}
函数traceNative() {
var targetSo=module。findbaseaddress( libencrypt。所以’);
console.log(标签Trace # # # # # # # # # # # # lib encrypt。所以:‘targetSo’);
钩子_疑似_函数(targetSo);
}
//然后在钩子去校验码的时候打印微量结果
截击机。附加(targetso。添加(0x 2 B1 BC),{
onEnter:函数(参数){
跟踪native();
var strCls=Java。使用( Java。郎。string’);
var strA=Java。投(这个。语境。x2,str cls);
控制台。log(TAG -de校验码a= strA);
},
onLeave: function(retval){
var strCls=Java。使用( Java。郎。string’);
var strRc=Java.cast(retval,strCls);
控制台。日志(标签-de校验码RC=对比结果对比的方法比较低,先把unidbg跟踪块的结果复制到文本文件1,然后把弗里达胡克打印的结果复制到文本文件2。最后开启无与伦比来对比
1:化学机械抛光
过程虽然很低,但是结果可一点都不低,从对比的结果看,大家之前都是好朋友,不过sub_18650之后就开始分道扬镳了。
这时候就需要问问艾达山了。
1:fopen
这里打开文件了一个文件,文件名是做了base64。
base64谁不会呢,随便写两行代码就可以解出来了/proc/%d/cmdline
这又在考我们的机器人编程知识了,问了下谷哥,哥说了,这是在读进程名,对于应用来说,进程名就是他的包名。
回想在unidbg中有个不起眼的报错
INFO[com。github。unidbg。Linux。arm 64 syscallhandler](arm 64 syscallhandler:1309)-在dirfd=-100处打开,路径名=/proc/2256/cmdline,oflags=0x0,模式=0这也是在提醒我们,读取进程名失败了。
重定向iounidbg是支持这种情况的,先让CaranywhereDemo多继承一个IOResolver来做超正析象管重定向
公共类CaranywhereDemo扩展抽象Jni实现IOResolver AndroidFileIO {
.
公共carany where(字符串apkFilePath)引发DecoderException,IOException {
.
emulator.getSyscallHandler().addIOResolver(this);
.
}
FileResult AndroidFileIO f1
公共文件结果AndroidFileIO get f1(String pathname,int) {
if (f1==null) {
f1=文件结果AndroidFileIO success(new ByteArrayFileIO(of lags,pathname, com.xxx.aeri.caranywhere ).getBytes()));
}
系统。出去。println( new f1==路径名=== VM。获取包名());
返回f1;
}
@覆盖
公共文件结果Android fileio resolve(Emulator Android fileio Emulator,String pathname,int) {
System.out.println(路径名);
if (/proc/self/cmdline ).等于(路径名) (/proc/模拟器。getpid()/cmdline ).等于(路径名)){
返回getF1(路径名滞后);
}
返回空
}
}好的了,这几步又和应用跑的一样了,大家又是好朋友了。
不过问题还是没有解决,跑出来的结果还是不对,木有解密成功。而且貌似这个应用还有坑,挂钩点一多就摆烂,直接崩溃。
我们必须找到一种新的武器来对付它。期待下一章的大结局。
第三,总结如何解决烦恼,只留痕迹。
能破断点调试的App肯定是逃不掉的。所以现在App的重点是抵抗调试和断点。
如果结果是错误的,将过程与正确的结果进行比较。你跑起来和你一模一样。有什么问题吗?
1:ffshow
难说的在于知道说了什么,说了就能说出来。
版权归作者所有:来自博主fenfei331的原创作品,转载请联系作者授权,否则将追究法律责任。
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。