昨天晚上折腾的laravel的时候,又是发现本地的laravel的model自动对字段有类型识别,感到很奇怪,在想难不成是每次都直接先拉了一次表信息,那这样岂不是代价太大了,于是趁着这次有时间,好好的钻研了一番。

    一路跟着laravel的流程一点点的打log,发现了下面这段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
public function select($query, $bindings = [], $useReadPdo = true)
{
    return $this->run($query, $bindings, function ($me, $query, $bindings) use ($useReadPdo) {
        if ($me->pretending()) {
            return [];
        }

        // For select statements, we'll simply execute the query and return an array
        // of the database result set. Each element in the array will be a single
        // row from the database table, and will either be an array or objects.
        $statement = $this->getPdoForSelect($useReadPdo)->prepare($query);

        $statement->execute($me->prepareBindings($bindings));

        return $statement->fetchAll($me->getFetchMode());
    });
}

    很明显,这里直接就是操作pdo执行了,然后那句execute的结果发现就已经是识别出数据类型了的,也就是说,PDO做了这件事,不像是laravel自己做的,难不成是laravel升级之后怎么配置了下PDO(因为这次本地用的是最新的laravel)?甚感诧异之下,我又跑服务器上去运行了下,发现服务器给的结果还是没有数据类型的,当时我就**了。

    想想看,我跟服务器上的php版本确实是不大相同,我是php5.5.25,服务器是5.5.9,但就一个小版本号的差距不至于吧!于是十分蛋疼的在Google上好好搜了一番,结果发现在php5.3之后,php mysql换了个mysqlnd的驱动,这个驱动本身提供了对数据类型的识别,而之前的libmysql不支持,可是我都已经php5.5了,怎么还没有,也是醉了!

想来defcon结束也差不多两周了吧,等了这么久,终于是等到了官方的比赛成绩出炉,结果不得不说有点惊喜,竟然20分绝杀Dragon Sector拿了第六,本来还在想第三天名次搞不好还得往下降个一两名的……

不过说实在的,名次其实也已经无所谓了,这次体验才是最重要的吧,反正又不是最前面那一拨(想想也是逗,中国三个队正好拿下4-6名,而只有1-3名是当场公布的)。想想我也算是幸运了吧,搞ctf之后的第一场defcon就能够去现场玩一圈(>_>),虽然是搭着0ops的顺风车,但又有什么关系呢~~~

这次defcon比赛的过程也是曲折,留下了不少的遗憾,最搞的想来还是ombdsu那题了吧,题目出来的时候我们毫不知情,主办方说放新题了,然后我们去服务器上就看到了tachikoma,完全不知道ombdsu的存在,然后一血出现看着屏幕上给的名字感觉有点奇怪,但也没多想,然后又查不到flag有被读,一直不知道什么情况,晚上离线分析看其他端口流量,发现个莫名其妙的端口被连,然后又联想下诸葛老师微博上说蓝莲花过的题,才感觉出大事了,最后也果然就是这道题被打的最惨……

然后要说这次defcon,不得不说是深深的感受了一下什么叫棒子吊打全场,愈发的认识到自己到底是有多弱,defkor简直就跟打了鸡血一样,上来就拿ppp开刀,然后以绝对优势引领全场,颇有当时xctf的感觉……然后领奖时,韩国领队一句“all problems are solved by lokihardt”也是霸气。

最后离开拉斯维加斯之前也跟slipper稍微聊了下,0ops也好,蓝莲花也好,跟最前面那一拨的差距终究还是不小,而我与0ops那些主力的差距也绝不是一点点,这次比赛说实在的,感觉自己基本没能帮上什么忙(由于是第一次参加defcon,心态终归还是有不小的问题,看着那恶心的要死的题目,上来就有点不太能接受了),最后很长一段时间更是跳到那个游戏题的坑里一无所获,直到最后都没有队做出来,想想也算是防止了其他人跳坑吧~~~

关于defcon再就也没太多好说的了,该说的早就不知道被人说的多少次了,不过可以提提拉斯维加斯倒是,纸醉金迷的不夜赌城、罪恶之都。

今天偶然把困扰好久的Python命令行给解决了,简直不能更爽,于是特此记录一下。

