2019-9-14 1012 0
2019-9-14 853 0
奇巧淫技

难点头部顺序反爬、某些框架内部会对头部处理造成获取不到CookieTip:文末可以下载例子猿人学该题官网:http://match.yuanrenxue.com/match/3确定逆向目标请求加密一般在参数头部(Cookie,token,还有其他的自定义的),这里就只有Cookie了开始逆向既然第二个请求,带有Cookie,那么就需要确定该Cookie是生成,还是服务器返回,这里还有第一个请求,所以大概率是服务器返回分析请求第一个请求状态码是202(表示服务器端已经收到请求消息,但是尚未进行处理),还有Set-Cookie,那就可以确定是服务器返回的了获取不到Cookie的原因这里获取不到Cookie,有两个原因一、反爬(对请求头部的顺序有要求,最好是使用原始顺序)二、requests框架的原因(框架会对设置的头部进行优化修改)获取Cookie规避了上面两个问题后,就可以获取到Cookie请求数据importrequestsdefapp(page:int)->dict:session=requests.Session()session.headers={'Host':'match.yuanrenxue.com','Connection':'keep-alive','Pragma':'no-cache','Cache-Control':'no-cache','User-Agent':'Mozilla/5.0(WindowsNT10.0;Win64;x64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/91.0.4472.101Safari/537.36','DNT':'1','Accept':'*/*','Origin':'http://match.yuanrenxue.com','Referer':'http://match.yuanrenxue.com/match/3','Accept-Encoding':'gzip,deflate','Accept-Language':'zh-CN,zh;q=0.9',}session.post(url="http://match.yuanrenxue.com/jssm",)print(session.cookies)s=session.get(url="http://match.yuanrenxue.com/api/match/3",headers={"User-Agent":"yuanrenxue.project",},params={"page":page})returns.json()if__name__=='__main__':for_inrange(1,6):print(app(_))结果正确Tip:直接访问数据接口,会返回一段Js其实这段代码一点用都没有。完整代码:https://pan.bigdataboy.cn/s/JZh7

奇巧淫技

难点加密在Cookie,Cookie有时间限制,暗桩反调,混淆,调试阻塞Tip:文末可以下载例子猿人学该题官网:http://match.yuanrenxue.com/match/2确定逆向目标开始逆向加密值在Cookie有两种情况,该加密Cookie是服务器返回或者是本地生成分析请求反ob混淆这段代码经过了ob混淆,格式化后是这样的一键ob反混淆(猿人学提供有)网址:http://tool.yuanrenxue.com/decode_obfuscator分析加密处反混淆后,直接看见了document["cookie"],还有location["reload"]();这个不就就是:设置Cookie然后再次请求嘛调试加密值发现M()函数为空,V(Y)函数疑似加密结果导出加密值这个加密值在函数内部,想要使用就用全局变量接收一下暗桩一这段代码,其中!a6["test"](a2)执行起来很慢,并且一直返回true,所以直接写死,就会发现执行起来很快了,一些调试工具也不会阻塞了vara2=B(this,function(){vara5=function(){vara6=a5["constructor"]("return/\"+this+\"/")()["compile"]("^([^]+(+[^]+)+)+[^]}");return!a6["test"](a2);//true};returna5();});暗桩二如果说暗桩一是技巧,那暗桩二才是真正的暗桩,可以看这段代码改造了console.log只要我们一使用就会陷入,虽然这个暗桩对Js执行没有影响,但是调试就很容易陷入console=newObject()console.log=function(s){while(1){for(i=0;i<1100000;i++){history.pushState(0,0,i)}}}console.toString='[objectObject]'console.log.toString='?toString(){[nativecode]}'请求计算结果稍微修改Js,计算结果importexecjsimportrequestsdefget_c():returnexecjs.compile(open('./main.js','r',encoding="utf-8").read()).call('f')defapp(page:int)->dict:rep=requests.get(url="http://match.yuanrenxue.com/api/match/2",headers={"Cookie":f"sessionid=你的sessionid;m={get_c()};","User-Agent":"yuanrenxue.project",},params={"page":page})returnrep.json()if__name__=='__main__':sum=0for_inrange(1,6):data=app(_)forvalueindata.get('data'):sum=sum+value.get("value")print(sum)结果正确完整代码:https://pan.bigdataboy.cn/s/EYSW

奇巧淫技

