【符号执行】angrCTF模板

模板1号

使用条件:知道输入长度,用户输入储存在固定地址
需要修改5个变量,如何修改,见代码注释
【还没测试过,现在在上课,晚上测试】

import angr
import claripy
import sys


# angr模板:用户输入储存在固定地址
# 运行:python3 ./exp.py binPath

# START_ADDR main函数开始的位置,或者程序获取了用户输入之后的位置。
# AVOID_ADDR 需要避开的地址,如果有多个,往列表里头加就对了
# FIND_ADDR 需要达到的地址,比如说输出“contulation”的位置
# INPUT_ADDR 储存用户输入的内存地址
# INPUT_LENGTH 看不懂这个就爬吧

START_ADDR = 0x4005bd # first part of program that does computation
AVOID_ADDR = [0x400850] # address of function that prints wrong
FIND_ADDR = 0x400830 # address of function that prints correct
INPUT_ADDR = 0x6042c0 # location in memory of user input
INPUT_LENGTH = 0xf2 - 0xc0 + 1 # derived from the first and last character

def main(argv):
    p = angr.Project(argv[1])
    state = p.factory.blank_state(addr=START_ADDR, add_options={angr.options.LAZY_SOLVES})
    flag_chars = [state.solver.BVS('flag_%d' % i, 8) for i in range(0x43)]
    for flag_chr in flag_chars:
        state.add_constraints(state.solver.And(flag_chr >= ord(' '),flag_chr <= ord('~')))

    for i in range(INPUT_LENGTH):
        state.memory.store(INPUT_ADDR+i,flag_chars[i])

    ex = p.factory.simulation_manager(state)


    ex.explore(find=(FIND_ADDR,), avoid=AVOID_ADDR)

    found = ex.found[0]
    flag = found.solver.eval(state.memory.load(INPUT_ADDR, INPUT_LENGTH), cast_to=bytes)
    print(flag)

if __name__ == '__main__':
    main(sys.argv)

模板2号:暴力破解

使用条件:用户输入通过stdin获取
需要修改两个变量
FIND_STR:输入为正确输入时,程序会输出的字符串,记得用b”修饰
AVOID_STR:输入为错误输入时,程序会输出的字符串,记得用b”修饰

import angr
import sys

FIND_STR = b'Good Job'
AVOID_STR = b'Try_again'

def main(argv):
    bin_path = argv[1]
    p = angr.Project(bin_path)
    init_state = p.factory.entry_state()
    sim = p.factory.simulation_manager(init_state)

    def is_good(state):
        return FIND_STR in state.posix.dumps(1)

    def is_bad(state):
        return AVOID_STR in state.posix.dumps(1)

    sim.explore(find = is_good ,avoid = is_bad)

    if sim.found:
        found_state = sim.found[0]
        print(found_state.posix.dumps(0))
    else :
        print("cannot found a solution")

if __name__ == "__main__":
    main(sys.argv)

【C++逆向】C++面向对象逆向与代码美化(二)

写在前面

在上一篇文章中我们尝试了使用ida的结构体,类型指定和变量重命名来美化C伪代码。
但是仔细看的话我们可以发现,虽然我们知道C++中带有虚函数的类在编译的时候会带入这个类的类型,类名等信息,但是我们并没有自动化的利用上这个信息。
有没有什么办法能自动分析RTTI并且自动的为各个类和对象进行结构创建、类型指定和类型重命名呢?

先说结论

貌似还真没有,我爬了

ida插件:classinfomer for ida7.0

这个插件只支持MSVC编译的文件。它的唯一功能就是搜索整个程序文件中的RTTI信息并且以列表形式展出所有找到的vftable。
展示出的信息里头还包括了类的继承信息,这对C++逆向是十分有帮助的,

安装方式

https://sourceforge.net/projects/classinformer/
打开上述网址,下载ClassInfomer
下载完成后解压,可以看到文件夹里头有这些东西:

将红框中的文件复制到你的ida的安装目录的plugins文件夹下,即可完成安装。

使用方法

这里使用的程序和上一篇文章中的代码相同。只不过上一次用的是G++编译,这一次使用的时MSVC编译。

使用release方式编译,64bits,优化程度为O0,不生成pdf文件,抹除符号表。

编译完之后,拖入ida64看看:

在汇编界面打开 edit->plugins菜单,点击ClassInfomer

弹出的这个界面没有什么需要修改的设置,全选就对了(如果要修改,记住不要把.text段纳入搜索范围,否则ida会崩溃),然后点击CONTINUE

在新弹出的页面里头,以列表形式展示了Class Informer搜索到的所有vftable,还包括了继承关系。

双击后可以在IDA-View窗口中查看vftable

IDA插件: auto_RE

自动化函数重命名,精准度比较差。
项目地址:https://github.com/a1ext/auto_re
安装方式:见README,太简单了懒得截图了。

使用方法:

同上,在IDA-View界面打开edit->plugins菜单,选中AutoRE插件

我们对比一下应用AutoRE前后的函数列表:

只能说这是“最后手段”。

【C++逆向】C++面向对象逆向与代码美化(一)

写在前面:我是傻逼,下午面面试的时候这种问题都回答不出来,还好意思说自己懂C++

示例代码