很早之前就曾经尝试过给Python命令行加上Tab自动补全,可是网上的办法试了半天也没有作用,一直没搞清楚是什么情况,以为是Mac太奇葩,于是就一直搁置,今天心血来潮,又搜了一下,搜到了这个,说是升到Lion之后才跪的,好吧,我买电脑的时候就已经是Lion了吧,真悲催……

于是乎,赶快按照里面给的方案加上了几句话,果断就生效了,终于不用再羡慕用node命令行时补全的爽了~~~不得不说stack overflow大法好,果然都是我没有用stack overflow的习惯惹的祸

然后这次还顺手把历史记录一起给加上了,这样新开python的时候可以上箭头找到上一次运行时的命令了,最后的.pystartup文件如下:

# Add auto-completion and a stored history file of commands to your Python
# interactive interpreter. Requires Python 2.0+, readline. Autocomplete is
# bound to the Esc key by default (you can change it - see readline docs).
#
# Store the file in ~/.pystartup, and set an environment variable to point
# to it:  "export PYTHONSTARTUP=~/.pystartup" in bash.

import atexit
import os
import readline
import rlcompleter

# for Mac OS X
if 'libedit' in readline.__doc__:
    readline.parse_and_bind("bind ^I rl_complete")
else:
    readline.parse_and_bind("tab: complete")

historyPath = os.path.expanduser("~/.pyhistory")

def save_history(historyPath=historyPath):
    import readline
    readline.write_history_file(historyPath)

if os.path.exists(historyPath):
    readline.read_history_file(historyPath)

atexit.register(save_history)
del os, atexit, readline, rlcompleter, save_history, historyPath

当然,不要忘了在shell启动脚本(我是.zshrc)中加上:

export PYTHONSTARTUP=~/.pystartup

这次出于某些原因,就又去overthewire上找了下题,于是乎找到了Narnia,大概看了下也是溢出的题,然后难度要比vortex看起来低一些,然后就决定抽空探下路,也顺便试下pwntools。

这次趁着去北京拿题的间隙,也是看了下pwntools,感觉确实是比zio要强大的多(或者说zio还有各种我没注意的功能),感觉还是慢慢的习惯用pwntools好些,这次拿narnia来练手也是在适合不过了,ssh模块简直就是极大的方便了做这种题~~~

最后还是贴下repo的地址,先滚回去预习去了,有空再来刷通……

这两天给******开了台vps,试用投票下来,最后决定是用Linode,然后vps的话,显然很大的一个用途就是应付gfw了,我用shadowsocks,于是乎折腾了一下优化,这里记录一下。

为了优化,我们拥塞控制算法最好是采用hybla,而内核不自带,所以我们需要自己来编译(Digital Ocean可以不用编译,只要加载一下就好)。

  1. 查看机器内核版本:

    uname -r

    我的机器显示为:

    4.0.4-x86_64-linode57
  2. https://www.kernel.org/pub/linux/kernel下载相同版本的源码:

    mkdir kernel
    cd kernel
    wget https://www.kernel.org/pub/linux/kernel/v3.0/linux-3.11.6.tar.gz 
    tar xzvf linux-3.11.6.tar.gz
  3. 安装以下内核编译工具,不然会编译失败:

    CentOS and Fedora
    yum update && yum install -y ncurses-devel make gcc
    
    Ubuntu and Debian
    sudo apt-get install -y build-essential libncurses5-dev
  4. 配置内核编译文件,导出官方的配置文件再修改增加hybla htcp模块:

    cd linux-3.11.6
    zcat /proc/config.gz > .config

    编辑.config文件,查找CONFIG_TCP_CONG_CUBIC=y,要编译hybla模块在下面一行增加CONFIG_TCP_CONG_HYBLA=y,要编译htcp模块在下面一行增加CONFIG_TCP_CONG_HTCP=y,两个都要的话,都添加在下面,然后编译:

    make

    耐心等待编译内核完成,单核编译大约需15分钟

  5. 修改编译模块的Makefile

    cd net/ipv4/
    mv Makefile Makefile.old
    vim Makefile

    需要hybla的话修改Makefile改成:

    # Makefile for tcp_hybla.ko
    obj-m := tcp_hybla.o
    KDIR := ../..
    PWD := $(shell pwd)
    default:
    $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules

    需要htcp的替换下关键词就好

  6. 开始编译模块

    cd ../..
    make modules
  7. 测试加载模块

    cd net/ipv4
    insmod ./tcp_hybla.ko
    sysctl net.ipv4.tcp_available_congestion_control

    如果加载成功,会显示:

    net.ipv4.tcp_available_congestion_control = cubic reno hybla
  8. 设置开机自动加载模块

    首先将我们需要开机自动加载的模块复制到/lib/modules/4.0.4-x86_64-linode57/kernel/net/ipv4

    sudo mkdir -p /lib/modules/4.0.4-x86_64-linode57/kernel/net/ipv4
    sudo cp -a ~/kernel/linux-4.0.4/net/ipv4/tcp_hybla.ko /lib/modules/4.0.4-x86_64-linode57/kernel/net/ipv4

    然后由于我是Ubuntu,所以直接修改/etc/modules,再最后加入tcp_hybla即可。

