逆向一些常识

__stdcall、__cdecl和__fastcall是三种函数调用协议,函数调用协议会影响函数参数的入栈方式、栈内数据的清除方式、编译器函数名的修饰规则等。

调用协议常用场合

__stdcall:Windows API默认的函数调用协议。
__cdecl:C/C++默认的函数调用协议。
__fastcall:适用于对性能要求较高的场合。

函数参数入栈方式

__stdcall:函数参数由右向左入栈。
__cdecl:函数参数由右向左入栈。
__fastcall:从左开始不大于4字节的参数放入CPU的ECX和EDX寄存器,其余参数从右向左入栈。

栈内数据清除方式

__stdcall:函数调用结束后由被调用函数清除栈内数据。
__cdecl:函数调用结束后由函数调用者清除栈内数据。
__fastcall:函数调用结束后由被调用函数清除栈内数据。

函数调用方式例子:

●printf 是什么方式?
答:C方式,_cdecl,CALL完之后在函数外面平衡参数的堆栈

●MessageBoxW是什么方式?
答:_stdcall方式,CALL内部平衡堆栈

2赞

当我们分析一个东西的时候,看到都是什么sub_xxxxxx什么什么,要不就是ida没给你分析出来什么东西,要不染就是这个样本可能是MFC、SDK、Delphi或者是其他什么编写的,用了其他什么库函数。
这时候你就需要使用ida签名文件,识别库函数。

如何操作?

view->open subviews->Signatures->右键->Apply new signature

如何判断样本是否开启ASLR?

什么是ASLR,Address space layout randomization,地址随机化

什么时候会用到?

ida静态配合dbg动态调试会用到,保持ida和dbg的基地址一致

怎么判断是否开启ASLR?

第一种

查看标志位,一般如果开启了ASLR在PE段会多一个.reloc
image

第二种

使用010editor查看IMAGE_NT_HEADER——>IMAGE_OPTIONAL_HEADER——>DLL_CHARACTERISTICS字段值,如果开启了value的为1


将1改成0,保存在进行调试就可以了

小姿势

关键用大标题 /滑稽,进入正文


最近分析的很多样本都是go的,拿到样本之后放到ida里几乎什么也分析不出来,就很懵,通过后续了解,是因为ida本身是没有go的函数库的,所以我们需要导入go的函数库,是个大佬的写的ida的插件


项目地址

使用以及安装方法

git clone之后,将GO_Utilsgo_entry.py移动到idapython
然后重启idaFile——>Script Command,载入插件


然后点击run

看以上图片 ,第一步第二步是检测样本go版本,检测结果会输出到OUTPUT Windows

基本就识别出来了,function names对应的函数名也会相应的显示出来

除了以上这个IDAGolangHelper这项目,github上还有其他类似的项目


对于go的样本分析的思路

判断哪些是传入参数,那些是返回值

直接看函数末尾,看看有没有返回值arg_xx,如果有返回值,除了在末尾出现的就是传入参数

go的入口函数判断

一般情况下,我们将样本投入ida之后,分析完会自动停在一个入口函数处
对于go样本,64位的PE文件,通常会自动跳转到_rt0_amd64_windows
64位的elf文件,通常会自动跳转到_t0_amd64_linux
但,_rt0_amd64_xxxx并不是真正的go程序入口函数,因为它是go runtime的入口函数

什么是go runtime
我个人理解可能是调度器或者标准库,具体可以查看
https://zhuanlan.zhihu.com/p/95056679

真正的go程序逻辑入口是main.main(),但在main.main()函数启动之前还有个函数init(),它会在main.main()函数之前执行

init()函数特点

  1. 不能被其他函数调用,这里我们可以按x,查看交叉引用情况
  2. 该函数没有输入参数和返回值
  3. 每个包可能有多个init函数
  4. 包的每个源文件可以有多个init函数
  5. 简单理解就是,导入包依赖关系决定init函数执行顺序(官方解释,不同包的init函数,按照导入包的依赖关系决定init函数的执行顺序)
  6. init函数在main执行之前,会由runtime来调用

