hsdecomp修复
hsdecomp修复日志#
oldkingOK/hsdecomp: A decompiler for GHC-compiled Haskell (github.com)
pip install capstone pyelftools
样例#
文件 hw.hs
main :: IO ()
main = do
putStrLn "Please enter your name:"
name <- getLine
putStrLn ("Hello, " ++ name ++ "!")
ghc hw.hs -o hw
python3 runner.py hw
报错
Error in processing code at Main_main1_info
Error: 'UnknownValue' object has no attribute 'untagged'
Error Location: 240
Disassembly:
lea rax, [rbp - 8]
cmp rax, r15
jb 0x407ef7
mov qword ptr [rbp - 8], 0x407f18
lea rdi, [rip + 0xf7122]
lea rsi, [rip + 0xed6c1]
lea r14, [rip + 0xf0962]
add rbp, -8
jmp 0x439690
Main_main_closure = \state# -> (\state# -> !!ERROR!!) state#
用 python3 runner.py ../hw --verbose
可以得到更多输出
Main_main_closure 是入口,hsdecomp 第一步找的就是这个
![[image-20240613133732535.png]]
看起来,会先把下一步去的地方先扔进栈里,然后减 rbp 到放 loc 的值
![[image-20240613135004396.png]]
这里有个判断函数类型的,有个判断版本,应该就是这的原因了,现在 version 是 (9,4,8)
$ ghc --version
The Glorious Glasgow Haskell Compilation System, version 9.4.8
![[image-20240613135315451.png]]
函数前面的数据是函数的特征值,翻翻 ghc 源码,看看特征值 ghc/ghc(github.com)
搜索最大的 pppppppp
找到 rts/include/rts/storage/FunTypes.h
:
![[image-20240613140841026.png|200]]
同目录下的 rts/include/rts/storage/ClosureTypes.h
对应了另一张表
继续调
![[image-20240613141856653.png]]
![[image-20240613141937366.png]]
这里应该对应了取三个参数,但是奇怪的是,拿到的都是 Unknown
随便试试#
# ipython3
In [1]: %run hsdecomp/show.py
In [2]: demangle('base_GHCziIOziEncodingziUTF32_zdwutf32bezudecode_info')
Out[2]: 'base_GHC.IO.Encoding.UTF32_$wutf32bezudecode_info'
源码:
def demangle(ident):
table = {'L': '(', 'R': ')', 'M': '[', 'N': ']', 'C': ':', 'Z': 'Z', 'a': '&', 'b': '|', 'c': '^', 'd': '$', 'e': '=', 'g': '>', 'h': '#', 'i': '.', 'l': '<', 'm': '-', 'n': '!', 'p': '+', 'q': '\'', 'r': '\\', 's': '/', 't': '*', 'v': '%', 'z': 'z'}
out = ""
i = 0
while i < len(ident):
if ident[i] == 'z' or ident[i] == 'Z':
if ident[i+1] in table:
out += table[ident[i+1]]
i += 2
continue
out += ident[i]
i += 1
return out
找到一个在线demangle,支持Haskell的,好像效果稍微好一点
blukat29/demangle-online —— 需要手动 build,指令在 run.sh
里
base_GHCziIOziEncodingziUTF32_zdwutf32bezudecode_info
base_GHC.IO.Encoding.UTF32_$wutf32be_decode_info
草,原来是调用了原生的Haskell接口
-- ghc -o demangler demangler.hs -static -v -package ghc-boot
import Data.Char
import qualified GHC.Utils.Encoding as Encoding
main :: IO ()
main = do
inp <- getContents
let encoded = lines inp
decoded = map Encoding.zDecodeString encoded
mapM_ putStrLn decoded
但是调用了一个动态库导致运行出错,手动编译一下
hackage | 镜像站使用帮助 | 清华大学开源软件镜像站
记得关代理
cabal update
cabal install encoding
cabal install --lib encoding
cabal install zenc
ghc -o demangler demangler.hs -static -v -package ghc-boot
# 编译巨慢,慢慢等
复制到 hsdecomp/bin
修改代码
逆向#
ghczmprim_GHCziCString_unpackCStringzh_info 就是解压字符串的地方
ghczmprim_GHCziCString_unpackAppendCStringzh_info 连接字符串的时候,字符串会出现在这
总结#
Main_main_closure
是程序入口,指向 Main_main1_info
调用函数的方式是 jmp
,调用之前会把参数按顺序放进寄存器,顺序是
R14 -> RSI -> RDI -> R8 -> R9
多的参数会放进栈里,下一个 jmp
的地址会提前装进参数寄存器/栈
![[image-20240613160930642.png]]
用了多少栈,jmp
之前会把 rbp
减去对应的值