最近在用laravel写后台,不得不说laravel用的真爽,彻底不同于php之前给我的感觉,虽说还是对于弱类型有时候感觉有点不爽。

然而,今天用ORM更新数据库的时候却遇到个神奇的问题,laravel的文档中对于模型的更新是这样举例的:

$user = User::find(1);
$user->email = 'john@foo.com';
$user->save();

方法很明确,和模型插入的例子是相似的:

$user = new User;
$user->name = 'John';
$user->save();

然而,自己在使用的时候,却发生了一个神奇的现象,对于模型的插入,数据库正常插入了,可是对于模型的更新,数据库却没有正常的更新。

首先的第一反应当然是检查save的返回值,可是却发现返回的是true,就是说laravel是认为save的操作是成功执行了的。

然后我尝试了类似如下的代码:

$user = new User;
$user->NAME = 'A';
$user->save();
$user->NAME = 'B';
$user->save();

执行之后发现,第二个save确实成功更新了数据库,那就更让我疑惑了。

最后折腾了半天之后,在一个朋友的帮助下,大概找出来问题所在。

我们的数据库里面字段名都是用的大写,把主键名改为小写即可解决这个问题,不得不说真是神奇,检查ORM的log会发现,在主键名大写的时候,SQL语句的是类似:

select * from `table_name` where `id` is null

而主键名小写的时候,SQL语句是类似:

select * from `table_name` where `id` = ?

也就是说,大写的时候,ORM没能成功找到主键的条件,具体ORM的源码也没时间研究,但出这种问题真是有点让人无言以对,看来这真是天都来谴责我使用ORM了,不过就是看到文档里专门大费章节写以为是推荐这样才用的,不然直接SQL语句多好!!!

说句实在话,感觉lab2不算难,只要认真看给的资料应该都能完成。

lab2主要是涉及到了虚拟地址、线性地址、物理地址之间的转换。其实lab1中就用分段的段选择子虚拟地址和线性地址的转换,只是所有段选择子对应的基地址都是0,故而转换了和没转换看起来一样。

在lab2中,一切都有所改变了。最终的目标是实现:

VirtualAddress=LinearAddress=0xC0000000+PhysicalAddress

为了实现这个目标,最开始会临时修改段基址,使得:

VirtualAddress=0xC0000000+LinearAddress=0xC0000000+PhysicalAddress

而在启用了分页机制,又还没来得及恢复段基址前,甚至会出现:

VirtualAddress=0xC0000000+LinearAddress=0xC0000000+0xC0000000+PhysicalAddress

所以,整个启动段页式内存管理的过程涉及到各种地址之间的换算,只要弄明白了这一点,完成lab2的题目就压力不大了。

稍微有点麻烦的反倒不是后面几题,而是第一题,需要特别仔细,因为看起来似乎本来的程序就已经实现了要求,而事实上本来的程序对于链表的顺序没有维护,故只要加入对链表顺序的维护即可。

至于challenge,不得不说是相当麻烦,没有个几百行代码搞不定,最近实在是太忙了,没有时间,于是乎就pass了~~~

最后,还是附上lab2的实验报告和修改的代码。

lab2.zip

最近愈发觉得,评论被回复时候能有邮件提醒很重要,于是乎,今天就倒腾了下。

在网上找了下,相关的资料不少,于是乎最后选了个网上的代码,稍加修改使用。

将下面代码放在主题的functions.php中即可:

