Paul C's Blog

To be funny,to grow up!

0%

xctf的一道Go语言逆向题,涉及AES加密和base64换表加密。

用IDA打开g0Re报告SP-Analysis failed错误。

1690613919182

静态检测

将文件拷贝到kali后,静态检测一番。

1
2
3
4
$ file g0Re               
g0Re: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, no section header

readelf -hlS --w g0Re

报告如下错误:

1
2
3
4
5
6
readelf: Warning: Section 49 has an out of range sh_link value of 2302008908
Section Headers:
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[ 0] <no-strings> 00010102: <unknown> 00000001003e0002 481cb0 000040 400003 0 0 15762873573703680
readelf: Warning: [ 1]: Link field (533963) should index a symtab section.
readelf: Warning: [ 3]: Unexpected value (1482181455) in info field.

strings找到一些特征字符串

1
2
3
4
5
6
7
8
9
10
┌──(kali㉿kali)-[~/idawork]
└─$ strings -a -tx g0Re|grep -E '\s{4,30}'
ec OKXX$
13e FN{o
1e6 r mL
284 dRe6eYPgXygMd
295 fSCPpMP/C9DU36D2kliiYS5D9wKG/E_p
2b9 XkJb3WwcGMbUPd63r/bG8gVDS6EsZ5vv
81e18 $Info: This file is packed with the
81e67 $Id:

猜测是UPX加壳,但是UPX标志被抹去了。

UPX! 标志被抹除

unpack时会依次在三个地方检查UPX_MAGIC_LE32(即”UPX!”):

1
2
3
1、在倒数第36字节偏移处检查,如果特征值不符就会转入throwNotPacked()异常抛出函数,打印not packed by UPX;
2、".ELF"魔数前36字节处,如果这里的特征值不符就会转入throwCantUnpack()异常抛出函数,打印l_info corrupted;
3、 在倒数第46字节偏移处检查,如果特征值不符就会转入throwCompressedDataViolation()异常抛出函数,打印Exception: compressed data violation;

1690711701628

结合上面的规则2,可以很清晰地判断出出题人用0KXX代替了UPX!

1690712364661

在对应的三个位置处换上UPX!魔数。

1690713002432

1690712555711

upx脱壳。

1
2
3
4
5
6
7
8
9
10
11
┌──(kali㉿kali)-[~/idawork]
└─$ upx -d g0Re_upx
Ultimate Packer for eXecutables
Copyright (C) 1996 - 2020
UPX 3.96 Markus Oberhumer, Laszlo Molnar & John Reiser Jan 23rd 2020

File size Ratio Format Name
-------------------- ------ ----------- -----------
1331200 <- 534092 40.12% linux/amd64 g0Re_upx

Unpacked 1 file.

输入flag后的处理流程

1
_int64 __usercall sub_48CD80@<rax>(__int64 i%16@<rdi>, __int64 key[i%16]@<rsi>, __int64 a3@<r14>, __int128 a4@<xmm15>)

1.aes 2.base64 3.简单运算

1690723199822

1690723104727

aes加密 密钥获取

1
动态调试获取密钥:wvgitbygwbk2b46d

启动ida服务端。

1
2
3
4
5
6
#端口,密码,详细模式
#若希望同时维护几个调试实例,就要在不同端口启动调试服务器。
./linux_server64 -p23946 -p23946 -v
IDA Linux 64-bit remote debug server(ST) v1.22. Hex-Rays (c) 2004-2017
Listening on 0.0.0.0:23946...

1690716983450

Hex窗口与RSI同步。

1690718805904

base64换表加密

1
2
strings -tx g0Re_upx|grep -E '[A-Z]{10,64}' 
12c040 456789}#IJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123ABCDEFGH

解密脚本

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
#-*-encoding:utf-8
import base64
import string
string1 = "456789}#IJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123ABCDEFGH"
string2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
#ascii字符范围
def filter_printable_chars(flag):
filtered_flag = ''
for byte in flag:
if byte in string1:
filtered_flag+=byte
#else:
#print("ERROR")
#break
return filtered_flag

