Logo 逆向知识库

Haskell

gereeter/hsdecomp: A decompiler for GHC-compiled Haskell (github.com)

反编译脚本

pip install pyelftools
pip install capstone
git clone https://github.com/gereeter/hsdecomp.git
cd hsdecomp 
python3 setup.py install

一切都错了:像m一样对Haskell进行逆向工程|Kishou Yusa

LANGS | Haskell 趣學指南 (mno2.org)

安装#

GHCup (haskell.org) —— 最好用linux

curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh

ghci#

输入 ghci 可进入交互模式, :l myfunctions.hs 可以加载Haskell脚本,修改 .hs 之后,再 :l myfunctions.hs 或者 :r 都可以重载脚本

:set prompt "ghci> "

把前缀改成 ghci> 不然以后装载模块之后会很长

运算#

ghci> 2 + 15
17
ghci> 49 * 100
4900
ghci> 5 == 5
True
ghci> 5 /= 5
False

函数#

ghci> div 8 2
4
ghci> 8 `div` 2
4

前缀函数转换成中缀函数,用 `` 包裹起来

ghci> succ 2
3
ghci> max 2 3
3
ghci> min 1 2
1

succ函数返回一个数的后继

定义函数#

ghci> doubleMe x = x + x
ghci> doubleMe 2
4
ghci> doubleUs x y = doubleMe x + doubleMe y

也可以写进 doub.hs

doubleMe x = x + x
doubleUs x y = doubleMe x + doubleMe y

然后

$ ghci
ghci> :l doub.hs
ghci> doubleUs 2 3
10

函数声明是没有顺序的,哪个先声明都可以

控制流#

doubleSmallNumber x = if x > 100
                      then x
                      else x*2

必须有else

这样定义也可以:

doubleSmallNumber' x = (if x > 100 then x else x*2) + 1

这里的 ' 是合法标识符,通常用于区分修改了一点点的函数

甚至这样:

conanO'Brien = "It's a-me, Conan O'Brien!"
  1. 函数首字母不能大写
  2. 这个是没有参数的函数,被称作”定义“(或者”名字“),一旦定义,conanO'Brien 就和 "It's a-me, Conan O'Brien!" 完全等价

List入门#

Note: ghci 下,用 let 关键字来定义一个常量。在 ghci 下 let a=1 与在脚本中 a=1 是等价的。

ghci> lostNumbers = [1,2,3,4,5]
ghci> lostNumbers 
[1,2,3,4,5]

不能包含不同类型的元素

ghci> nums = [1,'2',3]

<interactive>:20:9: error:
    • No instance for (Num Char) arising from the literal ‘1’
    • In the expression: 1
      In the expression: [1, '2', 3]
      In an equation for ‘nums’: nums = [1, '2', 3]

字符串 "Hello" 实际上是 ['H','e','l','l','o'] 的语法糖

ghci> str = ['H','e','l','l','o']
ghci> str
"Hello"
ghci> str = "Hello"
ghci> str
"Hello"

所以可以用处理List的方式处理字符串

ghci> "Hel" ++ "lo"
"Hello"
ghci> "H":"ello"

<interactive>:34:5: error:
    • Couldn't match type ‘Char’ with ‘[Char]’
      Expected: [String]
        Actual: String
    • In the second argument of ‘(:)’, namely ‘"ello"’
      In the expression: "H" : "ello"
      In an equation for ‘it’: it = "H" : "ello"
ghci> 'H':"ello"
"Hello"
ghci> ['H','e']++"llo"
"Hello"
ghci> ['H','e']++['l','l','o']
"Hello"

++: 的语法糖,"He"++"llo" 等价于 'H':'e':"llo"

在使用 ++ 运算子处理长字串时要格外小心(对长 List 也是同样),Haskell 会遍历整个 List(++ 符号左边的那个)。在处理较短的字串时问题还不大,但要是在一个 5000 万长度的 List 上追加元素,那可得执行好一会儿了。所以说,用 : 运算子往一个 List 前端插入元素会是更好的选择。

索引获取元素、数组长度判断

ghci> "Hello" !! 2
'l'
ghci> [2,3,4] > [4,4,2,2]
False

![[image-20240612104447969.png]]

ghci> list = "abcdef"
ghci> head list
'a'
ghci> tail list
"bcdef"
ghci> init list
"abcde"
ghci> last list
'f'

危险:如果 list 是空,就会报错,先用 null 或者 length 判断是否为空

ghci> null list
False
ghci> null []
True
ghci> length list
6
ghci> length []
0

reverse 反转、 take 拿取n个元素、 drop 扔掉前n个元素

ghci> reverse list
"fedcba"
ghci> take 4 list
"abcd"
ghci> drop 2 list
"cdef"

maximum 获取最大, minimum 获取最小

ghci> list = [1,2,3,4,5]
ghci> maximum list
5
ghci> minimum list
1