至此,我们就已经将hybla算法添加好了,接下来开始修改配置:

  1. 首先修改/etc/security/limits.conf,在最后加入:

    * soft nofile 51200
    * hard nofile 51200
  2. 然后执行:

    ulimit -n 51200
    # 其实我执行的:ulimit -n unlimited
  3. 然后修改/etc/sysctl.conf,在最后加入:

    # max open files
    fs.file-max = 51200
    # max read buffer
    net.core.rmem_max = 67108864
    # max write buffer
    net.core.wmem_max = 67108864
    # default read buffer
    net.core.rmem_default = 65536
    # default write buffer
    net.core.wmem_default = 65536
    # max processor input queue
    net.core.netdev_max_backlog = 4096
    # max backlog
    net.core.somaxconn = 4096
    
    # resist SYN flood attacks
    net.ipv4.tcp_syncookies = 1
    # reuse timewait sockets when safe
    net.ipv4.tcp_tw_reuse = 1
    # turn off fast timewait sockets recycling
    net.ipv4.tcp_tw_recycle = 0
    # short FIN timeout
    net.ipv4.tcp_fin_timeout = 30
    # short keepalive time
    net.ipv4.tcp_keepalive_time = 1200
    # outbound port range
    net.ipv4.ip_local_port_range = 10000 65000
    # max SYN backlog
    net.ipv4.tcp_max_syn_backlog = 4096
    # max timewait sockets held by system simultaneously
    net.ipv4.tcp_max_tw_buckets = 5000
    # turn on TCP Fast Open on both client and server side
    net.ipv4.tcp_fastopen = 3
    # TCP receive buffer
    net.ipv4.tcp_rmem = 4096 87380 67108864
    # TCP write buffer
    net.ipv4.tcp_wmem = 4096 65536 67108864
    # turn on path MTU discovery
    net.ipv4.tcp_mtu_probing = 1
    
    # for high-latency network
    net.ipv4.tcp_congestion_control = hybla
    
    # for low-latency network, use cubic instead
    # net.ipv4.tcp_congestion_control = cubic

    然后执行:

    sudo sysctl -p

    是配置生效

  4. 最后设置好shadowsocks的配置文件,启动即可:

    {
        "server": "::",
        "server_port": 8888,
        "password": "your password",
        "timeout": 300,
        "method": "rc4-md5",
        "fast_open": true
    }
    sudo ssserver -c /etc/shadowsocks.json -d start --user nobody

参考文章:http://www.777s.me/linode-hybla-htcp.html

这次比赛应该是至今为止,人数限制最严格的线上赛了,规定一个队只有三个人,然后还通过技术手段,非要登录帐号才能做题,然后直接输网址也不能看题,真是限制的做个题都蛋疼,想想也是醉了……

这次比赛的结果应该说是情理之中吧,还没开始比的时候,其实就觉得这次估计难得进决赛了,但是,由于直到最后个把小时之前,一直在前十之中,甚至还曾经冲到第二,所以最后被各种反超真的是十分伤心,真的就是眼看着希望破灭,而手头那个协议的80分题却怎么也不知道错在了哪,要是过了就勉强进决赛了 (T_T)。

