duangsuse::Echo
714 subscribers
4.24K photos
127 videos
583 files
6.46K links
import this:
美而不丑、明而不暗、短而不凡、长而不乱,扁平不宽,读而后码,行之天下,勿托地上天国。
异常勿吞,难过勿过,叹一真理。效率是很重要,盲目最是低效。
简明是可靠的先验,不是可靠的祭品。
知其变,守其恒,为天下式;穷其变,知不穷,得地上势。知变守恒却穷变知新,我认真理,我不认真。

技术相干订阅~
另外有 throws 闲杂频道 @dsuset
转载频道 @dsusep
极小可能会有批评zf的消息 如有不适可退出
suse小站(面向运气编程): https://WOJS.org/#/
Download Telegram
Radare2 反汇编出来的东西 size 居然是错的... emmmm 害得我又熬夜了这么久
duangsuse::Echo
Radare2 反汇编出来的东西 size 居然是错的... emmmm 害得我又熬夜了这么久
那个 ecx 偏移量运行时会取一个双字(long int)的(本地变量 [ebp-0xe])数据到寄存器,然而其实只给了它(这个本地变量) 4 字节的空间!结果就是它会把 0x0008 后面的 0x0064 也取到一起,加到 char *s 上面就是个无效偏移量 unmapped memory,直接段错误
duangsuse::Echo
Radare2 反汇编出来的东西 size 居然是错的... emmmm 害得我又熬夜了这么久
根本不是一个双字吧!就是一个字的数据而已
cooltok.zip
24.6 KB
第二次,明天就上学了 emmmm #life #school #GeekAPk #reveng #CoolApk
🤔 r2 明明知道一个 int 只有 word 那么长,为什么还要上 dword address 呢...
duangsuse::Echo
cooltok.zip
byte = 8
halfword = 16
word = 32
size = 32
dword = 64

byte char
size char *
dword double
word float
word int
halfword int16_t
word int32_t
dword int64_t
byte int8_t

dowrd long
dword long long
halfword short
size size_t

halfword uint16_t
word uint32_t
dword uint64_t
byte uint8_t

byte unsigned char
word unsigned int
halfword unsigned short
size void *
duangsuse::Echo
🤔 r2 明明知道一个 int 只有 word 那么长,为什么还要上 dword address 呢...
我没想到的是 i686 里寄存器看起来也有一个双字么... 🤔
我以为 32 位的意思是寄存器都是 32 位的,可是 edb 里却有 8 个 16 进制数位...
duangsuse::Echo
我没想到的是 i686 里寄存器看起来也有一个双字么... 🤔 我以为 32 位的意思是寄存器都是 32 位的,可是 edb 里却有 8 个 16 进制数位...
用 radare2 的其他反汇编模式看看,发现 64 位模式下只是 ebp 改成了 rbp,这... 🤔

我只是想上动态分析看汇编啊...
这显然是错误的,明明只分配了四个字的空间,却全用的是 dword,这根本不可能可以正常运行,存储位置全是冲突的!反汇编只是为了能让你看得懂机器代码而不可能简单地再重新汇编上吗?
这就是一个正确的栗子,它给栈帧分配的是两个 dword 的空间,而上面那个是没有完成分配的任务,居然只给 64 位的东西分配 32 位的空间,读写都会冲突
duangsuse::Echo
这就是一个正确的栗子,它给栈帧分配的是两个 dword 的空间,而上面那个是没有完成分配的任务,居然只给 64 位的东西分配 32 位的空间,读写都会冲突
执行时是怎么样的呢? 🤔(当然这里可能不严谨,不过也够了,反正大家很多人连 CDEF 系统栈是怎么维护的都不知道,也算是科普一下)

我们的 caller 叫做 main,它执行如下代码以调用我们的子程序 bd

