首页
社区
课程
招聘
[原创]Go解析
发表于: 2022-4-12 22:25 21190

[原创]Go解析

2022-4-12 22:25
21190

闲谈: 在IDA7.5有插件支持反编译Go, IDA7.6已经支持的情况下, 为什么写这篇文章呢, 就算能反编译, 也是需要对照汇编的, 对于反编译出来的很多函数, 在调试时也要很快的知道哪些才是真正存储数据的地方, 增加分析效率.

https://github.com/golang/go

这里简单提一下main_main的查找
*有符号,直接ctrl+f,IDA中搜索main_main即可

*无符号:

runtime_main调用main_main

runtime_mainIDA反编译代码参考

可以利用查找那些runtime_main中的字符串在无符号的Go程序中找到runtime_main,从而找到main_main

图片描述

notice:

本文的数据均在 64位 环境下得出.

可关掉编译优化

图片描述

图片描述
Go程序会将字符串全部存放到一块连续的内存当中,使用的时候会利用runtime_convTstring来构造真正的字符串内存格式

图片描述

数组有三种存储位置,当数组内元素较少时可以直接存于栈上,较多时存于数据区,而当数据会被返回时会存于堆上

变量a的汇编如下,它直接在栈上定义并初始化:

图片描述

变量b的汇编如下,它的初始值被定义在了数据段并进行拷贝初始化:

图片描述

数据拷贝函数

图片描述

变量c的汇编如下,尽管它和a的值一样,但是它的地址是runtime.newobject函数新返回的,该函数传入的是数据的类型指针,它将在堆上申请空间存放对象实例,返回的是新的对象指针:

图片描述

经常会遇到的情况是返回一个结构体变量,然后将其赋值给newobject申请的新变量上。
下面也是这类情况

图片描述
通常会有一个地址作为runtime_newobject的参数,这个地址存放的是我们将要构造的数组的总大小,比如这里qword_234820地址处存放的就是40(5 * 8)

runtime_newobject里面会调用runtime_mallocgc分配内存

再之后会将runtime_newobject返回的指向这块内存的指针,存放到当前函数栈中的一个局部变量中,以后对这个数组的操作都是通过这个局部变量来的,比如这里的rsp+168

再下方就是对那块内存赋值, 也就是对数组初始化

图片描述

一个slice是一个数组某个部分的引用。

在内存中,它是一个包含3个域的结构体:

这里解释一下长度和容量的概念:

长度:下标操作的上界,如x[i]中i必须小于长度

容量:分割操作的上界,如x[i:j]中j不能大于容量

图片描述
数组的slice并不会实际复制一份数据,它只是创建一个新的数据结构,包含了另外的一个指针,一个长度和一个容量数据。

比如上面的分割表达式x[1:3]并不分配更多的数据:它只是写了一个新的slice结构的属性来引用相同的存储数据。

在例子中,长度为2,只有y[0]和y[1]是有效的索引,但是容量为4,y[0:4]是一个有效的分割表达式。

图片描述
这里先创建一个数组,然后rax存放的是构造好的数组的首地址,add rax, 8之后就会指向数组的下标1对应的元素,也就是3存放的地址,也就是我们切片[1:3]的第一个元素的指针,之后

.text:00000000004A79A8 mov [rsp], rax
.text:00000000004A79AC mov qword ptr [rsp+8], 2
.text:00000000004A79B5 mov qword ptr [rsp+16], 4

这里就在内存中构造出来了一个切片的结构

调用了runtime_convTslice,返回值是一个指向新分配的内存的指针,这个新分配的内存存放的元素数量就是切片的容量,比如这里就存放了4个元素,[3, 5, 7, 11] (从切片的那个元素开始,往后取容量那么多个),所以这里就是为什么y[0:4]是一个有效的分割表达式。

图片描述

slice := make([]int, len)

图片描述
make的底层会调用runtime_makeslice分配Array,真正的切片结构是后面的runtime_convTslice时创建的

makeslice返回的是一个指向实际数据的指针(不含管理slice的结构体)相当于 malloc(sizeof(Type) * len)

在访问slice中元素时,一般会检测下标是否小于len,如果越界则调用runtime_panicIndex

append 的时候会检测目标 slice1.len + 1 与 slice1.cap 的大小关系

若 slice1.len + 1 > slice1.cap 则调用runtime_growslice扩容