难点源码压缩,混淆,反调,时间检测Tip:文末可以下载例子猿人学官网:http://match.yuanrenxue.com/确定逆向目标这个m值开始逆向过反调跟值此处是加密的url在发送出去最后出现的地方,我们可以通过函数调用的栈,进行跟值,找到大体加密的地方本地调试最终我们跟到了此处,发现它在<script>标签里,我们无法格式化,就无法断点调试我采用把整个网页扒下来,修改其中的<href>为绝对路径,采用fiddler本地替换网页响应,实现把在本地把<script>压缩的代码格式化,在手动加断点寻找window.f值逆向需要有怀疑精神,oo0O0()这个函数返回空,不会这么简单吧,进入它定位加密位置经过分析最终确定在这一行改造加密函数到这里,差不多就分析完成,就改造一下加密函数就完成了functionf(){varmv=Date.parse(newDate())+100000000;varwindow={};varm=eval("把eval里的全部复制过来window.f=hex_md5('bigdataboy')".replace(/bigdataboy/,mv))//这里有个小坑必须要用替换,因为拼接的md5值是不对的returnm+'丨'+mv/1000}请求计算结果importexecjsimportrequestsdefget_m():returnexecjs.compile(open('./main.js','r',encoding="utf-8").read()).call('f')defapp(page:int)->dict:rep=requests.get(url="http://match.yuanrenxue.com/api/match/1",headers={"Cookie":"自己的sessionid",'User-Agent':'yuanrenxue.project',},params={"page":page,"m":get_m(),})returnrep.json()if__name__=='__main__':sum=0count=0for_inrange(1,6):data=app(_)count=count+len(data.get("data"))forvalueindata.get("data"):sum=sum+value.get("value")print(sum/count)结果成功完整代码:https://pan.bigdataboy.cn/s/Nnsy

编程杂谈

二叉树的遍历因为使用二叉树最多是链式存储结构,所遍历的方式也是按照链式结构来的。遍历的宗旨在遍历是按照某种次序依次访问二叉树的所有节点,并且每个节点仅仅被访问一次。前序遍历(根左右)遍历顺序ABDHIEJCFKG中序遍历(左根右)遍历顺序HDIBEJAFKCG后序遍历(左右根)遍历顺序HIDJEBKFGC层序遍历遍历顺序ABCDEFGHIJK二叉树代码实现前序遍历(根左右)二叉树的遍历方式,在创建二叉树时就确定好了#include<stdio.h>#include<stdlib.h>typedefcharElemType;//二叉树结构typedefstructBiTNode{ElemTypedata;structBiTNode*lchild,*rchild;}BiTNode,*BiTree;//创建二叉树按照用户遵循的前序遍历的(根左右)方式输入数据voidcreateBiTree(BiTree*T){charc;printf("输入元素:");scanf("%c",&c);getchar();if(c=='#'){*T=NULL;}else{*T=(BiTree)malloc(sizeof(BiTNode));(*T)->data=c;createBiTree(&(*T)->lchild);//先创建左子节点createBiTree(&(*T)->rchild);//再创建右子节点}}//遍历二叉树voidpreOrderTraverse(BiTreeT,intlevel){if(T!=NULL){printf("数据%c在第%d层\n",T->data,level);preOrderTraverse(T->lchild,level+1);preOrderTraverse(T->rchild,level+1);}}intmain(){intlevel=1;BiTNode*T;createBiTree(&T);preOrderTraverse(T,level);return0;}输入顺序中序遍历(左根右)输入方式还是前序的输入方式,只是遍历的打印位置修改了一下//中序遍历二叉树voidpreOrderTraverse(BiTreeT,intlevel){if(T!=NULL){preOrderTraverse(T->lchild,level+1);printf("数据%c在第%d层\n",T->data,level);preOrderTraverse(T->rchild,level+1);}}后序遍历(左右后)输入方式还是前序的输入方式,只是遍历的打印位置修改了一下//后序遍历二叉树voidpreOrderTraverse(BiTreeT,intlevel){if(T!=NULL){preOrderTraverse(T->lchild,level+1);preOrderTraverse(T->rchild,level+1);printf("数据%c在第%d层\n",T->data,level);}}

编程杂谈