enc=[ 0xE6, 0xCE, 0x89, 0xC8, 0xCF, 0xC5, 0xF5, 0xC9, 0xD2, 0xD9, 0xC0, 0x91, 0xCE, 0x7F, 0xAC, 0xCC, 0xE9, 0xCF, 0xB7, 0xC0, 0x96, 0xD4, 0xEA, 0x92, 0xE2, 0xD7, 0xDF, 0x84, 0xCB, 0xA5, 0xAE, 0x93, 0xA6, 0xCA, 0xBE, 0x97, 0xDF, 0xCE, 0xF0, 0xC9, 0xB7, 0xE1, 0xAE, 0x6B, 0xC4, 0xB1, 0x65, 0xDB, 0xCE, 0xED, 0x92, 0x93, 0xD6, 0x8C, 0xED, 0xC3, 0xA3, 0xDA, 0x94, 0xA5, 0xAA, 0xB2, 0xB5, 0xA7, 0x55]
key=b"wvgitbygwbk2b46d"
base = ""
for i in range(len(enc)):
base+=chr(((enc[i]-key[i%16])^0x1a) &0xff)
print(base)#uB8EAyfxAmOEvQlrhCJM8hk1qonHskb55NM4qvmxZeY#xg5mMm10x0nF6b3iRdeYÄ

#过滤base64编码表里的字符
str1=filter_printable_chars(base)
result=base64.b64decode(str1.translate(str.maketrans(string1,string2)))

#AES解密
from Crypto.Cipher import AES#
aes = AES.new(key,mode=AES.MODE_ECB)
print(aes.decrypt(result)) #b'flag{g0_1s_th3_b3st_1anguage_1n_the_wOrld!_xxx}\x01'

反汇编算法

  • 1.线性扫描反汇编 关键是确定代码起始位置,之后线性扫描整个代码段,并逐条反汇编每条指令。不会识别分支来解释控制流。
    • 优点:覆盖程序的所有代码段;
    • 缺点:假设代码段中全是代码,没法处理代码段中混入的数据。
  • 2.递归下降反汇编。根据指令间的引用关系决定是否反汇编,在一个代码块内部,还是使用线性扫描算法。
    • 优点:大部分情况下可以区分代码和数据。
    • 缺点:无法处理间接代码路径。

IDA pro是递归下降反汇编器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//add、xor、mov、栈操作push
case 顺序流指令:
线性扫描反汇编
//jnz、ja
case 条件分支指令:
反汇编两个路径,直接反汇编下一条指令对应的分支,将跳转目标指令的地址加入延迟反汇编地址列表。
//jmp
case 无条件分支指令:
确定跳转目标,反汇编目的地址;对于无条件分支后的字节不作处理。

//call
case 函数调用指令:
运行方式类似于无条件跳转,但是其后的返回地址会被直接反汇编,将跳转目标指令的地址加入延迟反汇编地址列表。
//ret,
case 返回指令:
获取接下来将要执行的指令信息;有时需要从栈顶获取,而静态反汇编器不具备访问栈的能力。此时反汇编器会开始处理延迟反汇编地址列表。

反汇编出问题的情形

1.对于无条件分支指令,例如jmp eax这样的运行时才能确定跳转地址的指令,需要人工赋值。

2.call指令,在函数内部篡改了函数返回地址。

1
2
3
4
5
6
7
8
9
10
11
12
13
foo proc near
inc dword ptr[esp];
retn
endp
//错误的反汇编
E8 F7 FF FF FF call foo
05 89 45 F8 90 add eax,90F84589h

//正确的反汇编
E8 F7 FF FF FF call foo
05 db 5
89 45 F8 mov [ebp-8],eax
90 nop

3.ret没有提供返回地址。

1690714484074

IDA遇到的函数返回语句,检测到其栈指针值不为0。

反汇编的困难

1.编译,和自然语言的翻译一样是一个多对多操作。除了C编译器,还有Go、Python、Delphi编译器、WinAPI库,反编译器非常依赖语言和库。

2.编译过程中会丢失命名和类型信息。反汇编后最多知道变量的位数,类型信息需要通过变量的用途确定。

一些静态工具用法

  • Linux:ldd; nm展示符号,C++filter、展示重定义。

  • OSX:otool; 处理MACH-O。

  • Windows:VS里的dumpbin /dependents ;objdump

ldd显示依赖库

1
2
3
4
5
6
7
8
9
10
11
12
13
┌──(kali㉿kali)-[~/idawork]
└─$ ldd linux_server64
linux-vdso.so.1 (0x00007fff323bf000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fbfef767000)
libthread_db.so.1 => /lib/x86_64-linux-gnu/libthread_db.so.1 (0x00007fbfef75c000)
librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007fbfef751000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fbfef730000)
libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fbfef515000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fbfef3d2000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fbfef1f6000)
/lib64/ld-linux-x86-64.so.2 (0x00007fbfef782000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fbfef1d6000)

dumpbin /dependents

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
Dump of file baby.exe

File Type: EXECUTABLE IMAGE

Image has the following dependencies:

KERNEL32.dll
msvcrt.dll
msvcrt.dll

Summary