#include<typeinfo>
class A
{
    int a1;
public:
    virtual int A_virt1(){
        char text[] = "A::A_virt1";
        text[0] = 'a';
        return 1;
    }
    virtual int A_virt2(){
        char text[] = "A::A_virt2";
        text[0] = 'a';
        return 2;
    }
    static void A_static1(){
        char text[] = "A::A_static1";
        text[0] = 'a';
    }
    void A_simple1(){
        char text[] = "A::A_simple1";
        text[0] = 'c';
    }
};

class B
{
    int b1;
    int b2;
public:
    virtual int B_virt1(){
        char text[] = "B::B_virt1";
        text[0] = 'b';
        return 1;
    }
    virtual int B_virt2(){
        char text[] = "B::B_virt2";
        text[0] = 'b';
        return 2;
    }
};

class C: public A, public B
{
    int c1;
    int c2;
    int c3;
    int c4;
public:
    virtual int A_virt2(){
        char text[] = "C::A_virt2";
        text[0] = 'c';
        return 2;
    }
    virtual int B_virt2(){
        char text[] = "C::B_virt2";
        text[0] = 'b';
        return 1;
    }
    C():c1(0x233),c2(0x234),c3(0x235),c4(0x236){} 
};

int main(){
    C* test = new C();
    test->B_virt2();
    test->A_virt2();
    test->A_simple1();
    test->A_static1();
    A* A_pointer = test;
    B* B_pointer = test;
    A_pointer -> A_virt1();
    A_pointer -> A_virt2();
    typeid(test).name();
    return 0;
}

RTTI信息

这一块不说太详细,参考文章:http://www.openrce.org/articles/full_view/23
RTTI (Run-Time Type Identification) is special compiler-generated information which is used to support C++ operators like dynamic_cast<> and typeid(), and also for C++ exceptions. Due to its nature, RTTI is only required (and generated) for polymorphic classes, i.e. classes with virtual functions.
没啥好说的,虚基类也好,继承了它的子类也好,其所用的函数和数据类新在编译期间都是全部确定了的。
由于C++允许使用父类指针指向子类(OOP多态,不多叙述),对于继承了带有虚函数的子类而言,类型系统是一个值得探讨的话提。
将之前的示例代码使用如下指令编译:
g++ -s -O0 -static-libgcc -o test.exe ./text.cpp
拖入ida,分析其中数据。可以在rdata段找到这些结构体:

vtable for C


这个结构体记录的信息很简单,看图就行了。里面的几个函数指针是类C的类成员函数。类C继承的时候实现的两个虚函数,已经以函数指针的形式固定在了这里

struct vtable_for_C{
    Qword* offset_to_this0;
    type_info _ZVT1C;
    vftable vf1;
    Qword* offset_to_this10;
    vftable vf2;
}

点进_ZVT1C这个变量,我们可以看到这个类的名字

memory map of “Test”

现在回过头来看看这个test的本体储存情况

C++的继承机制从这张图可以看的很清楚了。源码中C类先继承A再继承B,那么A的数据在前,B的数据在后。

获取这些信息有什么用?

结论:C++带有虚函数的类,编译器在编译过程中会保留一定的类型信息。
这些类型信息并不能完全还原这个类的全貌,但是理解并处理这些信息对我们逆向C++程序是十分有帮助的。
具体什么帮助?别忘了我们用的IDA的类型分析系统,我们可以创建结构体,重命名变量和指定变量类型。通过使用这三种操作,我们可以让ida的伪代码变得好看很多。这个操作过程正是本篇博客的重点。
在开始美化之前,我们先看看我们原来的代码长什么样

ida伪C代码美化

ida的反编译系统包含了变量名,变量、函数类型和结构体分析。
我们可以根据实际需要和分析过程自主去指定伪代码中的这些信息。

创建结构体

注意红框处的窗口。在这里我们可以创建结构体类型。

根据我们之前逆向工程找到的信息,我们需要创建的结构体如下:

Class C

Class C的vftable

(当然,不止这三个变量,这里偷个懒,就只创建这么三个变量)

设定结构体成员变量类型

上一步创建结构体并重命名变量类型只是第一步。此时创建出来的结构体中的变量并没有指定类型,反编译成果依旧难看。
我们需要手动逐条为结构体中的变量指定类型。
步骤:选中结构体中的某一条变量,按Y键,在弹出的窗口键入变量类型

修改变量类型

在Pseudocode窗口中选中目标变量指针,按Y键指定其类型为Class_C*
然后再按一次f5
现在可以看到,更新后的伪码好看了很多

进一步为函数/变量命名

源码和伪码对比结果
还记得我在第一次二进制组会的时候说过,一款好的逆向分析工具可以使逆向分析过程缩短很多很多
现在想想,也许需要改一下说法,是用好一款好的逆向分析工具可以使逆向分析过程简单很多。

博客停止更新,后台维护中

暂时停止更新

目前在做的工作:

域名备案

后端整理

备份网站

整理操作系统内核相关知识

期待与你8月相见

【第二届网鼎杯】使用angr帮pwn队友找到控制流

faster0

这道题一开始要求解哈希。解哈希之后拿到一个以base64字符串形式给出的压缩包。解压压缩包,并对文件进行upx -d之后,拖入ida,查看程序流程:
func100存在栈溢出漏洞,利用十分简单。但是在哪之前,有个大问题。
类似下面这种函数一共100个,从func000func099,必须依次执行。