function comment_mail_notify($comment_id) {
    $admin_notify = '1'; // admin 要不要收回复通知 ( '1'=要 ; '0'=不要 )
    $admin_email = get_bloginfo ('admin_email'); // $admin_email 可改为你指定的 e-mail.
    $comment = get_comment($comment_id);
    $comment_author_email = trim($comment->comment_author_email);
    $parent_id = $comment->comment_parent ? $comment->comment_parent : '';
    global $wpdb;
    // if ($wpdb->query("Describe {$wpdb->comments} comment_mail_notify") == '') {
    //     $wpdb->query("ALTER TABLE {$wpdb->comments} ADD COLUMN comment_mail_notify TINYINT NOT NULL DEFAULT 0;");
    // }
    if (($comment_author_email != $admin_email && isset($_POST['comment_mail_notify'])) ||
        ($comment_author_email == $admin_email && $admin_notify == '1')) {
        $wpdb->query("UPDATE {$wpdb->comments} SET comment_mail_notify='1' WHERE comment_ID='$comment_id'");
    }
    $notify = $parent_id ? get_comment($parent_id)->comment_mail_notify : '0';
    $spam_confirmed = $comment->comment_approved;
    if ($parent_id != '' && $spam_confirmed != 'spam' && $notify == '1') {
        $wp_email = 'no-reply@' . preg_replace('#^www.#', '', strtolower($_SERVER['SERVER_NAME']));

        // e-mail 发出点, no-reply 可改为可用的 e-mail.
        $to = trim(get_comment($parent_id)->comment_author_email);
        $subject = '您在 [' . html_entity_decode(get_option("blogname"), ENT_QUOTES) . '] 的留言有了回复';
        $message = '<div style="background-color:#eef2fa; border:1px solid #d8e3e8; color:#111; padding:0 15px; -moz-border-radius:5px; -webkit-border-radius:5px; -khtml-border-radius:5px;">
        <p>' . trim(get_comment($parent_id)->comment_author) . ', 您好!</p>
        <p>您曾在《' . get_the_title($comment->comment_post_ID) . '》的留言:<br />'
        . trim(get_comment($parent_id)->comment_content) . '</p>
        <p>' . trim($comment->comment_author) . ' 给您的回复:<br />'
        . trim($comment->comment_content) . '<br /></p>
        <p>您可以点击<a href="' . htmlspecialchars(get_comment_link($parent_id)) . '">查看回复的完整內容</a></p>
        <p>还要再度光临 <a href="' . get_option('home') . '">' . get_option('blogname') . '</a></p>
        <p>(此邮件由系统自动发送,请勿回复)</p>
        </div>';
        $from = "From: "" . html_entity_decode(get_option('blogname'), ENT_QUOTES) . "" <$wp_email>";
        $headers = "$fromnContent-Type: text/html; charset=" . get_option('blog_charset') . "n";
        wp_mail( $to, $subject, $message, $headers );
    }
}
add_action('comment_post', 'comment_mail_notify');

/* 自动加勾选栏 */
function add_checkbox() {
    echo '<input type="checkbox" name="comment_mail_notify" id="comment_mail_notify" value="comment_mail_notify" checked="checked" /><label for="comment_mail_notify">有人回复时邮件通知我</label>';
}
add_action('comment_form', 'add_checkbox');

就是给评论comment_post加了个钩子,这样每次评论的时候就可以检测是否应该发送邮件而自动发送。

最后再给加上一个勾选框来让用户决定是否收到通知。


前段时间,邮件突然就收不到了,不知道是出了什么问题,无奈之下,就又折腾了一下,为了稳定起见,注册了一个sina邮箱,然后用SAE自带的MAIL服务来发送邮件,于是发送邮件部分代码修改如下:

    if ($parent_id != '' && $spam_confirmed != 'spam' && $notify == '1') {
        $from = 'geekjayvic@sina.cn';
        $to = trim(get_comment($parent_id)->comment_author_email);
        $subject = '您在 [' . html_entity_decode(get_option("blogname"), ENT_QUOTES) . '] 的留言有了回复';
        $message = '<div style="background-color:#eef2fa; border:1px solid #d8e3e8; color:#111; padding:0 15px; -moz-border-radius:5px; -webkit-border-radius:5px; -khtml-border-radius:5px;">
        <p>' . trim(get_comment($parent_id)->comment_author) . ', 您好!</p>
        <p>您曾在《' . get_the_title($comment->comment_post_ID) . '》的留言:<br />'
        . trim(get_comment($parent_id)->comment_content) . '</p>
        <p>' . trim($comment->comment_author) . ' 给您的回复:<br />'
        . trim($comment->comment_content) . '<br /></p>
        <p>您可以点击<a href="' . htmlspecialchars(get_comment_link($parent_id)) . '">查看回复的完整內容</a></p>
        <p>还要再度光临 <a href="' . get_option('home') . '">' . get_option('blogname') . '</a></p>
        <p>(此邮件由系统自动发送,请勿回复)</p>
        </div>';
        $options = array('from' => $from,
            'to' => $to,
            'smtp_host' => 'smtp.sina.cn',
            'smtp_username' => $from,
            'smtp_password' => 'password',
            'subject' => $subject,
            'content' => $message,
            'content_type' => 'HTML',
            'charset' => get_option('blog_charset'),
            'nickname' => html_entity_decode(get_option('blogname'), ENT_QUOTES)
        );
        $mail = new SaeMail();
        $mail->setOpt($options);
        $ret = $mail->send();
        if ($ret === false) {
            sae_debug($mail->errno() . ' => ' . $mail->errmsg());
        }
    }