sub esp, (2*4)
<sp> [....] [....] |*****
mov ecx, ; buffer
; ecx = (char *) buffer
mov [esp+4], "SGVsbG8K"
<sp> [....] [*"SGV....] |*****
mov [esp], ecx
<sp> [*buffer] [*"SGV....] |*****
call bd
; eip = *bd

然后机器开始解释执行我们的程序逻辑:

push ebp
<sp> [*bp@main] [return@main] [*buffer] [*"SGV....] |*****

mov ebp, esp
<sp><bp> [*bp@main] [return@main] [*buffer] [*"SGV....] |*****

push ebx
<sp> [bx@main] <bp> [*bp@main] [return@main] [*buffer] [*"SGV....] |*****

lea esp, [esp-(4*2 * 2)]
<sp> [........] [........] [bx@main] <bp> [*bp@main] [return@main] [*buffer] [*"SGV....] |*****

==
call __x86_get_pc_thunk_bx
<sp> [return@bd] [........] [........] [bx@main] <bp> [*bp@main] [return@main] [*buffer] [*"SGV....]
; eip = *__x86_get_pc_thunk_bx

mov ebx, dword [esp]
; ebx = (long) *<sp>
(其实就是拿到位于 .text 段的 ip 指针,也就是 .text+bd+???,GCC 4.9 拿这个 bx (就是当前函数位于的 call __x86_get_pc_thunk_bx 时的 eip)指针去算 .rodata 段的静态只读数据地址)
(我们要重新用汇编重写的时候必须也重写这种 ebx 偏移量,把对它们的使用替换成 .rodata 段实际的偏移地址,NASM 可以帮我们做这件事,直接 section .rodata 然后定义 byte/dword 静态数据指针就可以了)

ret
<sp> [........] [........] [bx@main] <bp> [*bp@main] [return@main] [*buffer] [*"SGV....]
; eip = *bd
; eax = *bd+5i

== 然后我们又回到了自己的程序
add ebx, 0x23fb
; 现在 ebx 指向了 .rodata (ds)段的某个地址,不过,本函数是不用
; GCC(GNU Compiler Collection) 4.9 是相当老的编译器了,不要忘记现在 GCC 都出 8 了

mov eax, bd_arg2 (ebp+0xc)
eax = my_arg2
mov bd_suba2(esp+0x4), eax
mov eax, bd_arg1
(ebp+0x8)
eax = my_arg1
mov bd_suba1(esp), eax

<sp> [my_arg1|my_arg2] [........] [bx@main] <bp> [*bp@main] [return@main] [*buffer] [*"SGV....]
🤔所以,我们为什么要多分配这么多无用的东西?浪费了一个双字的空间(虽然 mov subarg2 的时候可能溢出到别的存储单元)

call BD
; BD(my_arg1, my_arg2)

🤔
lea esp, [esp+framesz]
[my_arg1|my_arg2] [........] <sp> [bx@main] <bp> [*bp@main] [return@main] [*buffer] [*"SGV....]

一瞬返回
pop ebx
<sp> <bp> [*bp@main] [return@main] [*buffer] [*"SGV....]

pop ebp
<sp> [return@main] [*buffer] [*"SGV....]

ret
一瞬重置代码指针返回 main+???

最后 main 收拾调用现场

== main+???
add esp, (2*4)
<sp> |*****

对 bd 的调用就完成了(迫真)
lea esp, [esp - framesz] 有什么用呢? 🤔
duangsuse::Echo
lea esp, [esp - framesz] 有什么用呢? 🤔
注意:

1. 是 lea esp, [esp-framesz] 不是 mov esp, [esp-framesz]
1.5. 0x10 是 16 的意思,16 / 4 等于 4
1.6. 我们要分配 4 个本地变量(4 * 4),当然也和我们自己调用子程序的部分有重叠
2. framesz 是 frame size 的意思,有时候你们看到的 sz 表示的 handle 们只是『碰巧』使用 size_t 机器字大小存储而已,所以叫 size,和这个无关
3. 我也不知道为什么 X86 的寄存器就会有 64 位了,因为这个是反汇编出来的程序又是 -m32 multlib 编译肯定有点奇怪啊
4. 我的机器很高级,支持 Debug Registers、FPU 还有 MMX、SSE、AVX 这些 SIMD 处理特性,可以快快的解码视频(迫真
6. 上面那些高等 Vectorize 处理特性,我们要分析的程序都不会用到(悲)

7. 这条指令执行前,esp 指向 0xb4f8,执行后,esp 指向 0xb4e4,下移了 20 个字节(合 5 个字)
8. 这啥 🐔 玩意,我也不知道它要干什么

9. 好吧,告诉你们,就是下移了 4 个字(4 * 4)而已,所谓 20 字节是我瞎 🐔 猜的
10. 所以现在看得懂汇编了吗?
duangsuse::Echo
lea esp, [esp - framesz] 有什么用呢? 🤔
This media is not supported in your browser
VIEW IN TELEGRAM
duangsuse::Echo
🐶 Sticker
(恍然大雾)我居然忘记给 NASM 加正确的 flag 了!
我没告诉 NASM,我是要给 i386 汇编!

难怪会出现机器字大小不兼容,frame 分配冲突错误(大寄存器 vs 小 frame local variable allocation = read/write 读取或覆盖无关变量)的问题


也就是说... 为什么 e** 寄存器都有 64 位?是因为我的机器是 x86_64 的,而且它自己的机器寄存器大小就有 128 位... 🙁
rax 是一个 qword,那 eax 自然有一个 dword... 🙊

真的是太 *** 了....
duangsuse::Echo
🤔 实际上还是得重写 liba.so 真正自己的逻辑,不能直接反汇编然后啥都 OK 了...
事实上:本来应该可以的,但是因为我根本没考虑过 *** 的问题,所以居然还有问题!
QEMU 也不能用,看来得彻底重写了(
duangsuse 一通莫名其妙的 local variable extract & reallocation 居然工作正常了,可喜可贺(又被当成编译器用的 duangsuse....)
🤔 觉得调试符号里会保存关于本地变量偏移的信息,正在迫真查找...