__int64 func000()
{
  int v0; // eax
  __int64 result; // rax

  v0 = nums++;
  if ( v0 )
    exit(-1);
  switch ( (unsigned int)read_one() )
  {
    case 0u:
      result = func064();
      break;
    case 1u:
      result = func026();
      break;
    case 2u:
      result = func020();
      break;
    case 3u:
      result = func068();
      break;
    case 4u:
      result = func060();
      break;
    case 5u:
      result = func022();
      break;
    case 6u:
      result = func043();
      break;
    case 7u:
      result = func065();
      break;
    case 8u:
      result = func001();
      break;
    case 9u:
      result = func016();
      break;
    default:
      result = func000();
      break;
  }
  return result;
}

可以使用angr来找到输入流:

import angr
import sys

def main(argv):
    bin_path = argv[1]
    p = angr.Project(bin_path)
    init_state = p.factory.blank_state(addr = 0x4008A4)
    sim = p.factory.simulation_manager(init_state)
    addr = 0x6090AC
    num = init_state.solver.BVV(0,size = 64)
    init_state.memory.store(addr,num,endness = p.arch.memory_endness)

    sim.use_technique(angr.exploration_techniques.dfs.DFS())
    sim.explore(find = 0x405EF7,avoid = 0x4006B0)
    if sim.found:
        found_state = sim.found[0]
        result = found_state.posix.dumps(0)
        for i in result:
            if i not in b'0123456789':
                print('0')
            else:
                print(chr(i))
    else :
        print("cannot found a solution")

if __name__ == "__main__":
    main(sys.argv)

这里说明一下,代码中这句设置路径查找方式的代码是必不可少的,如果不设置这个方法,则脚本会因为耗尽虚拟机内存而被killed。
sim.use_technique(angr.exploration_techniques.dfs.DFS())
因为angr默认使用BFS来查找路径。而显然,这里DFS更为有利。

【第二届网鼎杯】Write-up

bang

apk,邦邦壳

从源哥那里嫖了个脱壳机,一健脱壳。脱壳之后一目了然

signal

ida 打开,看到是个弱虚拟机。angr直接秒了
workon angr
python3 exp.py ./signal.exe

import angr
import sys

def main(argv):
    bin_path = argv[1]
    p = angr.Project(bin_path)
    init_state = p.factory.entry_state()
    sim = p.factory.simulation_manager(init_state)

    def is_good(state):
        return b'good,The answer format is:flag {}' in state.posix.dumps(1)

    def is_bad(state):
        return   b'what a shame...' in state.posix.dumps(1) \
              or b'WRONG!\n' in state.posix.dumps(1)

    sim.explore(find = is_good ,avoid = is_bad)

    if sim.found:
        found_state = sim.found[0]
        print("Flag: {}".format(found_state.posix.dumps(0)))
    else :
        print("cannot found a solution")

if __name__ == "__main__":
    main(sys.argv)

jocker

脑洞题,孤儿出题人
动调找到这里先,a1是用户输入。可以直接得到flag的前一大半。

后面一小半说是藏起来了,但是没有任何逻辑和提示说明藏在那里。
经过推测,在解密后的finally函数里头找到一串没有用过的字符串。

该字符串通过一个十分特定的方式进行了加密。这里直接给出EXP。希望下一次不要再有这样的脑洞题。

enc_flag = [0x66,0x6B,0x63,0x64,0x7F,0x61,0x67,0x64,0x3B,0x56,0x6B,0x61,0x7B,0x26,0x3B,0x50,0x63,0x5F,0x4D,0x5A,0x71,0x0C,0x37,0x66]
data = [0x0E,0x0D,0x09,0x06,0x13,0x05,0x58,0x56,0x3E,0x06,0x0C,0x3C,0x1F,0x57,0x14,0x6B,0x57,0x59,0x0D]
tar = 'hahahaha_do_you_find_me?'
for i in range(19):
    for c in range(32,128):
        if (c^ord(tar[i])) == data[i]:
            print(chr(c),end = '')
key = 58 ^ ord('}')
enc_flag = [37,116,112,38]
for i in range(4):
    print(chr(enc_flag[i]^key),end = '')
print('}')

【CPP feature】vector.earse()可能造成的uaf & double free漏洞

晚上,V&N的队友Sinon提出关于De1CTF2020上一个有关vector.earse()造成的UAF & double Free漏洞。这里总结一下讨论结果:

问题来源

关于vector.earse()的issue
有CPP用户指出,vector<class ?>.earse()在擦除非末端元素的时候,并不会调用该元素的析构函数,而是调用了末端元素的析构函数

漏洞重现

首先我们假设存在下面这样一个类,在构造函数中通过malloc申请了一块内存,在析构函数中归还了这块内存。

class test{
public:
    test();
    ~test();
    void set_index(int i);//设置序号
    bool edit(char*);     //修改字符串的内容,漏洞利用
private:
    int reg;
    unsigned char* vulunablePointer;
};

//C++标准并不建议用户使用malloc来分配内存空间,而是使用符号new
test::test():reg(0),vulunablePointer(nullptr){
    vulunablePointer = (unsigned char*)malloc(0x80);
    if(!vulunablePointer)exit(0xbadbeef);
    strcpy((char*)vulunablePointer,"Pwn!");
};


