前言(废话)
由于公司出现题述问题,导致严重影响本人学习Android,所以费劲心思终于找到了国内的镜像。
解决方法
1 | buildscript { |
修改build.gradle
为以上代码部分,即可同步阿里的源,一般家里第一次同步不要走代理,后续可以走,尽量等到同步完成。当然如果你要像这样同步阿里源还是飞快的。
由于公司出现题述问题,导致严重影响本人学习Android,所以费劲心思终于找到了国内的镜像。
1 | buildscript { |
修改build.gradle
为以上代码部分,即可同步阿里的源,一般家里第一次同步不要走代理,后续可以走,尽量等到同步完成。当然如果你要像这样同步阿里源还是飞快的。
dex是Android系统特有的可执行文件,具有应用的全部指令及运行时数据。
.class通过dx工具整合到一个dex,使得各个类都能互相通信,且减小占用的空间,使得结构更加紧凑。
class文件和dex的结构存在本质上的不同。
计算机硬件有两种储存数据的方式:大端字节序(big endian)和小端字节序(little endian)。
dex格式存储采用的是小端字节序,比如一个16进制数0x2211
,大端字节序跟该数字一致,采用高位字节在前低位字节在后。
而小端字节序则与大端字节序相反,采用高位字节在后低位字节在前,则该数位0x1122
同理0x987654
其小端字节序写做:0x456789
,即人一般读数是按大端字节序读取,而dex的读数是按小端字节序来读取的,其实这一块会由外部设备进行自动转换方便人的阅读,理解即可。
数据类型描述说的是用字符串表示不同的数据类型。在这方面,Dex和Class文件格式没有区别。
用来描述函数的参数和返回值信息,类似Class文件格式的MethodDescriptor。不过,Shorty Descriptor比MethodDescriptor要抠,省略了好些个字符。
定义ShortyDescriptor的描述规则,箭头后面是规则的组成:
定义ShortyReturnType的描述规则:
ShortyReturnType → ‘V’ | ShortyFieldType
ShortyFieldType → ‘Z’ | ‘B’ | ‘S’ |’C’ | ‘I’ | ‘J’ | ‘F’ | ‘D’ |’L’
定义ShortyFieldType的描述规则,注意,引用类型统一用”L”表示即可
ShortyDescriptor是”返回值类型”+”参数类型”,如果有参数就会带参数类型,没有参数就只有返回值类型。
在ShortyDescriptor的ShortyFieldType中,引用类型只需要用”L”表示,显然,ShortyDescriptor对于那些参数或返回值类型为引用类型的函数将无法区分。不过没关系,Dex中还会提供其他数据来指明参数或返回值的具体类型。这种做法的原因其实还是为了减少字符串的使用。
1 | /* |
注:邓凡平先生主要把dex_header分成
上面的注释也主要围绕这个分发加注
header_item是一个二进制数据集合,邓凡平先生通过编程语言还原了其结构
magic: 取值必须是字符串“dex\n035\0”,或者byte数组{0x640x650x780x0a 0x300x330x350x00}
checksum: 文件内容的校验和。不包括magic和checksum自己。该字段的内容用于检查文件是否损坏。
signature: 签名信息,不包括magic、checksum和signature。该字段的内容用于检查文件是否被篡改。
file_size: 整个文件的长度,单位为字节,包括所有内容。
header_size: 默认是0x70个字节。
endian_tag: 表示文件内容应该按什么字节序来处理。默认取值为0x12345678,Little Endian格式。如果为Big Endian时,该字段取值为0x78563412。
xxx_id_item: 包括string_id_item、type_id_item、proto_id_item、field_id_item和method_id_item这五种数据结构,它们对应为string_ids、type_ids、proto_ids、field_ids和method_ids数组元素的数据类型。
string_data_item: utf16_size是字符串中字符的个数。Dex文件的字符串采用了变种的UTF8格式,对于英文字符和数字字符而言,都只占一个字节。data是字符串对应的内容。
string_id_item: 类似Class文件的CONSTANT_String类型,它只有一个成员。
string_data_off: 用于指明string_data_item位于文件的位置,也就是索引。
type_id_item: descriptor_idx是指向string_ids的索引。
field_id_item: class_idx和type_idx是指向type_ids的索引,而name_idx是指向string_ids的索引。
method_id_item: class_idx是指向type_ids的索引,proto_idx是指向proto_ids的索引,name_idx是指向string_ids的索引。
proto_id_item: shorty_idx是指向string_ids的索引,return_type_idx是指向type_ids的索引,如果parameters_off不为0,则文件对应的地方存储类型为type_list的结构,用于描述函数参数的类型。
type_item: type_idx是指向type_ids的索引。
type_list: size表示list数组的个数,而list数组元素类型为type_item。函数的每一个参数都对应一个type_item元素。
于proto_id_item: 首先,它的成员域shorty_idx(也就是ShortyDescriptor字符串)已经描述了参数和返回值的类型,但这只是简单描述,比如所有引用类型都用”L”统一表示,所以ShortyDescriptor肯定无法完整描述那种参数或者返回值类型为引用类型的函数。为解决此问题,proto_id_item中的return_type_idx用来描述返回值的数据类型,而参数的类型则通过parameters_off域(如果取值不为0,则表示该函数有参数)指向一个type_list。这个type_list为每个参数都存储了对应的数据类型(通过type_item中的type_idx来索引type_ids中的元素)。
class_def:
class_idx:指向type_ids,代表本类的类型。
superclass_idx:指向type_ids,代表基类类型,如果没有基类则取值为NO_INDEX(值为-1)。
interfaces_off:如果本类实现了某些接口,则interfaces_off指向文件对应位置,那里存储了一个type_list。该type_list的list数组存储了每一个接口类的type_idx索引(参考图3-5和对应的解释)。
source_file_idx:指向string_ids,该类对应的源文件名。
annotations_off:存储和注解有关的信息。
class_data_off:指向文件对应位置,那里将存储更细节的信息,由class_data_item类型来描述。
static_values_off:存储用来初始化类的静态变量的值,静态变量如果没有显示设置初值的话,默认是0或者null。如果有初值的话,初值信息就存储在文件static_values_off的地方,对应的数据结构名为encoded_array_item
一个类的成员变量、成员函数等信息则是通过class_data_off域指向一个名为class_data_item结构体来描述的。
static_fields:类的静态成员信息,元素类型为encoded_field。
instance_fields:类的非静态成员信息,元素类型为encoded_field。
direct_methods:非虚函数信息,元素类型为encoded_method。
virtual_methods:虚函数信息,元素类型为encoded_method。
encoded_field和encoded_method用于描述类的成员变量和成员函数的信息。
encoded_field:field_idx_diff指向field_ids。注意这里是field_idx_diff,它表示除数组里第一个元素的field_idx_diff取值为索引值,该数组后续元素field_idx_diff取值为和前一个索引值的差。access_flags表示成员域的访问标志
encoded_method:method_idx_diff指向method_ids。diff的含义与field_idx_diff一样。access_flags表示该函数的访问标志,code_off指向文件对应位置处,那里有一个类型为code_item的结构体,code_item类似于Class文件的Code属性。
dex格式当中,我们需要了解的重中之重,因为函数抽取实际上就是把code_item进行抽空(置0)的方式实现的。
registers_size:此函数需要用到的寄存器个数。
ins_size:输入参数所占空间,以双字节为单位。
outs_size:该函数表示内部调用其他函数时,所需参数占用的空间。同样以双字节为单位。
insns_size和insns数组:指令码数组的长度和指令码的内容。Dex文件格式中JVM指令码长度为2个字节,而Class文件中JVM指令码长度为1个字节。
tries_size和tries数组:如果该函数内部有try语句块,则tries_size和tries数组用于描述try语句块相关的信息。注意,tries数组是可选项,如果tries_size为0,则此code_item不包含tries数组。
padding:用于将tries数组(如果有,并且insns_size是奇数长度的话)进行4字节对齐。
handlers:catch语句对应的内容,也是可选项。如果tries_size不为零才有handlers域。
padding则是需要对齐的时候才存在
一般我都会把data转成json再传给nodejs服务,结果总是获取不到参数,脑壳痛,这里记录一下。
首先发送请求的时候,要给定Content-Type
为application/json
gi
注意app.use那里,指定了从header的content-type为application/json的请求获取参数,这里的limit是为了扩大接收的参数大小,这里我随意写了一个足够大的大小。
如果不写limit会发生什么情况呢?
会出现请求体过大导致报错的情况。
究其原因是因为nodejs 做为服务器,在传输内容或者上传文件时,系统默认大小为100kb。
1 |
|
大数组+位移函数+解密函数,多个位置多次调用解密函数。
自执行函数如果带参数的都会有个加载器,我们看看兄弟节点有啥用吧。
这些setCookie这类的只是为某个obj的属性。只需观察这些函数里面有没有啥实参。
对这个自执行的函数来说,全局的变量只有这么两个_0x1032, 0x1a7
,就围绕这两个看看。
这两个实参到这个函数里是_0x263f33,_0x270f13
.
JS的函数的参数是啥传递。
也就是说,如果不通过属性来修改对象的值,那都不影响实参!!!
我们来看看这个函数里有哪里用到了实参。
只有这一处使用到了实参,数组实参只有加载器用到。
步进看看该函数是干啥的。
执行的函数是加载器,加载器的作用也只是为了执行参数而已。也就是说,其实整段代码其实也就只有++那段有用。
也就是这段。只需把函数名改为加载器的函数名,参数改为形参的参数名即可。
我们可以手动删掉,也可以通过AST来删。这里我们来用AST。
接下来我们来分析一下解密函数。
我们看到用到_0x270f13
只有一处。
再看rc4这函数是啥。
保留了以上提到的,后面这里===undefine处,仅仅只需保留
该处即可。
像这种通过switch和while来混淆代码的执行顺序,既是控制流平坦化,这类的代码比较麻烦,不过可以看看极验那块的代码,那块的控制流平坦化比ob混淆的要好些,这个相对简单,直接附上代码。
1 | const decode_while = { |
完整代码:
1 | /*********************************************************** |
telnet ip port:该命令用于检测该ip的这个端口是否已经开放。
curl ip.sb: 该命令用于查询本机当前IP是多少.
strace: 用于诊断和调试Linux用户空间syscall跟踪器,可以用来监控用户空间进程和内核的交互。简单来说就是看系统调用的整个过程。
lsof: 英文释义为list open files,是一个列出当前系统打开文件的工具
netstat -aple(或者用-tuulp) | grep procese_name: 用于查看某个进程其监听端口
du -h *:查看当前文件夹的每个文件的大小
scp /path/filename username@servername:/path : 通过ssh发送文件
是指允许在用户空间的程序向操作系统内核请求需要更高权限允许的服务,系统调用提供用户程序与操作系统内核之间的接口。
Java本质上调用了C/C++来完成。写完之后是一个art.so,之后调用安卓调用bionic-c
(kali是用glibc
)封装的函数,bionic-c其实是调用了syscall到内核里去写文件
HttpClient,现在已经没有人在用了。Android5时官方不再推荐其,Android6的SDK甚至直接去掉了,Android9时,Android更是彻底取消了对Apache HTTPClient的支持,Android开发官方推荐用HttpUrlConnection。
HttpUrlConnection, 虽然官方推荐,但是也很少有人用,跟python的urllib库一样,第三方库已经非常优秀了,所以多数人选择了第三方库。
Okhttp是Square公司开源的网络请求框架,优秀到HttpUrlConnection的底层实现也是基于Okhttp。现在主流是okhttp3,由于okhttp3与okhttp4的api改动较大,所以它还是主流,较新版本都已经基于kotlin,okhttp3是目前最流行的android网络请求框架。
一个基于okhttp框架的网络请求框架
该库基于HttpClient,目前由于HttpClient的过时和作者不再更新,所以该库仅仅作为了解。
13年Google发布的一款基于HttpURLConnection的异步网络请求框架。特别适合数据量小,通信频繁的网络操作,有一定的使用量。
目标:搭建一个按钮,点击之后发送网络请求。
先是创建一个activity,这里我们可以使用main_activity.xml
,在里面注册一个Button,只需在LinearLayout里创建button标签就行了。
接着就是在MainActivity.java里面创建Button对象,设定点击事件
先是测试一下,事件是否可以运行。
能跑就行。
该网站大致界面如下,我们需要获取的数据是红框里的数据,其加载为ajax.
其参数为:
经过检测,其必要的参数为
token为非必须,这个就好办多了,一看token就是一个加密,既然加密参数不重要,这个最重要的就是找到data的规律。参数分析好了,看看请求头。
这里我把cookie注释掉后请求返回了一串js,看来除了data还要处理cookie,我再测测看哪个cookie值是必须的。
也就是说必须的cookie值是这个rbzid
.
在看看不对的cookie返回的js
也就是说我们要破解这一串js。
先用fiddler看看那串cookie在那里被设置的。
这里没有,结果这个加密的链接这里就有设置这个cookie
小目标又变了,变成如何生成这串url并发送请求。先看第一个请求返回的html
看来不怎么多,搞得都不想写ast了。不过还是试试看。
把unicode转成字母
像这种的如果是StringLiteral
直接把它的extra删掉就好。但是这种是RegExpLiteral
这种需要把它运行一遍获取到真正的值时替换原节点即可,后来我发现直接转的话跟下一个参数没有空格导致报错,能力不行,自己手动加空格。
效果如上。
把16进制转成10进制
可以看到显示的是raw,而10进制则为上面的value,我们可以把extra删掉这样就能显示value了。
效果如上。
呼,做不是程序解决的活真是体力活。后面终于弄好了,开始解决逻辑的东西了~
我们要考虑的是自执行的函数,TOSS这个对象就不要管了。
先解决这种xxx.xx(number)
这种形式的代码。
我这里先解决var h = T0SS;
这类多余代码。
这里可以采用path.scope.rename(oldStr, NewStr)
这个方法,直接替换h为T0SS就可以解决了。
然后就出现了多余的代码var T0SS = T0SS;
我们可以通过一系列判断之后通过path.remove()
删除掉相应节点。
接下来进入正题,解决xxx.xx(number)
这种形式的代码。
碰到这种我们肯定要有T0SS以及它的相应函数,所以这一片代码要扣过来。这部分不讲了,直接上代码。
执行完以上的AST我们的待破解的代码就清晰很多了,这时候就需要补环境了。缺啥补啥,需要注意的是最后一行它的window.rbzns里的参数是动态的,所以我们也要考虑改成动态的。比如说碰到document.createElement这些。看看它的代码之间的关联,如果只是为HTML创建标签的话,直接删掉就好了。
可以考虑像我这样写,这样就会成动态的了。本篇只是在记录自己工作上碰到的一个网站,感觉用AST合适就记录下来了。大家可以练练手哦~
getchar:从文本流中读入下一个输入字符,并将其作为返回值返回,
putchar:打印一个字符。可以与printf交替调用,输出的次序与调用的次序一致。
不显示声明返回类型,默认返回int类型。
C语言的参数传递为值传递,即传递给函数的参数值存放在临时变量中。也就是说被调用函数不能直接修改主调函数中变量的值。
当把数组名用作参数时,传递给函数的值是数组起始元素的位置或地址——它并不复制数组元素本身。在被调用函数中,可以通过数组下标访问或修改数组元素的值。
主调函数传递地址(地址就是指向变量的指针),被调用函数也需要将对应的参数参数声明为指针类型,并通过它访问变量。
C语言使用<string.h>里提供的strlen(string)
函数来实现返回特定字符串的长度,相当于python的len(obj)
字符说白了就是一个机器字符集对应的数字,是一个整数。
字符串实际上是一个字符数组,数组的最后一位为’\0’代表结束。
比如’x’和”x”实际上,”x”相当于[‘x’, ‘\0’]
枚举是一种常量类型,是一个常量整型值的列表。
例如:enum boolean {NO, YES}
没有显式说明的前提下,默认第一个是0,第二个是1,依次往上叠加。值也可以是’a’之类的可以转为数字的char.不同枚举的值中的名字必须不同,不同的名字的值可以相同
听说头条更新了,而且我下个周确定入职,所以想练练JS逆向,好久不搞JS逆向这一块了,话不多说,搞起。
从以上三图可知我们需要的参数是max_behot_time(时间戳)、as、cp、_signature
as:
即可搜到as和cp,打个断点下拉就能看到了~我们可以看到as和cp来自e,而这个e是下图
找到a()
进入到a之后看看这个o是啥玩意。
再步进C
这个是是在某个自执行函数里面的,我们一般把这个自执行抠出来。报错的地方不影响的话直接去掉即可。
2. _signature
这个参数是最让一般人头疼的,我们来慢慢的解决它。
全局搜索找到这个地方。找到p.calcSignature
所代表的的函数p()
看到那么多location、window就知道这个东西有进行环境检测。能写死先写死。
这个参数是除了_signature之外所有参数拼接而成的url:"https://www.toutiao.com/toutiao/api/pc/feed/?max_behot_time=1595566720&category=__all__&utm_source=toutiao&widen=1&tadrequire=true&as=A1153FE1EB50763&cp=5F1BB08776134E1"
window.byted_acrawler.sign的方法实际上是在另一个文件里
这个文件存在着一个大大的自执行函数TAC,这个似乎是头条核心的加密了,我继续整整。
先看看这个window.byted_acrawler这里有个init函数,函数里面的vm就是TAC所在的文件,那我们这边大胆假设一下,这个init的执行之后才有了sign。
扣出p并把TAC整个扣出放入p内。构建参数,跑一遍看看。
是不是给抓包出来的参数对比出来的不一样?目测环境问题。把代码扣出来放浏览器跑跑。
果然是!那问题就是TAC文件里面有环境检测了。看看哪里有window或navigator一般检测就检测这两。
我们看到这里的v是global对象,很有可能通过这个进行了检测。
tag:
缺失模块。
1、请确保node版本大于6.2
2、在博客根目录(注意不是yilia根目录)执行以下命令:
npm i hexo-generator-json-content --save
3、在根目录_config.yml里添加配置:
jsonContent: meta: false pages: false posts: title: true date: true path: true text: false raw: false content: false slug: false updated: false comments: false link: false permalink: false excerpt: false categories: false tags: true