1000 .CRT C runtime Lib
1000 .bss
1000 .data
1000 .debug_abbrev
1000 .debug_aranges
1000 .debug_frame
2000 .debug_info
1000 .debug_line
1000 .eh_frame
1000 .idata
1000 .rdata
3000 .text
1000 .tls

strings

-t 显示字符的文件偏移

-a 使得strings扫描整个文件,而非只有文件中可加载的、经初始化的部分

-e 搜素其他字符编码,如Unicode

几乎不会用到的工具:x86流式反汇编器,ndisasm和diStorm。

x86汇编语法

  • AT&T:GNU工具(Gas:GNU汇编器、gcc、gdb)
    • 前缀:%reg,$立即数
    • 操作数排序:mov 源操作数,目的操作数 add $0x4,%eax
  • Intel :MASM(微软汇编器)、Turbo汇编器、NASM
    • add eax,0x4

.model flat,平坦模式,代码段和数据段共用一个4G段。

局部变量、

局部变量是堆栈变量。作用域是单个子程序,从子程序返回到主程序时被释放。一种规范写法是

1
LOCAL @buffer[10]:BYTE ;LOCAL 变量名1[重复数量]:类型,变量名2[重复数量]:类型

LOCAL用于为堆栈变量预留空间。

局部变量:只能在过程中用,主程序里不能用;

堆栈变量:没有初始值,只在调用时分配。C语言中的函数值传递的本质。

一些常用类型整理

1
2
byte db,word dw,DWORD dd,fword df,QWORD dq,tbyte dt  1,2,4,6,8,10
WNDCLASS
  • TYPE返回变量大小
  • LENGTHOF 返回变量元素个数
1
2
3
4
5
6
7
;,用于表示一个变量是否结束
var6 dw 10,20,30,40,50
dw 60,70,80,90,99
var7 dw 10,20,30,40,50,
60,70,80,90,99
LENGTHOF var6 ;5
LENGTHOF var7 ;10
  • SIZE,SIZEOF返回所占字节数=LENGTHOF*TYPR

1.=和EQU伪指令

由等号或者EQU定义的符号常量不占用存储空间。

EQU类似于等号伪指令,但是EQU伪指令不许重复定义,而等号伪指令可以(同一个常量名可重复定义多次)。

1
2
3
presskey EQU <"Output is:">
.data
propt db presskey

2.$ 当前地址运算符

3.LEA伪指令

OFFSET和ADDR编译时起作用,LEA是指令,在运行时起作用。

1
2
MOV ESI,ADDR BVAL  ;相当于
LEA ESi,BVAL

计算堆栈(程序执行是分配)变量的偏移地址时,只能用lea。

4.PTR 操作符

按照指定类型在内存中读写值。

1
2
LIST DB 12H,34H,56H,78H;
MOV EAX,DWORD PTR LIST ;EAX 78563412H

5.ALIGN和EVEN伪指令

CPU处理偶数地址比处理奇数地址要快。 可以在一个内存访问周期内获取该数据

ALIGN 对齐 (1,2,4) 按照n个字节的边界值对齐

EVEN 使下一地址从偶数地址开始

6.TYPEDEF和TYPEDEF PTR操作符

类型自命名和定义指针的类型,不占用存储空间

7.LABEL伪指令

别名,不占用存储空间

8.基数控制伪指令RAIDX

1
2
3
4
.RADIX  16;默认该指令后面的数据都是16进制数,其他进制的数据需要额外说明

MOV AX,0FF ;十六进制数,对应十进制255
MOV BX ,12D ;十进制数12

9.ORG伪指令

10.REPT伪指令

11.ASSUME伪指令

将程序的段与逻辑段绑定。

12.SHORT伪指令

循环

1
2
3
4
5
6
7
8
9
10
11
12
.while(条件)
.endw

.repeat

until(条件)

.if
.elseif
.endif
.break .if ;退出循环
.continue ;结束本次循环,进入下一次循环

14.结构体和共用体

15.宏定义

16.过程

模块化代码