最近做操作系统的实验,本来挺好的,结果被那个challenge给折磨了好久,真的想说challenge的题目描述实在让人无力吐槽。

扩展proj4,增加syscall功能,即增加一用户态函数(可执行一特定系统调用:获得时钟计数值),当内核初始完毕后,可从内核态返回到用户态的函数,而用户态的函数又通过系统调用得到内核态的服务。需写出详细的设计和分析报告。完成出色的可获得适当加分。
提示:规范一下 challenge 的流程。 
kern_init 调用 switch_test,该函数如下:
static void switch_test(void) {
    print_cur_status(); // print 当前 cs/ss/ds 等寄存器状态
    cprintf(“+++ switch to  user  mode +++n”);
    switch_to_user();  // switch to user mode
    print_cur_status();
    cprintf(“+++ switch to kernel mode +++n”);
    switch_to_kernel();         // switch to kernel mode
    print_cur_status();
}
switch_to_* 函数建议通过 中断处理的方式实现。主要要完成的代码是在 trap 里面处理 T_SWITCH_TO* 中断,并设置好返回的状态。

就这样的描述,反正从头到尾我也没能弄清楚他是想要让我们做什么,上来就说让我们增加syscall功能,可最后却check的是内核态与用户态的切换。

然后,最让我不能理解的还是,为什么他要让我们通过中断做一个从用户态到内核态的切换,用户态自己随便调用一个中断,就能把权限提升为内核权限,这样的操作系统真的还有安全性可言么,实在是不能理解!!!(PS:后听一同学说是内核启动第一个用户程序的时候,需要从用户态切回内核态,完全初始化好后再关掉此中断即可)

不过,既然让做,咱就来做吧:

首先是内核态切到用户态的函数:

题目建议通过中断处理实现,于是乎在trap.h中查找中断号,发现:

#define T_SWITCH_TOU                120    // user/kernel switch
#define T_SWITCH_TOK                121    // user/kernel switch

然后,查看中断处理函数入口(vector.S),会发现所有中断处理函数都是trapentry.S中的__alltraps函数:

而__alltraps就是将各种寄存器压栈保存,然后调用trap.c中的trap函数,最后恢复各寄存器并返回,在trap.h中可发现压栈保存的数据结构:

/* registers as pushed by pushal */
struct pushregs {
    uint32_t reg_edi;
    uint32_t reg_esi;
    uint32_t reg_ebp;
    uint32_t reg_oesp;            /* Useless */
    uint32_t reg_ebx;
    uint32_t reg_edx;
    uint32_t reg_ecx;
    uint32_t reg_eax;
};
struct trapframe {
    struct pushregs tf_regs;
    uint16_t tf_gs;
    uint16_t tf_padding0;
    uint16_t tf_fs;
    uint16_t tf_padding1;
    uint16_t tf_es;
    uint16_t tf_padding2;
    uint16_t tf_ds;
    uint16_t tf_padding3;
    uint32_t tf_trapno;
    /* below here defined by x86 hardware */
    uint32_t tf_err;
    uintptr_t tf_eip;
    uint16_t tf_cs;
    uint16_t tf_padding4;
    uint32_t tf_eflags;
    /* below here only when crossing rings, such as from user to kernel */
    uintptr_t tf_esp;
    uint16_t tf_ss;
    uint16_t tf_padding5;
} __attribute__((packed));

于是可发现,我们想要做特权级的切换,就是要修改cs、ds、es等段寄存器,而通过中断修改,最好的办法就是直接将保存在栈中这些数据修改,等返回时就会将寄存器调整为我们需要的值,实现权限切换。

然后需要注意的是,在由内核态切换成用户态的时候,一开始调用中断时,由于是从内核态调用的,没有权限切换,故ss、esp没有压栈,而iret返回时,是返回到用户态,故ss、esp会出栈,于是为了保证栈的正确性,需要在调用中断前将esp减8以预留空间,中断返回后,由于esp被修改,还需要手动恢复esp为正确值。

