Logo 逆向知识库

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 减去对应的值