//注意这里的析构函数,漏洞利用将从这里出发
test::~test(){
    cout<<"析构对象:"<<reg<<"    被free()释放的指针:"<<(void*)vulunablePointer<<'\n';
    free(vulunablePointer);
}

void test::set_index(int i){
    reg = i;
};

bool test::edit(char* newString){
    memset(vulunablePointer,0,0x80);
    strncpy((char*)vulunablePointer,newString,0x80);
}

然后思考一下,下面这个程序会搞出什么东西来

#include <iostream> 
#include <string>
#include <cstring> 
#include <vector>
#include <cstdlib> 

int main(){
    vector<test> vec(10);
    for(int i = 0;i < 10;++i){
        vec[i].set_index(i);
    } 
    system("pause");
    vec.erase(vec.begin()+1);
    vec.erase(vec.begin()+2);
    vec.erase(vec.begin()+3);
    system("pause");
    return 0;
}

输出如下:
显而易见的,不单单是UAF,double free也搞出来了。
除了上面这两个高危以外,还出现了内存泄漏,被earse的类并没有被析构掉(原因后面会讲)。
这在实际生产环境中是十分危险的。

请按任意键继续. . .
析构对象:9    被free()释放的指针:0x9a8490
析构对象:9    被free()释放的指针:0x9a8490
析构对象:9    被free()释放的指针:0x9a8490
请按任意键继续. . .
析构对象:0    被free()释放的指针:0x9a7dd0
析构对象:2    被free()释放的指针:0x9a7f50
析构对象:4    被free()释放的指针:0x9a80d0
析构对象:5    被free()释放的指针:0x9a8190
析构对象:7    被free()释放的指针:0x9a8310
析构对象:8    被free()释放的指针:0x9a83d0
析构对象:9    被free()释放的指针:0x9a8490

--------------------------------
Process exited after 1.99 seconds with return value 0
请按任意键继续. . .

漏洞成因:忽视stl的使用约定

通过翻阅stl的源码可以很快找到漏洞所在
先说明,这里看的源码是GCC 4.9.2版。不同版本的编译器,代码实现存在差异。

stl_vector.h

vector的类成员变量中有这么一个类:
其中三个类成员变量都是指针,前两个指向vector所申请的内存块的开始与结束位置。
先记住这个结构体,后面要用

struct _Vector_impl : public _Tp_alloc_type{
    pointer _M_start;
    pointer _M_finish;
    pointer _M_end_of_storage
    //.....下边是一大堆类成员函数
}_M_impl;

然后,我们找到vector.earse()的声明:
WTF?这个_M_earse()是个什么东西?

iterator erase(iterator __position){ 
    return _M_erase(__position); 
}

vector.tcc

_M_earse()的实现在vector.tcc中。

  template<typename _Tp, typename _Alloc>
    typename vector<_Tp, _Alloc>::iterator
    vector<_Tp, _Alloc>::
    _M_erase(iterator __position)
    {
      if (__position + 1 != end())
        _GLIBCXX_MOVE3(__position + 1, end(), __position);
      --this->_M_impl._M_finish;
      _Alloc_traits::destroy(this->_M_impl, this->_M_impl._M_finish);
      return __position;
    }

其中,_GLIBCXX_MOVE3这个宏调用了std::copy(InputIt first, InputIt last,OutputIt d_first)
std::copy详情
十分惹眼的,我们注意到了调用析构函数的元凶_Alloc_traits::destroy,指向vector最后一个元素的指针被作为参数传入其中
_Alloc_traits::destroy详情
接下来,我们读读源码,看看vector.earse()到底是如何工作的。
1. 判断被earse的元素是否是处于vector尾部的元素,如果不是,则把其后的元素全部复制过来,覆盖掉前面的元素
这里要注意!!!这个复制操作是bit-wise的!这个复制操作只是单纯的把类成员变量复制了过去,包括指针。但是指针所指向的内存并没有额外开辟一片出来。这就导致,如果对象中存在指针,则会出现两个指针指向同一个堆块的情况!
注意,被覆盖掉的那个元素,其析构函数并没有被调用!
2. 修改指向vector末尾元素的指针
3. 因为后边的元素被复制到前面一次,因此,末尾的元素出现重复,需要删掉那个多余的的元素。而对于一个对象而言,删除它的方法就是调用析构函数

ok,现在的问题是,析构函数中free掉的那个指针存在一个副本。而这个副本所属的对象任然是活跃的。高危漏洞就这么出来了。
(下面这个是Sinon画的流程图)

规避方法

It’s hard to find a solution

【javaFX】学习笔记(I)

JavaFX 入门笔记(I)

一、Helloworld

第一个java可视化程序。

Application是一个虚基类。其子类必须实现方法public void start(Stage) throws Exception

System.out.println("HelloWorld");将会输出在控制台,而不是窗口中。

import javafx.application.Application;
import javafx.stage.Stage;

public class javaFXTest extends Application{
  public static void main(String[] args){
    launch(args);
  }

  @Override
  public void start(Stage primaryStage) throws Exception{
    System.out.println("Hello World");
    primaryStage.setTitle("Hello World");
    primaryStage.show();
  }
}

二、Helloworld.class的生命周期

修改Helloword.java,重写部分虚函数,编译并运行

