• i春秋2020新春公益赛WP


    Re

    Factory

    主函数fork了一个子进程,父进程添加了一个信号处理器用于比对input,然后死循环挂起。子进程读入input,然后调用了关键函数。

    image-20200224092853714

    跟进关键函数,发现是从一段内存中读取数据,然后根据数据发送信号给父进程,感觉像个虚拟机。

    代码很长这里就不贴了,看一眼CFG,符合一般虚拟机的流程图:

    image-20200224103556748

    在虚拟机里面没见到对input的操作,肯定有遗漏的地方,对input查看交叉引用,在init里面发现两个函数:

    image-20200224112325787

    第一个函数给input分配了一段内存,第二个函数设置了一堆信号处理器。

    这里分配内存用的mmap,并且开了MAP_SHARED,子进程fork的是父进程指向这块内存的指针,所以这块内存是父子进程共享的。

    要注意的是第一个函数中存在反调试,检查了父进程的cmdline并且加密了,只有加密后的cmdline是指定数据才分配内存:

    image-20200224112601647

    很多人动态调试不成功就是因为这个,这题不需要动态调试。如果一定要调,attach或者把这段代码patch一下都行。

    第二个函数中建立了一堆handler,根据这些代码还原opcode就行。

    image-20200224112946254

    opcdode:

    17,52,0,42,5,16,20,9,23,0,32,5,3,17,29,6,0,0,5,3,17,64,6,0,64,5,17,29,23,14,1,21,4,15,1,22,2,0,0,4,3,5,16,20,50,5,9,2,19,29,5,18,21,4,16,20,61,10,1,19,52,3,4,18,14,1,21,4,7,1,22,2,0,0,4,3,5,16,20,85,5,9,1,19,64,5,18
    

    还原出来的伪代码(括号内是opcode,冒号前是signal的值,注释是语句含义):

    0:
    (17,52)42:q1[19]++;input[256+q1[19]-1]=q1[20];q1[20]=input[79]=52;//call 52
    
    2:
    (0,42)34:q1[19]++;input[256+q1[19]-1]=42;
    (5)35:q1[19]--;q1[17]=input[256+q1[19]];//q1[17]=42=strlen
    (16)41:q1[21]=q1[16]==q1[17];
    (20,9)45:if(q1[21])q1[20]=input[79]=9;//长度不是42就退出
    (0,32)34:q1[19]++;input[256+q1[19]-1]=32;
    (5)35:q1[19]--;q1[17]=input[256+q1[19]];//q1[17]=32
    (3)34:q1[19]++;input[256+q1[19]-1]=q1[18];//push len
    (17,29)42:q1[19]++;input[256+q1[19]-1]=q1[20];q1[20]=input[79]=29;//call 29
    (6)35:q1[19]--;q1[18]=input[256+q1[19]];//q1[18]=len
    (0,0)34:q1[19]++;input[256+q1[19]-1]=0;
    (5)35:q1[19]--;q1[17]=input[256+q1[19]];//q1[17]=0
    (3)34:q1[19]++;input[256+q1[19]-1]=q1[18];/push len
    (17,64)42:q1[19]++;input[256+q1[19]-1]=q1[20];q1[20]=input[79]=64;//call 64
    (6)35:q1[19]--;q1[18]=input[256+q1[19]];//q1[18]=len
    (0,64)34:q1[19]++;input[256+q1[19]-1]=64;
    (5)35:q1[19]--;q1[17]=input[256+q1[19]];//q1[17]=64
    (17,29)42:q1[19]++;input[256+q1[19]-1]=q1[20];q1[20]=input[79]=29;//call 29
    (23):break
    
    
    29:
    (14,1)39:q1[18]-=input[79]=1;// q1[18]=41
    (21)46:q1[19]++;input[256+q1[19]-1]=input[q1[18]]
    (4)35:q1[19]--;q1[16]=input[256+q1[19]];//q1[16]=input[q1[18]]
    (15)40:q1[16]^=q1[17];
    (1)34:q1[19]++;input[256+q1[19]-1]=q1[16];
    (22)47:q1[19]--;input[q1[18]]=input[256+q1[19]];//str^=32,34,36...
    (2)34:q1[19]++;input[256+q1[19]-1]=q1[17];//push 32,34,36...
    (0,0)34:q1[19]++;input[256+q1[19]-1]=0;
    (4)35:q1[19]--;q1[16]=input[256+q1[19]];
    (3)34:q1[19]++;input[256+q1[19]-1]=q1[18];
    (5)35:q1[19]--;q1[17]=input[256+q1[19]];//q1[17]=q1[18]
    (16)41:q1[21]=q1[16]==q1[17];//如果q[17]=0就返回
    (20,50)45:if(q1[21])q1[20]=input[79]=50;//je
    (5)35:q1[19]--;q1[17]=input[256+q1[19]];//q1[17]=32,34,36...
    (9,2)37:q1[17]+=input[79]=2
    (19,29)44:q1[20]=input[79]=29;//jmp
    
    50:
    (5)35:q1[19]--;q1[17]=input[256+q1[19]];//pop q1[17]
    (18)43:q1[19]--;q1[20]=input[256+q1[19]];//retn to 2
    
    
    52:
    (21)46:q1[19]++;input[256+q1[19]-1]=input[q1[18]]
    (4)35:q1[19]--;q1[16]=input[256+q1[19]];//q1[16]=input[q1[18]]
    (16)41:q1[21]=q1[16]==q1[17];
    (20,61)45:if(q1[21])q1[20]=input[79]=61;//je 当循环到字符串尾跳转
    (10,1)37:q1[18]+=input[79]=1;
    (19,52)44:q1[20]=input[79]=52;//jmp
    
    61:
    (3)34:q1[19]++;input[256+q1[19]-1]=q1[18];
    (4)35:q1[19]--;q1[16]=input[256+q1[19]];//q1[16]=q1[18]=42
    (18)43:q1[19]--;q1[20]=input[256+q1[19]];//retn goto 0
    
    64:
    (14,1)39:q1[18]-=input[79]=1;
    (21)46:q1[19]++;input[256+q1[19]-1]=input[q1[18]]
    (4)35:q1[19]--;q1[16]=input[256+q1[19]];
    (7)36:q1[16]+=q1[17]//+=0,1,2
    (1)34:q1[19]++;input[256+q1[19]-1]=q1[16];
    (22)47:q1[19]--;input[q1[18]]=input[256+q1[19]];//+q1[17]=0,1,2,3,4,5...
    (2)34:q1[19]++;input[256+q1[19]-1]=q1[17];
    (0,0)34:q1[19]++;input[256+q1[19]-1]=0;
    (4)35:q1[19]--;q1[16]=input[256+q1[19]];
    (3)34:q1[19]++;input[256+q1[19]-1]=q1[18];
    (5)35:q1[19]--;q1[17]=input[256+q1[19]];
    (16)41:q1[21]=q1[16]==q1[17];
    (20,85)45:if(q1[21])q1[20]=input[79]=85;//je
    (5)35:q1[19]--;q1[17]=input[256+q1[19]];
    (9,1)37:q1[17]+=input[79]=1//q1[17]++
    (19,64)44:q1[20]=input[79]=64;//jmp
    
    
    85:
    (5)35:q1[19]--;q1[17]=input[256+q1[19]];// pop q1[17]
    (18)43:q1[19]--;q1[20]=input[256+q1[19]];//retn to 2
    

    比较容易发现,q1[16]、q1[17]、q1[18]是通用寄存器,q1[19]是栈指针,q1[20]是EIP,input[256]是栈底,算法不是很难,异或之后加一些值再异或,脚本:

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    typedef long long LL;
    #define foru(i,a,b) for(int i=a;i<=b;i++)
    #define ford(i,a,b) for(int i=a;i>=b;i--)
    int main(){
    	unsigned char flag[]="xafxd4xb8xbdxbcxb9xfcxf1xf6xa1xf5xfexf1xe9x0bxf3x22x0fx14xe2xedxe5xe2x1fx56x54x4bx3ax7ex3ex5ax5ax5dx0bx6bx68x54x54x64x07x51x1d";
    	ford(i,41,0)flag[i]^=(64+(41-i)*2);
    	ford(i,41,0){flag[i]-=41-i;flag[i]=(flag[i]+128)%128;}
    	ford(i,41,0)flag[i]^=(32+(41-i)*2);
    	foru(i,0,41)printf("%c",(flag[i]+128)%128);
    }
    
    flag{e171a284-49e7-4817-ad8d-b704c02309e0}
    

    吃鸡神器

    是一道QT的题目,以前没做过,学了一波。

    image-20200224114136359

    找到创建窗体之前的位置,跟进sub_402250,在330行附近发现这段代码:

    image-20200224114330903

    这个函数把buttonclick、metaobject还有一个函数sub_402150传了进去,我猜是绑定了回调函数。

    跟进sub_402150,在底部找到检查输入的代码:

    image-20200224114648681

    get_ans逻辑很简单,可以动态调试直接获取返回值:

    image-20200224115502597

    自己算一遍也可以:

    #include<cstdio>
    using namespace std;
    typedef long long LL;
    #define foru(i,a,b) for(int i=a;i<=b;i++)
    #define ford(i,a,b) for(int i=a;i>=b;i--)
    char key[]="lubenwei";
    int main(){
    	int x=5381;//402101
    	foru(i,0,7){
    		x+=32*x+key[i];
    	}
    	printf("%x",x);
    }
    
    flag{41d26f00}
    

    EasyEncrypt

    IDA打开,发现是平坦化:

    image-20200224115816161

    python deflat EasyEncrypt 0x400A10
    

    反平坦化之后再看逻辑(其实这题不反平坦化也能看个大概,毕竟从提示字符串的内容可以判断出各个块的顺序):

    image-20200224120052653

    image-20200224122854164

    只加密了前16字节,key是thisisthekey!!!!

    解密:

    from Crypto.Cipher import AES
    from binascii import b2a_hex, a2b_hex
    
    
    def decrypt(text):
        key = 'thisisthekey!!!!'.encode('utf-8')
        mode = AES.MODE_ECB
        cryptor = AES.new(key, mode)
        plain_text = cryptor.decrypt(a2b_hex(text))
        return (plain_text)
    
    
    if __name__ == '__main__':
        s="398A08C585DB8F7EC8F2BF7986B68782"
        d = decrypt(s)
        print(d)
    

    image-20200224123203938

    发现是png,覆盖前16字节即可

    flag

    EasyVM

    打开之后发现真的是EasyVM,opcode都写明白啥意思了,比day1的VM简单很多。由于过于简单,这里选择angr一把梭:

    import angr
    import claripy
    import time
    
    def main():
    
        p = angr.Project('EasyVM',auto_load_libs=False)
        st = p.factory.entry_state()
        base=0x400000
        sm = p.factory.simulation_manager(st)
        sm.explore(find=base+0xB73,avoid=base+0xB81)
        print(sm.found[0].posix.dumps(0))
    
    if __name__ == "__main__":
        before = time.time()
        print(main())
        after = time.time()
        print("Time elapsed: {}".format(after - before))
    

    image-20200224124642935

    11s就出解了

    flag{vm_is_not_easy}
    

    Analysis of viruses

    还真是病毒分析,出题人真他娘的是个人才。

    逻辑很好懂,IDA看看结合一下高中生物知识,代码都不用读完就能知道在干啥。

    image-20200224125002427

    主函数先把输入的RNA逆转录成DNA,然后再转录成RNA丢进链表里,接着把密码子转化出反密码子,再转化为氨基酸,最后和一段氨基酸序列对比。

    由于氨基酸对应密码子不唯一,写个程序爆破:

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<string>
    #include<algorithm>
    #include<map>
    #include<vector>
    #include "md5.h"
    #include<iostream>
    #include<cmath>
    using namespace std;
    typedef long long LL;
    #define foru(i,a,b) for(int i=a;i<=b;i++)
    #define ford(i,a,b) for(int i=a;i>=b;i--)
    const int n=16;
    const int m=64;
    string a[m]={"Phe","Phe","Leu","Leu","Ser","Ser","Ser","Ser","Tyr","Tyr","sto","sto","Cys","Cys","sto","Trp","Leu","Leu","Leu","Leu","Pro","Pro","Pro","Pro","His","His","Gln","Gln","Arg","Arg","Arg","Arg","Ile","Ile","Ile","Met","Thr","Thr","Thr","Thr","Asn","Asn","Lys","Lys","Ser","Ser","Arg","Arg","Val","Val","Val","Val","Ala","Ala","Ala","Ala","Asp","Asp","Glu","Glu","Gly","Gly","Gly","Gly"};
    string b[n]={"Met","Cys","Leu","Ala","Arg","Leu","Phe","Ser","Ile","Leu","Asn","Val","Cys","Gly","Lys","Leu"};
    
    string ans;
    int count=0;
    
    MD5 md5;
    map<string,vector<string> > mp;
    void print(int k){
    	int tmp[4],cnt=0;
    	while(k){
    		tmp[++cnt]=k%4;
    		k/=4;
    	}
    	ford(i,3,1)
    		if(tmp[i]==0)printf("A");
    		else if(tmp[i]==1)printf("G");
    		else if(tmp[i]==2)printf("U");
    		else if(tmp[i]==3)printf("C");
    }
    
    
    void dfs(int k){
    	if(k==n){
    	    md5.reset();
    	    md5.update(ans+"UAA");
    	    if(md5.toString().substr(0,8)=="e03657e0"){
    	    	cout<<ans+"UAA"<<endl;
    		}
    	    md5.reset();
    	    md5.update(ans+"UAG");
    	    if(md5.toString().substr(0,8)=="e03657e0"){
    	    	cout<<ans+"UAG"<<endl;
    		}
    	    md5.reset();
    	    md5.update(ans+"UGA");
    	    if(md5.toString().substr(0,8)=="e03657e0"){
    	    	cout<<ans+"UGA"<<endl;
    		}
    		return;
    	}
    	std::vector<int>::size_type x= mp[b[k]].size();
    	for(std::vector<int>::size_type i = 0; i !=x; i++){
    		string tmps=ans;
    		ans+=mp[b[k]][i];
    		dfs(k+1);
    		ans=tmps;
    	}
    }
    
    int main(){
    	foru(i,0,n-1){
    		if(!mp[b[i]].empty())continue;
    		foru(j,0,m-1){
    			if(b[i]==a[j]){
    				string tmps="";int k=j;
    				int tmp[4],cnt=0;
    				foru(l,1,3)tmp[l]=0;
    				while(k){
    					tmp[++cnt]=k%4;
    					k/=4;
    				}
    				ford(l,3,1)
    					if(tmp[l]==0)tmps+="U";
    					else if(tmp[l]==1)tmps+="C";
    					else if(tmp[l]==2)tmps+="A";
    					else if(tmp[l]==3)tmps+="G";
    				mp[b[i]].push_back(tmps);
    			}
    		}
    	}
    	dfs(0);
    }
    

    如果是6位md5有几十组解,后来联系技术支持拿8位md5跑了一下,出了两组解,第二组就是flag。

    程序开O2跑了675s,c++速度比python不知道高到哪去。

    QQ截图20200223161616

    flag{AUGUGCCUUGCAAGACUUUUCUCGAUACUUAACGUCUGUGGAAAACUUUAA}
    
  • 相关阅读:
    03JavaScript程序设计修炼之道 2019-06-23_14-32-17
    03JavaScript程序设计修炼之道 2019-06-20_21-30-17
    03JavaScript程序设计修炼之道 2019-06-20_21-12-16
    03JavaScript程序设计修炼之道 2019-06-20_20-51-20
    03JavaScript程序设计修炼之道-2019-06-20_20-31-49
    03JavaScript程序设计修炼之道-2019-06-20_20-07-53
    02-CSS基础与进阶-day15
    02-CSS基础与进阶-day14
    02-CSS基础与进阶-day13_2018-09-21-21-13-20
    python基础(6)集合
  • 原文地址:https://www.cnblogs.com/y-m-y/p/12356516.html
Copyright © 2020-2023  润新知