前几天写了一个go 的加载过程,分析过程不明白的可以先去看一下,写的算是很详细

挺有道理,虽然看不懂

关于go逆向的坑

我们在使用ida去逆向东西的时候,有时候ida分析的时候会把部分代码当成数据解析,就是分布条中灰色的部分


就像图片中灰色的块,我们可以跳过去,将数据转换成db
然后再去用idagolanghelp去renamefunctions就可以正确识别了
——————
多亏大佬指点,这个坑又踩了好久

汇编基础

什么是栈

栈又叫做堆栈,是一种数据结构,一种结构表,是暂时存储数据的地方
详细的看百度
https://baike.baidu.com/item/栈/12808149?fr=aladdin

堆栈怎么用

程序运行需要在内存中产生数据,或者使用内存中的数据,但由于可用的寄存器很少,我需要把数据存放在堆栈中
程序想要高效运行就需要快速的找到存储堆栈的位置,需要一个固定的内存地址,通常叫做栈低
堆栈写入(PUSH)遵循先入后出(POP)的原则,从顶上开始写,图示如下


这时候,如果我们函数有使用局部变量,因为局部变量都是临时存储的,这时候你每次调用都需要往内存里写,而且写完了也不会删除,就会造成内存浪费,正好对堆栈应运而生

什么是栈平衡

官方的解释

如果要返回父程序,则当我们在堆栈中进行堆栈操作的时候,一定要保证在RET这条指令之前,ESP指向的是压入的我们压入的地址

大白话就是,我们比如要调用一个函数,这个函数使用堆栈来传递变量或者是函数的返回地址或者是局部变量,在函数执行到ret时候地址时必须要和ESP对应的压入地址保持一致,如果你ESP压入的地址和你ret返回的地址不一样就会导致找不到数据或者数据出错从而程序崩溃
一般我们把EBP作为堆栈的栈低,为什么把EBP作为栈低,因为EBP在程序中起着保存数据的基址的作用,但在汇编中,函数需要的数据会在运行前入栈,此时EBP就没什么意义,所以我们用它来保存栈低

关于IDA的交叉引用(XREF)

我们经常在IDA里边,在某个函数出按X打开交叉引用


runtime_cgoinstall就是被引用者,注释后边就是CODE XREF:runtime_main+1AD↓p就是引用者

交叉引用分类

  • 代码交叉引用(CODE XERF)
  • 数据交叉引用(DATA XERF)

关于代码交叉引用(CODE XERF)

代码交叉引用,用于表示一条指令将控制权转交给另一条指令,这种指令转交控制权的防水叫做流(flow)。
概念上说的很高大上比较落地的就是,我们在IDA中看交叉引用实际上就是看调用关系,分析它是怎么一个逻辑,去干了什么。

流分为几种

普通流

普通流表示由一条指令到另一条指令到顺序流,简单理解就是指令默认连续执行

调用流

调用流表示当前指令会调用一个函数,字面意思就是,调用函数,跳转到某个函数
我们在上边看到的CODE XREF:runtime_main+1AD↓p中的后缀↓p就是调用,箭头向下代表被引用者的地址比引用者地址高,简单点说就是在被调用函数的后边,那个p就是procedure,箭头向下就是在被调用函数前边

跳转流

跳转流表示当前指令跳转到或者可能跳转到某个连续的位置,字面意思就是即将跳转到某个函数
如图所示,跳转流一般会有两个箭头,满足条件跳转,否则继续执行,跳转就是绿色,继续执行就是红色


跳转流后缀是j↓,就是jump

关于数据交叉引用(DATA XERF)

读取交叉引用

意思就是某个地址什么时候被读取了,这个地址被读取的是某一段代码,后缀就是↓r

写入交叉引用

意思就是某个地址什么时候被写入了,这个地址被某个函数在什么位置写入,后缀是↓w

偏移量交叉引用

意思就是某个地址什么时候被引用了,这个地址被某个函数在什么位置引用,后缀是↓o


服务器资源由ZeptoVM赞助

Partners Wiki IRC