从控制台中可以看到程序的运行顺序,以及线程的名字

init:准备工作可以放在其中

start:程序的主要功能实现

stop:结束程序时的收尾工作

若无需要,initstop可以不实现,但是start必须实现。

需要注意的是,所有的GUI组件必须写在start()方法内,因为只有该方法属于UI线程。

import javafx.application.Application;
import javafx.stage.Stage;

public class javaFXTest extends Application{
  public static void main(String[] args){
    System.out.println("main()," + Thread.currentThread().getName());
    launch(args);
  }

  @Override
  public void start(Stage primaryStage) throws Exception{
    System.out.println("start()," + Thread.currentThread().getName());
    primaryStage.setTitle("Hello World");
    primaryStage.show();
  }

  @Override
  public void init() throws Exception{
    System.out.println("init()," + Thread.currentThread().getName());
  }

  @Override
  public void stop() throws Exception{
    System.out.println("stop()," + Thread.currentThread().getName());
  }
}

输出如下:

PS F:\javatest\javasockets> javac javaFXTest.java
PS F:\javatest\javasockets> java javaFXTest
main(),main
init(),JavaFX-Launcher
start(),JavaFX Application Thread
stop(),JavaFX Application Thread

三、初识Stage

一些Stage类的常用接口。详细的可以看用户手册

import java.util.Observable;

import javafx.application.Application;
import javafx.beans.value.*;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.stage.Stage;

public class javaFXTest extends Application{
  public static void main(String[] args){
    launch(args);
  }

  @Override
  public void start(Stage primaryStage) throws Exception{
    primaryStage.setTitle("Hello World");
    primaryStage.getIcons().add(new Image("./white.jpg"));//设置图标
    primaryStage.setOpacity(0.95);//设置透明度
  //  primaryStage.setAlwaysOnTop(false);//始终在最前方
  //  primaryStage.setX(100);//窗口出现时的位置,坐标为左上角的位置
  //  primaryStage.setY(100);
  //  primaryStage.setIconified(true);//设置最小化
  //  primaryStage.setMaximized(true);//设置最大化
  //  primaryStage.setWidth(500);//设置窗口大小
  //  primaryStage.setHeight(500);
  //  primaryStage.serMaxHeight(500);//设置窗口最大大小
  //  primaryStage.setMaxWidth(500);
  //  primaryStage.getWidth();//获取窗口大小
  //  primaryStage.getHeight();
  //  primaryStage.setResizable(false);//不可改变窗口大小

  //  primaryStage.setFullScreen(true);//设置全屏
  //  primaryStage.setScene(new Scene(new Group()));

    //监视窗口大小变化
    primaryStage.heightProperty().addListener(new ChangeListener<Number>(){
      @Override
      public void changed(ObservableValue<? extends Number>obervable,Number oldValue,Number newValue){
        System.out.println("当前高度:"+newValue.doubleValue());
      }
    });

    //监视窗口的位置
    primaryStage.xProperty().addListener(new ChangeListener<Number>() {
      @Override
      public void changed(ObservableValue<? extends Number> Observable,Number oldValue,Number newValue){
        System.out.println(newValue.doubleValue());
      }
    });

    primaryStage.show();
  //  primaryStage.close();//关闭
  }
}

四、多个Stage之间的权限控制、操作限制方法

当有多个窗口存在的时候

Stage.initOwner(Stage Mother)可以为目标窗口指定其所属对象

Stage.Modality(Modality)窗口或者子窗口可以通过该方法锁定用户对其它窗口的操作(类似保存文件,选目录的时候不能点击文本编辑页面)

import javafx.application.Application;
import javafx.scene.image.Image;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.StageStyle;

public class javaFXTest extends Application{
  public static void main(String[] args){
    launch(args);
  }
  @Override
  public void start(Stage primaryStage) throws Exception{
    primaryStage.setTitle("Hello World");
    primaryStage.getIcons().add(new Image("./white.jpg"));//设置图标
    primaryStage.setOpacity(0.90);//设置透明度
    primaryStage.initStyle(StageStyle.DECORATED);//设置风格

    primaryStage.show();

    Stage s1 = new Stage();
    s1.setTitle("s1");
    s1.setHeight(500.0d);
    s1.setWidth(500.0d);
    s1.initOwner(primaryStage);//WINDOW_MODAL下锁死母窗口
    s1.initModality(Modality.WINDOW_MODAL);//在此窗口结束之前,无法进行其它操作
    //s1.initModality(Modality.APPLICATION_MODAL);//锁死整个程序
    s1.show();
  }
}

五、platform

任务队列,程序空闲时处理

    Platform.runLater(new Runnable(){
      @Override
      public void run() {
        //如果有些任务你不是很着急做,或者想延后做,可以将其添加到这里
      }
    });

关闭窗口后程序是否继续运行

    Platform.setImplicitExit(false);
    Platform.exit();//关闭后台运行的程序

检测运行环境是否支持xxx

Platform.isSupported(ConditionalFeature.GRAPHICS)
//可以检测的项目包含在ConditionalFeature这个类中

六、Screen类——获取屏幕信息