在对slice进行append等操作时,可能会造成slice的自动扩容。其扩容时的大小增长规则是:

copy 就是复制一个新的切片

myvar的数据结构是新一个新的切片 struct.

Go语言的map使用Hash表作为底层实现,可以在 $GOROOT/src/pkg/runtime/hashmap.goc 找到它的实现,其中 [runtime.hmap](https://draveness.me/golang/tree/runtime.hmap)是最核心的结构体,我们先来了解一下该结构体的内部字段:

这个hash结构使用的是一个可扩展哈希的算法,由hash值mod当前hash表大小决定某一个值属于哪个桶,而hash表大小是2的指数,即上面结构体中的2^B。每次扩容,会增大到上次大小的两倍。结构体中有一个buckets和一个oldbuckets是用来实现增量扩容的。正常情况下直接使用buckets,而oldbuckets为空。如果当前哈希表正在扩容中,则oldbuckets不为空,并且buckets大小是oldbuckets大小的两倍。

具体的Bucket结构如下所示:

一般fastrand和makemap连用返回一个map,它为一个指针

读字典时使用mapaccess1和mapaccess2,写字典时会使用mapassign函数,它返回一个地址,将value写入该地址,另外还比较常见的是对字典进行遍历,会使用mapiterinit和mapiternext配合。

mapaccess和mapassign的第一个参数代表字典的类型,因此能很容易知道字典操作参数和返回值的类型。

赋值

3个参数,第三个参数是key的指针,rsp + 16, 返回值是key对应的数据指针.

访问

和赋值同理,区别是这个不会为不存在的 key 创建 key pair.

图片描述
比如上方在执行完
图片描述
之后,map buckets是存放了一个键值对的,可以过去查看v5(h *hmap) ,0x000000C00014FE38来查看

图片描述
可以看见buckets unsafe.Pointer为byte_C00014FE68

图片描述
前面8字节的是tophash数组,目前只存放了一个键值对,就只有一个存放了,hash(”France”)的高8bit,往下就是存放键值对

这里一个细节需要注意一下。不认真看可能会以为低位用于定位bucket在数组的index,那么高位就是用于key/valule在bucket内部的offset。事实上高8位不是用作offset的,而是用于加快key的比较的。

这里也有几个细节需要注意一下。

在扩容过程中,oldbucket是被冻结的,查找时会在oldbucket中查找,但不会在oldbucket中插入数据。如果在oldbucket是找到了相应的key,做法是将它迁移到新bucket后加入evalucated标记。并且还会额外的迁移另一个pair。

然后就是只要在某个bucket中找到第一个空位,就会将key/value插入到这个位置。也就是位置位于bucket前面的会覆盖后面的(类似于存储系统设计中做删除时的常用的技巧之一,直接用新数据追加方式写,新版本数据覆盖老版本数据)。找到了相同的key或者找到第一个空位就可以结束遍历了。不过这也意味着做删除时必须完全的遍历bucket所有溢出链,将所有的相同key数据都删除。所以目前map的设计是为插入而优化的,删除效率会比插入低一些。

删除元素实际上也是先查找元素,如果元素存在则把元素从相应的bucket中删除,如果不存在则什么也不做。

notice

Go调用汇编和C:https://tiancaiamao.gitbooks.io/go-internals/content/zh/03.1.html

在讲解函数调用之前,我们讲解一下GMP结构体

并发:一个逻辑流的执行在时间上与另一个流重叠,叫并发流,多个流并发地执行被成为并发。

并行:如果两个流并发地运行在不同的处理器核或者计算机上,那么我们成为它们为并行地执行。

并行流是并发流的真子集。

多线程或多进程是并行的基本条件,但单线程也可以用协程(coroutine)做到并发。简单将Goroutine归纳为协程并不合适,因为它运行时会创建多个线程来执行并发任务,且任务单元可被调度到其它线程执行。这更像是多线程和协程的结合体,能最大限度提升执行效率,发挥多核处理器能力。

Go的并发实现非常简单,使用一个go关键字即可

Go语言虽然使用一个Go关键字即可实现并发编程,但Goroutine被调度到后端之后,具体的实现比较复杂。Go调度器组成。

G是Goroutine的缩写,相当于操作系统中的进程控制块,在这里就是Goroutine的控制结构,是对Goroutine的抽象。其中包括执行的函数指令及参数;G保存的任务对象;线程上下文切换,现场保护和现场恢复需要的寄存器(SP、IP)等信息。

stack 描述了当前 goroutine 的栈内存范围[stack.lo, stack.hi),其中 stack 的数据结构:

stackguard0 和 stackguard1 均是一个栈指针,用于扩容场景,前者用于 Go stack ,后者用于 C stack。

M是一个线程或称为Machine,所有M是有线程栈的。如果不对该线程栈提供内存的话,系统会给该线程栈提供内存(不同操作系统提供的线程栈大小不同)。当指定了线程栈,则M.stack→G.stack,M的PC寄存器指向G提供的函数,然后去执行。

P(Processor)是一个抽象的概念,并不是真正的物理CPU。所以当P有任务时需要创建或者唤醒一个系统线程来执行它队列里的任务。所以P/M需要进行绑定,构成一个执行单元。

P决定了同时可以并发任务的数量,可通过GOMAXPROCS限制同时执行用户级任务的操作系统线程。可以通过runtime.GOMAXPROCS进行指定。在Go1.5之后GOMAXPROCS被默认设置可用的核数,而之前则默认为1。

首先创建一个G对象,G对象保存到P本地队列或者是全局队列。

P此时去唤醒一个M。P继续执行它的执行序。

M寻找是否有空闲的P,如果有则将该G对象移动到它本身。

接下来M执行一个调度循环(调用G对象->执行->清理线程→继续找新的Goroutine执行)。

M执行过程中,随时会发生上下文切换。

当发生上下文切换时,需要对执行现场进行保护,以便下次被调度执行时进行现场恢复。

Go调度器M的栈保存在G对象上,只需要将M所需要的寄存器(SP、PC等)保存到G对象上就可以实现现场保护。当这些寄存器数据被保护起来,就随时可以做上下文切换了,在中断之前把现场保存起来。如果此时G任务还没有执行完,M可以将任务重新丢到P的任务队列,等待下一次被调度执行。当再次被调度执行时,M通过访问G的vdsoSP、vdsoPC寄存器进行现场恢复(从上次中断位置继续执行)。

后面分析函数的开头和结尾的时候可以对照。

Go语言支持goroutine,每个goroutine需要能够运行,所以它们都有自己的栈。假如每个goroutine分配固定栈大小并且不能增长,太小则会导致溢出,太大又会浪费空间,无法存在许多的goroutine。

为了解决这个问题,goroutine可以初始时只给栈分配很小的空间,然后随着使用过程中的需要自动地增长。这就是为什么Go可以开千千万万个goroutine而不会耗尽内存。

Go1.3版本之后则使用的是continuous stack,下面将具体分析一下这种技术。

每次执行函数调用时Go的runtime都会进行检测,若当前栈的大小不够用,则会触发“中断”,从当前函数进入到Go的运行时库,Go的运行时库会保存此时的函数上下文环境,然后分配一个新的足够大的栈空间,将旧栈的内容拷贝到新栈中,并做一些设置,使得当函数恢复运行时,函数会在新分配的栈中继续执行,仿佛整个过程都没发生过一样,这个函数会觉得自己使用的是一块大小“无限”的栈空间。

图片描述
图片描述
Go语言和C不同,不是使用栈指针寄存器和栈基址寄存器确定函数的栈的。

在Go的运行时库中,每个goroutine对应一个结构体G,大致相当于进程控制块的概念。

这个结构体中存了stackbase和stackguard,用于确定这个goroutine使用的栈空间信息。

每个Go函数调用的前几条指令,先比较栈指针寄存器跟g->stackguard(偏移0x10),检测是否发生栈溢出。

如果栈指针寄存器值超越了stackguard就需要扩展栈空间。

函数开头首先gs:0x28,获取到了g结构体地址

后面的g+0x10也就是g->stackguard的地址

所以是把rsp和g->stackguard的值比较

如果SP大于g->stackguard了,则会跳转到函数结尾调用runtime.morestack函数。函数开头几条指令的作用就是检测栈是否溢出。

runtime.morestack作用:

将一些信息存在M结构体中,这些信息包括当前栈桢,参数,当前函数调用,函数返回地址(两个返回地址,一个是runtime.morestack的函数地址,一个是f的返回地址)。通过这些信息可以把新栈和旧栈链起来。

无论是 x86 还是 x86-64, 都采用栈传递参数

返回值传递不通过eax/rax等寄存器,也是通过栈。

位置是最后一个参数的下面。例如最后一个参数的地址是 rsp + 0x8, 则:

图片描述

图片描述
返回值都会存放到main函数的栈中,main的局部变量

图片描述
golang 的一种垃圾回收机制,逆向时可以认为这个表达式永真即可,不用关注runtime_gcWriteBarrier 分支
参考:

https://tiancaiamao.gitbooks.io/go-internals/content/zh

https://blog.csdn.net/star_of_science/article/details/121802354
https://panda0s.top/2021/04/14/Golang-underlying-data-representaion/#Golang-underlying-data-representaion

https://draveness.me/golang/docs/part2-foundation/ch03-datastructure/golang-hashmap/

https://tiancaiamao.gitbooks.io/go-internals/content/zh/03.1.html
https://blog.csdn.net/further_eye/article/details/112506505#t18
https://www.1024sou.com/article/287980.html

https://segmentfault.com/a/1190000040933033

 
void runtime_main()
{
  PVOID ArbitraryUserPointer; // rax
  __int64 v1; // rdx
  __int64 i; // rax
  __int64 v3; // [rsp+0h] [rbp-50h]
  __int64 v4; // [rsp+0h] [rbp-50h]
  __int64 v5; // [rsp+0h] [rbp-50h]
  __int64 v6; // [rsp+10h] [rbp-40h]
  __int64 v7; // [rsp+20h] [rbp-30h] BYREF
  __int64 v8; // [rsp+28h] [rbp-28h]
  __int64 v9; // [rsp+30h] [rbp-20h]
  __int128 v10; // [rsp+38h] [rbp-18h]
  void *retaddr; // [rsp+50h] [rbp+0h] BYREF
 
  while ( (unsigned __int64)&retaddr <= *(_QWORD *)(*(_QWORD *)NtCurrentTeb()->NtTib.ArbitraryUserPointer + 16LL) )
    runtime_morestack_noctxt();
  v10 = 0LL;
  HIBYTE(v7) = 0;
  v9 = *(_QWORD *)NtCurrentTeb()->NtTib.ArbitraryUserPointer;
  *(_QWORD *)(**(_QWORD **)(v9 + 48) + 304LL) = 0LL;
  runtime_maxstacksize = 1000000000LL;
  runtime_maxstackceiling = 2000000000LL;
  runtime_mainStarted = 1;
  _InterlockedExchange((volatile __int32 *)&unk_5683A0, 1);
  runtime_systemstack((__int64)&off_4D6020);
  ArbitraryUserPointer = NtCurrentTeb()->NtTib.ArbitraryUserPointer;
  ++*(_DWORD *)(*(_QWORD *)(*(_QWORD *)ArbitraryUserPointer + 48LL) + 580LL);
  v1 = *(_QWORD *)NtCurrentTeb()->NtTib.ArbitraryUserPointer;
  *(_QWORD *)(*(_QWORD *)(v1 + 48) + 312LL) = v1;
  *(_QWORD *)(v1 + 216) = *(_QWORD *)(v1 + 48);
  if ( *(_UNKNOWN **)(v9 + 48) != &runtime_m0 )
    goto LABEL_28;
  byte_568678 = 1;
  runtime_nanotime1(v3);
  runtime_runtimeInitTime = v4;
  if ( !v4 )
  {
LABEL_27:
    runtime_throw((__int64)"nanotime returning zero", 23LL);
LABEL_28:
    runtime_throw((__int64)"runtime.main not on m0", 22LL);
    runtime_deferreturn(v5);
    return;
  }
  if ( dword_5AB048 )
  {
    qword_5AAE88 = *(_QWORD *)(*(_QWORD *)NtCurrentTeb()->NtTib.ArbitraryUserPointer + 152LL);
    runtime_inittrace = 1;
  }
  runtime_doInit((__int64)&runtime__inittask);
  HIWORD(v7) = 257;
  *((_QWORD *)&v10 + 1) = &off_4D6028;
  *(_QWORD *)&v10 = (char *)&v7 + 6;
  runtime_gcenable();
  v6 = runtime_makechan((__int64)&unk_4B2580, 0LL);
  if ( runtime_writeBarrier )
    runtime_gcWriteBarrier();
  else
    runtime_main_init_done = v6;
  if ( !runtime_iscgo )
    goto LABEL_12;
  if ( !cgo_thread_start )
  {
LABEL_26:
    runtime_throw((__int64)"_cgo_thread_start missing", 25LL);
    goto LABEL_27;
  }
  if ( !cgo_notify_runtime_init_done )
  {
    runtime_throw((__int64)"_cgo_notify_runtime_init_done missing", 37LL);
    goto LABEL_26;
  }
  runtime_startTemplateThread();
  runtime_cgocall(cgo_notify_runtime_init_done, 0LL, v6);
LABEL_12:
  runtime_doInit((__int64)&main__inittask);
  runtime_inittrace = 0;
  runtime_closechan(runtime_main_init_done);
  BYTE6(v7) = 0;
  runtime_unlockOSThread();
  if ( !runtime_isarchive && !runtime_islibrary )
  {
    main_main();
    if ( runtime_runningPanicDefers )
    {
      for ( i = 0LL; i < 1000 && runtime_runningPanicDefers; i = v8 + 1 )
      {
        v8 = i;
        runtime_mcall();
      }
    }
    if ( runtime_panicking )
      v7 = runtime_gopark(0LL, 0LL, 4104, 1LL);
    runtime_exit(0);
    while ( 1 )
      MEMORY[0] = 0;
  }
  HIBYTE(v7) = 0;
  runtime_main_func2(v10);
}
void runtime_main()
{
  PVOID ArbitraryUserPointer; // rax
  __int64 v1; // rdx
  __int64 i; // rax
  __int64 v3; // [rsp+0h] [rbp-50h]
  __int64 v4; // [rsp+0h] [rbp-50h]
  __int64 v5; // [rsp+0h] [rbp-50h]
  __int64 v6; // [rsp+10h] [rbp-40h]
  __int64 v7; // [rsp+20h] [rbp-30h] BYREF
  __int64 v8; // [rsp+28h] [rbp-28h]
  __int64 v9; // [rsp+30h] [rbp-20h]
  __int128 v10; // [rsp+38h] [rbp-18h]
  void *retaddr; // [rsp+50h] [rbp+0h] BYREF
 
  while ( (unsigned __int64)&retaddr <= *(_QWORD *)(*(_QWORD *)NtCurrentTeb()->NtTib.ArbitraryUserPointer + 16LL) )
    runtime_morestack_noctxt();
  v10 = 0LL;
  HIBYTE(v7) = 0;
  v9 = *(_QWORD *)NtCurrentTeb()->NtTib.ArbitraryUserPointer;
  *(_QWORD *)(**(_QWORD **)(v9 + 48) + 304LL) = 0LL;
  runtime_maxstacksize = 1000000000LL;
  runtime_maxstackceiling = 2000000000LL;
  runtime_mainStarted = 1;
  _InterlockedExchange((volatile __int32 *)&unk_5683A0, 1);
  runtime_systemstack((__int64)&off_4D6020);
  ArbitraryUserPointer = NtCurrentTeb()->NtTib.ArbitraryUserPointer;
  ++*(_DWORD *)(*(_QWORD *)(*(_QWORD *)ArbitraryUserPointer + 48LL) + 580LL);
  v1 = *(_QWORD *)NtCurrentTeb()->NtTib.ArbitraryUserPointer;
  *(_QWORD *)(*(_QWORD *)(v1 + 48) + 312LL) = v1;
  *(_QWORD *)(v1 + 216) = *(_QWORD *)(v1 + 48);
  if ( *(_UNKNOWN **)(v9 + 48) != &runtime_m0 )
    goto LABEL_28;
  byte_568678 = 1;
  runtime_nanotime1(v3);
  runtime_runtimeInitTime = v4;
  if ( !v4 )
  {
LABEL_27:
    runtime_throw((__int64)"nanotime returning zero", 23LL);
LABEL_28:
    runtime_throw((__int64)"runtime.main not on m0", 22LL);
    runtime_deferreturn(v5);
    return;
  }
  if ( dword_5AB048 )
  {
    qword_5AAE88 = *(_QWORD *)(*(_QWORD *)NtCurrentTeb()->NtTib.ArbitraryUserPointer + 152LL);
    runtime_inittrace = 1;
  }
  runtime_doInit((__int64)&runtime__inittask);
  HIWORD(v7) = 257;
  *((_QWORD *)&v10 + 1) = &off_4D6028;
  *(_QWORD *)&v10 = (char *)&v7 + 6;
  runtime_gcenable();
  v6 = runtime_makechan((__int64)&unk_4B2580, 0LL);
  if ( runtime_writeBarrier )
    runtime_gcWriteBarrier();
  else
    runtime_main_init_done = v6;
  if ( !runtime_iscgo )
    goto LABEL_12;
  if ( !cgo_thread_start )
  {
LABEL_26:
    runtime_throw((__int64)"_cgo_thread_start missing", 25LL);
    goto LABEL_27;
  }
  if ( !cgo_notify_runtime_init_done )
  {
    runtime_throw((__int64)"_cgo_notify_runtime_init_done missing", 37LL);
    goto LABEL_26;
  }
  runtime_startTemplateThread();
  runtime_cgocall(cgo_notify_runtime_init_done, 0LL, v6);
LABEL_12:
  runtime_doInit((__int64)&main__inittask);
  runtime_inittrace = 0;
  runtime_closechan(runtime_main_init_done);
  BYTE6(v7) = 0;
  runtime_unlockOSThread();
  if ( !runtime_isarchive && !runtime_islibrary )
  {
    main_main();
    if ( runtime_runningPanicDefers )
    {
      for ( i = 0LL; i < 1000 && runtime_runningPanicDefers; i = v8 + 1 )
      {
        v8 = i;
        runtime_mcall();
      }
    }
    if ( runtime_panicking )
      v7 = runtime_gopark(0LL, 0LL, 4104, 1LL);
    runtime_exit(0);
    while ( 1 )
      MEMORY[0] = 0;
  }
  HIBYTE(v7) = 0;
  runtime_main_func2(v10);
}
 
 
 
-gcflags "-N -l"
 
go build -o hello.exe -gcflags "-N -l" hello.go
-gcflags "-N -l"
 
go build -o hello.exe -gcflags "-N -l" hello.go
struct String{
    char * strPtr;   //指向目标字符串的开始地址
    int64 size;      //字符串大小
}
struct String{
    char * strPtr;   //指向目标字符串的开始地址
    int64 size;      //字符串大小
}
func ArrDemo() *[3]int {
 
    a := [...]int{1, 2, 3}
 
    b := [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7}
 
    c := [...]int{1, 2, 3}
 
    if len(a) < len(b) {return &c}
 
    return nil
 
}
func ArrDemo() *[3]int {
 
    a := [...]int{1, 2, 3}
 
    b := [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7}
 
    c := [...]int{1, 2, 3}
 
    if len(a) < len(b) {return &c}
 
    return nil
 
}
type arrayHeader struct {
    Data uintptr       
    Len int
}
type arrayHeader struct {
    Data uintptr       
    Len int
}
 
 
 
 
 
 
 
x := []int{1, 2, 3, 4, 5}
x := []int{1, 2, 3, 4, 5}
 
 
 
y := []float32{1000.0, 2.0, 3.4, 7.0, 50.0}
y := []float32{1000.0, 2.0, 3.4, 7.0, 50.0}
 
{
    指向slice中第一个元素的指针
    slice的长度
    slice的容量
}
{
    指向slice中第一个元素的指针
    slice的长度
    slice的容量
}
 
 
 
 
 
 
 
 
 
 
slice1 := make([]int, 5)
slice1[3] = 66
slice1 := make([]int, 5)
slice1[3] = 66
// runtime_makeslice
func makeslice(et *_type, len, cap int) unsafe.Pointer {
    mem, overflow := math.MulUintptr(et.size, uintptr(cap))
    if overflow || mem > maxAlloc || len < 0 || len > cap {
        // NOTE: Produce a 'len out of range' error instead of a
        // 'cap out of range' error when someone does make([]T, bignumber).
        // 'cap out of range' is true too, but since the cap is only being
        // supplied implicitly, saying len is clearer.
        // See golang.org/issue/4085.
        mem, overflow := math.MulUintptr(et.size, uintptr(len))
        if overflow || mem > maxAlloc || len < 0 {
            panicmakeslicelen()
        }
        panicmakeslicecap()
    }
 
    return mallocgc(mem, et, true)
}
// runtime_makeslice
func makeslice(et *_type, len, cap int) unsafe.Pointer {
    mem, overflow := math.MulUintptr(et.size, uintptr(cap))
    if overflow || mem > maxAlloc || len < 0 || len > cap {
        // NOTE: Produce a 'len out of range' error instead of a
        // 'cap out of range' error when someone does make([]T, bignumber).
        // 'cap out of range' is true too, but since the cap is only being
        // supplied implicitly, saying len is clearer.
        // See golang.org/issue/4085.
        mem, overflow := math.MulUintptr(et.size, uintptr(len))
        if overflow || mem > maxAlloc || len < 0 {
            panicmakeslicelen()
        }
        panicmakeslicecap()
    }
 
    return mallocgc(mem, et, true)
}
 
slice1 = append(slice1, 123)
slice1 = append(slice1, 123)
 
 
myvar := slice1[1:3]
myvar := slice1[1:3]
type hmap struct {
    count     int     //map中键值对数量
    flags     uint8   //map当前是否处于写入状态登
    B         uint8   //2的B次幂表示当前map中桶的数量
    noverflow uint16  //map中溢出桶的数量,当溢出桶太多时,map会进行等量扩容
    hash0     uint32  //生成hash的随机数种子
 
    buckets    unsafe.Pointer  //当前map对应的桶的指针
    oldbuckets unsafe.Pointer  //map扩容时指向旧桶的指针,当所有旧桶中的数据转移到新桶时,清空
    nevacuate  uintptr         //扩容时,用于标记当前旧桶中小于nevacute的数据都已经转移到了新桶
 
    extra *mapextra            //存储map的溢出桶
}
 
type mapextra struct {
    overflow    *[]*bmap
    oldoverflow *[]*bmap
    nextOverflow *bmap
}
type hmap struct {
    count     int     //map中键值对数量
    flags     uint8   //map当前是否处于写入状态登
    B         uint8   //2的B次幂表示当前map中桶的数量
    noverflow uint16  //map中溢出桶的数量,当溢出桶太多时,map会进行等量扩容
    hash0     uint32  //生成hash的随机数种子
 
    buckets    unsafe.Pointer  //当前map对应的桶的指针
    oldbuckets unsafe.Pointer  //map扩容时指向旧桶的指针,当所有旧桶中的数据转移到新桶时,清空
    nevacuate  uintptr         //扩容时,用于标记当前旧桶中小于nevacute的数据都已经转移到了新桶
 
    extra *mapextra            //存储map的溢出桶
}
 
type mapextra struct {
    overflow    *[]*bmap
    oldoverflow *[]*bmap
    nextOverflow *bmap
}
 
type bmap struct {
    tophash [8]uint8 //存储Hash值的高8
    data []byte    //key value数据:key/key/key.../value/value/value...
    overflow *bmap    //溢出bucket的地址
}
type bmap struct {
    tophash [8]uint8 //存储Hash值的高8
    data []byte    //key value数据:key/key/key.../value/value/value...
    overflow *bmap    //溢出bucket的地址
}
 
func fastrand() uint32
 
func makemap(mapType *byte, hint int, mapbuf *any) (hmap map[any]any)
 
func mapaccess1(mapType *byte, hmap map[any]any, key *any) (val *any)
 
func mapaccess2(mapType *byte, hmap map[any]any, key *any) (val *any, pres bool)
 
func mapassign(mapType *byte, hmap map[any]any, key *any) (val *any)
 
func mapiterinit(mapType *byte, hmap map[any]any, hiter *any)
 
func mapiternext(hiter *any)
func fastrand() uint32
 
func makemap(mapType *byte, hint int, mapbuf *any) (hmap map[any]any)
 
func mapaccess1(mapType *byte, hmap map[any]any, key *any) (val *any)
 
func mapaccess2(mapType *byte, hmap map[any]any, key *any) (val *any, pres bool)
 
func mapassign(mapType *byte, hmap map[any]any, key *any) (val *any)
 
func mapiterinit(mapType *byte, hmap map[any]any, hiter *any)
 
func mapiternext(hiter *any)
func mapassign(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer{}
func mapassign(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer{}
 
func mapaccess1(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer {}
func mapaccess1(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer {}
package main
 
import "fmt"
 
func main() {
    countryCapitalMap := map[string]string{"France": "Paris", "Italy": "Rome", "Japan": "Tokyo", "India": "New delhi"}
 
    fmt.Println("France首都是", countryCapitalMap ["France"])
}
package main
 
import "fmt"
 
func main() {
    countryCapitalMap := map[string]string{"France": "Paris", "Italy": "Rome", "Japan": "Tokyo", "India": "New delhi"}
 
    fmt.Println("France首都是", countryCapitalMap ["France"])
}
 
 
package main
 
import "fmt"
 
func main() {
    var countryCapitalMap map[string]string /*创建集合, 默认map是nil*/
    //如果不初始化 map,那么就会创建一个 nil map, nil map 不能用来存放键值对
    countryCapitalMap = make(map[string]string)
 
    countryCapitalMap [ "France" ] = "巴黎"
    countryCapitalMap [ "Italy" ] = "罗马"
    countryCapitalMap [ "Japan" ] = "东京"
    countryCapitalMap [ "India " ] = "新德里"
 
    //或者:countryCapitalMap := map[string]string{"France": "Paris", "Italy": "Rome", "Japan": "Tokyo", "India": "New delhi"}
 
    /*使用键输出地图值 */
    for country := range countryCapitalMap {
        fmt.Println(country, "首都是", countryCapitalMap [country])
    }
}
package main
 
import "fmt"
 
func main() {
    var countryCapitalMap map[string]string /*创建集合, 默认map是nil*/
    //如果不初始化 map,那么就会创建一个 nil map, nil map 不能用来存放键值对
    countryCapitalMap = make(map[string]string)

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2022-4-17 13:42 被SYJ-Re编辑 ,原因: 更新
收藏
免费 17
支持
分享
打赏 + 100.00雪花
打赏次数 1 雪花 + 100.00
 
赞赏  Editor   +100.00 2022/05/05 恭喜您获得“雪花”奖励,安全圈有你而精彩!
最新回复 (9)
雪    币: 8745
活跃值: (5673)
能力值: ( LV13,RANK:296 )
在线值:
发帖
回帖
粉丝
2
mark
2022-4-13 09:32
0
雪    币: 2216
活跃值: (6558)
能力值: ( LV7,RANK:102 )
在线值:
发帖
回帖
粉丝
3
go语言确实挺不好逆向的,IDA代码很难阅读
2022-4-13 09:47
0
雪    币: 47147
活跃值: (20310)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
4
论坛上深度解析Go语言的帖子不错,感谢分享!
2022-4-13 15:19
0
雪    币: 13992
活跃值: (17371)
能力值: ( LV12,RANK:290 )
在线值:
发帖
回帖
粉丝
5
fjqisba go语言确实挺不好逆向的,IDA代码很难阅读
go不仅是不好逆向,正向也是不好写,环境配置就开始掉头发了
2022-4-15 16:17
0
雪    币: 229
活跃值: (130)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
kanxue 论坛上深度解析Go语言的帖子不错,感谢分享!

有点意思,求个查重率:https://mp.weixin.qq.com/s/CrmgqLwXUaR7Uccj_72f3g

最后于 2022-5-4 07:48 被Beta猫编辑 ,原因:
2022-5-4 07:47
0
雪    币: 3660
活跃值: (9330)
能力值: ( LV9,RANK:319 )
在线值:
发帖
回帖
粉丝
7
Beta猫 kanxue 论坛上深度解析Go语言的帖子不错,感谢分享! 有点意思,求个查重率:https://mp.weixin.qq.com/s/Crmg ...
可以,在数据结构里面是有引用内容的,也有自己实际去分析的内容,而且最后给出了参考链接
2022-5-5 22:29
0
雪    币: 3660
活跃值: (9330)
能力值: ( LV9,RANK:319 )
在线值:
发帖
回帖
粉丝
8

补充:

调用约定(新)

基于寄存器的参数传递AMD64 平台上,golang 使用如下9个寄存器用于整数参数和返回值的传递:

RAX, RBX, RCX, RDI, RSI, R8, R9, R10, R11

使用 X0 – X14 浮点寄存器来传递浮点数。

最后于 2022-7-14 18:49 被SYJ-Re编辑 ,原因:
2022-7-14 18:48
0
雪    币: 55
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
9
大佬,可否留个联系方式,有个luac的问题
2022-8-16 15:48
0
雪    币: 783
活跃值: (1121)
能力值: ( LV5,RANK:78 )
在线值:
发帖
回帖
粉丝
10
这个些内容比一个插件的作用要大很多.至少在插件无法使用的环境下.可以快速的肉眼识别函数.
2022-8-16 22:37
0
游客
登录 | 注册 方可回帖
返回
//