PROC [NEAR] 或者[FAR],默认近调用,即在ODS实模式下的一个段里(<64KB(。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
主程序中 CALL {PROC_noarg_name}

PUSH 参数2
PUSH 参数1
INVOKE {PROC_noarg_name}


{PROC_noarg_name} PROC [NEAR]
LOCAL 变量名1[重复数量]:类型,变量名2[重复数量]:类型
RET ;从栈中弹出返回地址,返回到主程序
{PROC_noarg_name} ENDP

{PROC_name} PROC,参数1:类型,参数2:类型,...


17.ENTER和LEAVE

18.RET和RETN

19.IDA的反汇编

0day 安全

文件偏移地址 = 虚拟内存地址(VA)−装载基址(Image Base)−节偏移
= RVA -节偏移

对于 栈桢可能发生移位的情况

解决办法:

一个函数返回时,esp正好指向原来存储返回地址的下一位,我们将shellcode从ret_addr的后一个位置开始填充,并将ret_addr填充为一个进程中的”jmp esp”的指令的地址,这样函数返回后就会跳到esp指向的栈顶的位置开始执行shellcode。

缓冲区组成方式,现阶段已经讲了两种:

  1. 将shellcode放到缓冲区,然后覆盖返回地址到缓冲区的起始地址。这种适用于缓冲区较大的场合。
  2. 将shellcode放到函数返回地址以后,然后覆盖返回地址为”jmp esp”之类的指令,使得函数返回时跳转到shellcode处执行指令。这种适用于缓冲区较小的场合。

  3. 找到程序运行的线程环境块TEB。

  4. TEB的起始地址偏移0x30的地方指向进程环境块PEB。
  5. PEB的地址偏移0x0C的地方存放指向PEB_LDR_DATA结构体的指针,该指针指向一个存放着被进程装载的动态链接库的信息的结构体。
  6. PEB_LDR_DATA结构体偏移位置位0x1C的地方指向模块初始化链表的头指针InInitializationOrderModuleList。
  7. 4中的链表存放PE被载入时初始化的模块信息,第一个链表节点时ntdll.dll,第二个位kernel32.dll。
  8. kernel32.dll的节点偏移0x08是kernel32.dll在内存中载入的基址。
  9. kernel32.dll的基址加0xe3C是PE头的地址。
  10. PE头偏移0x78存放着指向函数导出表的指针。
  11. 安照下述方法寻址:
    • 导出表偏移0x1C的指针指向存储导出函数偏移地址(RVA)的列表。
    • 导出表偏移0x20指针指向存储导出函数名的列表。
    • 根据函数名找到我们要的函数是导出表中的第几个,然后再地址列表中找到对应RVA。
    • RVA加上动态链接库的基址即是VA,这个也是我们在shellcode中需要的地址。

这里shellcode的构造为了尽可能的短,所以需要给每个API名字用一个hash去代替。
MessageBoxA:0x1e380a6a
ExitProcess:0x4fd18963
LoadLibraryA:0x0c917432

1
2
3
4
5
push 0x1e380a6a
push 0x4fd18963
push 0x0c917432
mov esi,esp
lea edi,[esi-0xC]

如何实现一款 shellcodeLoader

https://cloud.tencent.com/developer/article/1755926

本文记录那些恶意软件使用的关键API。

NtProtectVirtualMemory ,创建PAGE_GUARD属性的内存页,通常用于反逆向和反调试 ; 将可读可写的内存属性改为可读可执行 。

这道题是一道已知RC4的密文和密钥,反求明文的伪装成逆向的密码题。

1689514292270

​ 图1

思考

反汇编时,要深入每个关键函数看一看,不能想当然。

IDA给出的伪代码代码大部分时候还是十分正确的,直接看伪代码比反汇编代码正确率要来得高。

干扰项:

1689514736957

在IDA的反汇编界面看代码,会误以为密钥为key

1
key_rodata = b"\xBE\xBA\xFE\xCA\xEF\xBE\xAD\xDE"

也会误认为密文为enc

1689514809165

不管任何一个弄错,都会使得结果错误。

干扰原因:按照C调用约定,在call之前的部分应该是函数的参数部分,但是

1
2
__int64 __usercall RC4_setkey@<rax>(__int64 RC4_key@<r12>, __int64 RC4_keytable@<r13>)
char __usercall memcmp@<al>(unsigned int a1@<edx>, __int64 a2@<r14>)

这里是作者自定义的call行为,call之前的内容并不全是函数的参数。

对于RC4_setkey,其真正的RC4_key是r12,。

1689515709788

而r12早就被赋予了值,其值为val,在got表中。

1
2
3
4
5
6
7
8
9
10
11
12
main         push    rbp
main+1 sub rsp, 200h
main+8 mov ecx, 200h
main+D xor eax, eax
main+F mov rdi, rsp
main+12 repne stosb
main+14 mov ebx, 6
main+19 lea rbp, flag ; "FLAG: "
main+20 lea r12, val
main+27 mov r15, rsp
main+2A lea r14, [r15+80h]
main+31 lea r13, [r15+100h]

对于memcmp,也类似,在其函数内部,再次修改rsi,其真正比较的是dat和r14.

1689516066778

正确的密钥和密文都在got表里

如图1所示,密钥应该为图1所示的val所对应的内容,密文应该为dat对应内容。

1689515029709

密文获取

memcmp里返回dat数组和传入的结果数组的比较情况,真正的密文应该是dat数组。

1
result |= *((_BYTE *)&dat + v3) ^ *(_BYTE *)(a2 + v3);

点击第一处字节后,按下(Alt+L),再点击最后一处字节,就可以选定待导出的密文部分。

1689513948497

Shift+E,导出Array数组,复制粘贴到python代码里。

1689513831064

解密代码:

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
# 已知的密钥表和加密文本
RC4_key= b"\x31\x09\x81\x19\x19\x14\x45\x11"
cmp_data =bytes([
0x78, 0xCF, 0xC4, 0x85, 0xDC, 0x33, 0x07, 0x4C, 0x93, 0x35,
0xFB, 0x7C, 0x10, 0x8E, 0xBE, 0x93, 0x28, 0xE6, 0x2E, 0x75,
0xDA, 0x5E, 0x85, 0xC5, 0x91, 0x15, 0x75, 0x89, 0x48, 0x0E,
0x29, 0xA4, 0xF9, 0xA6, 0x3A, 0x6E, 0x1F, 0x84, 0xF7, 0x42,
0xB0, 0x93, 0x31, 0xF0, 0x68, 0xC0, 0x43, 0x38, 0x07, 0x32,
0x09, 0x57, 0xDA, 0x32, 0x44, 0xCF, 0xCD, 0x8F, 0xE5, 0xBF,
0xE3, 0xD6, 0xBB, 0x59, 0x9A, 0x6A, 0x84, 0x85, 0xD3, 0x22,
0xA9, 0x8E, 0xB5, 0xEA, 0xBD, 0x57, 0xDE, 0xB1, 0x6C, 0x93,
0xE4, 0x74, 0x70, 0xAC, 0x1A, 0x03, 0xD9, 0x16, 0x9F, 0xBC,
0x97, 0xFB, 0x85, 0xD9, 0xA6, 0x9E, 0xD4, 0xD6, 0x02, 0x59,
0xD5, 0x28, 0xB3, 0x93, 0x16, 0xB6, 0xC4, 0x78, 0xC4, 0xA2,
0x12, 0xD2, 0xEF, 0xB1, 0x54, 0x18, 0xFD, 0x76, 0x51, 0xA3,
0x5E, 0x57, 0xB8, 0x58, 0x4B, 0x1E, 0xE2, 0x41
])

def RC4_setkey(key, keytable):
keytable_length = 256#len(keytable)

# 初始化密钥表
for i in range(keytable_length):
keytable[i] = i

# KSA (密钥调度算法)
j = 0
for i in range(keytable_length):
j = (j + keytable[i] + key[i % len(key)]) % keytable_length
keytable[i], keytable[j] = keytable[j], keytable[i]

return keytable

#zer0pts要求的字符范围
def filter_printable_chars(flag):
filtered_flag = bytearray()
for byte in flag:
if 0x20 <= byte <= 0x7E:
filtered_flag.append(byte)
#else:
#print("ERROR")
#break
return filtered_flag

def gen_keytable(keytable, enc_text):
flag_length = len(enc_text)
v4 = 0
v5 = 0
for v3 in range(flag_length):
v4 = (v4 + 1) % 256
v5 = (keytable[v4] + v5) % 256
keytable[v4], keytable[v5] = keytable[v5], keytable[v4]
#flag[v3] = enc_text[v3] ^ keytable[(keytable[v5] + keytable[v4]) % 256]
return v4,v5

def RC4_decrypt(keytable, enc_text,v4,v5):
flag_length = len(enc_text)
flag = bytearray([0] * flag_length)


for v3 in range(flag_length-1,-1,-1):
flag[v3] = enc_text[v3] ^ keytable[(keytable[v5] + keytable[v4]) % 256]
keytable[v4], keytable[v5] = keytable[v5], keytable[v4]
v5 = (v5-keytable[v4]) % 256
v4 = (v4 - 1) % 256

return flag


# 获得密钥表
RC4_keytable = bytearray([0] * 520)

# 设置密钥表
RC4_setkey(RC4_key, RC4_keytable)
hex_string = ' '.join([f'\\x{byte:02X}' for byte in RC4_keytable])
# 打印密钥表
print("密钥表:\n", hex_string)

#获得解密需要的初始值
v4,v5=gen_keytable(RC4_keytable,cmp_data)
hex_string = ' '.join([f'\\x{byte:02X}' for byte in RC4_keytable])
# 打印密钥表
#print("密钥表:\n", hex_string)
print(v4,v5)

# 解密获取flag
flag = RC4_decrypt(RC4_keytable, cmp_data,v4,v5)
filtered_flag=filter_printable_chars(flag)
# 打印flag
print("Flag:", filtered_flag.decode())

最终结果:

1
Flag: zer0pts{d0n'7_4lw4y5_7ru57_d3c0mp1l3r}

ADVAPI.dll 提供高级系统功能,Advanced,如 注册表、安全性、事件日志、服务管理等 ;

查看API对应的源代码路径:

1
C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\um

只运行一个实例

1
2
3
CreateMutexA
FindWindowA
使用共享Section

5.1节的Serial.exe只能运行一个实例。

1688962133346

1688963259744

修改FindWindow后面的判断逻辑即可。一开始改为了75(jnz)

此时可以运行多个,但是运行前需要找到已存在的一个窗口才能继续创建,再次修改程序,把jz改成jmp。

微信

先根据break定位关闭微信的函数CloseWechat,进而找到关键函数sub_405FD0,它控制着返回结果。

1688970540191

关键函数int __stdcall sub_405FD0(DWORD a1, int a2, int a3, int a4)

跟进去后,可以看到一开始检查注册表语言配置和更新配置。

1688968867614

微信整体打开,靠WechatWin.dl里的函数。

1688969199372

打开WechatWin.dll,发现其导入表里存在实例判断函数。

1688969733507

1688969749959

双击进入后,等待库加载完成,需要几分钟,之后x查看两个函数的上下文:

注意这里一定要多等一会,不然调用CreateMutex的地址可能检索不全。

1688970084672

调用FindWindowW函数的上下文十分明显:

1688971265497

对于上图尾行的这个大跳,是一个经典的判断Mutex是否存在的代码段。

Mutex存在,则GetLastError返回 ERROR_ALREADY_EXISTS.,不存在时,返回0.

1688971805972

把jnz StartNewInstance改变成为jmp。

爆破成功。

1688972258464

利用QQProtect.exe提权,获得NT Authority\SYSTEM权限。

漏洞成因:

QQProtect.exe没有使用ASLR保护,篡改函数指针,利用ROP链(Ret Oriented Programming,即修改函数返回指针)实现了任意代码执行。

1.QQProtect.exe+0x40c9f8处,有任意地址写漏洞

待添加自己的截图,网络截图可见:

image0

可以在任意地址写入DWORD(1)。

2.QQProtectEngine.dll+0x3B4F6处,

待添加自己的截图,网络截图可见:

image1

可以完成对任意地址的指针ptr,偏移2个ptr指针类型大小的位置处,赋值为(unsigned int )(ptr+3)。

Affected Products:

  • QQ 9.7.1.28940 ~ 9.7.8.29039
  • TIM 3.4.5.22071 ~ 3.4.7.22084

Affected Components:

  • QQProtect.exe 4.5.0.9424 (in TIM 3.4.5.22071)
  • QQProtect.exe 4.5.0.9426 (in QQ 9.7.1.28940)
  • QQProtectEngine.dll 4.5.0.9424 (in TIM 3.4.5.22071)
  • QQProtectEngine.dll 4.5.0.9426 (in QQ 9.7.1.28940)

POC

POC整体用rust构建,先来解析配置文件里的内容。

1.配置文件解析

针对32位系统使用静态链接的C运行时,构建可执行文件。

1
cargo +stable-i686-pc-windows-msvc build --release --config "build.rustflags = [\"-C\", \"target-feature=+crt-static\"]"

对应的toml文件

1
2
3
4
5
[profile.release]
opt-level = 3

[profile.release.overrides.'cfg(target_os = "windows")']
rustflags = ["-C", "target-feature=+crt-static"]

此时,只需要cargo build --release即可。

开发过程中,要利用一些依赖库,来调用Windows API、服务和命令行参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[dependencies.rhexdump]
version = "0.1.1"

[dependencies.windows]
version = "0.44.0"
features = [
"Win32_Foundation",
"Win32_System_LibraryLoader",
"Win32_System_Threading",
"Win32_System_SystemServices",
"Win32_System_Console"
]

[dependencies.windows-service]
version = "0.5.0"

[dependencies.windows-args]
version = "0.2.0"

#生成动态链接库,即dll文件
[lib]
crate-type = ["cdylib"]

2.定义的src\lib.def文件中的导出符号。

C++名称修饰规则

C++ 的名称修饰规则,也称为名称重整(name mangling)。用于编译器生成符号表,会因平台和编译器有所不同。它将函数和变量的名称、参数类型、返回类型等信息编码成一个唯一的符号。

名称修饰的目的是确保在链接阶段可以正确地解析函数和变量的符号,避免符号冲突和重复定义的问题。 为了实现不同编程语言之间的互操作性,可以使用 extern "C" 来指定使用 C 风格的符号。

导出符号的格式是 ?SymbolName@ClassName@@FunctionSignature=_alias@0

下面的文件规定了导出的类和符号命,并 将所有导出符号重命名为 _die

1
2
3
4
5
6
7
8
9
10
11
12
13
LIBRARY
EXPORTS
?NextSiblingElement@TiXmlNode@@QAEPAVTiXmlElement@@PBD@Z=_die@0
?FirstChildElement@TiXmlNode@@QAEPAVTiXmlElement@@PBD@Z=_die@0
??1TiXmlDocument@@UAE@XZ=_die@0
?RootElement@TiXmlDocument@@QAEPAVTiXmlElement@@XZ=_die@0
?GetText@TiXmlElement@@QBEPBDXZ=_die@0
?Attribute@TiXmlElement@@QBEPBDPBD@Z=_die@0
?Attribute@TiXmlElement@@QBEPBDPBDPAH@Z=_die@0
?LoadXML@TiXmlDocument@@QAE_NPADHW4TiXmlEncoding@@@Z=_die@0
??0TiXmlDocument@@QAE@XZ=_die@0
DllMain

QAE 指示该构造函数是一个非静态成员函数,

  • QB 表示该函数是一个非静态成员函数,并且返回一个指针。
  • EPBD 表示函数的第一个参数是一个 const char* 类型的指针。
  • PAH 表示函数的第二个参数是一个 int* 类型的指针。
  • PAVTiXmlElement 表示返回类型是一个指向TiXmlElement` 类的指针。
  • 0TiXmlDocument@@QAE@XZ中0表示该类的默认构造函数
  • @Z:函数参数的结尾标记 ,XZ 表示该构造函数不接受任何参数。

代码解析

1
CreateProcessAsUserW(token2, None, PWSTR("cmd.exe".encode_utf16().chain(Some(0)).collect::<Vec<u16>>().as_mut_ptr()), None, None, FALSE, PROCESS_CREATION_FLAGS(0), None, None, &si, &mut pi).ok().unwrap();

大数据在医疗、金融、安全、管理等方面都有很多应用,可以用来化解这些领域中出现的风险;而大数据的应用本身又带来一系列安全隐患,需要靠机器学习技术去解决或缓解。

Prompt:本篇论文采用了{}机器学习技术,对大数据的{}{}特征进行建模,处理{分类}任务,达成了{}效果,在{}领域有所应用。

大数据应用

大数据的一些应用,推荐系统、辅助决策、异常检测、关联分析

  1. 数据驱动的决策:大数据分析可以提供大量的实时和历史数据,帮助组织做出更准确的决策。通过对海量数据进行挖掘和分析,可以发现隐藏的关联和趋势,从而指导业务决策和战略规划。
  2. 个性化服务和推荐系统:基于大数据分析的个性化服务和推荐系统可以根据用户的历史行为和偏好,提供定制化的产品和服务。这使得用户能够更好地满足其需求,提升用户体验。
  3. 预测和趋势分析:通过对大数据进行分析,可以预测未来趋势和行业发展方向。这对于市场营销、销售预测、风险管理等领域具有重要意义,可以帮助组织做出战略决策和规划。
  4. 预测和趋势分析:数据挖掘可以通过分析历史数据,发现潜在的趋势和模式,从而进行未来的预测。这在市场预测、销售预测、天气预测等领域具有重要应用。
  5. 客户细分和市场分析:通过对客户行为和偏好的分析,可以将客户划分为不同的细分群体,了解他们的需求和行为模式,并制定针对性的市场策略和营销活动。
  6. 金融欺诈检测:数据挖掘可以帮助金融机构检测异常交易和欺诈行为。通过分析大量的金融交易数据,可以识别潜在的欺诈模式和指标,及时采取相应的措施。
  7. 社交网络分析:数据挖掘可以揭示社交网络中的关系和影响力。通过分析社交媒体数据、用户之间的互动和信息传播,可以了解社交网络中的关键人物、群体动态和舆论趋势。
  8. 生物信息学:数据挖掘在生物信息学中发挥重要作用,例如基因表达数据的分析、蛋白质结构预测、基因组序列分析等,有助于理解生物学的复杂性和进行基因研究。
  9. 物流和供应链优化:通过对物流和供应链数据的挖掘和分析,可以优化物流路径、减少库存成本、提高配送效率,并优化供应链的整体运作。
  10. 医疗诊断和治疗:数据挖掘可以帮助医疗领域进行疾病诊断、药物选择和治疗计划的优化。通过分析医疗数据和患者病历,可以发现潜在的疾病模式和个体化的治疗方案。

大数据在风险管理领域的应用

可靠性方面

基于新能源汽车大数据平台 ,研究电动汽车的锂离子电池可靠性问题, 电池故障诊断与健康状态预估

[1]李放,闵永军,张涌.基于大数据的动力锂电池可靠性关键技术研究综述[J].储能科学与技术,2023,12(06):1981-1994.DOI:10.19799/j.cnki.2095-4239.2023.0316.

医疗

在医疗领域,大数据的应用可以带来以下益处:

  1. 疾病预测和诊断:通过分析大规模的医疗数据,可以发现潜在的疾病风险因素和诊断模式,提前进行疾病预测和诊断,从而提高治疗效果和患者生存率。
  2. 药物研发和治疗方案优化:大数据分析可以加速药物研发过程,帮助科学家识别潜在的药物靶点和候选化合物。此外,通过分析大量的患者数据,可以优化治疗方案,提供更个性化和有效的治疗。

金融

  1. 风险管理和欺诈检测:通过分析大数据,可以识别金融交易中的异常模式和风险指标,帮助金融机构及时发现潜在的欺诈行为,并采取相应措施进行风险管理。
  2. 个人信用评估和贷款决策:利用大数据分析个人的消费行为、信用记录和其他相关数据,可以更准确地评估个人的信用状况,并基于此做出贷款决策和利率定价。

计算机

隐私保护

[1]方滨兴,贾焰,李爱平等.大数据隐私保护技术综述[J].大数据,2016,2(01):1-18.

环境、资源等其他方面

大数据带来的风险

大数据由于需要从网络等多个渠道收集数据,别有用心的攻击者可以进行数据投毒,污染模型,从而导致模型有后门生成。因此,大数据可能带来数据投毒风险。

  1. 数据隐私和安全:大数据涉及大量的个人敏感信息和机密数据,如个人身份信息、医疗记录、财务数据等。如果这些数据未经适当保护,可能会导致隐私泄露和滥用,甚至成为黑客攻击的目标。
  2. 偏见和歧视:大数据分析依赖于大量的历史数据,而这些数据可能反映了现实社会的偏见和歧视。如果不加以适当的处理,分析结果可能具有偏见性,导致不公平的结果和决策。
  3. 数据质量问题:大数据分析对数据的质量和准确性要求很高。如果数据存在错误、缺失或不完整,分析结果可能不准确或误导性,影响决策的可靠性。
  4. 安全漏洞和数据泄露:大数据系统中可能存在安全漏洞和弱点,攻击者可以利用这些漏洞来获取敏感数据或操纵分析结果。此外,数据共享和合作过程中的数据传输也可能面临被窃取或泄露的风险。

AI对大数据风险的解决

社交图谱、差分隐私、安全计算。

金融

计算机

隐私保护

环境、资源等其他方面

RSA

对RSA的攻击:

AES

DES

RC4

流密码 , 一次一个字节地加密消息 ,速度快。

1687250986520

对RC4的攻击:

ECC

多态字符串混淆、

基于哈希的导入解析

运行时常量计算

Zhang R, Che T, Ghahramani Z, et al. Metagan: An adversarial approach to few-shot learning[J]. Advances in neural information processing systems, 2018, 31. pdf

基础知识

1、小样本情况下,人利用经验和先前的知识学习任务,而传统机器学习不使用先前知识容易过拟合。

2.元学习是一种利用先前知识的技术,从相似任务分布上训练提取一种可迁移的模式。

3.半监督小样本学习:有标签的样本是很少,但是无标签的样本也可以获取的到。

问题挑战

半监督小样本分类的元学习

难以从小样本里获得可泛化的决策边界。

解决办法

核心思路:假样本可以帮助小样本分类器学到更陡峭的边界。真假决策边界是如何帮助小样本分类的???

思路来源

小样本学习很像半监督学习。修改GAN在半监督学习领域的应用,将其应用到小样本场景下。

1.两者都没有足够数量的有标签样本。

2.都能从不完美的生成器获益。

创新点

对RN和MAML,叠加MetaGAN,让准确率,上升2到3个百分点。

实验

1.数据集

Omniglot Mini-Imagenet

2.实验内容

在2个数据集上,做3类任务,实验设置有5-way 1-shot,5-way 5-shot,20-way 1-shot,20-way 5-shot。加粗部分在3类任务上都有做。

2.1 监督小样本学习

2.2样本级的半监督小样本学习

允许在任务中有一些未标记的训练样本,来自与标签相同类或者干扰类(是二者择一,还是都选???)。

2.3任务级的半监督小样本学习

更允许无监督学习,即在support Set里和Query Set里都不加标签。

未来研究方向