这样之后,系统特权级已经成功切换,但是由于切换到了用户态,导致IO操作没有权限,故之后的printf无法成功输出,为了能够正常输出,我们需要将eflags中的IOPL设成用户级别,即3,同样也是通过修改栈中值来达到修改的目的。

这个IOPL确实是让人无语至极,实验给的资料里完全没有提及过这个东西,但是没有设置这个,会导致在printf的时候走入自动中断,简直看不出来是什么原因导致的!

IOPL(EFLAGS寄存器的bits 12 and 13) [I/O privilege level field]:指示当前运行任务的I/O特权级(I/O privilege level),正在运行任务的当前特权级(CPL)必须小于或等于I/O特权级才能允许访问I/O地址空间。这个域只能在CPL为0时才能通过POPF以及IRET指令修改。

接下来是从用户态切换到内核态,此时由于调用中断时,是从用户态调用的,故会将ss、esp压栈,但是iret返回时,是返回到内核态,故ss、esp不会出栈,所以此时需要手动出栈,而ss由于维持了中断内部的ss,即已经是kernel,无需修改,故直接出栈esp即可。最后,为了能够让用户态的时候可以调用此中断,需要在idt的初始化函数中将该中断的DPL改成USER级别。

最后,再实现一下syscall,获得时钟计数值,syscall number采取255,于是便可以得到:

static int get_ticks(void) {
    asm("mov $0xff, %eax");
    asm("int $0x80");
}

而对于中断内部的处理代码就更简单了,直接在trap_dispatch的switch里面增加一个分支返回ticks即可。

最后在lab1_switch_test里面的kernel切到user后,再切回kernel前调用此函数进行测试,发现可正确获取ticks。

最后,附上整个lab1的实验报告和修改的代码。

lab1_result.zip

最近学习swift和SpriteKit,在处理背景音乐上遇到了一点问题,特此mark一下。

本来在游戏中,音效是使用SKAction来实现,于是乎最开始,我还是尝试使用SKAction来做背景音乐,可是却遇到了一点问题。

最开始的代码如下:

let playBGM = SKAction.playSoundFileNamed("background-music-aac.caf", waitForCompletion: true)
self.runAction(SKAction.repeatActionForever(playBGM))

意思很明确,一直重复播放这个Action,但是实际运行时却发现,在游戏过程中,背景音乐确实正常播放了,但是,当企图暂停场景或者切换场景的时候,却发现背景音乐并没有立即暂停,而似乎是只是暂停了循环,但还是会把当前这一段播放完。

思前想后也不知道该怎么解决,于是乎,最后换成使用AVAudioPlayer,首先给工程加入AVFoundation.framework框架,然后生成一个AVAudioPlayer对象:

var bgmPlayer = AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("background-music-aac", ofType: "caf")!), error: nil)

然后开始播放:

self.bgmPlayer.numberOfLoops = -1
self.bgmPlayer.play()

最后可以通过bgmPlayer.pause()来暂停,bgmPlayer.stop()来终止播放。

最近在学swift,想要像OC一样,从数组删除特定元素(removeObject),swift的数组本身没有这个功能,于是便想用extension来实现,没想到却遇到了麻烦。

extension的时候,不能写Array<>,这就导致出现一个问题,因为需要用===比较是否为同一个元素,但是只有AnyObject可以===,而数组元素并没有限定为AnyObject,最后几番折腾,终于在StackOverflow上面找到了解决方案:

extension Array {
    // Remove certain object in the array.
    mutating func removeObject<X: AnyObject>(obj: X) {
        for (idx, one) in enumerate(self) {
            if one as X === obj {
                self.removeAtIndex(idx)
                break
            }
        }
    }
}

要注意的是,其中的one as X不能写作one as AnyObject,否则会得到如下编译错误:

Cannot downcast from 'T' to non-@objc protocol type 'AnyObject'

然后,测试的时候,却又神奇的发现,并非只有以类为元素的数组可以使用,Int数组也可以正常removeObject。

这一题的题目由“丁同学”提供,特此表示感谢,题目如下:

2.(取证)从一个磁盘镜像(DD格式)中恢复一个有关于“A hall in DUKE SOLINUS'S palace”的html文件。递交KEY为其MD5值。(500分)

