整体思路和切入点
看标题大概就能猜出来是Go语言
IDA加载进去看,还是没符号的Go语言逆向……
之前做到有符号的是从main.main函数入手,现在连符号都没有,只好从字符串突破了
运行发现有输入提示“Enter pass:“和错误提示"Wrong”
Shift+F12的字符串检索中没有出现
这里要知道与C语言用’\0’表示字符串结束不同,Go语言会将所有字符串连接在一起,通过起始指针和字符串长度来表示整个串
而IDA无论从之后再说的传参和返回值,还是字符串检索功能都针对的是C语言,对于不按套路的Go语言局限很多
字符串检索失败就是一点
这种超长字符串虽然会被检索到,但是搜索功能和显示功能都仅能表示前边的部分,对于index稍大,例如达到上百的时候甚至上千的时候就无能为力了
因此只能使用菜单栏中的Search-Text或者Sequence of bytes来搜索
Text会根据交叉引用来查找,在稍等片刻以后应该就能找到
而Sequence of bytes则是根据字节流,将"Enter pass:"转成hex形式的字节"45 6e 74 65 72 20 70 61 73 73"进行搜素,秒出。不过双击过去会跳到整个字符串变量的开头,如果还需要具体值的话还得首先undefine这个串,然后重新双击找到0x534aa5,再Ctrl+A生成字符串即可查看交叉引用
总之最后会找到
00000000004EBE73 lea rcx, hint_input
这一行,而往下几句的0x4EBEA0处就有一个call
动态调试即可确认它就是print函数,然后通过这个交叉引用还能找到一个关键函数doServerStuff
,之后再说
接下来的东西都我都是依靠汇编级的动态调试来猜
一来由于Go语言传参和返回值都是mov 到栈中,不再有栈帧这个概念,虽然可以节省资源,但是IDA并不认识233
并且最关键的一点事,IDA目前并不支持寄存器传返回值,尤其是多个返回值
所以函数分析基本没用,不幸中的万幸是Go语言目前每个函数的传参和提取返回值都很有规律,非常利于动态调试
大多数函数都是这样
.text:00000000004EBF1E mov [rsp+198h+var_198], rax
.text:00000000004EBF22 mov [rsp+198h+var_190], rcx
.text:00000000004EBF27 mov [rsp+198h+var_188], rdx
.text:00000000004EBF2C call sub_4EC0D0
.text:00000000004EBF31 mov rax, [rsp+198h+var_180]
.text:00000000004EBF36 mov rcx, [rsp+198h+var_178]
.text:00000000004EBF3B mov rdx, [rsp+198h+var_170]
.text:00000000004EBF40 mov rbx, [rsp+198h+va
上面三个参数分别通过rax, rcx, rdx送入栈中
调用完函数后再用rax,rcx,rdx,rbx从栈中取出返回值
有时参数过多或者寄存器有他用时会复用寄存器,但大差不离
动态调试的时候即可在函数前断下依次查看参数,调用完之后待取出返回值再依次查看返回值,从而黑箱猜功能
黑箱调试的思路就是遇到call先F8,如果能直接根据输入和输出猜出功能,或者似乎无事发生,就不用跟进去了。如果产生了不知道怎么来的结果,那么首先调整输入,看是否该结果由输入产生,如果是则跟进去,不是则忽视。
主线程
于是继刚才的print之后继续往后走,后一个call是scanf接收输入
.text:00000000004EBEDA call scanf ; high->low, error, len, pointer
.text:00000000004EBEDF mov rax, [rsp+198h+var_180]
.text:00000000004EBEE4 mov rcx, [rsp+198h+var_178]
.text:00000000004EBEE9 mov rdx, [rsp+198h+var_188]
.text:00000000004EBEEE test rcx, rcx
返回