sum 求和, product 求积

ghci> sum list
15
ghci> product list
120

elem 判断一个元素是否在 list 中,通常用中缀

ghci> elem 'a' "abcde"
True
ghci> 'a' `elem` "abcde"
True

使用 Range#

ghci> [1..20]
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
ghci> ['a'..'z']
"abcdefghijklmnopqrstuvwxyz"
ghci> ['a'..'z']++['A'..'Z']
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

指定步长

ghci> [2,4..20]
[2,4,6,8,10,12,14,16,18,20]
ghci> [3,6..20]
[3,6,9,12,15,18]

反过来

ghci> [20,19..1]
[20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1]

最好不要用浮点数!

ghci> [0.1,0.2..1]
[0.1,0.2,0.30000000000000004,0.4,0.5,0.6,0.7000000000000001,0.8,0.9,1.0]

如果不表明上限,就可以拿到无限长的 List,怎么取到 24 个 13 的倍数呢?

ghci> [13,26..24*13]
[13,26,39,52,65,78,91,104,117,130,143,156,169,182,195,208,221,234,247,260,273,286,299,312]
ghci> take 24 [13,26..]
[13,26,39,52,65,78,91,104,117,130,143,156,169,182,195,208,221,234,247,260,273,286,299,312]

由于 Haskell 是惰性的,它不会对无限长度的 List 求值,否则会没完没了的。它会等着,看你会从它那儿取多少。

repeat 接受一个值作参数,返回一个包含该值的无限List

ghci> repeat 5
[5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,....

replicate 可以获取指定长度相同元素的list

ghci> take 5 (repeat 3)
[3,3,3,3,3]
ghci> replicate 5 3
[3,3,3,3,3]

List Comprehension#

Haskell 魔法!

![[image-20240612120129046.png]]

Haskell中也有类似的表示,称为 “过滤” —— 从一个 List 中筛选出符合条件的的操作

ghci> [ x*2 | x <- [1..10]]
[2,4,6,8,10,12,14,16,18,20]

再加一些限制条件

ghci> [ x*2 | x <- [1..10], x*2 >= 12]
[12,14,16,18,20]
ghci> [ x*2 | x <- [1..10], odd x]
[2,6,10,14,18]
ghci> [ x*2 | x <- [1..50], x `mod` 7 == 3]
[6,20,34,48,62,76,90]

更高级的使用#

boomBangs xs = [ if x < 10 then "BANG!" else "BOOM!" | x <- xs, odd x]
ghci> boomBangs [7..13]
["BANG!","BANG!","BOOM!","BOOM!"]

多个限制条件#

ghci> [x | x <- [1..20], not (x `elem` [5..9]), x /= 2, x /= 12]
[1,3,4,10,11,13,14,15,16,17,18,19,20]

多个 List#

ghci> [x*y | x <- [2..5], y <- [1..3]]
[2,4,6,3,6,9,4,8,12,5,10,15]

自己实现 length#

ghci> list = "HelloWorld!"
ghci> length list
11
ghci> length' xs = sum [1| _ <- xs]
ghci> length' list
11

_ 表示我们并不关心从 List 中取什么值,与其弄个永远不用的变量,不如直接一个 _

嵌套 List Comprehension#

ghci> let xxs = [[1,3,5,2,3,1,2,4,5],[1,2,3,4,5,6,7,8,9],[1,2,4,2,1,6,3,1,3,2,3,6]]
ghci> [[x1 | x1 <- x, even x1] | x <- xxs]
[[2,2,4],[2,4,6,8],[2,4,2,6,2,6]]

Tuple#

类似Python的Tuple,但是一个 list 只能有同一种类型的 Tuple

ghci> [(1,2),(3,4)]
[(1,2),(3,4)]
ghci> [(1,2),(3,4,5)]
error
ghci> [(1,2),(3,"a")]
error

获取元素(仅适用于二元元组)

ghci> t = (1,2)
ghci> fst t
1
ghci> snd t
2

zip 自动交叉分配元组

ghci> zip [1,2,3,4,5] ['a','b','c','d','e']
[(1,'a'),(2,'b'),(3,'c'),(4,'d'),(5,'e')]

因为惰性求值,所以无限长的也可以处理

ghci> zip [1..] [1..5]
[(1,1),(2,2),(3,3),(4,4),(5,5)]

实验:获取指定三角形#

取得所有三边长度皆为整数且小于等于 10,周长为 24 的直角三角形

ghci> [(x,y,z)|x<-[1..10],y<-[1..10],z<-[1..10],x+y+z==24,x+y>z,x+z>y,y+z>x, x^2+y^2==z^2, x<z, y<z, x>=y]
[(8,6,10)]

得到正确结果!这便是函数式编程语言的一般思路:先取一个初始的集合并将其变形,执行过滤条件,最终取得正确的结果。