无需多言

    Screen screen = Screen.getPrimary();
    //获取整个屏幕的大小
    Rectangle2D rec2 = screen.getBounds();
    //获取屏幕可视大小
    Rectangle2D rec1 = screen.getVisualBounds();
    System.out.println(rec1.getMinY()+" "+rec1.getMinX());
    System.out.println(rec1.getMaxY()+" "+rec1.getMaxX());
    System.out.println(rec1.getHeight()+" "+rec1.getWidth());

七、scene类 和一些小知识

Gui的本质就是套娃

  1. 组件不能直接放在Stage上
  2. 组件需要放在scene上

scene类

//创建一个布局Group
    Group group = new Group();
    //创建一个标签
    Label label = new Label("Gstalker");
    //设置标签的鼠标响应
    label.setCursor(Cursor.DISAPPEAR);
    group.getChildren().add(label);

    //创建一个scene,以group为根节点
    Scene scene = new Scene(group);
    primaryStage.setScene(scene);

    //设置鼠标放到该场景上时的样子
    scene.setCursor(javafx.scene.Cursor.WAIT);

当然,鼠标还可以这么设置

    URL url = getClass().getClassLoader().getResource(binpath)
    String path = url.toExternalForm();
    widgt.setCursor(Cursor.cursor(path));

调用浏览器打开一个网页

    HostServices host = getHostServices();
    host.showDocument("http://139.155.83.108/");
    primaryStage.show();

八、Group组件 层次结构的开始

Group组件可以容纳多个Object对象

import java.net.URL;

import javafx.application.Application;
import javafx.application.ConditionalFeature;
import javafx.application.HostServices;
import javafx.application.Platform;
import javafx.geometry.Rectangle2D;
import javafx.scene.Cursor;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.stage.Modality;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.StageStyle;

public class javaFXTest extends Application{
  public static void main(String[] args){
    launch(args);
  }
  @Override
  public void start(Stage primaryStage) throws Exception{
    primaryStage.setTitle("Hello World");
    primaryStage.getIcons().add(new Image("./white.jpg"));
    primaryStage.setOpacity(0.90);

    Button b1 = new Button("b1");
    //设置组件的绝对布局坐标
    b1.setLayoutX(value);
    b1.setLayoutY(value);
    //设置组件的固定大小
    b1.setPrefSize(prefWidth, prefHeight);
    //设置组件的透明度
    b1.setOpacity(0.90d);
    Button b2 = new Button("b2");
    Button b3 = new Button("b3");
    Group group = new Group();

    //group.getChildren().add(b1);
    //group.getChildren().add(b2);
    //group.getChildren().add(b3);
    //上面这三句和下面这一句是等同的
    group.getChildren().addAll(b1,b2,b3);
    //移除一个组件 
    group.getChildren().remove(b3);
    //该group内的组件是否自动设定大小
    group.setAutoSizeChildren(true);
    //检测该坐标点是否有组件
    System.out.println(group.contains(0,0));
    //取出该组中的所有元素
    Object[] obj = group.getChildren().toArray();
    //批量处理组件
    for(Object o : obj){
      Button b = (Button)o;
      b.setPrefHeight(value);
      //...
    }
    Scene scene = new Scene(group);
    primaryStage.setScene(scene);
    primaryStage.show();
  }
}

九、Button类——按钮

RGB颜色标记

'#'+6位十六进制数代表颜色 + 2位十进制数代表透明度

Button 基本设置

    Button b1 = new Button("按钮1");
    b1.setLayoutX(100);
    b1.setLayoutY(200);
    b1.setPrefWidth(100);
    b1.setPrefHeight(60);
    //设置组件的字体,【字号】
    b1.setFont(Font.font("微软雅黑",10));
    //设置背景(颜色)
    BackgroundFill bgf = new BackgroundFill(Paint.valueOf("#8FBC8F90"),new CornerRadii(20),new Insets(2,2,2,2));//Insets:可以设置边框线距离按钮的范围
    Background bg = new Background(bgf);
    b1.setBackground(bg);
    //设置按钮边框颜色
    BorderStroke bos = new BorderStroke(Paint.valueOf("#8A2BE290"),BorderStrokeStyle.DASHED, new CornerRadii(20), new BorderWidths(2));
    Border bo = new Border(bos);
    b1.setBorder(bo);
    //设置文字颜色
    b1.setTextFill(Paint.valueOf("#CD000090"));

    //b1按键触发
    b1.setOnAction(new EventHandler<ActionEvent>(){

      @Override
      public void handle(ActionEvent event) {
        Button bu = (Button)event.getSource();
        System.out.println(bu.getText()+"被按下");
      }
    });

十、组件事件监测与相应,全局事件监测与组件响应

局部监测,当按钮被按下的时候做出对应的响应

//b1单击按钮事件触发
    b1.setOnAction(new EventHandler<ActionEvent>(){

      @Override
      public void handle(ActionEvent event) {
        Button bu = (Button)event.getSource();
        System.out.println(bu.getText()+"的单击事件");
      }
    });

全局监测,用户按下按键的时候,该组件做出对应响应(重要)

//监测,按下了什么按键
    b1.setOnKeyPressed(new EventHandler<KeyEvent>() {
      @Override
      public void handle(KeyEvent event){
        System.out.println("Pressed: "+event.getCode().getName());
      }
    });

    //监测,松开了什么按键
    b1.setOnKeyReleased(new EventHandler<KeyEvent>() {
      @Override
      public void handle(KeyEvent event){

        System.out.println("Released: "event.getCode().getName());
      }
    });

