hgame-week4-real笔记

hgame-2019-wp归总:https://www.danisjiang.xyz/2019/02/22/hgame-2019-wp%E5%BD%92%E6%80%BB/

初识

这道题刚拿到手,看到了主要的处理函数

但是这道题的处理步骤似乎太多了,多而且看起来很复杂,我不知道应该如何下手,到底是应该硬着头皮去解读还是有什么取巧的方式

可以看到的是sub_400c5c这个函数有5个参数,但是只有2个参数和我们输入的数字有关。而且之前有一个atoll函数,这个函数是将数字字符串转化为字符,也就是说讲道理这个输入的flag是一串数字。还有一个为题是,之前用于输入的fgets是读取50个字符,相当于这个数字最大也就50位,但是sub_400c5c函数的第三个参数是v3>>63,v3是64位数,但是它最多存50,也就是说这个参数就是0???

进入函数,这里的各种处理是真的多。主体是一个while循环,前面还有一个处理参数的函数,但是这个函数的参数都是已知的。所以,我打算先处理这个已知参数的函数,sub_400ff0的参数分别是0x3737373737373737uLL,-608728913220678437LL,-1LL。前两个是非常大的数,第一个参数是128位的,后两个是64位的,C语言好像原生不支持128位的int,我对于这些参数的运算有些迷茫,我不知道有不同位数的数字相加后的一些处理

但是还是看一下这个子函数吧,把v5的值求出来

发现这是一个fake的世界

昨天并没有做出来,早早地就睡了。今天发了一个hint:程序就是从main开始执行的吗?

这个hint让我很奇怪,因为我觉得所有的字符串读入和处理,比较都是在main函数中进行的,那么在main函数之前的函数做了什么呢?有什么特殊的意义呢?接下来要做的事就是找到main函数之前的函数是什么

对此我想到一个方法,就是动态调试,试了一下,却遇到了问题。这个程序的坑似乎很大。有一个syscall,在这里F7之后,F7!,竟然就直接输出imput flag了,然后我输入一个字串后就直接就结束了。所以,我怎么样可以进syscall里一探究竟呢?我决定先上网搜一下syscall这个是啥

syscall是linux的系统调用,然后我应该找错了,那个是libc中的调用,应该是由前面这个system()调用的,然后其实重点应该是这个函数了

但是这里的函数就非常的古怪了

access?fopen???

看到这个fopen我想到了之前第二周的一道题目,但是第二周我没怎么看这道题,我打算先去看一下那道题的wp

然而好像看不懂wp,好像没有啥关系,我打算再看看这个函数

前面有一段mov,走完以后是这样的,这是啥???LD_PRELOAD=./.real.so ./real?

寻找real的世界

这个有什么意义吗?我主观认为是有意义的,所以我去网上查了一下这个 LD_PRELOAD,得到了一个很有趣的东西

很有意思吧,也就是说虽然是后面的这些调用时有问题的,可能调用的是别的有趣的东西

但是,网上的内容似乎大多都是讲如何使用动态链接库去逆向的,而没有讲题目中的这种情况

后来我发现,原来这一段是在写.real.so。但是为什么目录下看不到.real.so呢?

ps:今天我在看关于linux的一本书,偶然发现了ls -a能显示隐藏文件,然后我就发现.real.so只是被隐藏了。另外,我又发现,原来linux系统中的隐藏文件都是”.”开头的,所以我后来在复制为aaa.so之后可以被我看到了XD看来看一点linux的东西还是蛮有用的

// 我也不知道,但是我发现我可以看.real.so中的内容

我看到了我想看到的东西,那就是真正在使用的汇编代码

但是,我应该怎么应用呢???程序执行了System(LD_PRELOAD…)之后就突然输出input flag并让我输入了。我认为我在这个中间一定遗漏了什么内容。明明成功运行了,我却没法调试,我不知道是什么问题

我一开始想到一个方法,那就是我自己来编写一个real,然后直接LD_PRELOAD,编译成elf之后再放到IDA里面调试。但是我遇到了问题,这个看似简单的步骤似乎有很多的兼容性问题,我也没法每个函数都复制过来再修改,用gcc编译疯狂报错

至此,我遇到了一堵墙,空有.so却不知道该怎么办。无奈地去看非自然死亡去了

我又回来了,让我想想怎么可以做到。我是不是可以直接改RIP?我是不是可以试着去跳过那段写.so,直接让它 System(LD_PRELOAD…)

我看到了这个,看来这个System()后面的exit()就是已经修改过的了,那么看来这个是可行的,我决定试试看

