内容简介:遇到VM的题目就是坐穿屁股,这个题又做了一下午加一晚上首先放进ida发现入口地址有点奇怪,
遇到VM的题目就是坐穿屁股,这个题又做了一下午加一晚上
首先放进ida发现入口地址有点奇怪,
入口地址为 0xc023dc
,那先跑起来试试,跑起来之后发现一个奇怪的点:
程序起了两个子进程,用 gdb attach
到 224
上去之后,然后用gcore把内存转储出来,放进gdb里,然后过了一下发现了其中 sub_8048556
有个可显 input
字符串,这应该就是主要逻辑了
sub_8048460(1, "Input : ", 8);
。例如这个 sub_8048460
void __cdecl sub_8048460(int a1, int a2, int a3) { JUMPOUT(0xF7DF6B70); }
明显是在调用库函数,查看一下进称的maps
计算一下偏移
>>> hex(0xF7DF6B70-0xf7d21000) '0xd5b70'
ida打开这个 /lib/i386-linux-gnu/libc-2.23.so
看看这个偏移上面的函数
所以知道这个 sub_8048460
就是write。
然后依次确认了几个关键函数
sub_80489FE = getuid sub_8048460 = write sub_8048470 = pipe sub_8048480 = fork sub_8048400 = read
然后代码可读性就比较高了,核心就是这一部分
write(1, "Input : ", 8); pipe(&v4); if ( pipe((int)&v4) != -1 && (pipe(&v6), v2 != -1) ) { v8 = fork__(); if ( v8 == -1 ) { v10 = 0; for ( i = 1; i <= 6; ++i ) { v9 = byte_804B06E[i - 1] ^ i; // error! write(1, &v9, 1); } } else if ( v8 ) { v14 = 0; v15 = 0; v16 = 0; read(v4, &v14, 9); read(v4, &dword_804B0A0, 200); for ( i = 0; i <= 199; ++i ) *(i + 0x804B0A0) ^= 0x20u; dword_804B0A0 = v14; dword_804B0A4 = v15; for ( i = 0; i <= 199; ++i ) *(i + 0x804B0A0) ^= 0x10u; if ( sub_8048C6D() == 1 ) { if ( cur_pc ) { v10 = 0; for ( i = 1; i <= 9; ++i ) { v9 = *(&word_804B07A + i - 1) ^ i;//correct write(1, &v9, 1); } } else { v10 = 0; for ( i = 1; i <= 6; ++i ) { v9 = *(&dword_804B074 + i - 1) ^ i;// Wrong write(1, &v9, 1); } } } else { v10 = 0; for ( i = 1; i <= 6; ++i ) { v9 = *(&dword_804B074 + i - 1) ^ i; // Wrong write(1, &v9, 1); } } } else { v11 = 0; v12 = 0; v13 = 0; read(0, &v11, 10); if ( v13 ) { v10 = 0; for ( i = 1; i <= 6; ++i ) { v9 = *(&dword_804B074 + i - 1) ^ i; // Wrong write(1, &v9, 1); } } else { write(v5, &v11, 9); for ( i = 0; i <= 199; ++i ) { v3 = sub_80489AA(*(i + 0x804B0A0), 3); *(i + 0x804B0A0) = v3; } sub_8048410(); write(v5, &dword_804B0A0, 200); } }
根据 fork()
的返回值不同进行不同的操作,即区分父子进程,子进程的返回值为0,父进程的返回值是新子进程的ID。也就是父子进程进行不同的操作,这里先看子进程,子进程将用户输入写到了v5里面,然后对 0x804B0A0
处200字节内存进行了一顿异或操作也写到了v5。
再回来看看父进程,父进程两个read不用说了把,就是等着子进程写过去的数据。然后还是取到 0x804B0A0
位置,之后又进行了两波异或操作,然后就是关键的 sub_8048C6D
函数是否 correct
。
跟进看一下:
出现了,vm的部分,又是屁股坐穿的一个题,为了读了好久之后,还是有点迷糊,而且反编译出的c还有很多错误的地方,而且动态调试很麻烦,先跑起来,然后gdb连接到两个子进程,然后子进程的子进程先 c
,然后子进程下断点再 c
,然后再输入,然后再跟,调起来头都是晕的。
于是花了一个小时改成了python,同时也是人工纠正了一下反编译中的一些问题。
基本就如下面所示了。基本逻辑就是将我们的输入放在 0x804B0A0
开始的200字节的前七个字节,然后开始循环调用如下:
cnt=7 sub_8048C13() while cnt: #逐位比较,某一位错误即退出while cnt-=1 sub_8048B92() sub_8048ABB() sub_8048B92() sub_8048B31() sub_8048BCE() sub_8048ABB() sub_8048C22()
逐位调用函数进行对比,前一位正确了才能继续循环校验下一位,核心的校验点
在 sub_8048B31
中对比了 dword_804B198
和 dword_804B194
值,这样只需要倒推这两个值的来源就能计算到正确的值,不过我就懒得算了,知道大概算法之后,动态跟几个点,主要就是每次循环的第一次调用 sub_8048B92
,对 804B0A0[7]
赋值,比如以校验第一位输入为例:
这里第一次调用 sub_8048B92
时, set_code
即将对 804B0A0[7]
赋值 96^0x10
,
然后到了 sub_8048B31
进行比较的时候, dword_804b194
的值为 9
,所以第一位输入的ascii码值即为 96^9
,具体的逻辑大家可以进去分析。
这里附上当时调试的 python 代码:
input=[96^9,102^2,21^38,7^45,76^34,99^7,16^120] tmp1=[] for i in input: tmp1.append(i^0x10) tmp2=[0x1a,0x10,0x1b,0x1a,0x16,0x16,0x10,0x10,0x1b,0x12,0x17,0x70,0x16,0x10,0x17,0x12,0x17,0x19,0x17,0x10,0x17,0x19,0x12,0x12,0x17,0x76,0x16,0x11,0x17,0x12,0x17,0x12,0x17,0x11,0x17,0x19,0x12,0x12,0x17,0x05,0x16,0x12,0x17,0x12,0x17,0x36,0x17,0x12,0x17,0x19,0x12,0x12,0x17,0x17,0x16,0x13,0x17,0x12,0x17,0x3d,0x17,0x13,0x17,0x19,0x12,0x12,0x17,0x5c,0x16,0x14,0x17,0x12,0x17,0x32,0x17,0x14,0x17,0x19,0x12,0x12,0x17,0x73,0x16,0x15,0x17,0x12,0x17,0x17,0x17,0x15,0x17,0x19,0x12,0x12,0x17,0x68,0x16,0x16,0x17,0x12,0x17,0x00,0x17,0x16,0x17,0x19,0x12,0x12,0x10,0x11,0x1b,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10] dword_804B0A0=tmp1+tmp2 byte_804B0A9=0x1a cur_pc=0 byte_804B0AA=0x1a dword_804B198=0 dword_804B194=0 def log(myfunc): def wrapper(): myfunc() global byte_804B0A9,cur_pc,byte_804B0AA,dword_804B194,dword_804B198 print("="*15+myfunc.__name__+"="*15) print("byte_804B0A9: ",hex(byte_804B0A9)) print("cur_pc: ",hex(cur_pc)) print("="*40) return wrapper def logs(): global byte_804B0A9,cur_pc,byte_804B0AA,dword_804B194,dword_804B198 print("="*30) print("byte_804B0A9: ",hex(byte_804B0A9)) print("cur_pc: ",hex(cur_pc)) print("="*30) def set_pc(): #sub_8048A48 global byte_804B0A9,cur_pc,byte_804B0AA,dword_804B194,dword_804B198 cur_pc=dword_804B0A0[byte_804B0A9^0x10]^0x10 byte_804B0A9 = ((byte_804B0A9^0x10)+1)^0x10 def set_pc_2(): #sub_8048A0B global byte_804B0A9,cur_pc,byte_804B0AA,dword_804B194,dword_804B198 cur_pc=dword_804B0A0[cur_pc]^0x10 def set_code(): #sub_8048A2F global byte_804B0A9,cur_pc,byte_804B0AA,dword_804B194,dword_804B198 dword_804B0A0[cur_pc]=dword_804B198^0x10 def sub_8048A92(): global byte_804B0A9,cur_pc,byte_804B0AA,dword_804B194,dword_804B198 byte_804B0A9=((byte_804B0AA^0x10)+cur_pc)^0x10 @log def sub_8048B92(): global byte_804B0A9,cur_pc,byte_804B0AA,dword_804B194,dword_804B198 set_pc() dword_804B198 = cur_pc set_pc() dword_804B194 = cur_pc cur_pc = dword_804B198 dword_804B198 = dword_804B194 set_code() @log def sub_8048ABB(): global byte_804B0A9,cur_pc,byte_804B0AA,dword_804B194,dword_804B198 dword_804B198 = cur_pc dword_804B18C = dword_804B194 set_pc() dword_804B18C = cur_pc set_pc() set_pc_2() dword_804B194 = cur_pc cur_pc = dword_804B18C set_pc_2() dword_804B198 = cur_pc^dword_804B194 cur_pc = dword_804B18C set_code() @log def sub_8048B31(): global byte_804B0A9,cur_pc,byte_804B0AA,dword_804B194,dword_804B198 set_pc() set_pc_2() dword_804B198 = cur_pc set_pc() set_pc_2() dword_804B194 = cur_pc if(dword_804B198 == dword_804B194): dword_804B198 = 1 else: dword_804B198 = 0 cur_pc = 8 set_code() @log def sub_8048BCE(): global byte_804B0A9,cur_pc,byte_804B0AA,dword_804B194,dword_804B198 dword_804B198 = cur_pc set_pc() dword_804B198 = cur_pc cur_pc=8 set_pc_2() if(cur_pc==0): cur_pc = dword_804B198 sub_8048A92() @log def sub_8048C13(): set_pc() sub_8048A92() @log def sub_8048C22(): global byte_804B0A9,cur_pc,byte_804B0AA,dword_804B194,dword_804B198 cur_pc=0 set_pc_2() dword_804B198 = cur_pc cur_pc=1 set_pc_2() dword_804B194 = cur_pc cur_pc = dword_804B198 dword_804B198 = dword_804B194 ''' b *0x8048C70 b *0x8048B92 b *0x8048ABB b *0x8048B31 b *0x8048BCE b *0x8048C13 b *0x8048C22 x/bx 0x804b0a9 x/bx 0x804b190 ''' cnt=7; set_pc() sub_8048C13() set_pc() while cnt: #逐位比较,某一位错误即退出while cnt-=1 sub_8048B92() set_pc() sub_8048ABB() set_pc() sub_8048B92() set_pc() sub_8048B31() set_pc() sub_8048BCE() set_pc() sub_8048ABB() set_pc() sub_8048C22() ''' byte_804B0A9 = 0x1a cur_pc = 0 set_pc() sub_8048B92: set_pc();------------------------------------------------------------------- | cur_pc=dword_804B0A0[byte_804B0A9^0x10]^0x10 | | byte_804B0A9 = (byte_804B0A9^0x10+1)^0x10 | ---------------------------------------------------------- dword_804B198 = cur_pc; set_pc(); dword_804B194 = cur_pc; cur_pc = dword_804B198 dword_804B198 = dword_804B194 set_code();----------------------------------------------------------- | dword_804B0A0[cur_pc]=dword_804B198^0x10 | ---------------------------------------------------- sub_8048ABB: dword_804B198 = cur_pc; dword_804B18C = dword_804B194; set_pc(); dword_804B18C = cur_pc; set_pc(); set_pc_2();------------------------------------------------------ | cur_pc=dword_804B0A0[cur_pc]^0x10 | ----------------------------------------------- dword_804B194 = cur_pc; cur_pc = dword_804B18C; set_pc_2(); dword_804B198 = cur_pc^dword_804B194 cur_pc = dword_804B18C set_code(); sub_8048B31: set_pc(); set_pc_2(); dword_804B198 = cur_pc; set_pc(); set_pc_2(); dword_804B194 = cur_pc; if(dword_804B198 == dword_804B194): dword_804B198 = 1; else: dword_804B198 = 0; cur_pc = 8; set_code(); sub_8048BCE: dword_804B198 = cur_pc; set_pc(); dword_804B198 = cur_pc; cur_pc=8; set_pc_2(); if(cur_pc!=0): cur_pc = dword_804B198 sub_8048A92();-------------------------------------------------------- | byte_804B0A9=(byte_804B0AA^0x10+cur_pc)^0x10 | -------------------------------------------------- sub_8048C13: set_pc(); sub_8048A92(); sub_8048C22: cur_pc=0; set_pc_2(); dword_804B198 = cur_pc; cur_pc=1; set_pc_2(); dword_804B194 = cur_pc; cur_pc = dword_804B198 dword_804B198 = dword_804B194 '''
AutoHotkey2
运行了一下发现直接报错程序崩溃,但是这不是系统报出的错误,肯定是程序的自校验出了问题,拖进ida空荡荡的符号表意味着又要开始脱壳….
跟了一会儿发现有点熟悉,确认了下不就是AutoHotkey1一样的吗。。。
同样 lordpe+importrec
之后直接去 sub_4508C7
函数,
发现和AutoHotkey1一模一样,那这个题意思就很明确了,第一步肯定是想办法绕过或者修改exe通过检测。
还是分为两部分,第一部分如下:
这部分代码的作用就是将文件从开头到倒数第五位计算一个值,然后异或 0xaaaaaaaa
,之后和最后四位组成的值进行比较。这绕过很简单动态调试跟到比较那一步,就知道最后四位的值需要等于多少了。
然后下一部分
读取 5-8
字节数据作为下一次文件读取的偏移,然后读取16字节数据,和固定的16字节比较,那就很容易了,直接找一下该16字节内容在文件的偏移就可以,
所以修改 5-8
字节为 \x00\x38\x03\x00
即可,注意这里改了之后要重新调试看最后四位的值,最后8位改为如下之后程序即可正常运行:
查一下 bastard son of Eddard Stark
,得到一个人名 Jon Snow
,所以最后结果就是 jonsnow
x64_lotto
很明确的一个题目,64位,放到ida里面,直接进到主函数 wmain
,分为几个部分来说,第一部分如下:
读取我们输入的六个数字,然后和随机的6个数字对比,不一样就继续输入,而且观察一下后面的代码,之后就再也没有用到过输入的数字,那就很简单了,动态调试,直接在 while(v3!=6)
判断的时候patch一下通过判断就行了
第二部分如下:
对一段固定的值一顿异或操作之后将结果存在栈上,由于都是固定的值,动态跟一下就知道最后的异或结果。
第三部分如下:
patch一下,绕过对v2的判断,之后动态直接跟到wprintf处就拿到flag了:
flag就是 from_GHL2_-_!
CSharp
又是一道 .net
的题目,直接上dnspy看反编译的代码,其中比较引人关注的是名叫 MetMett
的函数,没有被反编译出来,
再看到 form1
这里在初始化的时候对这个函数体进行了一个解密操作,那这样其实动态调试是来的最快的,
两个断点一下,把两次的值复制出来,
解密前:
028D681E0B3FDEFFFFFF0115179B0215901F8FFFFFFF601E492D030115169B0218901F8FFFFFFF601E452D030115169B0216901F8FFFFFFF601E562D030115169B0217901F8FFFFFFF601E4C2D030115169B021E0A901F8FFFFFFF601E2B2D030115169B021D901F8FFFFFFF601FF0FFFFFF2D030115169B0219901F8FFFFFFF601E1C2D030115169B021A901F8FFFFFFF601E302D030115169B021E08901F8FFFFFFF601FE1FFFFFF2D030115169B021C901F8FFFFFFF601FEDFFFFFF2D030115169B021E09901F8FFFFFFF601FA2FFFFFF2D030115169B021B901F8FFFFFFF601E742D030115169B29
解密后:
038E691F0C40DF0000000216189C0316912010000000611F4A2E040216179C0319912033000000611F462E040216179C0317912011000000611F572E040216179C0318912021000000611F4D2E040216179C031F0B912011000000611F2C2E040216179C031E9120900000006120F10000002E040216179C031A912044000000611F1D2E040216179C031B912066000000611F312E040216179C031F099120B50000006120E20000002E040216179C031D9120A00000006120EE0000002E040216179C031F0A9120EE0000006120A30000002E040216179C031C912033000000611F752E040216179C2A
然后就是想看到解密后的代码,直接hex 编辑器上用解密后替换掉解密前的部分,再放进dnspy里反编译,拿到如下代码:
private static void MetMett(byte[] chk, byte[] bt) { if (bt.Length == 12) { chk[0] = 2; if ((bt[0] ^ 16) != 74) { chk[0] = 1; } if ((bt[3] ^ 51) != 70) { chk[0] = 1; } if ((bt[1] ^ 17) != 87) { chk[0] = 1; } if ((bt[2] ^ 33) != 77) { chk[0] = 1; } if ((bt[11] ^ 17) != 44) { chk[0] = 1; } if ((bt[8] ^ 144) != 241) { chk[0] = 1; } if ((bt[4] ^ 68) != 29) { chk[0] = 1; } if ((bt[5] ^ 102) != 49) { chk[0] = 1; } if ((bt[9] ^ 181) != 226) { chk[0] = 1; } if ((bt[7] ^ 160) != 238) { chk[0] = 1; } if ((bt[10] ^ 238) != 163) { chk[0] = 1; } if ((bt[6] ^ 51) != 117) { chk[0] = 1; } } }
那这个就挺清晰的了,算出来之后需要base64解码一下得到最后答案就是 dYnaaMic
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
浪潮之巅(上册)
吴军 / 人民邮电出版社 / 2013-5-1 / 35.00元
《浪潮之巅(第2版)(上册)》不是一本科技产业发展历史集,而是在这个数字时代,一本IT人非读不可,而非IT人也应该阅读的作品。一个企业的发展与崛起,绝非只是空有领导强人即可达成。任何的决策、同期的商业环境,都在都影响着企业的兴衰。《浪潮之巅》不只是一本历史书,除了讲述科技顶尖企业的发展规律,对于华尔街如何左右科技公司,以及金融风暴对科技产业的冲击,也多有着墨。此外,《浪潮之巅》也着力讲述很多尚在普......一起来看看 《浪潮之巅(上册)》 这本书的介绍吧!