【DwagCTF】exploit.pcap分析过程(未完成)

写在前面:感谢CSU_XZLang,V&N_5k1l在解题过程中的帮助
这道题作为逆向来出……脑洞略大,而且十分的难受。我自认为我和队友们合作,能分析到这一步已经是基于目前知识的极限……

第一步,分析流量包

题目下载下来,发现是一个.pacp后缀的流量包。使用wireShark打开可以看到其中的内容,这里CSU_XZLang直接帮我把数据提取出来了。
显而易见,这是一个任意写漏洞的执行过程

搜索关键词? (1 = yes, 0 = no)1
,可以发现hacker向受害者写入了965个字节的数据。
我们把写入的数据提取出来,转化为16进制,按照偏移量排序列出来如下:
根据数据中所写的内存地址数据,我们推测受害者主机是linux系统。又根据linux系统定义的栈空间排布,我们不难推测出,程序被劫持的控制流的实际地址。
接下来,把codes写成二进制文件,拖入ida分析。

offset:任意写执行的时候所使用的指针的偏移量

offset:1001    0x3e9~0x746
<codes>          #运行时的实际地址:0x1181489

offset:352    0x160
13 1e 40 0 0 0 0 0
0 10 18 1 0 0 0 0
11 1e 40 0 0 0 0 0
0 20 0 0 0 0 0 0
37 13 0 0 0 0 0 0
8e ab 3f a3 25 7f 0 0
7 0 0 0 0 0 0 0             
80 d7 4d a3 25 7f 0 0       0x7F25A34DD780
89 14 18 1 0 0 0            0x1181489  #实际返回地址 √!

offset:-144   -0x90
88 12 43 a3 25 7f 0 0 
0 12 18 1 0 0 0 0       
a7 32 43 a3 25 7f 0 0    

offset:-12050568    -0xb7e088 #位于bss段内的一个变量
a7 32 43 a3 25 7f 0 0

第二步 分析恶意代码

整个任意写漏洞的执行过程中,写入的代码大约0x300字节。
分析恶意代码,我们首先得确定恶意代码的入口点在哪里
目光很快聚焦在sub_1F0这个具有完整栈帧的函数上。分析其工作流程,结合wireShark对流量包的分析,我们确定这个函数就是入口点。

__int64 sub_1F0()
{
  int v1; // [rsp+0h] [rbp-150h]
  char code; // [rsp+8h] [rbp-148h]
  char fd; // [rsp+130h] [rbp-20h]

  read(4i64, &v1);
  sub_165();
  read(32i64, &code);
  sub_1D(&fd, &code, 32i64);
  open(0i64, 0i64);
  memset(&code, 0, 0x20ui64);
  read(32i64, &code);
  sub_1D(&fd, &code, 32i64);
  return write(32i64, &fd);
}

sub_1F0调用了3次read,
第一次从stdin中读取4个字节,

第二次从stdin中读取32个字节,

第三次从open打开的本地文件中读取32个字节。这32个字节我们推测,就是flag
3次read之后调用了一次write,向stdout中写入了32个字节。我们推测,读取的flag经过未知加密后被write到了stdout中

注:hacker的端口是35724,受害者的端口是5001
经查询,该函数没有交叉引用,并且函数行为和数据包中的数据完全对应。由此,确定该函数就是恶意代码的入口点。

卡壳

在获取了以上信息以后,我们的分析工作就陷入了停滞。因为sub_1Dsub_165这两个函数并不具有完整的栈帧,并且疑似破坏了栈空间布局,我们无法继续分析下去。
它ret到了那里,我们没有线索,无从得知。
第一次read读取的4个字节,和第二次read读取的32个字节,我们也没能从恶意代码中追踪到其用途。很迷。总之就是很迷。

【DwagCTF】Reversing部分题解

asknicely

打开IDA,f5,发现flag()函数

int flag()
{
  putchar('D');
  putchar('a');
  putchar('w');
  putchar('g');
  putchar('C');
  putchar('T');
  putchar('F');
  putchar('{');
  putchar('+');
  putchar('h');
  putchar('@');
  putchar('n');
  putchar('K');
  putchar('_');
  putchar('Y');
  putchar('0');
  putchar('U');
  putchar('}');
  return putchar('\n');
}

dinner_party2

打开ida,f5,发现是一个cpp程序
其中v6 == 0XDEADDEAD会在程序实际运行的时候改变。

int __cdecl main(int argc, const char **argv, const char **envp)
{
  DWORD NumberOfBytesRead; // [esp+2Ch] [ebp-20h]
  char Dst; // [esp+32h] [ebp-1Ah]
  int v6; // [esp+3Ch] [ebp-10h]
  HANDLE hFile; // [esp+40h] [ebp-Ch]

  sub_40BFF0();
  sub_4A39B0((int)&dword_4B0800, "Enter the password: \n");
  sub_469750(&dword_4B0620, (int)&v6);
  if ( v6 == 0xDEADDEAD )                       // 67305985
  {
    sub_4A39B0((int)&dword_4B0800, "Access Granted.\n");
    hFile = CreateFileW(L"C:\\flag.txt", 0x80000000, 1u, 0, 3u, 0x80u, 0);
    memset(&Dst, 0, 0xAu);
    ReadFile(hFile, &Dst, 0xAu, &NumberOfBytesRead, 0);
    puts(&Dst);
  }
  else
  {
    sub_4A39B0((int)&dword_4B0800, "Access Denied.\n");
  }
  return 0;
}