我试了一下,可以直接改RIP,但是它仍然在system这个地方进去出不来了,直接就让我输flag了

但是这个地方执行的命令的确是System(LD_PRELOAD=./.real.so

我试着跟进去,不出意料地进了linux的库,这个跟我之前最早的时候的调试是一样的

也就是说,从之前动态调试到现在,我并没有对程序的流程做出任何改变,只是知道了这个函数的作用

那么,到底是为什么呢?是什么原因导致它直接就调用了

程序就是在这里进去

最后在这个syscall就跟不了了,直接就让我输入了

这就超出了我的预期理解,我原先认为会完美地执行完LD_PRELOAD,然后执行main函数的时候就可以得到新的真正的main函数

但是这么看的感觉怎么是程序直接把执行流交给了.so?.so可以直接执行吗?

哦!!!!可以!

我现在不就是在libc的.so中吗???

我可以直接用IDA打开这个so呀!!!

我得到了!!!

这里就可以理解了,这个是在原来的main第一次puts的时候先跳过if,然后对encrypt进行处理,之后在第二次puts的时候执行encrypt函数

但是我还有一个问题,那就是我如何来得到这串代码,我想动态调试,我要不去看下上周的wp吧,看看上周的wp大家是怎么做的

我无语了,我逛了整个google也没有找到有人上周做了RE的,re选手真的这么少而且都不传wp的嘛,哭哭

无奈,我只能自己去做了

遇到了一个很奇怪的问题(大概只有我遇到了)

做了很久感觉很奇怪,有问题。新的encrypt函数很早就结束了,后面都不知道是什么

但是我发现一个东西,getcwd()函数是得到当前文件的绝对地址的,而unlink()函数是删除文件的。这是不是最后找不到.real.so的原因?

不是很确定,但是我还是不知道这个encrypt函数是怎么回事

我又看了看,发现之前的xor循环到c37就结束了,后面这些代码没有处理,那么这是怎么回事呢

让我先来梳理一下,我得到了一个不完整的代码,后面这段肯定还是需要处理的。那么,有可能是在之前处理的,也有可能是在之后处理的

之前处理,是不是在real的init()函数里处理的呢?我觉得不会,因为。等等,我好像知道了,这个.real.so是我自己写代码生成的(可能漏掉了什么内容),是不是这个.so相比程序自己生成的少了点什么处理?我决定先得到真正的.real.so

可惜的是,结果是一样的,看来我之前生成的.so也没有问题

那么,是不是在之后处理的呢?例如在encrypt函数中处理?自解压?

我从前面的代码中并没有找到,因为我实在没有找到指向这个地址的任何指针,另外,这个函数到底在干什么呢,我在前面已经看到了“hgame”

从这段代码中我也没有看到任何让我输入的函数,这是有问题的,显然在运行程序的时候在输出两句话之后有一个输入和判断我输入的函数

并且从.so文件中的字符串来看,应该也有success和faild的字串,而且目前还没有被任何函数引用,那么显然它应该在encrypt函数中引用,和hgame这些一样,还有这个%50s,它应该是一个scanf,但是它也没有被引用

我现在的问题,就是如何解决后半段的代码。这个后半段是如何处理的?

会不会是sub_eb1和sub_f91这两个函数呢?

还是没头绪,事到如今,再去看一集非自然死亡吧

第5话真是虐啊,555

本来以为还有第二段加密,但我发现是我的脚本有问题,for循环310次,不知为何生成了311个byte!哭辽,我最后手动检验的

附上代码,真的不知道为何会在后面多一个0d啊(下面这个代码还是有问题的,看看就好,或者生成出来以后把后面不知道第几行的0dh给删了)

int main()
{
	int s[] = {0x48,0x88,0x47,0xFB,0x35,0xC5,0xB9,0x07,0x09,0x09,0x0A,0xE3
,0x60,0xF3,0xF1,0xF0,0x58,0x98,0x97,0x73,0xEA,0xEA,0xE9,0x5F,0x95,0x1C,0x89,0x1E
,0x1C,0x1D,0x56,0x96,0xA5,0x49,0xDC,0xDC,0xDB,0x6D,0xAB,0xB2,0x58,0xD7,0xD5,0xD4
,0x94,0x2D,0x2E,0x2F,0x30,0x88,0x22,0x33,0x34,0x35,0x7E,0xBE,0xEF,0xCA,0x72,0x90
,0xFA,0xB8,0x4E,0xC1,0xBF,0xBE,0x01,0x85,0xC1,0x34,0xB8,0xB8,0xB7,0x6D,0x8C,0xCE
,0x3E,0xB3,0xB1,0xB0,0xB5,0x97,0xD7,0x20,0xAA,0xAA,0xA9,0xF6,0x9E,0xDC,0x2E,0xA5
,0xA3,0xA2,0x9B,0x99,0xE5,0x14,0x9C,0x9C,0x9B,0x78,0xA0,0xE2,0x1E,0x97,0x95,0x94
,0x1E,0xAB,0xEB,0x18,0x8E,0x8E,0x8D,0xA1,0xB2,0xF0,0x0E,0x89,0x87,0x86,0x52,0xBD
,0xF9,0x04,0x80,0x80,0x7F,0x6E,0x44,0x06,0xFE,0x7B,0x79,0x78,0x36,0x4F,0x0F,0xF0
,0x72,0x72,0x71,0x65,0x56,0x14,0xEE,0x6D,0x6B,0x6A,0x33,0x51,0x1D,0xE4,0x64,0x64
,0x63,0x0A,0x58,0x1A,0xDE,0x5F,0x5D,0x5C,0xE0,0x63,0x23,0xD8,0x56,0x56,0x55,0xCB
,0x6A,0x28,0x2E,0x51,0x4F,0x4E,0x6B,0x75,0x31,0x34,0x48,0x48,0x47,0xB6,0x7C,0x3E
,0x3E,0x43,0x41,0x40,0xEC,0x07,0x47,0x40,0x3A,0x3A,0x39,0xA8,0x0E,0x4C,0x4E,0x35
,0x33,0x32,0x90,0x09,0x55,0x54,0x2C,0x2C,0x2B,0xF3,0x10,0x52,0x5E,0x27,0x25,0x24
,0x6F,0x1B,0x5B,0x58,0x1E,0x1E,0x1D,0xE9,0x22,0x60,0x6E,0x19,0x17,0x16,0x16,0x2D
,0x69,0x64,0x10,0x10,0x0F,0x25,0x34,0x76,0x7E,0x0B,0x09,0x08,0x4B,0xB1,0x77,0x6E
,0x0C,0x03,0x01,0x00,0xB8,0x01,0x02,0x03,0x04,0xBC,0x16,0x07,0x08,0x09,0x42,0x82
,0xDB,0xFE,0x46,0xA4,0x58,0x9C,0x87,0x63,0xEB,0xEA,0xE9,0xAF,0x18,0x19,0x1A,0x1B
,0xA5,0x0D,0x1E,0x1F,0x20,0x69,0xAB,0xF4,0xD7,0x6D,0x8D,0x6F,0xA5,0xAC,0xDA,0xD5
,0xD3,0xD2,0x66,0xA6,0xF6,0x79,0xBF,0x0E,0xB6,0x31,0x00,0x00,0xB8,0x00,0x00,0x00};
	FILE *fp;
	fp = fopen("dex", "w");
	for (int i = 0; i < 310; i++)
	{
		s[i] ^= i;
		fprintf(fp,"%c", s[i]);
	}
	fclose(fp);
}

发现real的世界

然后要批量修改内存,这里用python脚本(这个之前就用了,但是之前做的有问题,就没放脚本)

def patch(start):

         fileName = AskFile(0, "*.*", 'Import File')

         buffer = open(fileName, "rb").read()

         begin = start;

         for index, byte in enumerate(buffer):

                   PatchByte(begin+index, ord(byte))

最后,终于终于步入正轨了

接下来主要的问题就是这两个处理的函数了

chip学长说这是一个常见的流加密,看了一眼后,这个“[email protected]#”不就是密钥k嘛,这个sub_eb1就是密钥流生成器吧,这个sub_f91就是加密吧

仔细看了看,发现这个就是RC4密码,而且还是标准的。解密脚本如下

import libnum
from Crypto.Cipher import ARC4
 
def encrypt(message, key):
    des = ARC4.new(key)
    cipher_text = des.encrypt(message)
    return cipher_text
 
def decrypt(cipher_text, key):
    des3 = ARC4.new(key)
    message = des3.decrypt(cipher_text)
    return message
 
if __name__ == "__main__":
    key = "[email protected]#"
    cipher_text = libnum.n2s(0x4324e5a1c51d72d228efbeeaa5974460d90f2c6f5e26b30afcd4b3)
    message = decrypt(cipher_text, key)
    print message.decode()

终于,终于得到flag了

其实本来这是一道知道了真相后做起来特别爽的一道题,可是中间我莫名其妙地被那个解密encrypt的部分卡住了,而且还非常奇怪,以至于我想了好长时间也不知道为什么,本来的话,没准今天早上就能做完了

说点什么

avatar

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据

  Subscribe  
提醒