这道题的文件和取证样题中的文件竟然又是一模一样,不过好在题目要取的东西不一样了,也不完全算原题了。

首先,我们在文件中查找DUKE,会发现一段看起来像HTML的内容,然后向前找到其开头处:

0d6cff0: dd3d 2f42 f816 80dd b3a0 78b6 9258 1d1b  .=/B......x..X..
0d6d000: 3c21 444f 4354 5950 4520 4854 4d4c 2050  <!DOCTYPE HTML P
0d6d010: 5542 4c49 4320 222d 2f2f 5733 432f 2f44  UBLIC "-//W3C//D
0d6d020: 5444 2048 544d 4c20 342e 3020 5472 616e  TD HTML 4.0 Tran
0d6d030: 7369 7469 6f6e 616c 2f2f 454e 220a 2022  sitional//EN". "
0d6d040: 6874 7470 3a2f 2f77 7777 2e77 332e 6f72  http://www.w3.or
0d6d050: 672f 5452 2f52 4543 2d68 746d 6c34 302f  g/TR/REC-html40/
0d6d060: 6c6f 6f73 652e 6474 6422 3e0a 203c 6874  loose.dtd">. <ht

很显然,这个文件是从0x0d6d000处开始了,然后再从此处开始,查找</html>,找到其结尾处:

0dc4820: 3c2f 413e 3c62 723e 0a3c 703e 3c69 3e45  </A><br>.<p><i>E
0dc4830: 7865 756e 743c 2f69 3e3c 2f70 3e0a 3c2f  xeunt</i></p>.</
0dc4840: 626f 6479 3e0a 3c2f 6874 6d6c 3e0a 0a8f  body>.</html>...
0dc4850: a9f4 343b 01b4 32e1 6544 b7dd 0e41 cb8b  ..4;..2.eD...A..

于是乎,我们可以很轻松的提取出从开头到结尾的内容。

然后打开我们提取出来的文件,我们会发现其中有一段内容是乱码的,由于没法测试,我也不知道这段乱码是否应该除去。

联系HTML的内容,我们可以发现这段乱码是从0x0d7ae00到0x0da93ff,具体如下:

0d7ae00: ffd8 ffe0 0010 4a46 4946 0001 0101 0064  ......JFIF.....d
……
0da9250: 3eb9 fce7 f39f ce7f 39f5 cfaf ffd9 540e  >.......9.....T.
……
0da9400: 323e 5b57 6974 6869 6e5d 2020 5269 6768  2>[Within]  Righ

其中,0x0d7ae00到0x0da925d为一张JPG图片,其余内容暂不太清楚是什么。

鉴于没有办法测试,这题就先这样了。

PS:据题目提供者称,提取出来的文件最后</html>后面要有换行,也就是那个0x0a别落了。

5.1.Image.rar

这题我拿到手的就这个程序,想来逆向的题,也不需要什么题目描述了。

作为逆向的题目,第一反应当然是打开反汇编器,但是这题是个GUI程序,面对如此大一个程序,傻傻的理解所有代码显然是不划算的,那么我们要找些捷径来解决问题。

首先,我们运行这个程序,运行界面很明确就一个输入框和确定按钮,我们随便输入一点东西并点击确定,会弹出一个提示窗。那么,我们就从这个提示窗下手,因为在这个提示窗的之前就应该是判断密码是否正确的地方。

想要找到这个提示窗,我们通常是给MessageBox函数下断点或者对提示窗中现在内容下内存断点。在这里,我们采取后一种办法,首先在源程序中查找字符串"Try again!",找到这个字符串之后,再我们下断点之前,我们发现,在这个字符串的后面,紧跟着一句The key is :….CraCKS0EAsY!,这么看来,我们应该是已经找到了我们需要的东西,连断点都不用了。

我们在输入框中输入"CraCKS0EAsY!"(PS:注意中间是个数字0,而不是字母O,我刚开始就被这个给坑了,果然还是复制粘贴好!),点击确定,程序如我们所愿,弹出成功的提示窗,此题便得以解决。

这题的难道着实很低,只是刚看的时候显得有点吓人而已。


