Paul C's Blog

To be funny,to grow up!

0%

Windows汇编语言基础

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