不过,不得不说的是,这场比赛真是槽点比ACTF还多,简直就是个猜谜大赛,大多数题根本就是说多难,而是纯粹脑洞有多大,对上脑电波立破,对不上那就呵呵,然后分值还一题抵前面N题的和,也是无言以对了。

然后这场比赛,感觉我就是各种酱油,不过也正好,要去本部上课,本来就没啥时间做,没太多好可惜的,想想看似乎我也就把协议20和系统80水过了,感觉完全靠队友脑洞~~~然后神马系统10分超水溢出怎么交都不对,神马协议10分找到坐标了各种姿势交无果,真的就是呵了个呵。

既然过去了就让它过去吧,吐槽一下就算了,反正本来我们也很弱,能拿到今天这样的成绩已是万幸,又怎么可能每次都这么幸运,只是可惜了那个神马训练营,虽然不知道神马玩意,但看起来不错滴样子。

最后,还是附上GitHub这次比赛的地址

PS:刚去复制地址,竟然发现有几个人star,吓哭了……

这次强网杯,应该算是加了一批新人之后的第一场比赛了,比赛结果其实应该是挺满意的了,不过就个人而言,这次比赛应该说还是感觉不怎么好。

不得不说,这段时间确实整个人都挺乱的,各种乱七八糟的事堆积在一块,感觉自己忙的要死,可却又总是觉得不知该做什么好,明明还有很多事要去做,却又什么都不想去做,有些选择题也是怎么也狠不下心来做个了断,徘徊犹豫,无止境的拖延。

大概就这么个状况下,迎来了这么一场比赛,于是乎比赛的状态可想而知,刚开始的一天,整个人就跟在神游一样,不知道在想什么,就是感觉眼前的一切都好陌生,好像那些题都不是我能做的,IDA什么的就好像根本不是我曾经用过的软件一样。而偏偏,这次的pwn题,还是标准的堆溢出,之前做堆溢出从来都是有明确的指针可以覆盖,很简单的实现dword shoot,而这次,就是裸的malloc,早已记不清标准的堆分配策略了,好不容易搞清楚了之后,却又发现unlink中有着简直不知道怎么破的check,然后整个程序core dump之后从来都是在raise里面,每次挂都是不知道自己怎么挂掉的,拉着各种资料、libc的源码看,然后最后在去年HITCON一道裸堆溢出题的WriteUp的帮助下,才好不容易过掉300分的那道堆溢出,接下来400分的则是绞尽脑汁也没能在比赛结束前想到解法。

不过这次队友真心挺给力的了,mhx过来一起比后果断搞定几道密码题,新队员又有能做逆向吧,把简单逆向给做了,然后最最神奇的是,在各种报团一起想之后,这次竟然把一道正统的web题给做了,然后最后果断只剩下了四五百分的题没做出来,可以说应该是已经基本做到了我们的当下的极限了。然后这一次,活跃的人数也是挺多的了,毕竟加了好几个新队友,总之,就是各方面都算比较满意的了。

然后这周末又是一场360的比赛,不过看起来是要没有什么人参加了,最近比赛实在是来的太频繁,堆积了的各种债总归是要还的,真的是不可能每场比赛都让那么用心的参加了,反正现在当下所在意的下一场比赛,应该就是XCTF的决赛了,其他的什么乱七八糟的比赛,就只有得过且过了。

然后现在在GitHub上开了个repo,打算把各次比赛的一点东西和平常做到的题统一的留个档,免得总是不知道删还是不删,一次比赛就链接克隆一个新的虚拟机,只要有一道题没整理好就一直不会删,搞得是越积越多,硬盘完全不够用了,也是该清理下了。

最后附一个这次比赛汇总的WriteUp

作为逆向题,题目自然木有神马描述,但看一眼就知道,这题是要算个序列号,但是比较蛋疼的是,这题的大多数对单个参数的限制条件都是不等于什么,而做等于判断的各个条件又都是些涉及到好几个参数的检验,所以很是麻烦。

程序首先读入一个序列号的字符串,然后会转换为5个dword,一个word来进行一些check,然后check的大致流程如下:

__int64 __fastcall check_key(char *a1)
{
  __int64 result; // rax@3
  __int16 v2; // [sp+1Ah] [bp-16h]@1
  unsigned int v3; // [sp+1Ch] [bp-14h]@1
  unsigned int v4; // [sp+20h] [bp-10h]@1
  unsigned int v5; // [sp+24h] [bp-Ch]@1
  int v6; // [sp+28h] [bp-8h]@1
  unsigned int v7; // [sp+2Ch] [bp-4h]@1

  v3 = *(_DWORD *)a1;
  v4 = *((_DWORD *)a1 + 1);
  v5 = *((_DWORD *)a1 + 2);
  v6 = *((_DWORD *)a1 + 3);
  v7 = *((_DWORD *)a1 + 4);
  v2 = *((_WORD *)a1 + 10);
  if ( (unsigned int)sub_E10(v2) || (unsigned int)sub_DA2(v2) )
  {
    result = 0LL;
  }
  else if ( (sub_F0F(a1, 20) & 0x7FFF) == v2 )
  {
    if ( (unsigned int)sub_E40(v3, v4, v5) == v6 )
    {
      if ( (unsigned int)sub_1566(v7) )
      {
        if ( (unsigned int)no_equal_adj_4(v3) )
        {
          result = 0LL;
        }
        else if ( (unsigned int)no_equal_adj_8(v3) )
        {
          result = 0LL;
        }
        else if ( (unsigned int)is_prime_larger(v3) )
        {
          if ( (unsigned int)some_bits_mod7_0(v4) )
          {
            if ( (unsigned int)some_bits_saf(v5) )
            {
              if ( (unsigned int)odd_v4_v5_mul_v3(v4, v5, v3) )
                result = (unsigned __int16)calc_jiaoyan(v4, v5) == v2;
              else
                result = 0LL;
            }
            else
            {
              result = 0LL;
            }
          }
          else
          {
            result = 0LL;
          }
        }
        else
        {
          result = 0LL;
        }
      }
      else
      {
        result = 0LL;
      }
    }
    else
    {
      result = 0LL;
    }
  }
  else
  {
    result = 0LL;
  }
  return result;
}

这里的check很多,为了我们能够高效的找到key,这里我们需要选择一个合适的搜索顺序,尽可能的将强的条件放在外面判断。

  1. 首先先是some_bits_mod7_0和some_bits_saf分别是对v4、v5的偶数位的判断,我们暴力穷举会发现两者分别有9363和5042种可能;

  2. 然后在odd_v4_v5_mul_v3这里面,对v4、v5的奇数位做了很弱的限制,但是有个v4_odd * v5_odd = v3 + 1的限制,也就是说,这里我们通过枚举v4、v5的奇数位,我们可以计算出v3,然后就可以将v3带到前面的各个对v3单独限制的地方,很好的做一个筛选;

  3. 接下来,看calc_jiaoyan,由于v4、v5我们已经枚举过了,根据v4、v5我们可以计算出对应的v2,然后带入前面对v2的单独校验中检查;

  4. 然后sub_1566是对v7单独的校验,这个校验可以确定v7总共有171955可能;

  5. 此时,我们仅仅剩下了一个判断(sub_F0F(a1, 20) & 0x7FFF) == v2,这句判断实际是用v3-v7计算出一个校验和,与v2进行比较,当我们进行到这一步之前,我们可以秒出很多的结果,但是,当我们加上了这个判断之后,我跑了很久也没能跑出结果,很是无奈,不过想想也是,本来其实v4、v5枚举完后量还好,但是和v7的这近20W一乘起来,也确实恐怖;

  6. 最后,仔细再观察一下最后那个校验和的判断,我们会发现,v2其实隐式的被限制必须要小于0x7FFF,于是我们完全可以在v4、v5算出v2后,先进行这个判断,然后再来和v7联合起来校验,这样其实我们搜到一个解的工作量直接就相当于是减少了10W倍这个级别的,尤其是当v2大于0x7FFF的量比较大的时候。因为在这里,其实可行解的量是巨大的,最后一个校验也没有很严,我们现在一次判断就可以排除10W多组不可能的情况。