栈相关概念什么是栈是限定仅在表尾(栈顶)进行插入和删除操作的线性表栈又称为后进先出(LastInFirstOut)的线性表,简称LIFO结构栈顶(top)我们把允许插入和删除的一端称为栈顶栈底(bottom)另一端称为栈底空栈不含任何任何数据元素的栈称为==空栈==栈的操作入栈入栈又称压栈,就是向栈中放数据,每次放入一个top指针就+1出栈取出栈中数据的操作,每取出一个top指针就-1清空栈清空栈,是把top指针直接指向bottom指针,原本物理空间上数据还存在栈的实现创建队列结构体#include<stdio.h>#include<stdlib.h>#defineSTACK_INIT_SIZE20//栈大小#defineSTACK_INCREMENT10//申请空间大小typedefcharElemType;//定义栈typedefstruct{ElemType*base;//栈低ElemType*top;//栈顶intstackSize;//当前栈容纳的大小}sqStack;初始化队列//初始化栈voidinitStack(sqStack*s){s->base=(ElemType*)malloc(STACK_INCREMENT*sizeof(ElemType));if(s->base==NULL)exit(-1);//空间申请失败s->top=s->base;s->stackSize=STACK_INIT_SIZE;printf("s->top:%p\n",s->top);printf("s->base:%p\n",s->base);}入栈(压栈)//压栈voidpush(sqStack*s,ElemTypee){//判断是否满栈,满栈则扩容if(s->top-s->base>=s->stackSize){s->base=(ElemType*)realloc(s->base,(s->stackSize+STACK_INCREMENT)*sizeof(ElemType));if(!s->base)exit(-1);}*(s->top)=e;//栈顶的值赋值es->top++;//栈顶地址向前推进}出栈//出栈voidpop(sqStack*s,ElemType*e){//判断下溢出if(s->top==s->base){return;}*e=*--s->top;//减之后赋值,*为取值,不是指针}当前栈长度//当前栈长度intsqStackLen(sqStack*s){returns->top-s->base;}主函数使用用户输入值,进行压栈,#号结束,输出栈内数据intmain(){sqStacks;initStack(&s);//初始化栈ElemTypec;printf("输入栈:");while((c=getchar())!='#'){getchar();push(&s,c);//压栈}getchar();//把\n从缓冲区去掉ElemTypee;while(sqStackLen(&s)){pop(&s,&e);//出栈printf("%c",e);}return0;}扩展(栈链)就是把栈和单链表结合,如下图一样栈链内元素是指针相连,而栈是顺序的地址队列概念什么是队列队列(queue)是只允许在一端进行插入的操作,而在另一端进行删除操作的线性表。队列主要特征是先进先出(FirstInFirstOut)队列操作队列的结构体#include<stdio.h>#include<stdlib.h>#defineElemTypechar//队列元素结构体(链表)typedefstructQNode{ElemTypedata;//队列节点数据structQNode*next;//队列指针}QNode,*QueuePrt;//队列头尾typedefstruct{QueuePrtfront,rear;//队头指针,队尾指针}LinkQueue;创建队列/*创建结构体在内存中创建一个头结点,将队列的头尾指针指向头结点,此时队列为空*/voidinitQueue(LinkQueue*q){q->front=q->rear=(QueuePrt)malloc(sizeof(QNode));if(!q->front)exit(-1);q->front->next=NULL;}入队列//入队列(下面是在从尾部入)voidinertQueue(LinkQueue*q,ElemTypee){QueuePrtnode=(QueuePrt)malloc(sizeof(QNode));if(!node)exit(-1);node->data=e;node->next=q->rear->next;q->rear->next=node;q->rear=node;}出队操作(取出队列的数据)//出队操作(下面是从队列头部取值)voidgetQueue(LinkQueue*q,ElemType*e){if(q->front==q->rear)return;//队列为空QueuePrtnode=q->front->next;*e=node->data;q->front->next=node->next;//队列头部向移动一位if(q->rear==node)q->rear=q->front;//队列最后数据被取出是,初始化为空free(node);};销毁队列//销毁队列voiddeleteQueue(LinkQueue*q){while(q->front){q->rear=q->front->next;free(q->front);q->front=q->rear;}}主函数使用intmain(){LinkQueueq;initQueue(&q);ElemTypee=0;printf("输入队列数据:");scanf("%c",&e);while(e!='#'){inertQueue(&q,e);scanf("%c",&e);}printf("队列中的数据:\n");while(q.rear!=q.front){getQueue(&q,&e);printf("%c",e);}return0;}

编程杂谈

常规想法遍历一遍单链表,记录长度再一次遍历,输出中间元素这样的效率不是最优另一种方式(来源于网络)search的速度是mid的两倍,search走完恰好mid走一半voidprintInt(intList*head){intList*mid,*search;//search的速度是mid的两倍,search走完恰好mid走一半mid=search=head;//保证midsearch启点一样while(search->next!=NULL){search=search->next;//search取下一个if(search->next!=NULL){search=search->next;//search再去下一个mid=mid->next;//mid就取一次}else{mid=mid->next;//search取到头,mid再往下取一位}}printf("\n%d",mid->num);}完整代码随机生成任意长度单链表输出单链表值输出位于中间的值奇数个是最中间值,偶数个是中间两个的前一个#include<stdio.h>#include<stdlib.h>#include<time.h>typedefstructIntList{intnum;structintList*next;}intList;intList*applyList();//生成随机长度单链表voidprintList(intList*head);//查看单链表voidprintInt(intList*head);//输出单链表中间值intList*applyList(){srand((unsigned)time(NULL));intlen=rand()%10+10;//10到20的随机数intList*head=(intList*)malloc(sizeof(intList));if(head==NULL)exit(-1);//链表头部申请失败head->next=NULL;while(len){intList*node=(intList*)malloc(sizeof(intList));node->num=rand()%10+20;node->next=head->next;head->next=node;len--;}returnhead;}voidprintList(intList*head){intList*t=head->next;while(t){printf("%d",t->num);t=t->next;}};voidprintInt(intList*head){intList*mid,*search;//search的速度是mid的两倍,search走完恰好mid走一半mid=search=head;//保证midsearch启点一样while(search->next!=NULL){search=search->next;//search取下一个if(search->next!=NULL){search=search->next;//search再去下一个mid=mid->next;//mid就取一次}else{mid=mid->next;//search取到头,mid再往下取一位}}printf("\n%d",mid->num);}intmain(){intList*list=applyList();//随机生成单链表printList(list);//查看链表的值printInt(list);//输出中间值return0;}