由于有人说答案错了,于是乎又跑去看了下程序,通过下内存断点,发现了在程序相对地址0x510的地方有点击事件的处理函数,函数内就是将输入框字符取出,并与"CraCKS0EAsY!"这个字符串做了个__mbscmp,根据比较结果分别弹出不同的内容。

然后消息分发函数应该是在程序相对地址0x157B5处,对编号0xC的消息调用了上面的处理函数。

crackme.exe.zip

这道题我拿到手的就是一张图片

按照惯例,首先直接查看下文件的内容,利用strings命令提取出可见字符:

strings -10 3.1.jpg | less

可以发现,在最后部分,有这么一段内容:

Q^DgC)io v
bsR--%|U8IL
*~}(w/mUnn
5uQERcRjx`
eg59<rx~^]cK
MTk3MsTqMtTCMjHI1aOsw8C5+tfczbPE4b/Ly8m3w87KsbG+qaGj1eLKx9bQw8DBvbn6t8fV/bOju6/H6b/2z8K1xLXa0ru0ztSqyte8trfDzsqho7WxzOzPws7no6zDq9b3z6/U2tSiy/m74bz7wcvE4b/Ly8mho7C01dXLq7e9ysLHsNS8tqijrLTLtM674cy4xNrI3b371rnN4tC5oaO74cy4veHK+Lrzo6zW0Le9vau74cy4vMfCvNf3zqq++MPczsS8/qGjDQpXREZMQUd7dGhlIGludGVsbGlnZW5jZSBhYm91dCBBbWVyaWNhIFByZXNpZGVudCBOaXhvbiB2aXNpdGluZyBCZWlqaW5nfQ==
"!&+7/&)4)!"0A149;>>>%.DIC<H7=>;
%&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz
]dYb5FDc<
bwqJd-(E<U

很明显,我们会发现期中有一段特别显眼,以两个=号结束,这不禁又让我们想到了base64:

➜  echo "MTk3MsTqMtTCMjHI1aOsw8C5+tfczbPE4b/Ly8m3w87KsbG+qaGj1eLKx9bQw8DBvbn6t8fV/bOju6/H6b/2z8K1xLXa0ru0ztSqyte8trfDzsqho7WxzOzPws7no6zDq9b3z6/U2tSiy/m74bz7wcvE4b/Ly8mho7C01dXLq7e9ysLHsNS8tqijrLTLtM674cy4xNrI3b371rnN4tC5oaO74cy4veHK+Lrzo6zW0Le9vau74cy4vMfCvNf3zqq++MPczsS8/qGjDQpXREZMQUd7dGhlIGludGVsbGlnZW5jZSBhYm91dCBBbWVyaWNhIFByZXNpZGVudCBOaXhvbiB2aXNpdGluZyBCZWlqaW5nfQ==" | base64 -D
1972��2��21�գ������ͳ����ɷ��ʱ�����������������������µĵ�һ��Ԫ�׼����ʡ��������磬ë��ϯ��Ԣ����������ɡ�����˫����ǰԼ�����˴λ����ݽ���й����������з�������¼��Ϊ�����ļ���
WDFLAG{the intelligence about America President Nixon visiting Beijing}%

很明显,在解出来内容的末尾就是我们想要的flag了,于是这题也得以解决,这个信息隐藏程度只能说至少比样题要稍微高点了。

3.1.jpg

不得不说,对于这个比赛,我实在无话可说,决赛竟然会拿样题当比赛题,我也真是醉了。

决赛的密码类与溢出类的第一题都是原题,题解可以参见这里这里

而最神奇的是,如果做出来两道题就能得二等奖……这我只想说,乃们去比赛前都不把样题看看的么?


由iroser指正,决赛的溢出类题目稍有修改,于是特去专门又check了一下,果然不一样,好吧,当时没有diff是我的错,就大概看了眼反汇编的代码流程基本一致,没想到他竟然就把buffer长度改了,然后把输出的结果由md5("kernel32.GetVersion")改成了md5(md5("kernel32.GetVersion")),确实是没料到他竟然做了这么无聊的修改,只要懂了样题,做这题应该也是毫无压力瞬秒。

不过既然buffer的长度变了,如果想用溢出的方法来做这道题的话,payload中的填充字节自然也得跟着增长了,不过这题还是通过改程序流程要方便多了,并且不会被这小小的修改影响。

在此特别感谢iroser指出此问题!

溢出类决赛第一题test.exe.zip