总之,程序写好之后,很快便可以跑出刷屏的解,随便选个就好,然后再按照规则,用算出来的6个数,反向去计算序列号即可,这一步是队友做的,我这里就没有了……

pr0dk3y.zip

这题作为逆向题的第一题,难道自然不会太高,但其实如果没想到点,硬生生的去逆的话,也是会足够蛋疼的……

和其他逆向题一样,这题题目描述还是也是看不出啥,然后就不得不说很佩服某逆向机的搜索能力了,当我们还在逆的时候,他就直接在GitHub上找到了这个东西

仔细看下会发现,这个里面的服务端和我们这里的服务端是一样一样滴,只是配置文件会不同,那么我们所要做的就是找到对应的端口,按顺序knock一下,就可以开放目标端口了。

于是乎动态调试,可以拉出pcap_compile的参数:

((udp dst port 13102 or 18264 or 18282) or (udp dst port 31717 or 35314 or 39979 or 15148 or 14661))

我们可以通过猜测或者暴力的方式来弄,这里完全可以猜测要依次以udp的方式knock 13102、18264、18282三个端口或者31717、35314、39979、15148、14661这五个端口,其实前三个就是这题的,而后5个是后面一道题的。

这里knock,我们可以用刚那个GitHub程序中的客户端,编译后运行:

./knock 52.74.114.1 13102:udp 18264:udp 18282:udp --delay 5

于是就轻松完成了knock,不过这里的–delay如果不加的话是会跪的,刚开始由于没加,怎么也knock不开……

然后我们看规则,可以找到:

/bin/bash -c "/sbin/iptables -I KNOCKEDUPD 1 -s %IP% -p tcp --dport 10785 -j ACCEPT"
/bin/bash -c " sbin/iptables -I KNOCKEDUPD 1 58 -s %IP% -p tcp --dport 9889 -j ACCEPT"

那么,knock完之后,这题开放的是10785端口,后面一题开放的是9889端口,对于这题而言,knock完之后,直接nc上去就可以拿到flag了。

这题作为这次defcon预选赛中唯一的一道coding题,还是1分,很显然是送分题了,直接连上服务器可以得到如下返回:

$ nc catwestern_631d7907670909fc4df2defc13f2057c.quals.shallweplayaga.me 9999
****Initial Register State****
rax=0xfcf7659c7a4ad096
rbx=0x1df0e8dfe8f70b53
rcx=0x55004165472b9655
rdx=0x1aa98e77006adf1
rsi=0x949a482579724b11
rdi=0x1e671d7b7ef9430
r8=0x3251192496cee6a6
r9=0x278d01e964b0efc8
r10=0x1c5c8cca5112ad12
r11=0x75a01cef4514d4f5
r12=0xe109fd4392125cc7
r13=0xe5e33405335ba0ff
r14=0x633e16d0ec94137
r15=0xb80a585e0cd42415
****Send Solution In The Same Format****
About to send 74 bytes: 
hŒråRI‡ÔA]HÿÊIÇ¢éNhIÿÊHÿÃHÎIÇ^…6H¤Ã
                                       MÃI÷ëH)ðHÆQØ8eHÿÀIÁÕH5Œm'Ã^C

即这里是给出了一些寄存器的初始状态,让我们执行下面给的机器指令,那显然,相比于写个解释器模拟执行,我们写个程序直接能运行这段指令是最好的。这里如果用汇编写可能会方便点吧,不过鉴于好久没写过汇编的了,用C++写了个内嵌汇编的:

#!/usr/bin/env python
#encoding:utf-8

import zio
import pwn
import commands

TARGET = ('catwestern_631d7907670909fc4df2defc13f2057c.quals.shallweplayaga.me', 9999)


io = zio.zio(TARGET)
io.readline()
regs = io.read_until(['****Send Solution In The Same Format****']).split('n')[:-1]