动态调试时,v6==0XDEADDEAD会变为v6==67305985。nc连接靶机,输入67305985即可获取flag

missyelliott

程序流程:获取用户输入,用户输入按字节取反,每个字节进行按位逆序,用户输入分组交换
所有的运算都是可逆运算。直接上exp:

enc_flag = """ 41 F5 51 D1 4D 61 D5 E9
69 89 19 DD 09 11 89 CB  9D C9 69 F1 6D D1 7D 89
D9 B5 59 91 59 B1 31 59  6D D1 8B 21 9D D5 3D 19
11 79 DD 00"""
enc_flag = enc_flag.replace('  ',' ')
enc_flag = enc_flag.replace('\n',' ')
enc_flag = enc_flag.replace(' ',',0x')

print(enc_flag)

enc_flag = [0x41,0xF5,0x51,0xD1,0x4D,0x61,0xD5,0xE9,0x69,0x89,0x19,0xDD,0x09,0x11,0x89,0xCB,0x9D,0xC9,0x69,0xF1,0x6D,0xD1,0x7D,0x89,0xD9,0xB5,0x59,0x91,0x59,0xB1,0x31,0x59,0x6D,0xD1,0x8B,0x21,0x9D,0xD5,0x3D,0x19,0x11,0x79,0xDD]

for i in range(21):
    a = enc_flag[i]
    enc_flag[i] = enc_flag[42 - i]
    enc_flag[42 - i] = a

for i in range(len(enc_flag)):
    ans = 0
    for j in range(8):
        if enc_flag[i] & (1<<j) != 0:
            ans = ans | 1<<(8-1-j)
    ans = ~ans
    enc_flag[i] = ans
print(enc_flag)
print(len([chr(i) for i in range(ord("*"), ord("z")+1)]))

ocean_boutique

这个题目挺有意思的。慢了一步,只拿了二血
首先说一下程序的意思,不然不好理解解题方法。

这个程序是一个情景故事。你的老板要带你坐飞机出远门,他吩咐了你几件事情:(序号是switch中的case标号)
3.要带什么物件,并统计物件重量和物品总价值(没有先选择4来说明腰带几个,则默认为1)
4.要带几个物件
6.付钱,等于物品总价值
8.确认订单
9.拿好小票

知道这个前置信息之后,我们来看看完成任务需要哪些条件:

  1. 用户输入的是什么?
    用户输入的是若干个二元组。op num。并且最后一组输入必须是9 10第一个数字用于确定操作,第二个数字是操作数
  2. cost == 3133742total_heavy<1000
    这是一个重点,解题的关键在于这里.首先看dword_2020E0处的结构体数组
    每一件物品我们都可以带若干个。要求是总开销 == 3133742,总重量小于1000
    这里有一个bug,在计算重量的时候,不论你带了多少件物品,都只计为一个物品的重量
    同时我们注意到有一个物品名为”Authentic Meme”,重量为1,价格为1,id为2,那我们只带它就好了
struct item{
    int id;
    int zero = 0;
    const char* name;
    int value;
    int heavy;
}items[6];
.data:00000000002020E0 dword_2020E0    dd 1                    ; DATA XREF: check_1+29↑o
.data:00000000002020E0                                         ; check_1+49↑o
.data:00000000002020E4                 dd 0
.data:00000000002020E8                 dq offset aPlushRetriever ; "Plush Retriever"
.data:00000000002020F0                 dd 133700
.data:00000000002020F4                 dd 8
.data:00000000002020F8                 dd 30
.data:00000000002020FC                 dd 0
.data:0000000000202100                 dq offset aCruiseShipTick ; "Cruise Ship Ticket"
.data:0000000000202108                 dd 1000
.data:000000000020210C                 dd 3
.data:0000000000202110                 dd 0Ah
.data:0000000000202114                 dd 0
.data:0000000000202118                 dq offset aRetrieverStick ; "Retriever Sticker"
.data:0000000000202120                 dd 42
.data:0000000000202124                 dd 1
.data:0000000000202128                 dd 2
.data:000000000020212C                 dd 0
.data:0000000000202130                 dq offset aAuthenticMeme ; "Authentic Meme"
.data:0000000000202138                 dd 1
.data:000000000020213C                 dd 1
.data:0000000000202140                 dd 2Ah
.data:0000000000202144                 dd 0
.data:0000000000202148                 dq offset aAcmeAnvil    ; "ACME Anvil"
.data:0000000000202150                 dd 2FD12Eh
.data:0000000000202154                 dd 3E9h
.data:0000000000202158                 dd 4Dh
.data:000000000020215C                 dd 0
.data:0000000000202160                 dq offset aLinkOfBlockcha ; "Link of Blockchain"
.data:0000000000202168                 dd 0C350h
.data:000000000020216C                 dd 0Ah
  1. 带上物品以后,要干什么
    确认订单,付钱,拿小票。

综上,可以得出我们需要的输入

nc连接目标靶机,输入以下字符即可

4 3133742
3 2
8 8
6 3133742
9 10