while True:
    io.read_until('bytes: n')
    ops = io.read_until_timeout()
    if ops[-1] != 'xc3':
        print "Error, not end with xc3."
        exit(1)
    print ''
    print len(ops)

    regs = [x.split('=') for x in regs]
    ops = 'x48xb8' + zio.l64(int(regs[0][1], 16)) + ops

    # headers
    cpp = open('run.cpp', 'w')
    cpp.write('''
    #include <stdio.h>

    using namespace std;

    int main()
    {
    char *ops = "%s";
    ''' % ''.join(['\x%02x' % ord(x) for x in ops]))

    for x in regs:
        # if int(x[1], 16) > int('0xffffffff', 16):
            # x[1] = str(int(x[1], 16) & 0xffffffff)
        cpp.write('__asm__("mov $%s, %%%s");n' % (x[1], x[0]))

    # pwn.o
    # codes = pwn.disasm(ops).split('n')
    # for x in codes:
    #     cpp.write('__asm__("%s");n' % x.split(' ' * 6)[-1].strip())
    cpp.write("((void (*)())ops)();n")

    for x in regs:
        cpp.write('__asm__("push %%%s");n' % x[0])

    i = 6
    for x in regs:
        # cpp.write('printf("%%%d$p\n");n' % i)
        cpp.write('printf("0x%%%d$llx\n");n' % i)
        # cpp.write('printf("0x%%%d$016llx\n");n' % i)
        i += 1

    # footers
    cpp.write('''
        return 0;
    }
    ''')

    cpp.close()

    commands.getstatusoutput('g++ -z execstack -fno-stack-protector -Wall -o ./run ./run.cpp')
    status, output = commands.getstatusoutput('./run')
    output = output.split('n')[::-1]

    i = 0
    for x in regs:
        output[i] = x[0] + '=' + output[i]
        i += 1

    io.writelines(output)
    io.readline()
# print io.readline()
# print io.read_until_timeout()

这里中间产生的C++代码类似:

#include <cstdio>

using namespace std;

int main()
{
char *ops = "……";
__asm__("mov $0x365a6103367515ed, %rax");
__asm__("mov $0x3cb1a6fff5b038b6, %rbx");
__asm__("mov $0x2a5e4d432e3810e7, %rcx");
__asm__("mov $0x3fc79069d112d35e, %rdx");
__asm__("mov $0xe9b6862f68ddc08b, %rsi");
__asm__("mov $0xc9bb5638f34a4ed2, %rdi");
__asm__("mov $0x1eea6b2bcfa19f55, %r8");
__asm__("mov $0xb48d0fe2a6217fcd, %r9");
__asm__("mov $0x7ff063bcf81b25d7, %r10");
__asm__("mov $0x7ecfd1750b003532, %r11");
__asm__("mov $0x2e45b91b25474403, %r12");
__asm__("mov $0xb9661518b82c711, %r13");
__asm__("mov $0x3f397cafd35489db, %r14");
__asm__("mov $0x1d0dc0438a2a9b1f, %r15");
((void (*)())ops)();
__asm__("push %rax");
__asm__("push %rbx");
__asm__("push %rcx");
__asm__("push %rdx");
__asm__("push %rsi");
__asm__("push %rdi");
__asm__("push %r8");
__asm__("push %r9");
__asm__("push %r10");
__asm__("push %r11");
__asm__("push %r12");
__asm__("push %r13");
__asm__("push %r14");
__asm__("push %r15");
printf("%6$pn");
printf("%7$pn");
printf("%8$pn");
printf("%9$pn");
printf("%10$pn");
printf("%11$pn");
printf("%12$pn");
printf("%13$pn");
printf("%14$pn");
printf("%15$pn");
printf("%16$pn");
printf("%17$pn");
printf("%18$pn");
printf("%19$pn");

    return 0;
}

即先将寄存器初始设置好,然后运行所给指令,然后将所有寄存器压栈,通过printf输出。然后这里要注意的是,所给指令最后一句几乎可以肯定说是ret,所以可以很方便的直接call上去就好,但是由于编译出来call的时候,是先将地址移到rax中,然后再call,所以说我们需要在所给指令前面加上一段指令重新设置rax的值,也即上面Python代码中的:

ops = 'x48xb8' + zio.l64(int(regs[0][1], 16)) + ops

然后这题比较蛋疼的是,最开始主办方题目弄错了点啥,搞得一直过不了,各种无语,最关键的是,当时题目有问题的情况下有队过了,所以无奈啊……